diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9c46442..a707ba34b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +#2.0.3 +- New coupon type: Free product if selected products are in the cart. + #2.0.2 - Coupon UI has been redesigned. - New coupon types: diff --git a/core/lib/Thelia/Action/Coupon.php b/core/lib/Thelia/Action/Coupon.php index b8e2a7553..4dc708394 100644 --- a/core/lib/Thelia/Action/Coupon.php +++ b/core/lib/Thelia/Action/Coupon.php @@ -109,6 +109,17 @@ class Coupon extends BaseAction implements EventSubscriberInterface $this->createOrUpdateCondition($modelCoupon, $event); } + /** + * Clear all coupons in session. + */ + public function clearAllCoupons() + { + // Tell coupons to clear any data they may have stored + $this->couponManager->clear(); + + $this->request->getSession()->setConsumedCoupons(array()); + } + /** * Occurring when a Coupon condition is about to be consumed * @@ -139,21 +150,22 @@ class Coupon extends BaseAction implements EventSubscriberInterface $consumedCoupons[$event->getCode()] = $event->getCode(); $this->request->getSession()->setConsumedCoupons($consumedCoupons); - - $totalDiscount = $this->couponManager->getDiscount(); - - $this->request - ->getSession() - ->getCart() - ->setDiscount($totalDiscount) - ->save(); - $this->request - ->getSession() - ->getOrder() - ->setDiscount($totalDiscount) - ; } - } + + $totalDiscount = $this->couponManager->getDiscount(); + + $this->request + ->getSession() + ->getCart() + ->setDiscount($totalDiscount) + ->save(); + + $this->request + ->getSession() + ->getOrder() + ->setDiscount($totalDiscount) + ; + } } $event->setIsValid($isValid); @@ -339,7 +351,8 @@ class Coupon extends BaseAction implements EventSubscriberInterface } } - $this->request->getSession()->setConsumedCoupons(array()); + // Clear all coupons. + $event->getDispatcher()->dispatch(TheliaEvents::COUPON_CLEAR_ALL); } /** @@ -368,6 +381,7 @@ class Coupon extends BaseAction implements EventSubscriberInterface TheliaEvents::COUPON_CREATE => array("create", 128), TheliaEvents::COUPON_UPDATE => array("update", 128), TheliaEvents::COUPON_CONSUME => array("consume", 128), + TheliaEvents::COUPON_CLEAR_ALL => array("clearAllCoupons", 128), TheliaEvents::COUPON_CONDITION_UPDATE => array("updateCondition", 128), TheliaEvents::ORDER_SET_POSTAGE => array("testFreePostage", 132), TheliaEvents::ORDER_BEFORE_PAYMENT => array("afterOrder", 128), diff --git a/core/lib/Thelia/Condition/Implementation/StartDate.php b/core/lib/Thelia/Condition/Implementation/StartDate.php index 251ba395b..2524e1b6d 100644 --- a/core/lib/Thelia/Condition/Implementation/StartDate.php +++ b/core/lib/Thelia/Condition/Implementation/StartDate.php @@ -55,6 +55,8 @@ class StartDate extends ConditionAbstract */ public function setValidatorsFromForm(array $operators, array $values) { + $this->checkComparisonOperatorValue($operators, self::START_DATE); + if (! isset($values[self::START_DATE])) { $values[self::START_DATE] = time(); } diff --git a/core/lib/Thelia/Config/I18n/en_US.php b/core/lib/Thelia/Config/I18n/en_US.php index 6b7e79092..6b92a7fb6 100644 --- a/core/lib/Thelia/Config/I18n/en_US.php +++ b/core/lib/Thelia/Config/I18n/en_US.php @@ -50,6 +50,7 @@ return array( 'Cart item count is' => 'Cart item count is', 'Cart total amount' => 'Cart total amount', 'Cart total amount is' => 'Cart total amount is', + 'Catalog' => 'Catalog', 'Category title *' => 'Category title *', 'Cellphone' => 'Cellphone', 'Chapo' => 'Chapo', @@ -111,14 +112,18 @@ return array( 'First Name' => 'First Name', 'Firstname' => 'Firstname', 'Fixed Amount Discount' => 'Fixed Amount Discount', + 'Fixed amount discount for selected attribute values' => 'Fixed amount discount for selected attribute values', 'Fixed amount discount for selected categories' => 'Fixed amount discount for selected categories', 'Fixed amount discount for selected products' => 'Fixed amount discount for selected products', + 'Folder' => 'Folder', 'Folder title *' => 'Folder title *', 'For one ore more customers' => 'For one ore more customers', + 'Free product when buying one or more selected products' => 'Free product when buying one or more selected products', 'Full Name' => 'Full Name', 'Greater than' => 'Greater than', 'Greater than or equals' => 'Greater than or equals', 'HTML Message' => 'HTML Message', + 'Home' => 'Home', 'Host' => 'Host', 'I would like to receive the newsletter or the latest news.' => 'I would like to receive the newsletter or the latest news.', 'ISO 4217 code *' => 'ISO 4217 code *', @@ -190,6 +195,7 @@ return array( 'Password *' => 'Password *', 'Password confirmation' => 'Password confirmation', 'Per customer' => 'Per customer', + 'Percentage discount for selected attribute values' => 'Percentage discount for selected attribute values', 'Percentage discount for selected categories' => 'Percentage discount for selected categories', 'Percentage discount for selected products' => 'Percentage discount for selected products', 'Percentage of the product price' => 'Percentage of the product price', @@ -199,8 +205,12 @@ return array( 'Please enter your email address' => 'Please enter your email address', 'Please enter your password' => 'Please enter your password', 'Please select a category' => 'Please select a category', + 'Please select an attribute' => 'Please select an attribute', + 'Please select at least one attribute value' => 'Please select at least one attribute value', 'Please select at least one category' => 'Please select at least one category', 'Please select at least one product' => 'Please select at least one product', + 'Please select the category of the offred product' => 'Please select the category of the offred product', + 'Please select the offered product' => 'Please select the offered product', 'Please specify either \'path\' or \'file\' parameter in {url} function.' => 'Please specify either \'path\' or \'file\' parameter in {url} function.', 'Port' => 'Port', 'Post Scriptum' => 'Post Scriptum', @@ -288,10 +298,13 @@ return array( 'This category is online.' => 'This category is online.', 'This condition is always true' => 'This condition is always true', 'This content is online.' => 'This content is online.', + 'This coupon adds a free product to the cart if one of the selected products is in the cart.' => 'This coupon adds a free product to the cart if one of the selected products is in the cart.', 'This coupon does not exists' => 'This coupon does not exists', 'This coupon subtracts from the order total a percentage of the price of each product which belongs to the selected categories. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon subtracts from the order total a percentage of the price of each product which belongs to the selected categories. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', + 'This coupon subtracts from the order total the specified percentage of each product price which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon subtracts from the order total the specified percentage of each product price which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', 'This coupon subtracts from the order total the specified percentage of each selected product price. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon subtracts from the order total the specified percentage of each selected product price. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', 'This coupon subtracts the specified amount from the order total for each product which belongs to the selected categories. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon subtracts the specified amount from the order total for each product which belongs to the selected categories. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', + 'This coupon subtracts the specified amount from the order total for each product which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon subtracts the specified amount from the order total for each product which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', 'This coupon subtracts the specified amount from the order total for each selected product. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon subtracts the specified amount from the order total for each selected product. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', 'This coupon will offert a flat percentage off a shopper\'s entire order (not applied to shipping costs or tax rates). If the discount is greater than the total order corst, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon will offert a flat percentage off a shopper\'s entire order (not applied to shipping costs or tax rates). If the discount is greater than the total order corst, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', 'This coupon will subtracts a set amount from the total cost of an order. If the discount is greater than the total order corst, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'This coupon will subtracts a set amount from the total cost of an order. If the discount is greater than the total order corst, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', @@ -324,7 +337,6 @@ return array( 'Valid only from %date% to the coupon expiration date' => 'Valide à partir du %date% jusqu\'à la date d\'expiration', 'Value' => 'Value', 'Value %val for Discount Amount is invalid. Please enter a positive value.' => 'Value %val for Discount Amount is invalid. Please enter a positive value.', - 'Value %val for Disount Amount is invalid. Please enter a positive value.' => 'Value %val for Disount Amount is invalid. Please enter a positive value.', 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.' => 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.', 'Value *' => 'Value *', 'Warnings' => 'Warnings', diff --git a/core/lib/Thelia/Config/I18n/fr_FR.php b/core/lib/Thelia/Config/I18n/fr_FR.php index a9f14dbee..9d73c6420 100644 --- a/core/lib/Thelia/Config/I18n/fr_FR.php +++ b/core/lib/Thelia/Config/I18n/fr_FR.php @@ -50,6 +50,7 @@ return array( 'Cart item count is' => 'Le nombre d\'articles dans le panier est', 'Cart total amount' => 'Montant total du panier', 'Cart total amount is' => 'Le total du panier est', + 'Catalog' => 'Catalogue', 'Category title *' => 'Titre de la catégorie *', 'Cellphone' => 'Numéro de portable', 'Chapo' => 'Chapeau', @@ -111,14 +112,18 @@ return array( 'First Name' => 'Prénom', 'Firstname' => 'Prénom', 'Fixed Amount Discount' => 'Remise d\'un montant fixe', + 'Fixed amount discount for selected attribute values' => 'Remise constante pour certaines valeurs de déclinaison', 'Fixed amount discount for selected categories' => 'Remise fixe pour certaines catégories', 'Fixed amount discount for selected products' => 'Remise fixe pour certains produits', + 'Folder' => 'Dossier', 'Folder title *' => 'Titre du dossier *', 'For one ore more customers' => 'Valable pour un ou plusieurs clients', + 'Free product when buying one or more selected products' => 'Un produit offert pour l\'achat d\'un ou plusieurs produits', 'Full Name' => 'Nom complet', 'Greater than' => 'Supérieur à', 'Greater than or equals' => 'Supérieur ou égal à', 'HTML Message' => 'Message au format HTML', + 'Home' => 'Accueil', 'Host' => 'Nom de l\'hôte', 'I would like to receive the newsletter or the latest news.' => 'Je souhaite recevoir la lettre d\'information ou les dernières actualités.', 'ISO 4217 code *' => 'Code ISO 4217 *', @@ -190,6 +195,7 @@ return array( 'Password *' => 'Mot de passe *', 'Password confirmation' => 'Confirmation du mot de passe.', 'Per customer' => 'Par client', + 'Percentage discount for selected attribute values' => 'Remise en pourcentage pour certaines valeurs de déclinaison', 'Percentage discount for selected categories' => 'Remise en pourcentage pour certaines catégories', 'Percentage discount for selected products' => 'Remise en pourcentage pour certains produits', 'Percentage of the product price' => 'Pourcentage du prix du produit', @@ -199,8 +205,12 @@ return array( 'Please enter your email address' => 'Renseignez votre adresse mail', 'Please enter your password' => 'Entrez votre mot de passe.', 'Please select a category' => 'Merci de choisir une catégorie', + 'Please select an attribute' => 'Merci de choisir une déclinaison', + 'Please select at least one attribute value' => 'Merci de choisir au moins une valeur de déclinaison', 'Please select at least one category' => 'Merci de choisir au moins une catégorie.', 'Please select at least one product' => 'Merci de choisir au moins un produit', + 'Please select the category of the offred product' => 'Merci de choisir la catégorie du produit offert', + 'Please select the offered product' => 'Merci de choisir le produit offert', 'Please specify either \'path\' or \'file\' parameter in {url} function.' => 'Veuillez spécifier soit le paramètre \'chemin\' ou \'fichier\' dans la fonction {url}', 'Port' => 'Port', 'Post Scriptum' => 'Post-scriptum', @@ -288,10 +298,13 @@ return array( 'This category is online.' => 'Cette catégorie est en ligne.', 'This condition is always true' => 'Cette condition est troujours vérifiée', 'This content is online.' => 'Ce contenu est en ligne.', + 'This coupon adds a free product to the cart if one of the selected products is in the cart.' => 'Ce coupon ajoute un produit gratuit (sous forme de remise) au panier, si un des produits indiqués est présent dans le panier.', 'This coupon does not exists' => 'Ce code promo n\'existe pas', 'This coupon subtracts from the order total a percentage of the price of each product which belongs to the selected categories. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire du total de la commande le pourcentage indiqué du prix de chacun des produits appartenant aux catégories sélectionnées. Si la remise est supérieure au total de la commande, seul le port sera dû, à moins que le code promo n\'offre aussi le port.', + 'This coupon subtracts from the order total the specified percentage of each product price which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire du total de la commande le pourcentage indiqué du prix de chacun des produits ayant au moins une des valeurs de déclinaisons indiquées. Si la remise est supérieure au total de la commande, seul le port sera dû, à moins que le code promo n\'offre aussi le port.', 'This coupon subtracts from the order total the specified percentage of each selected product price. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire du total de la commande le pourcentage indiqué du prix de chacun des produits sélectionnés. Si la remise est supérieure au total de la commande, seul le port sera dû, à moins que le code promo n\'offre aussi le port.', 'This coupon subtracts the specified amount from the order total for each product which belongs to the selected categories. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire le montant indiqué du total de la commande pour chacun des produits appartenant aux catégories sélectionnées. Si la remise est supérieure au total de la commande, seul le port sera dû, à moins que le code promo n\'offre aussi le port.', + 'This coupon subtracts the specified amount from the order total for each product which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire le montant indiqué du total de la commande pour chacun des produits ayant au moins une des valeurs de déclinaisons indiquées. Si la remise est supérieure au total de la commande, seul le port sera dû, à moins que le code promo n\'offre aussi le port.', 'This coupon subtracts the specified amount from the order total for each selected product. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire le montant indiqué du total de la commande pour chacun des produits sélectionnés. Si la remise est supérieure au total de la commande, seul le port sera dû, à moins que le code promo n\'offre aussi le port.', 'This coupon will offert a flat percentage off a shopper\'s entire order (not applied to shipping costs or tax rates). If the discount is greater than the total order corst, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire le pourcentage indiqué du total de la commande, hors frais de port et taxes. Si la remise est supérieur au total de la commande, seul le port sera facturé, à moins que le code promo n\'offre aussi les frais port.', 'This coupon will subtracts a set amount from the total cost of an order. If the discount is greater than the total order corst, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.' => 'Ce code promo retire le montant indiqué du total de la commande, hors frais de port et taxes. Si la remise est supérieur au total de la commande, seul le port sera facturé, à moins que le code promo n\'offre aussi les frais port.', @@ -324,7 +337,6 @@ return array( 'Valid only from %date% to the coupon expiration date' => 'Valide à partir de %date% jusqu\'à la date d\'expoiration', 'Value' => 'Valeur', 'Value %val for Discount Amount is invalid. Please enter a positive value.' => 'La valeur %val pour le montant de la remise est invalide. Merci d\'indiquer une valeur positive.', - 'Value %val for Disount Amount is invalid. Please enter a positive value.' => 'La valeur %val pour le montant de la remise est invalide. Merci d\'indiquer une valeur positive.', 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.' => 'La valeur %val pour le pourcentage de remise est invalide. Merci d\'indiquer un nombre entre 1 et 100', 'Value *' => 'Valeur *', 'Warnings' => 'Avertissements', diff --git a/core/lib/Thelia/Config/Resources/coupon.xml b/core/lib/Thelia/Config/Resources/coupon.xml index f08746d22..06e8f7bb6 100644 --- a/core/lib/Thelia/Config/Resources/coupon.xml +++ b/core/lib/Thelia/Config/Resources/coupon.xml @@ -51,6 +51,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Core/Event/Cart/CartEvent.php b/core/lib/Thelia/Core/Event/Cart/CartEvent.php index 1c719c0ae..d6a100c16 100644 --- a/core/lib/Thelia/Core/Event/Cart/CartEvent.php +++ b/core/lib/Thelia/Core/Event/Cart/CartEvent.php @@ -14,6 +14,7 @@ namespace Thelia\Core\Event\Cart; use Thelia\Core\Event\ActionEvent; use Thelia\Model\Cart; +use Thelia\Model\CartItem; class CartEvent extends ActionEvent { @@ -31,7 +32,8 @@ class CartEvent extends ActionEvent } /** - * @param mixed $append + * @param bool $append + * @return CartEvent */ public function setAppend($append) { @@ -41,7 +43,7 @@ class CartEvent extends ActionEvent } /** - * @return mixed + * @return bool */ public function getAppend() { @@ -49,7 +51,8 @@ class CartEvent extends ActionEvent } /** - * @param mixed $cartItem + * @param CartItem $cartItem + * @return CartEvent */ public function setCartItem($cartItem) { @@ -59,7 +62,7 @@ class CartEvent extends ActionEvent } /** - * @return mixed + * @return CartItem */ public function getCartItem() { @@ -67,7 +70,8 @@ class CartEvent extends ActionEvent } /** - * @param mixed $newness + * @param bool $newness + * @return CartEvent */ public function setNewness($newness) { @@ -77,7 +81,7 @@ class CartEvent extends ActionEvent } /** - * @return mixed + * @return bool */ public function getNewness() { @@ -85,7 +89,8 @@ class CartEvent extends ActionEvent } /** - * @param mixed $product + * @param int $product the product ID + * @return CartEvent */ public function setProduct($product) { @@ -95,7 +100,7 @@ class CartEvent extends ActionEvent } /** - * @return mixed + * @return int the product ID */ public function getProduct() { @@ -103,7 +108,8 @@ class CartEvent extends ActionEvent } /** - * @param mixed $productSaleElementsId + * @param int $productSaleElementsId + * @return CartEvent */ public function setProductSaleElementsId($productSaleElementsId) { @@ -113,7 +119,7 @@ class CartEvent extends ActionEvent } /** - * @return mixed + * @return int */ public function getProductSaleElementsId() { @@ -121,7 +127,8 @@ class CartEvent extends ActionEvent } /** - * @param mixed $quantity + * @param int $quantity + * @return CartEvent */ public function setQuantity($quantity) { @@ -131,7 +138,7 @@ class CartEvent extends ActionEvent } /** - * @return mixed + * @return int */ public function getQuantity() { diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 69c12fb73..571604b69 100644 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -464,6 +464,11 @@ final class TheliaEvents */ const COUPON_CONSUME = "action.consume_coupon"; + /** + * Sent when all coupons in the current session should be cleared + */ + const COUPON_CLEAR_ALL = "action.clear_all_coupon"; + /** * Sent just before an attempt to use a Coupon */ diff --git a/core/lib/Thelia/Coupon/BaseFacade.php b/core/lib/Thelia/Coupon/BaseFacade.php index 5794df673..25a177996 100644 --- a/core/lib/Thelia/Coupon/BaseFacade.php +++ b/core/lib/Thelia/Coupon/BaseFacade.php @@ -21,6 +21,7 @@ use Thelia\Core\HttpFoundation\Request; use Thelia\Core\Template\ParserInterface; use Thelia\Core\Template\TemplateHelper; use Thelia\Model\AddressQuery; +use Thelia\Model\Country; use Thelia\Model\Coupon; use Thelia\Model\CouponQuery; use Thelia\Cart\CartTrait; @@ -149,6 +150,14 @@ class BaseFacade implements FacadeInterface return $total; } + /** + * @return Country the delivery country + */ + public function getDeliveryCountry() + { + return $this->getContainer()->get('thelia.taxEngine')->getDeliveryCountry(); + } + /** * Return the Checkout currency EUR|USD * diff --git a/core/lib/Thelia/Coupon/CouponFactory.php b/core/lib/Thelia/Coupon/CouponFactory.php index 40a4f0483..f1b87ae19 100644 --- a/core/lib/Thelia/Coupon/CouponFactory.php +++ b/core/lib/Thelia/Coupon/CouponFactory.php @@ -69,7 +69,7 @@ class CouponFactory } // Check coupon usage count - if ($couponModel->getUsagesLeft($this->facade->getCustomer()->getId()) <= 0) { + if (! $couponModel->isUsageUnlimited() && $couponModel->getUsagesLeft($this->facade->getCustomer()->getId()) <= 0) { throw new CouponNoUsageLeftException($couponCode); } diff --git a/core/lib/Thelia/Coupon/CouponManager.php b/core/lib/Thelia/Coupon/CouponManager.php index ecb491673..e92aaaa86 100644 --- a/core/lib/Thelia/Coupon/CouponManager.php +++ b/core/lib/Thelia/Coupon/CouponManager.php @@ -58,14 +58,15 @@ class CouponManager /** * Get Discount for the given Coupons - * * @api * @return float checkout discount */ public function getDiscount() { $discount = 0.00; + $coupons = $this->facade->getCurrentCoupons(); + if (count($coupons) > 0) { $couponsKept = $this->sortCoupons($coupons); @@ -73,6 +74,7 @@ class CouponManager // Just In Case test $checkoutTotalPrice = $this->facade->getCartTotalTaxPrice(); + if ($discount >= $checkoutTotalPrice) { $discount = $checkoutTotalPrice; } @@ -83,6 +85,9 @@ class CouponManager /** * Check if there is a Coupon removing Postage + * + * @param Order $order the order for which we have to check if postage is free + * * @return bool */ public function isCouponRemovingPostage(Order $order) @@ -266,6 +271,19 @@ class CouponManager return $this->availableConditions; } + /** + * Clear all data kept by coupons + */ + public function clear() + { + $coupons = $this->facade->getCurrentCoupons(); + + /** @var CouponInterface $coupon */ + foreach ($coupons as $coupon) { + $coupon->clear(); + } + } + /** * Decrement this coupon quantity * @@ -278,57 +296,61 @@ class CouponManager */ public function decrementQuantity(Coupon $coupon, $customerId = null) { - $ret = false; + if ($coupon->isUsageUnlimited()) { + $ret = true; + } else { + $ret = false; - try { + try { - $usageLeft = $coupon->getUsagesLeft($customerId); + $usageLeft = $coupon->getUsagesLeft($customerId); - if ($usageLeft > 0) { + if ($usageLeft > 0) { - // If the coupon usage is per user, add an entry to coupon customer usage count table - if ($coupon->getPerCustomerUsageCount()) { + // If the coupon usage is per user, add an entry to coupon customer usage count table + if ($coupon->getPerCustomerUsageCount()) { - if (null == $customerId) { - throw new \LogicException("Customer should not be null at this time."); - } + if (null == $customerId) { + throw new \LogicException("Customer should not be null at this time."); + } - $ccc = CouponCustomerCountQuery::create() - ->filterByCouponId($coupon->getId()) - ->filterByCustomerId($customerId) - ->findOne() - ; + $ccc = CouponCustomerCountQuery::create() + ->filterByCouponId($coupon->getId()) + ->filterByCustomerId($customerId) + ->findOne() + ; - if ($ccc === null) { - $ccc = new CouponCustomerCount(); + if ($ccc === null) { + $ccc = new CouponCustomerCount(); + + $ccc + ->setCustomerId($customerId) + ->setCouponId($coupon->getId()) + ->setCount(0); + } + + $newCount = 1 + $ccc->getCount(); $ccc - ->setCustomerId($customerId) - ->setCouponId($coupon->getId()) - ->setCount(0); + ->setCount($newCount) + ->save() + ; + + $ret = $usageLeft - $newCount; + } else { + $usageLeft--; + + $coupon->setMaxUsage($usageLeft); + + $coupon->save(); + + $ret = $usageLeft; } - - $newCount = 1 + $ccc->getCount(); - - $ccc - ->setCount($newCount) - ->save() - ; - - $ret = $usageLeft - $newCount; - } else { - $usageLeft--; - - $coupon->setMaxUsage($usageLeft); - - $coupon->save(); - - $ret = $usageLeft; } + } catch (\Exception $ex) { + // Just log the problem. + Tlog::getInstance()->addError(sprintf("Failed to decrement coupon %s: %s", $coupon->getCode(), $ex->getMessage())); } - } catch (\Exception $ex) { - // Just log the problem. - Tlog::getInstance()->addError(sprintf("Failed to decrement coupon %s: %s", $coupon->getCode(), $ex->getMessage())); } return $ret; diff --git a/core/lib/Thelia/Coupon/FacadeInterface.php b/core/lib/Thelia/Coupon/FacadeInterface.php index 8a4d004a3..2201dfb69 100644 --- a/core/lib/Thelia/Coupon/FacadeInterface.php +++ b/core/lib/Thelia/Coupon/FacadeInterface.php @@ -18,6 +18,7 @@ use Symfony\Component\Translation\TranslatorInterface; use Thelia\Condition\ConditionEvaluator; use Thelia\Core\HttpFoundation\Request; use Thelia\Core\Template\ParserInterface; +use Thelia\Model\Country; use Thelia\Model\Coupon; /** @@ -51,6 +52,11 @@ interface FacadeInterface */ public function getDeliveryAddress(); + /** + * @return Country the delivery country + */ + public function getDeliveryCountry(); + /** * Return an Customer a CouponManager can process * diff --git a/core/lib/Thelia/Coupon/Type/AbstractRemove.php b/core/lib/Thelia/Coupon/Type/AbstractRemove.php new file mode 100644 index 000000000..4cf89c3d6 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/AbstractRemove.php @@ -0,0 +1,128 @@ + + */ +abstract class AbstractRemove extends CouponAbstract implements AmountAndPercentageCouponInterface +{ + /** + * Set the value of specific coupon fields. + * + * @param Array $effects the Coupon effects params + */ + abstract public function setFieldsValue($effects); + + /** + * Get the discount for a specific cart item. + * + * @param CartItem $cartItem the cart item + * @return float the discount value + */ + abstract public function getCartItemDiscount($cartItem); + + /** + * @inheritdoc + */ + public function set( + FacadeInterface $facade, + $code, + $title, + $shortDescription, + $description, + array $effects, + $isCumulative, + $isRemovingPostage, + $isAvailableOnSpecialOffers, + $isEnabled, + $maxUsage, + \DateTime $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ) + { + parent::set( + $facade, $code, $title, $shortDescription, $description, $effects, + $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ); + + $this->setFieldsValue($effects); + + return $this; + } + /** + * @inheritdoc + */ + public function exec() + { + // This coupon subtracts the specified amount from the order total + // for each product of the selected categories. + $discount = 0; + + $cartItems = $this->facade->getCart()->getCartItems(); + + /** @var CartItem $cartItem */ + foreach ($cartItems as $cartItem) { + if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { + $categories = $cartItem->getProduct()->getCategories(); + + /** @var Category $category */ + foreach ($categories as $category) { + + if (in_array($category->getId(), $this->category_list)) { + $discount += $this->getCartItemDiscount($cartItem); + + break; + } + } + } + } + + return $discount; + } + + /** + * @inheritdoc + */ + public function drawBaseBackOfficeInputs($templateName, $otherFields) + { + return $this->facade->getParser()->render($templateName, $otherFields); + } + + /** + * @inheritdoc + */ + public function getBaseFieldList($otherFields) + { + return array_merge($otherFields); + } + + /** + * @inheritdoc + */ + public function checkBaseCouponFieldValue($fieldName, $fieldValue) + { + return $fieldValue; + } +} diff --git a/core/lib/Thelia/Coupon/Type/AbstractRemoveOnAttributeValues.php b/core/lib/Thelia/Coupon/Type/AbstractRemoveOnAttributeValues.php new file mode 100644 index 000000000..3b54ce0e7 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/AbstractRemoveOnAttributeValues.php @@ -0,0 +1,180 @@ + + */ +abstract class AbstractRemoveOnAttributeValues extends CouponAbstract implements AmountAndPercentageCouponInterface +{ + const ATTRIBUTES_AV_LIST = 'attribute_avs'; + const ATTRIBUTE = 'attribute_id'; + + public $attributeAvList = array(); + public $attribute = 0; + + /** + * Set the value of specific coupon fields. + * @param Array $effects the Coupon effects params + */ + abstract public function setFieldsValue($effects); + + /** + * Get the discount for a specific cart item. + * + * @param CartItem $cartItem the cart item + * @return float the discount value + */ + abstract public function getCartItemDiscount($cartItem); + + /** + * @inheritdoc + */ + public function set( + FacadeInterface $facade, + $code, + $title, + $shortDescription, + $description, + array $effects, + $isCumulative, + $isRemovingPostage, + $isAvailableOnSpecialOffers, + $isEnabled, + $maxUsage, + \DateTime $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ) + { + parent::set( + $facade, $code, $title, $shortDescription, $description, $effects, + $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ); + + $this->attributeAvList = isset($effects[self::ATTRIBUTES_AV_LIST]) ? $effects[self::ATTRIBUTES_AV_LIST] : array(); + + if (! is_array($this->attributeAvList)) $this->attributeAvList = array($this->attributeAvList); + + $this->attribute = isset($effects[self::ATTRIBUTE]) ? $effects[self::ATTRIBUTE] : 0; + + $this->setFieldsValue($effects); + + return $this; + } + + /** + * @inheritdoc + */ + public function exec() + { + // This coupon subtracts the specified amount from the order total + // for each product which uses the selected attributes + $discount = 0; + + $cartItems = $this->facade->getCart()->getCartItems(); + + /** @var CartItem $cartItem */ + foreach ($cartItems as $cartItem) { + + if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { + $productSaleElements = $cartItem->getProductSaleElements(); + + $combinations = $productSaleElements->getAttributeCombinations(); + + /** @var AttributeCombination $combination */ + foreach ($combinations as $combination) { + + $attrValue = $combination->getAttributeAvId(); + + if (in_array($attrValue, $this->attributeAvList)) { + $discount += $this->getCartItemDiscount($cartItem); + + break; + } + } + } + } + + return $discount; + } + + /** + * Renders the template which implements coupon specific user-input, + * using the provided template file, and a list of specific input fields. + * + * @param string $templateName the path to the template + * @param array $otherFields the list of additional fields fields + * + * @return string the rendered template. + */ + public function drawBaseBackOfficeInputs($templateName, $otherFields) + { + return $this->facade->getParser()->render($templateName, array_merge($otherFields, [ + + // The attributes list field + 'attribute_field_name' => $this->makeCouponFieldName(self::ATTRIBUTE), + 'attribute_value' => $this->attribute, + + // The attributes list field + 'attribute_av_field_name' => $this->makeCouponFieldName(self::ATTRIBUTES_AV_LIST), + 'attribute_av_values' => $this->attributeAvList + ])); + } + + /** + * @inheritdoc + */ + public function getBaseFieldList($otherFields) + { + return array_merge($otherFields, [self::ATTRIBUTE, self::ATTRIBUTES_AV_LIST]); + } + + /** + * @inheritdoc + */ + public function checkBaseCouponFieldValue($fieldName, $fieldValue) + { + if ($fieldName === self::ATTRIBUTE) { + if (empty($fieldValue)) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select an attribute' + ) + ); + } + } elseif ($fieldName === self::ATTRIBUTES_AV_LIST) { + if (empty($fieldValue)) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select at least one attribute value' + ) + ); + } + } + + return $fieldValue; + } +} diff --git a/core/lib/Thelia/Coupon/Type/AbstractRemoveOnCategories.php b/core/lib/Thelia/Coupon/Type/AbstractRemoveOnCategories.php new file mode 100644 index 000000000..355cf596a --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/AbstractRemoveOnCategories.php @@ -0,0 +1,152 @@ + + */ +abstract class AbstractRemoveOnCategories extends CouponAbstract implements AmountAndPercentageCouponInterface +{ + const CATEGORIES_LIST = 'categories'; + + protected $category_list = array(); + + /** + * Set the value of specific coupon fields. + * + * @param Array $effects the Coupon effects params + */ + abstract public function setFieldsValue($effects); + + /** + * Get the discount for a specific cart item. + * + * @param CartItem $cartItem the cart item + * @return float the discount value + */ + abstract public function getCartItemDiscount($cartItem); + + /** + * @inheritdoc + */ + public function set( + FacadeInterface $facade, + $code, + $title, + $shortDescription, + $description, + array $effects, + $isCumulative, + $isRemovingPostage, + $isAvailableOnSpecialOffers, + $isEnabled, + $maxUsage, + \DateTime $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ) + { + parent::set( + $facade, $code, $title, $shortDescription, $description, $effects, + $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ); + + $this->category_list = isset($effects[self::CATEGORIES_LIST]) ? $effects[self::CATEGORIES_LIST] : array(); + + if (! is_array($this->category_list)) $this->category_list = array($this->category_list); + + $this->setFieldsValue($effects); + + return $this; + } + /** + * @inheritdoc + */ + public function exec() + { + // This coupon subtracts the specified amount from the order total + // for each product of the selected categories. + $discount = 0; + + $cartItems = $this->facade->getCart()->getCartItems(); + + /** @var CartItem $cartItem */ + foreach ($cartItems as $cartItem) { + if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { + $categories = $cartItem->getProduct()->getCategories(); + + /** @var Category $category */ + foreach ($categories as $category) { + + if (in_array($category->getId(), $this->category_list)) { + $discount += $this->getCartItemDiscount($cartItem); + + break; + } + } + } + } + + return $discount; + } + + /** + * @inheritdoc + */ + public function drawBaseBackOfficeInputs($templateName, $otherFields) + { + return $this->facade->getParser()->render($templateName, array_merge($otherFields, [ + + // The categories list field + 'categories_field_name' => $this->makeCouponFieldName(self::CATEGORIES_LIST), + 'categories_values' => $this->category_list + ])); + } + + /** + * @inheritdoc + */ + public function getBaseFieldList($otherFields) + { + return array_merge($otherFields, [self::CATEGORIES_LIST]); + } + + /** + * @inheritdoc + */ + public function checkBaseCouponFieldValue($fieldName, $fieldValue) + { + if ($fieldName === self::CATEGORIES_LIST) { + if (empty($fieldValue)) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select at least one category' + ) + ); + } + } + + return $fieldValue; + } +} diff --git a/core/lib/Thelia/Coupon/Type/AbstractRemoveOnProducts.php b/core/lib/Thelia/Coupon/Type/AbstractRemoveOnProducts.php new file mode 100644 index 000000000..aa407041a --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/AbstractRemoveOnProducts.php @@ -0,0 +1,162 @@ + + */ +abstract class AbstractRemoveOnProducts extends CouponAbstract implements AmountAndPercentageCouponInterface +{ + const CATEGORY_ID = 'category_id'; + const PRODUCTS_LIST = 'products'; + + public $category_id = 0; + public $product_list = array(); + + /** + * Set the value of specific coupon fields. + * + * @param Array $effects the Coupon effects params + */ + abstract public function setFieldsValue($effects); + + /** + * Get the discount for a specific cart item. + * + * @param CartItem $cartItem the cart item + * @return float the discount value + */ + abstract public function getCartItemDiscount($cartItem); + + /** + * @inheritdoc + */ + public function set( + FacadeInterface $facade, + $code, + $title, + $shortDescription, + $description, + array $effects, + $isCumulative, + $isRemovingPostage, + $isAvailableOnSpecialOffers, + $isEnabled, + $maxUsage, + \DateTime $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ) + { + parent::set( + $facade, $code, $title, $shortDescription, $description, $effects, + $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, + $freeShippingForCountries, + $freeShippingForModules, + $perCustomerUsageCount + ); + + $this->product_list = isset($effects[self::PRODUCTS_LIST]) ? $effects[self::PRODUCTS_LIST] : array(); + + if (! is_array($this->product_list)) $this->product_list = array($this->product_list); + + $this->category_id = isset($effects[self::CATEGORY_ID]) ? $effects[self::CATEGORY_ID] : 0; + + $this->setFieldsValue($effects); + + return $this; + } + + /** + * @inheritdoc + */ + public function exec() + { + // This coupon subtracts the specified amount from the order total + // for each product of the selected products. + $discount = 0; + + $cartItems = $this->facade->getCart()->getCartItems(); + + /** @var CartItem $cartItem */ + foreach ($cartItems as $cartItem) { + if (in_array($cartItem->getProduct()->getId(), $this->product_list)) { + if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { + $discount += $this->getCartItemDiscount($cartItem); + } + } + } + + return $discount; + } + + /** + * @inheritdoc + */ + public function drawBaseBackOfficeInputs($templateName, $otherFields) + { + return $this->facade->getParser()->render($templateName, array_merge($otherFields, [ + + // The category ID field + 'category_id_field_name' => $this->makeCouponFieldName(self::CATEGORY_ID), + 'category_id_value' => $this->category_id, + + // The products list field + 'products_field_name' => $this->makeCouponFieldName(self::PRODUCTS_LIST), + 'products_values' => $this->product_list, + 'products_values_csv' => implode(', ', $this->product_list) + ])); + } + + /** + * @inheritdoc + */ + public function getBaseFieldList($otherFields) + { + return array_merge($otherFields, [self::CATEGORY_ID, self::PRODUCTS_LIST]); + } + + /** + * @inheritdoc + */ + public function checkBaseCouponFieldValue($fieldName, $fieldValue) + { + if ($fieldName === self::CATEGORY_ID) { + if (empty($fieldValue)) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select a category' + ) + ); + } + } elseif ($fieldName === self::PRODUCTS_LIST) { + if (empty($fieldValue)) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select at least one product' + ) + ); + } + } + + return $fieldValue; + } +} diff --git a/core/lib/Thelia/Coupon/Type/AmountAndPercentageCouponInterface.php b/core/lib/Thelia/Coupon/Type/AmountAndPercentageCouponInterface.php new file mode 100644 index 000000000..8fa76e5bb --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/AmountAndPercentageCouponInterface.php @@ -0,0 +1,59 @@ + + * + */ +interface AmountAndPercentageCouponInterface +{ + + /** + * Set the value of specific coupon fields. + * @param Array $effects the Coupon effects params + */ + public function setFieldsValue($effects); + + /** + * Get the discount for a specific cart item. + * + * @param CartItem $cartItem the cart item + * @return float the discount value + */ + public function getCartItemDiscount($cartItem); + + /** + * Renders the template which implements coupon specific user-input, + * using the provided template file, and a list of specific input fields. + * + * @param string $templateName the path to the template + * @param array $otherFields the list of additional fields fields + * + * @return string the rendered template. + */ + public function drawBaseBackOfficeInputs($templateName, $otherFields); + + /** + * @inheritdoc + */ + public function getBaseFieldList($otherFields); + + /** + * + */ + public function checkBaseCouponFieldValue($fieldName, $fieldValue); +} diff --git a/core/lib/Thelia/Coupon/Type/AmountCouponTrait.php b/core/lib/Thelia/Coupon/Type/AmountCouponTrait.php new file mode 100644 index 000000000..86c3f65b1 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/AmountCouponTrait.php @@ -0,0 +1,93 @@ + + * @package Thelia\Coupon\Type + */ +Trait AmountCouponTrait { + + // The amount is already defined in CouponAbstract, and should not be redefined here. + // protected $amount = 0; + + /** + * Should return the amount field name, defined in the parent class. + * + * @return string the percentage field name + */ + abstract protected function getAmountFieldName(); + + /** + * @inheritdoc + */ + protected function setFieldsValue($effects) + { + $this->amount = $effects[$this->getAmountFieldName()]; + } + + /** + * @inheritdoc + */ + public function getCartItemDiscount($cartItem) + { + return $cartItem->getQuantity() * $this->amount; + } + + /** + * @inheritdoc + */ + public function callDrawBackOfficeInputs($templateName) + { + return $this->drawBaseBackOfficeInputs($templateName, [ + 'amount_field_name' => $this->makeCouponFieldName($this->getAmountFieldName()), + 'amount_value' => $this->amount + ]); + } + + /** + * @inheritdoc + */ + protected function getFieldList() + { + return $this->getBaseFieldList([$this->getAmountFieldName()]); + } + + /** + * @inheritdoc + */ + protected function checkCouponFieldValue($fieldName, $fieldValue) + { + $this->checkBaseCouponFieldValue($fieldName, $fieldValue); + + if ($fieldName === $this->getAmountFieldName()) { + + if (floatval($fieldValue) < 0) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Value %val for Discount Amount is invalid. Please enter a positive value.', + [ '%val' => $fieldValue] + ) + ); + } + } + + return $fieldValue; + } +} diff --git a/core/lib/Thelia/Coupon/Type/CouponAbstract.php b/core/lib/Thelia/Coupon/Type/CouponAbstract.php index 6d57b0d38..9766b7fbe 100644 --- a/core/lib/Thelia/Coupon/Type/CouponAbstract.php +++ b/core/lib/Thelia/Coupon/Type/CouponAbstract.php @@ -499,4 +499,12 @@ abstract class CouponAbstract implements CouponInterface return $effects; } + + /** + * @inheritdoc + */ + public function clear() + { + // Does nothing. Override this function as needed. + } } diff --git a/core/lib/Thelia/Coupon/Type/CouponInterface.php b/core/lib/Thelia/Coupon/Type/CouponInterface.php index a9382749f..d8190f3d7 100644 --- a/core/lib/Thelia/Coupon/Type/CouponInterface.php +++ b/core/lib/Thelia/Coupon/Type/CouponInterface.php @@ -239,4 +239,9 @@ interface CouponInterface */ public function getEffects($data); + /** + * Clear all the data the coupon may have stored, called after an order is completed. + */ + public function clear(); + } diff --git a/core/lib/Thelia/Coupon/Type/FreeProduct.php b/core/lib/Thelia/Coupon/Type/FreeProduct.php new file mode 100644 index 000000000..5fe65b1b0 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/FreeProduct.php @@ -0,0 +1,345 @@ + + */ +class FreeProduct extends AbstractRemoveOnProducts +{ + const OFFERED_PRODUCT_ID = 'offered_product_id'; + const OFFERED_CATEGORY_ID = 'offered_category_id'; + + /** @var string Service Id */ + protected $serviceId = 'thelia.coupon.type.free_product'; + + protected $offeredProductId; + protected $offeredCategoryId; + + /** + * This constant is user to mark a free product as in the process of being added to the cart, + * but the CartItem ID is not yet been defined. + */ + const ADD_TO_CART_IN_PROCESS = -1; + + /** + * @inheritdoc + */ + public function setFieldsValue($effects) + { + $this->offeredProductId = $effects[self::OFFERED_PRODUCT_ID]; + $this->offeredCategoryId = $effects[self::OFFERED_CATEGORY_ID]; + } + + /** + * @inheritdoc + */ + public function getCartItemDiscount($cartItem) + { + // This method is not used, we use our own implementation of exec(); + return 0; + } + + /** + * @return string The session variable where the cart item IDs for the free products are stored + */ + protected function getSessionVarName() + { + return "coupon.free_product.cart_items." . $this->getCode(); + } + /** + * Return the cart item id which contains the free product related to a given product + * + * @param Product $product the product in the cart which triggered the discount + * + * @return bool|int|CartItem the cart item which contains the free product, or false if the product is no longer in the cart, or ADD_TO_CART_IN_PROCESS if the adding process is not finished + */ + protected function getRelatedCartItem($product) + { + $cartItemIdList = $this->facade->getRequest()->getSession()->get( + $this->getSessionVarName(), + array() + ); + + if (isset($cartItemIdList[$product->getId()])) { + + $cartItemId = $cartItemIdList[$product->getId()]; + + if ($cartItemId == self::ADD_TO_CART_IN_PROCESS) { + return self::ADD_TO_CART_IN_PROCESS; + } elseif (null !== $cartItem = CartItemQuery::create()->findPk($cartItemId)) { + return $cartItem; + } + } else { + // Maybe the product we're offering is already in the cart ? Search it. + $cartItems = $this->facade->getCart()->getCartItems(); + + /** @var CartItem $cartItem */ + foreach ($cartItems as $cartItem) { + if ($cartItem->getProduct()->getId() == $this->offeredProductId) { + + // We found the product. Store its cart item as the free product container. + $this->setRelatedCartItem($product, $cartItem->getId()); + + return $cartItem; + } + } + + } + + return false; + } + + /** + * Set the cart item id which contains the free product related to a given product + * + * @param Product $product the product in the cart which triggered the discount + * @param bool|int $cartItemId the cart item ID which contains the free product, or just true if the free product is not yet added. + */ + protected function setRelatedCartItem($product, $cartItemId) + { + $cartItemIdList = $this->facade->getRequest()->getSession()->get( + $this->getSessionVarName(), + array() + ); + + if (! is_array($cartItemIdList)) $cartItemIdList = array(); + + $cartItemIdList[$product->getId()] = $cartItemId; + + $this->facade->getRequest()->getSession()->set( + $this->getSessionVarName(), + $cartItemIdList + ); + } + + /** + * Get the product id / cart item id list. + * + * @return array an array where the free product ID is the key, and the related cart item id the value. + */ + protected function getFreeProductsCartItemIds() + { + return $this->facade->getRequest()->getSession()->get( + $this->getSessionVarName(), + array() + ); + } + + /** + * Clear the session variable. + */ + protected function clearFreeProductsCartItemIds() + { + return $this->facade->getRequest()->getSession()->remove($this->getSessionVarName()); + } + + /** + * We overload this method here to remove the free products when the + * coupons conditions are no longer met. + * + * @inheritdoc + */ + public function isMatching() + { + $match = parent::isMatching(); + + if (! $match) { + // Cancel coupon effect (but no not remove the product) + $this->clearFreeProductsCartItemIds(); + } + + return $match; + } + + /** + * @inheritdoc + */ + public function exec() + { + $discount = 0; + + $cartItems = $this->facade->getCart()->getCartItems(); + + /** @var Product $eligibleProduct */ + $eligibleProduct = null; + + /** @var CartItem $cartItem */ + foreach ($cartItems as $cartItem) { + if (in_array($cartItem->getProduct()->getId(), $this->product_list)) { + if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { + $eligibleProduct = $cartItem; + break; + } + } + } + + if ($eligibleProduct !== null) { + + // Get the cart item for the eligible product + $freeProductCartItem = $this->getRelatedCartItem($eligibleProduct); + + // We add the free product it only if it not yet in the cart. + if ($freeProductCartItem === false) { + + if (null !== $freeProduct = ProductQuery::create()->findPk($this->offeredProductId)) { + + // Store in the session that the free product is added to the cart, + // so that we don't enter the following infinite loop : + // + // 1) exec() adds a product by firing a CART_ADDITEM event, + // 2) the event is processed by Action\Coupon::updateOrderDiscount(), + // 3) Action\Coupon::updateOrderDiscount() calls CouponManager::getDiscount() + // 4) CouponManager::getDiscount() calls exec() -> Infinite loop !! + + // Store a marker first, we do not have the cart item id yet. + $this->setRelatedCartItem($eligibleProduct, self::ADD_TO_CART_IN_PROCESS); + + $cartEvent = new CartEvent($this->facade->getCart()); + + $cartEvent->setNewness(true); + $cartEvent->setAppend(false); + $cartEvent->setQuantity(1); + $cartEvent->setProductSaleElementsId($freeProduct->getDefaultSaleElements()->getId()); + $cartEvent->setProduct($this->offeredProductId); + + $this->facade->getDispatcher()->dispatch(TheliaEvents::CART_ADDITEM, $cartEvent); + + // Store the final cart item ID. + $this->setRelatedCartItem($eligibleProduct, $cartEvent->getCartItem()->getId()); + + $freeProductCartItem = $cartEvent->getCartItem(); + + // Setting product price is dangerous, as the customer could change the ordered quantity of this product. + // We will instead add the product price to the order discount. + // $cartEvent->getCartItem()->setPrice(0)->save(); + } + } + + if ($freeProductCartItem instanceof CartItem) { + + $taxCountry = $this->facade->getDeliveryCountry(); + + // The discount is the product price. + $discount = $freeProductCartItem->getPromo() ? + $freeProductCartItem->getPromoPrice() : $freeProductCartItem->getPrice(); + } + } + // No eligible product was found ! + else { + // Remove all free products for this coupon, but no not remove the product from the cart. + $this->clearFreeProductsCartItemIds(); + } + + return $discount; + } + + /** + * @inheritdoc + */ + protected function getFieldList() + { + return $this->getBaseFieldList([self::OFFERED_CATEGORY_ID, self::OFFERED_PRODUCT_ID]); + } + + /** + * @inheritdoc + */ + protected function checkCouponFieldValue($fieldName, $fieldValue) + { + $this->checkBaseCouponFieldValue($fieldName, $fieldValue); + + if ($fieldName === self::OFFERED_PRODUCT_ID) { + + if (floatval($fieldValue) < 0) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select the offered product' + ) + ); + } + } elseif ($fieldName === self::OFFERED_CATEGORY_ID) { + if (empty($fieldValue)) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Please select the category of the offred product' + ) + ); + } + } + + return $fieldValue; + } + + /** + * Get I18n name + * + * @return string + */ + public function getName() + { + return $this->facade + ->getTranslator() + ->trans('Free product when buying one or more selected products', array(), 'coupon'); + } + + /** + * @inheritdoc + */ + public function getToolTip() + { + $toolTip = $this->facade + ->getTranslator() + ->trans( + 'This coupon adds a free product to the cart if one of the selected products is in the cart.', + array(), + 'coupon' + ); + + return $toolTip; + } + + /** + * @inheritdoc + */ + public function drawBackOfficeInputs() + { + return $this->drawBaseBackOfficeInputs("coupon/type-fragments/free-product.html", [ + 'offered_category_field_name' => $this->makeCouponFieldName(self::OFFERED_CATEGORY_ID), + 'offered_category_value' => $this->offeredCategoryId, + + 'offered_product_field_name' => $this->makeCouponFieldName(self::OFFERED_PRODUCT_ID), + 'offered_product_value' => $this->offeredProductId + ]); + } + + /** + * @inheritdoc + */ + public function clear() + { + // Clear the session variable when the coupon is cleared. + $this->clearFreeProductsCartItemIds(); + } +} diff --git a/core/lib/Thelia/Coupon/Type/PercentageCouponTrait.php b/core/lib/Thelia/Coupon/Type/PercentageCouponTrait.php new file mode 100644 index 000000000..caa446cd7 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/PercentageCouponTrait.php @@ -0,0 +1,92 @@ + + * @package Thelia\Coupon\Type + */ +Trait PercentageCouponTrait { + + public $percentage = 0; + + /** + * Should return the percentage field name, defined in the parent class. + * + * @return string the percentage field name + */ + abstract protected function getPercentageFieldName(); + + /** + * @inheritdoc + */ + protected function setFieldsValue($effects) + { + $this->percentage = $effects[$this->getPercentageFieldName()]; + } + + /** + * @inheritdoc + */ + public function getCartItemDiscount($cartItem) + { + return $cartItem->getQuantity() * $cartItem->getPrice() * ($this->percentage / 100); + } + + /** + * @inheritdoc + */ + public function callDrawBackOfficeInputs($templateName) + { + return $this->drawBaseBackOfficeInputs($templateName, [ + 'percentage_field_name' => $this->makeCouponFieldName($this->getPercentageFieldName()), + 'percentage_value' => $this->percentage, + ]); + } + + /** + * @inheritdoc + */ + protected function getFieldList() + { + return $this->getBaseFieldList([$this->getPercentageFieldName()]); + } + + /** + * @inheritdoc + */ + protected function checkCouponFieldValue($fieldName, $fieldValue) + { + $this->checkBaseCouponFieldValue($fieldName, $fieldValue); + + if ($fieldName === $this->getPercentageFieldName()) { + + $pcent = floatval($fieldValue); + + if ($pcent <= 0 || $pcent > 100) { + throw new \InvalidArgumentException( + Translator::getInstance()->trans( + 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.', + [ '%val' => $fieldValue] + ) + ); + } + } + + return $fieldValue; + } +} diff --git a/core/lib/Thelia/Coupon/Type/RemoveAmountOnAttributeValues.php b/core/lib/Thelia/Coupon/Type/RemoveAmountOnAttributeValues.php new file mode 100644 index 000000000..625de5302 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/RemoveAmountOnAttributeValues.php @@ -0,0 +1,65 @@ + + */ +class RemoveAmountOnAttributeValues extends AbstractRemoveOnAttributeValues +{ + use AmountCouponTrait; + + /** @var string Service Id */ + protected $serviceId = 'thelia.coupon.type.remove_amount_on_attribute_av'; + + protected function getAmountFieldName() + { + return self::AMOUNT_FIELD_NAME; + } + /** + * @inheritdoc + */ + public function getName() + { + return $this->facade + ->getTranslator() + ->trans('Fixed amount discount for selected attribute values', array(), 'coupon'); + } + + /** + * @inheritdoc + */ + public function getToolTip() + { + $toolTip = $this->facade + ->getTranslator() + ->trans( + 'This coupon subtracts the specified amount from the order total for each product which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', + array(), + 'coupon' + ); + + return $toolTip; + } + + /** + * @inheritdoc + */ + public function drawBackOfficeInputs() + { + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-amount-on-attributes.html'); + } +} diff --git a/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php b/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php index d65b462f7..54a0bcc17 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php +++ b/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php @@ -12,63 +12,25 @@ namespace Thelia\Coupon\Type; -use Thelia\Core\Translation\Translator; -use Thelia\Coupon\FacadeInterface; -use Thelia\Model\CartItem; -use Thelia\Model\Category; - /** * Allow to remove an amount from the checkout total * * @package Coupon - * @author Guillaume MOREL - * + * @author Franck Allimant */ -class RemoveAmountOnCategories extends CouponAbstract +class RemoveAmountOnCategories extends AbstractRemoveOnCategories { - const CATEGORIES_LIST = 'categories'; + use AmountCouponTrait; /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_amount_on_categories'; - public $category_list = array(); - - /** - * @inheritdoc - */ - public function set( - FacadeInterface $facade, - $code, - $title, - $shortDescription, - $description, - array $effects, - $isCumulative, - $isRemovingPostage, - $isAvailableOnSpecialOffers, - $isEnabled, - $maxUsage, - \DateTime $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ) + protected function getAmountFieldName() { - parent::set( - $facade, $code, $title, $shortDescription, $description, $effects, - $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ); - - $this->category_list = isset($effects[self::CATEGORIES_LIST]) ? $effects[self::CATEGORIES_LIST] : array(); - - if (! is_array($this->category_list)) $this->category_list = array($this->category_list); - return $this; + return self::AMOUNT_FIELD_NAME; } - /** + /** * @inheritdoc */ public function getName() @@ -94,89 +56,11 @@ class RemoveAmountOnCategories extends CouponAbstract return $toolTip; } - /** - * @inheritdoc - */ - public function exec() - { - // This coupon subtracts the specified amount from the order total - // for each product of the selected categories. - $discount = 0; - - $cartItems = $this->facade->getCart()->getCartItems(); - - /** @var CartItem $cartItem */ - foreach ($cartItems as $cartItem) { - - if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { - $categories = $cartItem->getProduct()->getCategories(); - - /** @var Category $category */ - foreach ($categories as $category) { - - if (in_array($category->getId(), $this->category_list)) { - $discount += $cartItem->getQuantity() * $this->amount; - - break; - } - } - } - } - - return $discount; - } - /** * @inheritdoc */ public function drawBackOfficeInputs() { - return $this->facade->getParser()->render('coupon/type-fragments/remove-amount-on-categories.html', [ - - // The standard "Amount" field - 'amount_field_name' => $this->makeCouponFieldName(self::AMOUNT_FIELD_NAME), - 'amount_value' => $this->amount, - - // The categories list field - 'categories_field_name' => $this->makeCouponFieldName(self::CATEGORIES_LIST), - 'categories_values' => $this->category_list - ]); + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-amount-on-categories.html'); } - - /** - * @inheritdoc - */ - protected function getFieldList() - { - return [self::AMOUNT_FIELD_NAME, self::CATEGORIES_LIST]; - } - - /** - * @inheritdoc - */ - protected function checkCouponFieldValue($fieldName, $fieldValue) - { - if ($fieldName === self::AMOUNT_FIELD_NAME) { - - if (floatval($fieldValue) < 0) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Value %val for Discount Amount is invalid. Please enter a positive value.', - [ '%val' => $fieldValue] - ) - ); - } - } elseif ($fieldName === self::CATEGORIES_LIST) { - if (empty($fieldValue)) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Please select at least one category' - ) - ); - } - } - - return $fieldValue; - } - } diff --git a/core/lib/Thelia/Coupon/Type/RemoveAmountOnProducts.php b/core/lib/Thelia/Coupon/Type/RemoveAmountOnProducts.php index 9a3fb8b93..867f959aa 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveAmountOnProducts.php +++ b/core/lib/Thelia/Coupon/Type/RemoveAmountOnProducts.php @@ -12,65 +12,24 @@ namespace Thelia\Coupon\Type; -use Thelia\Core\Translation\Translator; -use Thelia\Coupon\FacadeInterface; -use Thelia\Model\CartItem; use Thelia\Model\Product; /** * Allow to remove an amount from the checkout total * * @package Coupon - * @author Guillaume MOREL - * + * @author Franck Allimant */ -class RemoveAmountOnProducts extends CouponAbstract +class RemoveAmountOnProducts extends AbstractRemoveOnProducts { - const CATEGORY_ID = 'category_id'; - const PRODUCTS_LIST = 'products'; + use AmountCouponTrait; /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_amount_on_products'; - public $category_id = 0; - public $product_list = array(); - - /** - * @inheritdoc - */ - public function set( - FacadeInterface $facade, - $code, - $title, - $shortDescription, - $description, - array $effects, - $isCumulative, - $isRemovingPostage, - $isAvailableOnSpecialOffers, - $isEnabled, - $maxUsage, - \DateTime $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ) + protected function getAmountFieldName() { - parent::set( - $facade, $code, $title, $shortDescription, $description, $effects, - $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ); - - $this->product_list = isset($effects[self::PRODUCTS_LIST]) ? $effects[self::PRODUCTS_LIST] : array(); - - if (! is_array($this->product_list)) $this->product_list = array($this->product_list); - - $this->category_id = isset($effects[self::CATEGORY_ID]) ? $effects[self::CATEGORY_ID] : 0; - - return $this; + return self::AMOUNT_FIELD_NAME; } /** @@ -101,93 +60,11 @@ class RemoveAmountOnProducts extends CouponAbstract return $toolTip; } - /** - * @inheritdoc - */ - public function exec() - { - // This coupon subtracts the specified amount from the order total - // for each product of the selected products. - $discount = 0; - - $cartItems = $this->facade->getCart()->getCartItems(); - - /** @var CartItem $cartItem */ - foreach ($cartItems as $cartItem) { - - if (in_array($cartItem->getProduct()->getId(), $this->product_list)) { - if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { - $discount += $cartItem->getQuantity() * $this->amount; - } - } - } - - return $discount; - } - /** * @inheritdoc */ public function drawBackOfficeInputs() { - return $this->facade->getParser()->render('coupon/type-fragments/remove-amount-on-products.html', [ - - // The standard "Amount" field - 'amount_field_name' => $this->makeCouponFieldName(self::AMOUNT_FIELD_NAME), - 'amount_value' => $this->amount, - - // The category ID field - 'category_id_field_name' => $this->makeCouponFieldName(self::CATEGORY_ID), - 'category_id_value' => $this->category_id, - - // The products list field - 'products_field_name' => $this->makeCouponFieldName(self::PRODUCTS_LIST), - 'products_values' => $this->product_list, - 'products_values_csv' => implode(', ', $this->product_list) - ]); - } - - /** - * @inheritdoc - */ - protected function getFieldList() - { - return [self::AMOUNT_FIELD_NAME, self::CATEGORY_ID, self::PRODUCTS_LIST]; - } - - /** - * @inheritdoc - */ - protected function checkCouponFieldValue($fieldName, $fieldValue) - { - if ($fieldName === self::AMOUNT_FIELD_NAME) { - - if (floatval($fieldValue) < 0) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Value %val for Discount Amount is invalid. Please enter a positive value.', - [ '%val' => $fieldValue] - ) - ); - } - } elseif ($fieldName === self::CATEGORY_ID) { - if (empty($fieldValue)) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Please select a category' - ) - ); - } - } elseif ($fieldName === self::PRODUCTS_LIST) { - if (empty($fieldValue)) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Please select at least one product' - ) - ); - } - } - - return $fieldValue; + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-amount-on-products.html'); } } diff --git a/core/lib/Thelia/Coupon/Type/RemovePercentageOnAttributeValues.php b/core/lib/Thelia/Coupon/Type/RemovePercentageOnAttributeValues.php new file mode 100644 index 000000000..6d2dafebf --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/RemovePercentageOnAttributeValues.php @@ -0,0 +1,71 @@ + + */ +class RemovePercentageOnAttributeValues extends AbstractRemoveOnAttributeValues +{ + const PERCENTAGE = 'percentage'; + + use PercentageCouponTrait; + + /** @var string Service Id */ + protected $serviceId = 'thelia.coupon.type.remove_percentage_on_attribute_av'; + + /** + * @inheritdoc + */ + protected function getPercentageFieldName() + { + return self::PERCENTAGE; + } + + /** + * @inheritdoc + */ + public function getName() + { + return $this->facade + ->getTranslator() + ->trans('Percentage discount for selected attribute values', array(), 'coupon'); + } + + /** + * @inheritdoc + */ + public function getToolTip() + { + $toolTip = $this->facade + ->getTranslator() + ->trans( + 'This coupon subtracts from the order total the specified percentage of each product price which uses the selected attribute values. If the discount is greater than the total order, the customer will only pay the shipping, or nothing if the coupon also provides free shipping.', + array(), + 'coupon' + ); + + return $toolTip; + } + + /** + * @inheritdoc + */ + public function drawBackOfficeInputs() + { + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-percentage-on-attributes.html'); + } +} diff --git a/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php b/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php index 892984a14..e2db763d4 100644 --- a/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php +++ b/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php @@ -12,61 +12,24 @@ namespace Thelia\Coupon\Type; -use Thelia\Core\Translation\Translator; -use Thelia\Coupon\FacadeInterface; -use Thelia\Model\CartItem; -use Thelia\Model\Category; - /** * @author Franck Allimant - * */ -class RemovePercentageOnCategories extends CouponAbstract +class RemovePercentageOnCategories extends AbstractRemoveOnCategories { - const CATEGORIES_LIST = 'categories'; - const PERCENTAGE = 'percentage'; + const PERCENTAGE = 'percentage'; + + use PercentageCouponTrait; /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_percentage_on_categories'; - protected $category_list = array(); - - protected $percentage = 0; - /** * @inheritdoc */ - public function set( - FacadeInterface $facade, - $code, - $title, - $shortDescription, - $description, - array $effects, - $isCumulative, - $isRemovingPostage, - $isAvailableOnSpecialOffers, - $isEnabled, - $maxUsage, - \DateTime $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ) + protected function getPercentageFieldName() { - parent::set( - $facade, $code, $title, $shortDescription, $description, $effects, - $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ); - - $this->category_list = isset($effects[self::CATEGORIES_LIST]) ? $effects[self::CATEGORIES_LIST] : array(); - $this->percentage = isset($effects[self::PERCENTAGE]) ? $effects[self::PERCENTAGE] : 0; - - if (! is_array($this->category_list)) $this->category_list = array($this->category_list); - return $this; + return self::PERCENTAGE; } /** @@ -95,90 +58,11 @@ class RemovePercentageOnCategories extends CouponAbstract return $toolTip; } - /** - * @inheritdoc - */ - public function exec() - { - // This coupon subtracts the specified amount from the order total - // for each product of the selected categories. - $discount = 0; - - $cartItems = $this->facade->getCart()->getCartItems(); - - /** @var CartItem $cartItem */ - foreach ($cartItems as $cartItem) { - if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { - $categories = $cartItem->getProduct()->getCategories(); - - /** @var Category $category */ - foreach ($categories as $category) { - - if (in_array($category->getId(), $this->category_list)) { - $discount += $cartItem->getPrice() * $this->percentage; - - break; - } - } - } - } - - return $discount; - } - /** * @inheritdoc */ public function drawBackOfficeInputs() { - return $this->facade->getParser()->render('coupon/type-fragments/remove-percentage-on-categories.html', [ - - // The standard "Amount" field - 'percentage_field_name' => $this->makeCouponFieldName(self::PERCENTAGE), - 'percentage_value' => $this->percentage, - - // The categories list field - 'categories_field_name' => $this->makeCouponFieldName(self::CATEGORIES_LIST), - 'categories_values' => $this->category_list - ]); + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-percentage-on-categories.html'); } - - /** - * @inheritdoc - */ - protected function getFieldList() - { - return [self::PERCENTAGE, self::CATEGORIES_LIST]; - } - - /** - * @inheritdoc - */ - protected function checkCouponFieldValue($fieldName, $fieldValue) - { - if ($fieldName === self::PERCENTAGE) { - - $pcent = floatval($fieldValue); - - if ($pcent <= 0 || $pcent > 100) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.', - [ '%val' => $fieldValue] - ) - ); - } - } elseif ($fieldName === self::CATEGORIES_LIST) { - if (empty($fieldValue)) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Please select at least one category' - ) - ); - } - } - - return $fieldValue; - } - } diff --git a/core/lib/Thelia/Coupon/Type/RemovePercentageOnProducts.php b/core/lib/Thelia/Coupon/Type/RemovePercentageOnProducts.php index 030a87c23..cab227442 100644 --- a/core/lib/Thelia/Coupon/Type/RemovePercentageOnProducts.php +++ b/core/lib/Thelia/Coupon/Type/RemovePercentageOnProducts.php @@ -12,69 +12,29 @@ namespace Thelia\Coupon\Type; -use Thelia\Core\Translation\Translator; -use Thelia\Coupon\FacadeInterface; -use Thelia\Model\CartItem; use Thelia\Model\Product; /** * Allow to remove an amount from the checkout total * * @package Coupon - * @author Guillaume MOREL - * + * @author Franck Allimant */ -class RemovePercentageOnProducts extends CouponAbstract +class RemovePercentageOnProducts extends AbstractRemoveOnProducts { - const CATEGORY_ID = 'category_id'; - const PRODUCTS_LIST = 'products'; - const PERCENTAGE = 'percentage'; + const PERCENTAGE = 'percentage'; + + use PercentageCouponTrait; /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_percentage_on_products'; - public $category_id = 0; - public $product_list = array(); - public $percentage = 0; - /** * @inheritdoc */ - public function set( - FacadeInterface $facade, - $code, - $title, - $shortDescription, - $description, - array $effects, - $isCumulative, - $isRemovingPostage, - $isAvailableOnSpecialOffers, - $isEnabled, - $maxUsage, - \DateTime $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ) + protected function getPercentageFieldName() { - parent::set( - $facade, $code, $title, $shortDescription, $description, $effects, - $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ); - - $this->product_list = isset($effects[self::PRODUCTS_LIST]) ? $effects[self::PRODUCTS_LIST] : array(); - - if (! is_array($this->product_list)) $this->product_list = array($this->product_list); - - $this->category_id = isset($effects[self::CATEGORY_ID]) ? $effects[self::CATEGORY_ID] : 0; - - $this->percentage = $effects[self::PERCENTAGE]; - - return $this; + return self::PERCENTAGE; } /** @@ -105,94 +65,11 @@ class RemovePercentageOnProducts extends CouponAbstract return $toolTip; } - /** - * @inheritdoc - */ - public function exec() - { - // This coupon subtracts the specified amount from the order total - // for each product of the selected products. - $discount = 0; - - $cartItems = $this->facade->getCart()->getCartItems(); - - /** @var CartItem $cartItem */ - foreach ($cartItems as $cartItem) { - if (in_array($cartItem->getProduct()->getId(), $this->product_list)) { - if (! $cartItem->getPromo() || $this->isAvailableOnSpecialOffers()) { - $discount += $cartItem->getQuantity() * $cartItem->getPrice() * $this->percentage; - } - } - } - - return $discount; - } - /** * @inheritdoc */ public function drawBackOfficeInputs() { - return $this->facade->getParser()->render('coupon/type-fragments/remove-percentage-on-products.html', [ - - // The standard "Amount" field - 'percentage_field_name' => $this->makeCouponFieldName(self::PERCENTAGE), - 'percentage_value' => $this->percentage, - - // The category ID field - 'category_id_field_name' => $this->makeCouponFieldName(self::CATEGORY_ID), - 'category_id_value' => $this->category_id, - - // The products list field - 'products_field_name' => $this->makeCouponFieldName(self::PRODUCTS_LIST), - 'products_values' => $this->product_list, - 'products_values_csv' => implode(', ', $this->product_list) - ]); - } - - /** - * @inheritdoc - */ - protected function getFieldList() - { - return [self::PERCENTAGE, self::CATEGORY_ID, self::PRODUCTS_LIST]; - } - - /** - * @inheritdoc - */ - protected function checkCouponFieldValue($fieldName, $fieldValue) - { - if ($fieldName === self::PERCENTAGE) { - - $pcent = floatval($fieldValue); - - if ($pcent <= 0 || $pcent > 100) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.', - [ '%val' => $fieldValue] - ) - ); - } - } elseif ($fieldName === self::CATEGORY_ID) { - if (empty($fieldValue)) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Please select a category' - ) - ); - } - } elseif ($fieldName === self::PRODUCTS_LIST) { - if (empty($fieldValue)) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Please select at least one product' - ) - ); - } - } - - return $fieldValue; + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-percentage-on-products.html'); } } diff --git a/core/lib/Thelia/Coupon/Type/RemoveXAmount.php b/core/lib/Thelia/Coupon/Type/RemoveXAmount.php index 7ded8108c..c1ffb0675 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveXAmount.php +++ b/core/lib/Thelia/Coupon/Type/RemoveXAmount.php @@ -12,20 +12,25 @@ namespace Thelia\Coupon\Type; -use Thelia\Core\Translation\Translator; - /** * Allow to remove an amount from the checkout total * * @package Coupon - * @author Guillaume MOREL + * @author Guillaume MOREL , Franck Allimant * */ -class RemoveXAmount extends CouponAbstract +class RemoveXAmount extends AbstractRemove { + use AmountCouponTrait; + /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_x_amount'; + protected function getAmountFieldName() + { + return self::AMOUNT_FIELD_NAME; + } + /** * @inheritdoc */ @@ -52,43 +57,19 @@ class RemoveXAmount extends CouponAbstract return $toolTip; } + /** + * @inheritdoc + */ + public function exec() + { + return $this->amount; + } + /** * @inheritdoc */ public function drawBackOfficeInputs() { - return $this->facade->getParser()->render('coupon/type-fragments/remove-x-amount.html', [ - 'fieldName' => $this->makeCouponFieldName(self::AMOUNT_FIELD_NAME), - 'value' => $this->amount - ]); + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-x-amount.html'); } - - /** - * @inheritdoc - */ - protected function getFieldList() - { - return [self::AMOUNT_FIELD_NAME]; - } - - /** - * @inheritdoc - */ - protected function checkCouponFieldValue($fieldName, $fieldValue) - { - if ($fieldName === self::AMOUNT_FIELD_NAME) { - - if (floatval($fieldValue) < 0) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Value %val for Disount Amount is invalid. Please enter a positive value.', - [ '%val' => $fieldValue] - ) - ); - } - } - - return $fieldValue; - } - } diff --git a/core/lib/Thelia/Coupon/Type/RemoveXPercent.php b/core/lib/Thelia/Coupon/Type/RemoveXPercent.php index 554e3f5da..623cb7078 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveXPercent.php +++ b/core/lib/Thelia/Coupon/Type/RemoveXPercent.php @@ -12,86 +12,26 @@ namespace Thelia\Coupon\Type; -use Thelia\Core\Translation\Translator; -use Thelia\Coupon\FacadeInterface; - /** * @package Coupon - * @author Guillaume MOREL + * @author Guillaume MOREL , Franck Allimant * */ -class RemoveXPercent extends CouponAbstract +class RemoveXPercent extends AbstractRemove { const INPUT_PERCENTAGE_NAME = 'percentage'; + use PercentageCouponTrait; + /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_x_percent'; - /** @var float Percentage removed from the Cart */ - protected $percentage = 0; - /** * @inheritdoc */ - public function set( - FacadeInterface $facade, - $code, - $title, - $shortDescription, - $description, - array $effects, - $isCumulative, - $isRemovingPostage, - $isAvailableOnSpecialOffers, - $isEnabled, - $maxUsage, - \DateTime $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ) + protected function getPercentageFieldName() { - parent::set( - $facade, $code, $title, $shortDescription, $description, $effects, - $isCumulative, $isRemovingPostage, $isAvailableOnSpecialOffers, $isEnabled, $maxUsage, $expirationDate, - $freeShippingForCountries, - $freeShippingForModules, - $perCustomerUsageCount - ); - - $this->percentage = $effects[self::INPUT_PERCENTAGE_NAME]; - - return $this; - } - - /** - * @inheritdoc - */ - public function exec() - { - return round($this->facade->getCartTotalTaxPrice($this->isAvailableOnSpecialOffers()) * $this->percentage/100, 2); - } - - /** - * @inheritdoc - */ - protected function checkCouponFieldValue($fieldName, $fieldValue) - { - if ($fieldName === self::INPUT_PERCENTAGE_NAME) { - - $pcent = floatval($fieldValue); - - if ($pcent <= 0 || $pcent > 100) { - throw new \InvalidArgumentException( - Translator::getInstance()->trans( - 'Value %val for Percent Discount is invalid. Please enter a positive value between 1 and 100.', - [ '%val' => $fieldValue] - ) - ); - } - } - - return $fieldValue; + return self::INPUT_PERCENTAGE_NAME; } /** @@ -123,19 +63,16 @@ class RemoveXPercent extends CouponAbstract /** * @inheritdoc */ - public function drawBackOfficeInputs() + public function exec() { - return $this->facade->getParser()->render('coupon/type-fragments/remove-x-percent.html', [ - 'fieldName' => $this->makeCouponFieldName(self::INPUT_PERCENTAGE_NAME), - 'value' => $this->percentage - ]); + return round($this->facade->getCartTotalTaxPrice($this->isAvailableOnSpecialOffers()) * $this->percentage/100, 2); } /** * @inheritdoc */ - protected function getFieldList() + public function drawBackOfficeInputs() { - return [self::INPUT_PERCENTAGE_NAME]; + return $this->callDrawBackOfficeInputs('coupon/type-fragments/remove-x-percent.html'); } } diff --git a/core/lib/Thelia/Model/Coupon.php b/core/lib/Thelia/Model/Coupon.php index 921078eda..c8b15d43d 100644 --- a/core/lib/Thelia/Model/Coupon.php +++ b/core/lib/Thelia/Model/Coupon.php @@ -41,6 +41,9 @@ use Thelia\Model\Tools\ModelEventDispatcherTrait; class Coupon extends BaseCoupon { + // Define the value of an unlimited coupon usage. + const UNLIMITED_COUPON_USE = -1; + use ModelEventDispatcherTrait; /** @@ -265,6 +268,10 @@ class Coupon extends BaseCoupon return CouponModuleQuery::create()->filterByCouponId($this->getId())->find(); } + public function isUsageUnlimited() + { + return $this->getMaxUsage() == self::UNLIMITED_COUPON_USE; + } /** * Get coupon usage left, either overall, or per customer. * diff --git a/core/lib/Thelia/Model/Product.php b/core/lib/Thelia/Model/Product.php index 4028ef680..1d73c939d 100644 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -61,7 +61,7 @@ class Product extends BaseProduct */ public function getDefaultSaleElements() { - return ProductSaleElementsQuery::create()->filterByProductId($this->id)->filterByIsDefault(true)->find(); + return ProductSaleElementsQuery::create()->filterByProductId($this->id)->filterByIsDefault(true)->findOne(); } /** diff --git a/core/lib/Thelia/Tests/Condition/Implementation/CartContainsCategoriesTest.php b/core/lib/Thelia/Tests/Condition/Implementation/CartContainsCategoriesTest.php new file mode 100644 index 000000000..1a5259b5e --- /dev/null +++ b/core/lib/Thelia/Tests/Condition/Implementation/CartContainsCategoriesTest.php @@ -0,0 +1,365 @@ + + */ +class CartContainsCategoriesTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + $category1 = new Category(); + $category1->setId(10); + + $category2 = new Category(); + $category2->setId(20); + + $category3 = new Category(); + $category3->setId(30); + + $product1 = new Product(); + $product1->addCategory($category1)->addCategory($category2); + + $product2 = new Product(); + $product2->addCategory($category3); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem1Stub, $cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + + return $stubFacade; + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::setValidators + * @expectedException \Thelia\Exception\InvalidConditionOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsCategories($stubFacade); + $operators = array( + CartContainsCategories::CATEGORIES_LIST => Operators::INFERIOR_OR_EQUAL + ); + $values = array( + CartContainsCategories::CATEGORIES_LIST => array() + ); + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::setValidators + * @expectedException \Thelia\Exception\InvalidConditionValueException + */ + public function testInValidBackOfficeInputValue() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsCategories($stubFacade); + $operators = array( + CartContainsCategories::CATEGORIES_LIST => Operators::IN + ); + $values = array( + CartContainsCategories::CATEGORIES_LIST => array() + ); + + $condition1->setValidatorsFromForm($operators, $values); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::isMatching + * + */ + public function testMatchingRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsCategories($stubFacade); + $operators = array( + CartContainsCategories::CATEGORIES_LIST => Operators::IN + ); + $values = array( + CartContainsCategories::CATEGORIES_LIST => array(10, 20) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::isMatching + * + */ + public function testNotMatching() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsCategories($stubFacade); + + $operators = array( + CartContainsCategories::CATEGORIES_LIST => Operators::IN + ); + $values = array( + CartContainsCategories::CATEGORIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = false; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + public function testGetSerializableRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsCategories($stubFacade); + + $operators = array( + CartContainsCategories::CATEGORIES_LIST => Operators::IN + ); + $values = array( + CartContainsCategories::CATEGORIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $serializableRule = $condition1->getSerializableCondition(); + + $expected = new SerializableCondition(); + $expected->conditionServiceId = $condition1->getServiceId(); + $expected->operators = $operators; + $expected->values = $values; + + $actual = $serializableRule; + + $this->assertEquals($expected, $actual); + + } + + /** + * Check getName i18n + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::getName + * + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Number of articles in cart'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new CartContainsCategories($stubFacade); + + $actual = $condition1->getName(); + $expected = 'Number of articles in cart'; + $this->assertEquals($expected, $actual); + } + + /** + * Check tooltip i18n + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::getToolTip + * + */ + public function testGetToolTip() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Sample coupon condition'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new CartContainsCategories($stubFacade); + + $actual = $condition1->getToolTip(); + $expected = 'Sample coupon condition'; + $this->assertEquals($expected, $actual); + } + + /** + * Check validator + * + * @covers Thelia\Condition\Implementation\CartContainsCategories::generateInputs + * + */ + public function testGetValidator() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Price'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new CartContainsCategories($stubFacade); + + $operators = array( + CartContainsCategories::CATEGORIES_LIST => Operators::IN + ); + $values = array( + CartContainsCategories::CATEGORIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $actual = $condition1->getValidators(); + + $validators = array( + 'inputs' => array( + CartContainsCategories::CATEGORIES_LIST => array( + 'availableOperators' => array( + 'in' => 'Price', + 'out' => 'Price', + ), + 'value' => '', + 'selectedOperator' => 'in' + ) + ), + 'setOperators' => array( + 'categories' => 'in' + ), + 'setValues' => array( + 'categories' => array(50, 60) + ) + ); + $expected = $validators; + + $this->assertEquals($expected, $actual); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + +} diff --git a/core/lib/Thelia/Tests/Condition/Implementation/CartContainsProductsTest.php b/core/lib/Thelia/Tests/Condition/Implementation/CartContainsProductsTest.php new file mode 100644 index 000000000..f24dea73a --- /dev/null +++ b/core/lib/Thelia/Tests/Condition/Implementation/CartContainsProductsTest.php @@ -0,0 +1,365 @@ + + */ +class CartContainsProductsTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + $category1 = new Category(); + $category1->setId(10); + + $category2 = new Category(); + $category2->setId(20); + + $category3 = new Category(); + $category3->setId(30); + + $product1 = new Product(); + $product1->setId(10)->addCategory($category1)->addCategory($category2); + + $product2 = new Product(); + $product2->setId(20)->addCategory($category3); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem1Stub, $cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + + return $stubFacade; + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::setValidators + * @expectedException \Thelia\Exception\InvalidConditionOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsProducts($stubFacade); + $operators = array( + CartContainsProducts::PRODUCTS_LIST => Operators::INFERIOR_OR_EQUAL + ); + $values = array( + CartContainsProducts::PRODUCTS_LIST => array() + ); + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::setValidators + * @expectedException \Thelia\Exception\InvalidConditionValueException + */ + public function testInValidBackOfficeInputValue() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsProducts($stubFacade); + $operators = array( + CartContainsProducts::PRODUCTS_LIST => Operators::IN + ); + $values = array( + CartContainsProducts::PRODUCTS_LIST => array() + ); + + $condition1->setValidatorsFromForm($operators, $values); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::isMatching + * + */ + public function testMatchingRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsProducts($stubFacade); + $operators = array( + CartContainsProducts::PRODUCTS_LIST => Operators::IN + ); + $values = array( + CartContainsProducts::PRODUCTS_LIST => array(10, 20) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::isMatching + * + */ + public function testNotMatching() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsProducts($stubFacade); + + $operators = array( + CartContainsProducts::PRODUCTS_LIST => Operators::IN + ); + $values = array( + CartContainsProducts::PRODUCTS_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = false; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + public function testGetSerializableRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new CartContainsProducts($stubFacade); + + $operators = array( + CartContainsProducts::PRODUCTS_LIST => Operators::IN + ); + $values = array( + CartContainsProducts::PRODUCTS_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $serializableRule = $condition1->getSerializableCondition(); + + $expected = new SerializableCondition(); + $expected->conditionServiceId = $condition1->getServiceId(); + $expected->operators = $operators; + $expected->values = $values; + + $actual = $serializableRule; + + $this->assertEquals($expected, $actual); + + } + + /** + * Check getName i18n + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::getName + * + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Number of articles in cart'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new CartContainsProducts($stubFacade); + + $actual = $condition1->getName(); + $expected = 'Number of articles in cart'; + $this->assertEquals($expected, $actual); + } + + /** + * Check tooltip i18n + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::getToolTip + * + */ + public function testGetToolTip() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Sample coupon condition'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new CartContainsProducts($stubFacade); + + $actual = $condition1->getToolTip(); + $expected = 'Sample coupon condition'; + $this->assertEquals($expected, $actual); + } + + /** + * Check validator + * + * @covers Thelia\Condition\Implementation\CartContainsProducts::generateInputs + * + */ + public function testGetValidator() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Price'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new CartContainsProducts($stubFacade); + + $operators = array( + CartContainsProducts::PRODUCTS_LIST => Operators::IN + ); + $values = array( + CartContainsProducts::PRODUCTS_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $actual = $condition1->getValidators(); + + $validators = array( + 'inputs' => array( + CartContainsProducts::PRODUCTS_LIST => array( + 'availableOperators' => array( + 'in' => 'Price', + 'out' => 'Price', + ), + 'value' => '', + 'selectedOperator' => 'in' + ) + ), + 'setOperators' => array( + 'products' => 'in' + ), + 'setValues' => array( + 'products' => array(50, 60) + ) + ); + $expected = $validators; + + $this->assertEquals($expected, $actual); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + +} diff --git a/core/lib/Thelia/Tests/Condition/Implementation/ForSomeCustomersTest.php b/core/lib/Thelia/Tests/Condition/Implementation/ForSomeCustomersTest.php new file mode 100644 index 000000000..018d7210d --- /dev/null +++ b/core/lib/Thelia/Tests/Condition/Implementation/ForSomeCustomersTest.php @@ -0,0 +1,304 @@ + + */ +class ForSomeCustomersTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $customer = new Customer(); + $customer->setId(10); + + $stubFacade->expects($this->any()) + ->method('getCustomer') + ->will($this->returnValue($customer)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::setValidators + * @expectedException \Thelia\Exception\InvalidConditionOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new ForSomeCustomers($stubFacade); + $operators = array( + ForSomeCustomers::CUSTOMERS_LIST => Operators::INFERIOR_OR_EQUAL + ); + $values = array( + ForSomeCustomers::CUSTOMERS_LIST => array() + ); + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::setValidators + * @expectedException \Thelia\Exception\InvalidConditionValueException + */ + public function testInValidBackOfficeInputValue() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new ForSomeCustomers($stubFacade); + $operators = array( + ForSomeCustomers::CUSTOMERS_LIST => Operators::IN + ); + $values = array( + ForSomeCustomers::CUSTOMERS_LIST => array() + ); + + $condition1->setValidatorsFromForm($operators, $values); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::isMatching + * + */ + public function testMatchingRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new ForSomeCustomers($stubFacade); + $operators = array( + ForSomeCustomers::CUSTOMERS_LIST => Operators::IN + ); + $values = array( + ForSomeCustomers::CUSTOMERS_LIST => array(10, 20) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::isMatching + * + */ + public function testNotMatching() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new ForSomeCustomers($stubFacade); + + $operators = array( + ForSomeCustomers::CUSTOMERS_LIST => Operators::IN + ); + $values = array( + ForSomeCustomers::CUSTOMERS_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = false; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + public function testGetSerializableRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new ForSomeCustomers($stubFacade); + + $operators = array( + ForSomeCustomers::CUSTOMERS_LIST => Operators::IN + ); + $values = array( + ForSomeCustomers::CUSTOMERS_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $serializableRule = $condition1->getSerializableCondition(); + + $expected = new SerializableCondition(); + $expected->conditionServiceId = $condition1->getServiceId(); + $expected->operators = $operators; + $expected->values = $values; + + $actual = $serializableRule; + + $this->assertEquals($expected, $actual); + + } + + /** + * Check getName i18n + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::getName + * + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Number of articles in cart'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new ForSomeCustomers($stubFacade); + + $actual = $condition1->getName(); + $expected = 'Number of articles in cart'; + $this->assertEquals($expected, $actual); + } + + /** + * Check tooltip i18n + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::getToolTip + * + */ + public function testGetToolTip() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Sample coupon condition'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new ForSomeCustomers($stubFacade); + + $actual = $condition1->getToolTip(); + $expected = 'Sample coupon condition'; + $this->assertEquals($expected, $actual); + } + + /** + * Check validator + * + * @covers Thelia\Condition\Implementation\ForSomeCustomers::generateInputs + * + */ + public function testGetValidator() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Price'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new ForSomeCustomers($stubFacade); + + $operators = array( + ForSomeCustomers::CUSTOMERS_LIST => Operators::IN + ); + $values = array( + ForSomeCustomers::CUSTOMERS_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $actual = $condition1->getValidators(); + + $validators = array( + 'inputs' => array( + ForSomeCustomers::CUSTOMERS_LIST => array( + 'availableOperators' => array( + 'in' => 'Price', + 'out' => 'Price', + ), + 'value' => '', + 'selectedOperator' => 'in' + ) + ), + 'setOperators' => array( + 'customers' => 'in' + ), + 'setValues' => array( + 'customers' => array(50, 60) + ) + ); + $expected = $validators; + + $this->assertEquals($expected, $actual); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + +} diff --git a/core/lib/Thelia/Tests/Condition/Implementation/MatchBillingCountriesTest.php b/core/lib/Thelia/Tests/Condition/Implementation/MatchBillingCountriesTest.php new file mode 100644 index 000000000..61ac53c2a --- /dev/null +++ b/core/lib/Thelia/Tests/Condition/Implementation/MatchBillingCountriesTest.php @@ -0,0 +1,312 @@ + + */ +class MatchBillingCountriesTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $address = new Address(); + $address->setCountryId(10); + + $stubCustomer = $this->getMockBuilder('\Thelia\Model\Customer') + ->disableOriginalConstructor() + ->getMock(); + + $stubCustomer->expects($this->any()) + ->method('getDefaultAddress') + ->will($this->returnValue($address)); + + $stubFacade->expects($this->any()) + ->method('getCustomer') + ->will($this->returnValue($stubCustomer)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::setValidators + * @expectedException \Thelia\Exception\InvalidConditionOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchBillingCountries($stubFacade); + $operators = array( + MatchBillingCountries::COUNTRIES_LIST => Operators::INFERIOR_OR_EQUAL + ); + $values = array( + MatchBillingCountries::COUNTRIES_LIST => array() + ); + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::setValidators + * @expectedException \Thelia\Exception\InvalidConditionValueException + */ + public function testInValidBackOfficeInputValue() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchBillingCountries($stubFacade); + $operators = array( + MatchBillingCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchBillingCountries::COUNTRIES_LIST => array() + ); + + $condition1->setValidatorsFromForm($operators, $values); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::isMatching + * + */ + public function testMatchingRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchBillingCountries($stubFacade); + $operators = array( + MatchBillingCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchBillingCountries::COUNTRIES_LIST => array(10, 20) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::isMatching + * + */ + public function testNotMatching() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchBillingCountries($stubFacade); + + $operators = array( + MatchBillingCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchBillingCountries::COUNTRIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = false; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + public function testGetSerializableRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchBillingCountries($stubFacade); + + $operators = array( + MatchBillingCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchBillingCountries::COUNTRIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $serializableRule = $condition1->getSerializableCondition(); + + $expected = new SerializableCondition(); + $expected->conditionServiceId = $condition1->getServiceId(); + $expected->operators = $operators; + $expected->values = $values; + + $actual = $serializableRule; + + $this->assertEquals($expected, $actual); + + } + + /** + * Check getName i18n + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::getName + * + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Number of articles in cart'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new MatchBillingCountries($stubFacade); + + $actual = $condition1->getName(); + $expected = 'Number of articles in cart'; + $this->assertEquals($expected, $actual); + } + + /** + * Check tooltip i18n + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::getToolTip + * + */ + public function testGetToolTip() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Sample coupon condition'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new MatchBillingCountries($stubFacade); + + $actual = $condition1->getToolTip(); + $expected = 'Sample coupon condition'; + $this->assertEquals($expected, $actual); + } + + /** + * Check validator + * + * @covers Thelia\Condition\Implementation\MatchBillingCountries::generateInputs + * + */ + public function testGetValidator() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Price'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new MatchBillingCountries($stubFacade); + + $operators = array( + MatchBillingCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchBillingCountries::COUNTRIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $actual = $condition1->getValidators(); + + $validators = array( + 'inputs' => array( + MatchBillingCountries::COUNTRIES_LIST => array( + 'availableOperators' => array( + 'in' => 'Price', + 'out' => 'Price', + ), + 'value' => '', + 'selectedOperator' => 'in' + ) + ), + 'setOperators' => array( + 'countries' => 'in' + ), + 'setValues' => array( + 'countries' => array(50, 60) + ) + ); + $expected = $validators; + + $this->assertEquals($expected, $actual); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + +} diff --git a/core/lib/Thelia/Tests/Condition/Implementation/MatchDeliveryCountriesTest.php b/core/lib/Thelia/Tests/Condition/Implementation/MatchDeliveryCountriesTest.php new file mode 100644 index 000000000..623a1aeef --- /dev/null +++ b/core/lib/Thelia/Tests/Condition/Implementation/MatchDeliveryCountriesTest.php @@ -0,0 +1,304 @@ + + */ +class MatchDeliveryCountriesTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $address = new Address(); + $address->setCountryId(10); + + $stubFacade->expects($this->any()) + ->method('getDeliveryAddress') + ->will($this->returnValue($address)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::setValidators + * @expectedException \Thelia\Exception\InvalidConditionOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchDeliveryCountries($stubFacade); + $operators = array( + MatchDeliveryCountries::COUNTRIES_LIST => Operators::INFERIOR_OR_EQUAL + ); + $values = array( + MatchDeliveryCountries::COUNTRIES_LIST => array() + ); + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::setValidators + * @expectedException \Thelia\Exception\InvalidConditionValueException + */ + public function testInValidBackOfficeInputValue() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchDeliveryCountries($stubFacade); + $operators = array( + MatchDeliveryCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchDeliveryCountries::COUNTRIES_LIST => array() + ); + + $condition1->setValidatorsFromForm($operators, $values); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::isMatching + * + */ + public function testMatchingRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchDeliveryCountries($stubFacade); + $operators = array( + MatchDeliveryCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchDeliveryCountries::COUNTRIES_LIST => array(10, 20) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::isMatching + * + */ + public function testNotMatching() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchDeliveryCountries($stubFacade); + + $operators = array( + MatchDeliveryCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchDeliveryCountries::COUNTRIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = false; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + public function testGetSerializableRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new MatchDeliveryCountries($stubFacade); + + $operators = array( + MatchDeliveryCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchDeliveryCountries::COUNTRIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $serializableRule = $condition1->getSerializableCondition(); + + $expected = new SerializableCondition(); + $expected->conditionServiceId = $condition1->getServiceId(); + $expected->operators = $operators; + $expected->values = $values; + + $actual = $serializableRule; + + $this->assertEquals($expected, $actual); + + } + + /** + * Check getName i18n + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::getName + * + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Number of articles in cart'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new MatchDeliveryCountries($stubFacade); + + $actual = $condition1->getName(); + $expected = 'Number of articles in cart'; + $this->assertEquals($expected, $actual); + } + + /** + * Check tooltip i18n + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::getToolTip + * + */ + public function testGetToolTip() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Sample coupon condition'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new MatchDeliveryCountries($stubFacade); + + $actual = $condition1->getToolTip(); + $expected = 'Sample coupon condition'; + $this->assertEquals($expected, $actual); + } + + /** + * Check validator + * + * @covers Thelia\Condition\Implementation\MatchDeliveryCountries::generateInputs + * + */ + public function testGetValidator() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Price'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new MatchDeliveryCountries($stubFacade); + + $operators = array( + MatchDeliveryCountries::COUNTRIES_LIST => Operators::IN + ); + $values = array( + MatchDeliveryCountries::COUNTRIES_LIST => array(50, 60) + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $actual = $condition1->getValidators(); + + $validators = array( + 'inputs' => array( + MatchDeliveryCountries::COUNTRIES_LIST => array( + 'availableOperators' => array( + 'in' => 'Price', + 'out' => 'Price', + ), + 'value' => '', + 'selectedOperator' => 'in' + ) + ), + 'setOperators' => array( + 'countries' => 'in' + ), + 'setValues' => array( + 'countries' => array(50, 60) + ) + ); + $expected = $validators; + + $this->assertEquals($expected, $actual); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + +} diff --git a/core/lib/Thelia/Tests/Condition/Implementation/MatchForEveryoneTest.php b/core/lib/Thelia/Tests/Condition/Implementation/MatchForEveryoneTest.php index ddf3d25ae..84b57afb1 100644 --- a/core/lib/Thelia/Tests/Condition/Implementation/MatchForEveryoneTest.php +++ b/core/lib/Thelia/Tests/Condition/Implementation/MatchForEveryoneTest.php @@ -13,7 +13,6 @@ namespace Thelia\Condition\Implementation; use Thelia\Condition\ConditionEvaluator; - use Thelia\Coupon\FacadeInterface; use Thelia\Model\Currency; diff --git a/core/lib/Thelia/Tests/Condition/Implementation/MatchForTotalAmountTest.php b/core/lib/Thelia/Tests/Condition/Implementation/MatchForTotalAmountTest.php index bffbda092..fa599a866 100644 --- a/core/lib/Thelia/Tests/Condition/Implementation/MatchForTotalAmountTest.php +++ b/core/lib/Thelia/Tests/Condition/Implementation/MatchForTotalAmountTest.php @@ -12,13 +12,12 @@ namespace Thelia\Tests\Condition\Implementation; +use Thelia\Condition\ConditionCollection; use Thelia\Condition\ConditionEvaluator; use Thelia\Condition\ConditionFactory; use Thelia\Condition\Implementation\MatchForTotalAmount; use Thelia\Condition\Operators; -use Thelia\Condition\ConditionCollection; use Thelia\Coupon\FacadeInterface; - use Thelia\Model\Currency; use Thelia\Model\CurrencyQuery; diff --git a/core/lib/Thelia/Tests/Condition/Implementation/StartDateTest.php b/core/lib/Thelia/Tests/Condition/Implementation/StartDateTest.php new file mode 100644 index 000000000..6df0fddbb --- /dev/null +++ b/core/lib/Thelia/Tests/Condition/Implementation/StartDateTest.php @@ -0,0 +1,329 @@ + + */ +class StartDateTest extends \PHPUnit_Framework_TestCase +{ + + var $startDate; + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->startDate = time() - 2000; + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $address = new Address(); + $address->setCountryId(10); + + $stubFacade->expects($this->any()) + ->method('getDeliveryAddress') + ->will($this->returnValue($address)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + $lang = new Lang(); + $lang->setDateFormat("d/m/Y"); + + $stubSession = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Session\Session') + ->disableOriginalConstructor() + ->getMock(); + + $stubSession->expects($this->any()) + ->method('getLang') + ->will($this->returnValue($lang)); + + $stubRequest = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $stubRequest->expects($this->any()) + ->method('getSession') + ->will($this->returnValue($stubSession)); + + $stubFacade->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($stubRequest)); + + return $stubFacade; + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\StartDate::setValidators + * @expectedException \Thelia\Exception\InvalidConditionOperatorException + */ + public function testInValidBackOfficeInputOperator() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new StartDate($stubFacade); + + $operators = array( + StartDate::START_DATE => 'petite licorne' + ); + $values = array( + StartDate::START_DATE => $this->startDate + ); + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if validity test on BackOffice inputs are working + * + * @covers Thelia\Condition\Implementation\StartDate::setValidators + * @expectedException \Thelia\Exception\InvalidConditionValueException + */ + public function testInValidBackOfficeInputValue() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new StartDate($stubFacade); + $operators = array( + StartDate::START_DATE => Operators::SUPERIOR_OR_EQUAL + ); + $values = array( + StartDate::START_DATE => 'petit poney' + ); + + $condition1->setValidatorsFromForm($operators, $values); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\StartDate::isMatching + * + */ + public function testMatchingRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new StartDate($stubFacade); + $operators = array( + StartDate::START_DATE => Operators::SUPERIOR_OR_EQUAL + ); + $values = array( + StartDate::START_DATE => $this->startDate + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = true; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + /** + * Check if test inferior operator is working + * + * @covers Thelia\Condition\Implementation\StartDate::isMatching + * + */ + public function testNotMatching() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new StartDate($stubFacade); + + $operators = array( + StartDate::START_DATE => Operators::SUPERIOR_OR_EQUAL + ); + $values = array( + StartDate::START_DATE => time() + 2000 + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $isValid = $condition1->isMatching(); + + $expected = false; + $actual =$isValid; + $this->assertEquals($expected, $actual); + } + + public function testGetSerializableRule() + { + /** @var FacadeInterface $stubFacade */ + $stubFacade = $this->generateFacadeStub(); + + $condition1 = new StartDate($stubFacade); + + $operators = array( + StartDate::START_DATE => Operators::SUPERIOR_OR_EQUAL + ); + $values = array( + StartDate::START_DATE => $this->startDate + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $serializableRule = $condition1->getSerializableCondition(); + + $expected = new SerializableCondition(); + $expected->conditionServiceId = $condition1->getServiceId(); + $expected->operators = $operators; + $expected->values = $values; + + $actual = $serializableRule; + + $this->assertEquals($expected, $actual); + + } + + /** + * Check getName i18n + * + * @covers Thelia\Condition\Implementation\StartDate::getName + * + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Number of articles in cart'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new StartDate($stubFacade); + + $actual = $condition1->getName(); + $expected = 'Number of articles in cart'; + $this->assertEquals($expected, $actual); + } + + /** + * Check tooltip i18n + * + * @covers Thelia\Condition\Implementation\StartDate::getToolTip + * + */ + public function testGetToolTip() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Sample coupon condition'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new StartDate($stubFacade); + + $actual = $condition1->getToolTip(); + $expected = 'Sample coupon condition'; + $this->assertEquals($expected, $actual); + } + + /** + * Check validator + * + * @covers Thelia\Condition\Implementation\StartDate::generateInputs + * + */ + public function testGetValidator() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Price'); + + /** @var FacadeInterface $stubFacade */ + $condition1 = new StartDate($stubFacade); + + $operators = array( + StartDate::START_DATE => Operators::SUPERIOR_OR_EQUAL + ); + $values = array( + StartDate::START_DATE => $this->startDate + ); + + $condition1->setValidatorsFromForm($operators, $values); + + $actual = $condition1->getValidators(); + + $validators = array( + 'inputs' => array( + StartDate::START_DATE => array( + 'availableOperators' => array( + '>=' => 'Price', + ), + 'value' => '', + 'selectedOperator' => '>=' + ) + ), + 'setOperators' => array( + 'start_date' => '>=' + ), + 'setValues' => array( + 'start_date' => $this->startDate + ) + ); + $expected = $validators; + + $this->assertEquals($expected, $actual); + + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/FreeProductTest.php b/core/lib/Thelia/Tests/Coupon/Type/FreeProductTest.php new file mode 100644 index 000000000..72be02204 --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/FreeProductTest.php @@ -0,0 +1,412 @@ + + */ +class FreeProductTest extends \PHPUnit_Framework_TestCase +{ + + /** @var Product $freeProduct */ + var $freeProduct; + var $originalPrice; + var $originalPromo; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $currency = CurrencyQuery::create()->filterByCode('EUR')->findOne(); + + // Find a product + $this->freeProduct = ProductQuery::create()->findOne(); + + $this->originalPrice = $this->freeProduct->getDefaultSaleElements()->getPricesByCurrency($currency)->getPrice(); + $this->originalPromo = $this->freeProduct->getDefaultSaleElements()->getPromo(); + + $this->freeProduct->getDefaultSaleElements()->setPromo(false)->save(); + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + $stubDispatcher = $this->getMockBuilder('\Symfony\Component\EventDispatcher\EventDispatcher') + ->disableOriginalConstructor() + ->getMock(); + + $stubDispatcher->expects($this->any()) + ->method('dispatch') + ->will($this->returnCallback(function($dummy, $cartEvent) { + $ci = new CartItem(); + $ci->setId(3)->setPrice(123)->setPromo(0); + + $cartEvent->setCartItem($ci); + })); + + $stubFacade->expects($this->any()) + ->method('getDispatcher') + ->will($this->returnValue($stubDispatcher)); + + $stubSession = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Session\Session') + ->disableOriginalConstructor() + ->getMock(); + + $stubSession->expects($this->any()) + ->method('get') + ->will($this->onConsecutiveCalls(-1, 3)); + + $stubRequest = $this->getMockBuilder('\Thelia\Core\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $stubRequest->expects($this->any()) + ->method('getSession') + ->will($this->returnValue($stubSession)); + + $stubFacade->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($stubRequest)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade, $count) { + + $product1 = new Product(); + $product1->setId(10); + + $product2 = new Product(); + $product2->setId(20); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(100)) + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(150)) + + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + if ($count == 1) + $ret = [$cartItem1Stub]; + else + $ret = [$cartItem1Stub, $cartItem2Stub]; + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue($ret)); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $product2 = new Product(); + $product2->setId(30); + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)) + ; + $cartItem2Stub->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(11000)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new FreeProduct($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20], 'offered_product_id' => $this->freeProduct->getId(), 'offered_category_id' => 1), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + } + + public function testMatchOne() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new FreeProduct($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20], 'offered_product_id' => $this->freeProduct->getId(), 'offered_category_id' => 1), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 1); + + $this->assertEquals(123.00, $coupon->exec()); + } + + public function testMatchSeveral() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new FreeProduct($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20], 'offered_product_id' => $this->freeProduct->getId(), 'offered_category_id' => 1), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 2); + + $this->assertEquals(123.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new FreeProduct($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20], 'offered_product_id' => $this->freeProduct->getId(), 'offered_category_id' => 1), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new FreeProduct($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new FreeProduct($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->freeProduct->getDefaultSaleElements()->setPromo($this->originalPromo)->save(); + } +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnAttributeValuesTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnAttributeValuesTest.php new file mode 100644 index 000000000..67bd1ce60 --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnAttributeValuesTest.php @@ -0,0 +1,398 @@ + + */ +class RemoveAmountOnAttributeValuesTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade, $count) { + + $attrCombination1 = $this->getMockBuilder('\Thelia\Model\AttributeCombination') + ->disableOriginalConstructor() + ->getMock() + ; + + $attrCombination1 + ->expects($this->any()) + ->method('getAttributeAvId') + ->will($this->returnValue(10)) + ; + + $attrCombination2 = $this->getMockBuilder('\Thelia\Model\AttributeCombination') + ->disableOriginalConstructor() + ->getMock() + ; + + $attrCombination2 + ->expects($this->any()) + ->method('getAttributeAvId') + ->will($this->returnValue(20)) + ; + + $pse1 = $this->getMockBuilder('\Thelia\Model\ProductSaleElements') + ->disableOriginalConstructor() + ->getMock() + ; + $pse1 + ->expects($this->any()) + ->method('getAttributeCombinations') + ->will($this->returnValue([$attrCombination1])) + ; + + $pse2 = $this->getMockBuilder('\Thelia\Model\ProductSaleElements') + ->disableOriginalConstructor() + ->getMock(); + ; + $pse2 + ->expects($this->any()) + ->method('getAttributeCombinations') + ->will($this->returnValue([$attrCombination1, $attrCombination2])) + ; + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock() + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getProductSaleElements') + ->will($this->returnValue($pse1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock() + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getProductSaleElements') + ->will($this->returnValue($pse2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + if ($count == 1) + $ret = [$cartItem1Stub]; + else + $ret = [$cartItem1Stub, $cartItem2Stub]; + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue($ret)); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $attrCombination1 = $this->getMockBuilder('\Thelia\Model\AttributeCombination') + ->disableOriginalConstructor() + ->getMock() + ; + + $attrCombination1 + ->expects($this->any()) + ->method('getAttributeAvId') + ->will($this->returnValue(30)) + ; + + $pse1 = $this->getMockBuilder('\Thelia\Model\ProductSaleElements') + ->disableOriginalConstructor() + ->getMock() + ; + $pse1 + ->expects($this->any()) + ->method('getAttributeCombinations') + ->will($this->returnValue([$attrCombination1])) + ; + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock() + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getProductSaleElements') + ->will($this->returnValue($pse1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem1Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + } + + public function testMatchOne() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 1); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testMatchSeveral() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 2); + + $this->assertEquals(30.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemoveAmountOnAttributeValues($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemoveAmountOnAttributeValues($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnCategoriesTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnCategoriesTest.php new file mode 100644 index 000000000..01be96bde --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnCategoriesTest.php @@ -0,0 +1,330 @@ + + */ +class RemoveAmountOnCategoriesTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $category1 = new Category(); + $category1->setId(10); + + $category2 = new Category(); + $category2->setId(20); + + $category3 = new Category(); + $category3->setId(30); + + $product1 = new Product(); + $product1->addCategory($category1)->addCategory($category2); + + $product2 = new Product(); + $product2->addCategory($category3); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem1Stub, $cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $category3 = new Category(); + $category3->setId(30); + + $product2 = new Product(); + $product2->addCategory($category3); + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)); + + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnCategories($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'categories' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + + $this->generateMatchingCart($stubFacade); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnCategories($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'categories' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnCategories($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'categories' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemoveAmountOnCategories($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemoveAmountOnCategories($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnProductsTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnProductsTest.php new file mode 100644 index 000000000..55e64f861 --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/RemoveAmountOnProductsTest.php @@ -0,0 +1,343 @@ + + */ +class RemoveAmountOnProductsTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade, $count) { + + $product1 = new Product(); + $product1->setId(10); + + $product2 = new Product(); + $product2->setId(20); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + if ($count == 1) + $ret = [$cartItem1Stub]; + else + $ret = [$cartItem1Stub, $cartItem2Stub]; + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue($ret)); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $product2 = new Product(); + $product2->setId(30); + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)); + + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + } + + public function testMatchOne() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 1); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testMatchSeveral() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 2); + + $this->assertEquals(30.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemoveAmountOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('amount' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemoveAmountOnProducts($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemoveAmountOnProducts($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnAttributeValuesTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnAttributeValuesTest.php new file mode 100644 index 000000000..eaa5e4de9 --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnAttributeValuesTest.php @@ -0,0 +1,412 @@ + + */ +class RemovePercentageOnAttributeValuesTest extends \PHPUnit_Framework_TestCase +{ + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade, $count) { + + $attrCombination1 = $this->getMockBuilder('\Thelia\Model\AttributeCombination') + ->disableOriginalConstructor() + ->getMock() + ; + + $attrCombination1 + ->expects($this->any()) + ->method('getAttributeAvId') + ->will($this->returnValue(10)) + ; + + $attrCombination2 = $this->getMockBuilder('\Thelia\Model\AttributeCombination') + ->disableOriginalConstructor() + ->getMock() + ; + + $attrCombination2 + ->expects($this->any()) + ->method('getAttributeAvId') + ->will($this->returnValue(20)) + ; + + $pse1 = $this->getMockBuilder('\Thelia\Model\ProductSaleElements') + ->disableOriginalConstructor() + ->getMock() + ; + $pse1 + ->expects($this->any()) + ->method('getAttributeCombinations') + ->will($this->returnValue([$attrCombination1])) + ; + + $pse2 = $this->getMockBuilder('\Thelia\Model\ProductSaleElements') + ->disableOriginalConstructor() + ->getMock(); + ; + $pse2 + ->expects($this->any()) + ->method('getAttributeCombinations') + ->will($this->returnValue([$attrCombination1, $attrCombination2])) + ; + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock() + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getProductSaleElements') + ->will($this->returnValue($pse1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(100)) + ; + + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock() + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getProductSaleElements') + ->will($this->returnValue($pse2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(150)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + if ($count == 1) + $ret = [$cartItem1Stub]; + else + $ret = [$cartItem1Stub, $cartItem2Stub]; + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue($ret)); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $attrCombination1 = $this->getMockBuilder('\Thelia\Model\AttributeCombination') + ->disableOriginalConstructor() + ->getMock() + ; + + $attrCombination1 + ->expects($this->any()) + ->method('getAttributeAvId') + ->will($this->returnValue(30)) + ; + + $pse1 = $this->getMockBuilder('\Thelia\Model\ProductSaleElements') + ->disableOriginalConstructor() + ->getMock() + ; + $pse1 + ->expects($this->any()) + ->method('getAttributeCombinations') + ->will($this->returnValue([$attrCombination1])) + ; + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock() + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getProductSaleElements') + ->will($this->returnValue($pse1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(100)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem1Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + } + + public function testMatchOne() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 1); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testMatchSeveral() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 2); + + $this->assertEquals(40.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnAttributeValues($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'attribute_avs' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemovePercentageOnAttributeValues($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemovePercentageOnAttributeValues($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnCategoriesTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnCategoriesTest.php new file mode 100644 index 000000000..90b845531 --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnCategoriesTest.php @@ -0,0 +1,346 @@ + + */ +class RemovePercentageOnCategoriesTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $category1 = new Category(); + $category1->setId(10); + + $category2 = new Category(); + $category2->setId(20); + + $category3 = new Category(); + $category3->setId(30); + + $product1 = new Product(); + $product1->addCategory($category1)->addCategory($category2); + + $product2 = new Product(); + $product2->addCategory($category3); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(100)) + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(200)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem1Stub, $cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $category3 = new Category(); + $category3->setId(30); + + $product2 = new Product(); + $product2->addCategory($category3); + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(200000)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnCategories($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10, 'categories' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + } + + public function testMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnCategories($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10, 'categories' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnCategories($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10, 'categories' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + /** + * @covers Thelia\Coupon\Type\RemoveXAmount::getName + */ + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemovePercentageOnCategories($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + /** + * @covers Thelia\Coupon\Type\RemoveXAmount::getToolTip + */ + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemovePercentageOnCategories($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } +} diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnProductsTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnProductsTest.php new file mode 100644 index 000000000..4c150b969 --- /dev/null +++ b/core/lib/Thelia/Tests/Coupon/Type/RemovePercentageOnProductsTest.php @@ -0,0 +1,360 @@ + + */ +class RemovePercentageOnProductsTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + } + + /** + * Generate adapter stub + * + * @param int $cartTotalPrice Cart total price + * @param string $checkoutCurrency Checkout currency + * @param string $i18nOutput Output from each translation + * + * @return \PHPUnit_Framework_MockObject_MockObject + */ + public function generateFacadeStub($cartTotalPrice = 400, $checkoutCurrency = 'EUR', $i18nOutput = '') + { + $stubFacade = $this->getMockBuilder('\Thelia\Coupon\BaseFacade') + ->disableOriginalConstructor() + ->getMock(); + + $currencies = CurrencyQuery::create(); + $currencies = $currencies->find(); + $stubFacade->expects($this->any()) + ->method('getAvailableCurrencies') + ->will($this->returnValue($currencies)); + + $stubFacade->expects($this->any()) + ->method('getCartTotalPrice') + ->will($this->returnValue($cartTotalPrice)); + + $stubFacade->expects($this->any()) + ->method('getCheckoutCurrency') + ->will($this->returnValue($checkoutCurrency)); + + $stubFacade->expects($this->any()) + ->method('getConditionEvaluator') + ->will($this->returnValue(new ConditionEvaluator())); + + $stubTranslator = $this->getMockBuilder('\Thelia\Core\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $stubTranslator->expects($this->any()) + ->method('trans') + ->will($this->returnValue($i18nOutput)); + + $stubFacade->expects($this->any()) + ->method('getTranslator') + ->will($this->returnValue($stubTranslator)); + + return $stubFacade; + } + + public function generateMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade, $count) { + + $product1 = new Product(); + $product1->setId(10); + + $product2 = new Product(); + $product2->setId(20); + + $cartItem1Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem1Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product1)) + ; + + $cartItem1Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(1)) + ; + $cartItem1Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(100)) + + ; + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub + ->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)); + + $cartItem2Stub + ->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(150)) + + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + if ($count == 1) + $ret = [$cartItem1Stub]; + else + $ret = [$cartItem1Stub, $cartItem2Stub]; + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue($ret)); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function generateNoMatchingCart(\PHPUnit_Framework_MockObject_MockObject $stubFacade) { + + $product2 = new Product(); + $product2->setId(30); + + $cartItem2Stub = $this->getMockBuilder('\Thelia\Model\CartItem') + ->disableOriginalConstructor() + ->getMock(); + + $cartItem2Stub->expects($this->any()) + ->method('getProduct') + ->will($this->returnValue($product2)) + ; + $cartItem2Stub->expects($this->any()) + ->method('getQuantity') + ->will($this->returnValue(2)) + ; + $cartItem2Stub + ->expects($this->any()) + ->method('getPrice') + ->will($this->returnValue(11000)) + ; + + $cartStub = $this->getMockBuilder('\Thelia\Model\Cart') + ->disableOriginalConstructor() + ->getMock(); + + $cartStub + ->expects($this->any()) + ->method('getCartItems') + ->will($this->returnValue([$cartItem2Stub])); + + $stubFacade->expects($this->any()) + ->method('getCart') + ->will($this->returnValue($cartStub)); + } + + public function testSet() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $condition1 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::SUPERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 40.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition1->setValidatorsFromForm($operators, $values); + + $condition2 = new MatchForTotalAmount($stubFacade); + $operators = array( + MatchForTotalAmount::CART_TOTAL => Operators::INFERIOR, + MatchForTotalAmount::CART_CURRENCY => Operators::EQUAL + ); + $values = array( + MatchForTotalAmount::CART_TOTAL => 400.00, + MatchForTotalAmount::CART_CURRENCY => 'EUR' + ); + $condition2->setValidatorsFromForm($operators, $values); + + $conditions = new ConditionCollection(); + $conditions[] = $condition1; + $conditions[] = $condition2; + $coupon->setConditions($conditions); + + $this->assertEquals('TEST', $coupon->getCode()); + $this->assertEquals('TEST Coupon', $coupon->getTitle()); + $this->assertEquals('This is a test coupon title', $coupon->getShortDescription()); + $this->assertEquals('This is a test coupon description', $coupon->getDescription()); + + $this->assertEquals(true, $coupon->isCumulative()); + $this->assertEquals(true, $coupon->isRemovingPostage()); + $this->assertEquals(true, $coupon->isAvailableOnSpecialOffers()); + $this->assertEquals(true, $coupon->isEnabled()); + + $this->assertEquals(254, $coupon->getMaxUsage()); + $this->assertEquals($date, $coupon->getExpirationDate()); + } + + public function testMatchOne() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 1); + + $this->assertEquals(10.00, $coupon->exec()); + } + + public function testMatchSeveral() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateMatchingCart($stubFacade, 2); + + $this->assertEquals(40.00, $coupon->exec()); + } + + public function testNoMatch() + { + $stubFacade = $this->generateFacadeStub(); + + $coupon = new RemovePercentageOnProducts($stubFacade); + + $date = new \DateTime(); + + $coupon->set( + $stubFacade, + 'TEST', 'TEST Coupon', 'This is a test coupon title', 'This is a test coupon description', + array('percentage' => 10.00, 'products' => [10, 20]), + true, true, true, true, + 254, + $date->setTimestamp(strtotime("today + 3 months")), + new ObjectCollection(), + new ObjectCollection(), + false + ); + + $this->generateNoMatchingCart($stubFacade); + + $this->assertEquals(0.00, $coupon->exec()); + } + + public function testGetName() + { + $stubFacade = $this->generateFacadeStub(399, 'EUR', 'Coupon test name'); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemovePercentageOnProducts($stubFacade); + + $actual = $coupon->getName(); + $expected = 'Coupon test name'; + $this->assertEquals($expected, $actual); + } + + public function testGetToolTip() + { + $tooltip = 'Coupon test tooltip'; + $stubFacade = $this->generateFacadeStub(399, 'EUR', $tooltip); + + /** @var FacadeInterface $stubFacade */ + $coupon = new RemovePercentageOnProducts($stubFacade); + + $actual = $coupon->getToolTip(); + $expected = $tooltip; + $this->assertEquals($expected, $actual); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } +} diff --git a/local/modules/Front/Config/front.xml b/local/modules/Front/Config/front.xml index 0a987eaa2..c37b119ad 100644 --- a/local/modules/Front/Config/front.xml +++ b/local/modules/Front/Config/front.xml @@ -160,6 +160,11 @@ order-invoice + + Front\Controller\CouponController::clearAllCouponsAction + order-invoice + + Front\Controller\OrderController::pay diff --git a/local/modules/Front/Controller/CouponController.php b/local/modules/Front/Controller/CouponController.php index 98333dd0a..8bce7b206 100644 --- a/local/modules/Front/Controller/CouponController.php +++ b/local/modules/Front/Controller/CouponController.php @@ -40,9 +40,16 @@ use Thelia\Module\Exception\DeliveryException; */ class CouponController extends BaseFrontController { + /** + * Clear all coupons. + */ + public function clearAllCouponsAction() { + // Dispatch Event to the Action + $this->getDispatcher()->dispatch(TheliaEvents::COUPON_CLEAR_ALL); + } /** - * Test Coupon consuming + * Coupon consuming */ public function consumeAction() { diff --git a/templates/backOffice/default/I18n/en_US.php b/templates/backOffice/default/I18n/en_US.php index 025f1fb20..934dad7df 100644 --- a/templates/backOffice/default/I18n/en_US.php +++ b/templates/backOffice/default/I18n/en_US.php @@ -62,6 +62,7 @@ return array( 'Amount, e.g. 12.50' => 'Amount, e.g. 12.50', 'An error occured' => 'An error occured', 'And' => 'And', + 'Applies to attribute values :' => 'Applies to attribute values :', 'Applies to products :' => 'Applies to products :', 'Applies to products in categories :' => 'Applies to products in categories :', 'Apply' => 'Apply', @@ -97,6 +98,7 @@ return array( 'Catalog' => 'Catalog', 'Categories' => 'Categories', 'Categories in %cat' => 'Categories in %cat', + 'Category' => 'Category', 'Category created on %date_create. Last modification: %date_change' => 'Category created on %date_create. Last modification: %date_change', 'Category title' => 'Category title', 'Cellular phone' => 'Cellular phone', @@ -143,6 +145,7 @@ return array( 'Confirm' => 'Confirm', 'Confirm changes' => 'Confirm changes', 'Confirmation' => 'Confirmation', + 'Content' => 'Content', 'Content title' => 'Content title', 'Contents in %fold' => 'Contents in %fold', 'Copy source text in input field' => 'Copy source text in input field', @@ -331,7 +334,6 @@ return array( 'Do you really want to remove this zone ?' => 'Do you really want to remove this zone ?', 'Do you really want to set this coupon available to everyone ?' => 'Do you really want to set this coupon available to everyone ?', 'Do you really want to use this address by default ?' => 'Do you really want to use this address by default ?', - 'Document' => 'Document', 'Document informations' => 'Document information', 'Documents' => 'Documents', 'Don\'t repeat keywords over and over in a row. Rather, put in keyword phrases.' => 'Don\'t repeat keywords over and over in a row. Rather, put in keyword phrases.', @@ -493,6 +495,7 @@ return array( 'Flush the Thelia internal cache' => 'Flush the Thelia internal cache', 'Flush the assets cache directory' => 'Flush the assets cache directory', 'Flush the images and documents cache' => 'Flush the images and documents cache', + 'Folder' => 'Folder', 'Folder created on %date_create. Last modification: %date_change' => 'Folder created on %date_create. Last modification: %date_change', 'Folder title' => 'Folder title', 'Folders' => 'Folders', @@ -529,7 +532,6 @@ return array( 'If a translation is missing or incomplete :' => 'If a translation is missing or incomplete :', 'If yes, redirections through Redirect::exec() will be displayed as links' => 'If yes, redirections through Redirect::exec() will be displayed as links', 'Il seems that this string contains a Smarty variable ($). If \'s the case, it cannot be transleted properly.' => 'Il seems that this string contains a Smarty variable ($). If \'s the case, it cannot be transleted properly.', - 'Image' => 'Image', 'Image information' => 'Image information', 'Images' => 'Images', 'Impossible to change default country. Please contact your administrator or try later' => 'Impossible to change default country. Please contact your administrator or try later', @@ -766,8 +768,11 @@ return array( 'Select an attribute and click (+) to view available values' => 'Select an attribute and click (+) to view available values', 'Select an attribute value...' => 'Select an attribute value...', 'Select an attribute...' => 'Select an attribute...', + 'Select attribute :' => 'Select attribute :', 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.' => 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.', 'Select here the tax applicable to this product' => 'Select here the tax applicable to this product', + 'Select offered product :' => 'Select offered product :', + 'Select offrered product category :' => 'Select offrered product category :', 'Select product category :' => 'Select product category :', 'Send a mail to this customer' => 'Send a mail to this customer', 'Send files' => 'Send files', @@ -939,6 +944,7 @@ return array( 'Update this image' => 'Update this image', 'Usage count' => 'Usages left', 'Usages left' => 'Usages left', + 'Use Ctrl+click to select (or deselect) more that one attribute value' => 'Use Ctrl+click to select (or deselect) more that one attribute value', 'Use Ctrl+click to select (or deselect) more that one category' => 'Use Ctrl+click to select (or deselect) more that one category', 'Use Ctrl+click to select (or deselect) more that one country' => 'Utiliser Ctrl+clic pour sélectionner (ou dé-sélectionner) plusieurs pays.', 'Use Ctrl+click to select (or deselect) more that one product' => 'Use Ctrl+click to select (or deselect) more that one product', diff --git a/templates/backOffice/default/I18n/fr_FR.php b/templates/backOffice/default/I18n/fr_FR.php index 97b8e40ef..c00aea57c 100755 --- a/templates/backOffice/default/I18n/fr_FR.php +++ b/templates/backOffice/default/I18n/fr_FR.php @@ -59,8 +59,12 @@ return array( 'Alpha code 2' => 'Code alpha 2', 'Alpha code 3' => 'Code alpha 3', 'Amount' => 'Montant', + 'Amount, e.g. 12.50' => 'Montant, par ex. 12.50', 'An error occured' => 'Une erreur est survenue', 'And' => 'Et', + 'Applies to attribute values :' => 'Pour les valeurs de déclinaison :', + 'Applies to products :' => 'Pour les produits', + 'Applies to products in categories :' => 'Pour les produits des catégories :', 'Apply' => 'Appliquer', 'Associated countries' => 'Pays dans la zone', 'Associations' => 'Associations', @@ -286,6 +290,7 @@ return array( 'Disabled' => 'Désactivé', 'Discount' => 'Remise', 'Discount amount' => 'Montant de réduction', + 'Discount percentage' => 'Pourcentage de remise', 'Do not use a product template' => 'Ne pas utiliser de gabarit', 'Do you really want to add this attribute to all product templates ?' => 'Voulez-vous vraiment ajouter cette déclinaison de tous les gabarits de produit ?', 'Do you really want to add this feature to all product templates ?' => 'Voulez-vous vraiment ajouter cette caractéristique à tous les gabarits de produit ?', @@ -329,7 +334,6 @@ return array( 'Do you really want to remove this zone ?' => 'Voulez-vous vraiment supprimer cette zone ?', 'Do you really want to set this coupon available to everyone ?' => 'êtes-vous sur de vouloir rendre ce code promo disponible à tout le monde ?', 'Do you really want to use this address by default ?' => 'Voulez-vous vraiment utiliser cette adresse comme adresse par défaut ?', - 'Document' => 'Document', 'Document informations' => 'Informations du document', 'Documents' => 'Documents', 'Don\'t repeat keywords over and over in a row. Rather, put in keyword phrases.' => 'Ne répétez pas sans cesse les même mots-clés dans une ligne. Préférez utiliser des expressions de mots-clés', @@ -528,7 +532,6 @@ return array( 'If a translation is missing or incomplete :' => 'Si une traduction est manquante ou incomplète :', 'If yes, redirections through Redirect::exec() will be displayed as links' => 'Si oui, les redirections générés pas Redirect::exec seront affichés par des liens', 'Il seems that this string contains a Smarty variable ($). If \'s the case, it cannot be transleted properly.' => 'La chaîne semble contient une variable smarty ($). Si c\'est le cas elle ne peut pas être traduite correctement', - 'Image' => 'Image', 'Image information' => 'Information de l\'image', 'Images' => 'Images', 'Impossible to change default country. Please contact your administrator or try later' => 'Impossible de modifier le pays par défaut. Veuillez contacter votre administrateur ou réessayer plus tard', @@ -639,6 +642,7 @@ return array( 'Payment information' => 'Informations de paiement', 'Payment module' => 'Module de paiement', 'Payment modules' => 'Modules de payment', + 'Percent Discount' => 'Pourcentage de remise', 'Period' => 'Période', 'Phone' => 'Téléphone', 'Phone number' => 'Numéro de téléphone', @@ -655,6 +659,7 @@ return array( 'Please select the PDF template to translate' => 'Sélectionnez le template PDF à traduire', 'Please select the module component' => 'Choisissez un composant du module', 'Please select the module to translate' => 'Sélectionnez le module à traduire', + 'Please select...' => 'Merci de choisir...', 'Please wait, loading' => 'Chargement, veuillez patienter', 'Port' => 'Port', 'Port :' => 'Port : ', @@ -763,8 +768,12 @@ return array( 'Select an attribute and click (+) to view available values' => 'Sélectionnez une déclinaison et cliquez sur (+) pour voir les valeurs disponibles', 'Select an attribute value...' => 'Sélectionnez une valeur de déclinaison...', 'Select an attribute...' => 'Sélectionnez une déclinaison...', + 'Select attribute :' => 'Choisissez une déclinaison', 'Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.' => 'Sélectionnez les valeurs de déclinaison à combiner. Vous pouvez entrer une valeur par défaut pour certains champs des combinaisons générées.', 'Select here the tax applicable to this product' => 'Sélectionnez ici la taxe applicable sur ce produit', + 'Select offered product :' => 'Produit offert :', + 'Select offrered product category :' => 'Catégorie du produit offert :', + 'Select product category :' => 'Catégorie des propduits :', 'Send a mail to this customer' => 'Contacter ce client par mail', 'Send files' => 'Envoyer des fichiers', 'Sequential number of log line' => 'Sequential number of log line', @@ -935,6 +944,7 @@ return array( 'Update this image' => 'Modifier cette image', 'Usage count' => 'Nombre d\'utilisations', 'Usages left' => 'Utulisations restantes', + 'Use Ctrl+click to select (or deselect) more that one attribute value' => 'Ctrl+clic permet de séléctionner (ou dé-selectionner) plusieurs valeurs de déclinaison.', 'Use Ctrl+click to select (or deselect) more that one category' => 'Ctrl+Clic permet de sélectionner ou dé-sélectionner plus d\'une catégorie', 'Use Ctrl+click to select (or deselect) more that one country' => 'Ctrl+Clic permet de sélectionner ou dé-sélectionner plus d\'un pays', 'Use Ctrl+click to select (or deselect) more that one product' => 'Ctrl+Clic permet de sélectionner ou dé-sélectionner plus d\'un produit', diff --git a/templates/backOffice/default/assets/js/coupon.js b/templates/backOffice/default/assets/js/coupon.js index dae6d9574..1c7d962eb 100644 --- a/templates/backOffice/default/assets/js/coupon.js +++ b/templates/backOffice/default/assets/js/coupon.js @@ -214,10 +214,10 @@ $(function($){ // *********************************************** $.couponManager.displayEfffect = function(optionSelected) { - var mainDiv = $('#coupon-type'); - mainDiv.find('.typeToolTip').html(optionSelected.data('description')); + var typeDiv = $('#coupon-type'); + typeDiv.find('.typeToolTip').html(optionSelected.data('description')); - var inputsDiv = mainDiv.find('.inputs'); + var inputsDiv = $('.inputs', $('#coupon-inputs')); inputsDiv.html('
'); var url = $.couponManager.urlAjaxAdminCouponDrawInputs; url = url.replace('couponServiceId', optionSelected.val()); @@ -248,11 +248,11 @@ $(function($){ // Reload effect inputs when changing effect $.couponManager.onEffectChange = function() { - var mainDiv = $('#coupon-type'); - var optionSelected = mainDiv.find('#type option:selected'); - mainDiv.find('.typeToolTip').html(optionSelected.data('description')); + var typeDiv = $('#coupon-type'); + var optionSelected = typeDiv.find('#type option:selected'); + typeDiv.find('.typeToolTip').html(optionSelected.data('description')); - mainDiv.find('#type').on('change', function () { + typeDiv.find('#type').on('change', function () { var optionSelected = $('option:selected', this); $.couponManager.displayEfffect(optionSelected); diff --git a/templates/backOffice/default/coupon/form.html b/templates/backOffice/default/coupon/form.html index bdb8b7226..cc39b6574 100644 --- a/templates/backOffice/default/coupon/form.html +++ b/templates/backOffice/default/coupon/form.html @@ -54,7 +54,31 @@ {if $error}{$message}{/if} {/form_field} + +
+
+ {form_field form=$form field='type'} +
+ + + {if $error}{$message}{/if} + {$availableCoupons.0.toolTip} +
+ {/form_field} +
+
+ + +
+
{form_field form=$form field='isEnabled'}
-
- {form_field form=$form field='type'} -
- - - {if $error}{$message}{/if} - {$availableCoupons.0.toolTip} -
- {/form_field} - +
{$couponInputsHtml nofilter}
diff --git a/templates/backOffice/default/coupon/type-fragments/ajax-attribute-avs-list.html b/templates/backOffice/default/coupon/type-fragments/ajax-attribute-avs-list.html new file mode 100644 index 000000000..3033f94c9 --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/ajax-attribute-avs-list.html @@ -0,0 +1,3 @@ +{loop type="attribute_availability" attribute={$smarty.post.attribute_id} name="list-of-attribute_avs" backend_context="1"} + +{/loop} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/base-remove-on-attributes.html b/templates/backOffice/default/coupon/type-fragments/base-remove-on-attributes.html new file mode 100644 index 000000000..232ab68de --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/base-remove-on-attributes.html @@ -0,0 +1,65 @@ +{block name="discount-field"}{/block} + +
+ + + + +
+ + + +
+ + + + + + {intl l='Use Ctrl+click to select (or deselect) more that one attribute value'} +
+ + diff --git a/templates/backOffice/default/coupon/type-fragments/base-remove-on-categories.html b/templates/backOffice/default/coupon/type-fragments/base-remove-on-categories.html new file mode 100644 index 000000000..1987c6fbf --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/base-remove-on-categories.html @@ -0,0 +1,20 @@ +{block name="discount-field"}{/block} + +
+ + + + + + {intl l='Use Ctrl+click to select (or deselect) more that one category'} +
+ + diff --git a/templates/backOffice/default/coupon/type-fragments/base-remove-on-products.html b/templates/backOffice/default/coupon/type-fragments/base-remove-on-products.html new file mode 100644 index 000000000..db799b5d0 --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/base-remove-on-products.html @@ -0,0 +1,65 @@ +{block name="discount-field"}{/block} + +
+ + + + +
+ + + +
+ + + + + + {intl l='Use Ctrl+click to select (or deselect) more that one product'} +
+ + diff --git a/templates/backOffice/default/coupon/type-fragments/free-product.html b/templates/backOffice/default/coupon/type-fragments/free-product.html new file mode 100644 index 000000000..05e7bfe14 --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/free-product.html @@ -0,0 +1,69 @@ +{extends file="coupon/type-fragments/base-remove-on-products.html"} + +{block name="discount-field"} +
+ + + + +
+ + + +
+ + + + +
+{/block} + +{block name="javascript-init"} + + // Hide selected products category selection if the + // offered product is not yet selected. + if ({$offered_category_value|default:0} == 0) { + $('.input-coupon-category-id').hide(); + } + + var $freeCatSelect = $('#free-product-category-id'); + + $freeCatSelect.change(function(ev) { + + var $category_id = $(this).val(); + + $('.input-coupon-category-id').hide(); + $('#input-free-products-id').hide(); + $('#free-products-id').html(''); + + if ($category_id != 0) { + + $('#input-free-products-id-loading').show(); + + $.ajax({ + url: "{url path='/admin/coupon/type-fragments/ajax-products-list'}", + type: 'POST', + data: { + category_id: $category_id + }, + success: function(options) { + $('.input-coupon-category-id').show(); + $('#free-products-id').html(options); + $('#input-free-products-id').show(); + }, + complete: function() { + $('#input-free-products-id-loading').hide(); + } + }); + } + }); +{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-amount-on-attributes.html b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-attributes.html new file mode 100644 index 000000000..17f68cc28 --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-attributes.html @@ -0,0 +1,16 @@ +{extends file="coupon/type-fragments/base-remove-on-attributes.html"} + +{block name="discount-field"} +
+ + + +
+ + + {loop type="currency" name="get-symbol" default_only="true"} +
{$SYMBOL}
+ {/loop} +
+
+{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-amount-on-categories.html b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-categories.html index 15c528331..7f6a82c72 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-amount-on-categories.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-categories.html @@ -1,26 +1,16 @@ +{extends file="coupon/type-fragments/base-remove-on-categories.html"} -
+{block name="discount-field"} +
- + -
- +
+ - {loop type="currency" name="get-symbol" default_only="true"} -
{$SYMBOL}
- {/loop} + {loop type="currency" name="get-symbol" default_only="true"} +
{$SYMBOL}
+ {/loop} +
-
- -
- - - - - - {intl l='Use Ctrl+click to select (or deselect) more that one category'} -
+{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-amount-on-products.html b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-products.html index 57d45684c..033adeee1 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-amount-on-products.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-products.html @@ -1,77 +1,16 @@ +{extends file="coupon/type-fragments/base-remove-on-products.html"} -
+{block name="discount-field"} +
- + -
- +
+ - {loop type="currency" name="get-symbol" default_only="true"} -
{$SYMBOL}
- {/loop} + {loop type="currency" name="get-symbol" default_only="true"} +
{$SYMBOL}
+ {/loop} +
-
- -
- - - - - - {intl l='Use Ctrl+click to select (or deselect) more that one category'} -
- - - -
- - - - - - {intl l='Use Ctrl+click to select (or deselect) more that one category'} -
- - +{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-attributes.html b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-attributes.html new file mode 100644 index 000000000..262c10d97 --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-attributes.html @@ -0,0 +1,14 @@ +{extends file="coupon/type-fragments/base-remove-on-attributes.html"} + +{block name="discount-field"} +
+ + + +
+ + +
%
+
+
+{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-categories.html b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-categories.html index 08c3655f1..8a45e1a7a 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-categories.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-categories.html @@ -1,24 +1,14 @@ +{extends file="coupon/type-fragments/base-remove-on-categories.html"} -
+{block name="discount-field"} +
- + -
- +
+ -
%
+
%
+
-
- -
- - - - - - {intl l='Use Ctrl+click to select (or deselect) more that one category'} -
+{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-products.html b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-products.html index 4787da2ee..f60b2bf11 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-products.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-products.html @@ -1,75 +1,14 @@ +{extends file="coupon/type-fragments/base-remove-on-products.html"} -
+{block name="discount-field"} +
- + -
- +
+ -
%
+
%
+
-
- -
- - - - - - {intl l='Use Ctrl+click to select (or deselect) more that one category'} -
- - - -
- - - - - - {intl l='Use Ctrl+click to select (or deselect) more that one category'} -
- - +{/block} \ No newline at end of file diff --git a/templates/backOffice/default/coupon/type-fragments/remove-x-amount.html b/templates/backOffice/default/coupon/type-fragments/remove-x-amount.html index fe9cf285f..e715f43de 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-x-amount.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-x-amount.html @@ -2,7 +2,7 @@
- + {loop type="currency" name="get-symbol" default_only="true"}
{$SYMBOL}
diff --git a/templates/backOffice/default/coupon/type-fragments/remove-x-percent.html b/templates/backOffice/default/coupon/type-fragments/remove-x-percent.html index f06b42a6a..25fe083b0 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-x-percent.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-x-percent.html @@ -3,7 +3,7 @@
- +
%