diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d99d6fc8..b941a0bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ #2.0.2 +- Coupon UI has been redesigned. +- New coupon types: + - Constant discount on selected products + - Constant discount on products of selected categories + - Percentage discount on selected products + - Percentage discount on products of selected categories - New coupon conditions : - Start date - Billing country @@ -13,7 +19,7 @@ - example : Thelia\Core\EventListener\SessionListener - Creation of Thelia\Core\TheliakernelEvents class for referencing kernel event - Add new command line that refresh modules list `Thelia module:refresh` - +- Coupon internals have been simplified and improved. #2.0.1 - possibility to apply a permanent discount on a customer diff --git a/core/lib/Thelia/Config/I18n/en_US.php b/core/lib/Thelia/Config/I18n/en_US.php index ea9d3ff9e..6b7e79092 100644 --- a/core/lib/Thelia/Config/I18n/en_US.php +++ b/core/lib/Thelia/Config/I18n/en_US.php @@ -85,7 +85,6 @@ return array( 'Description' => 'Description', 'Detailed description' => 'Detailed description', 'Disabled' => 'Disabled', - 'Discount amount' => 'Discount amount', 'Document deleted successfully' => 'Document deleted successfully', 'Document position updated' => 'Document position updated', 'EAN Code' => 'EAN Code', @@ -112,7 +111,8 @@ return array( 'First Name' => 'First Name', 'Firstname' => 'Firstname', 'Fixed Amount Discount' => 'Fixed Amount Discount', - 'Fixed amount discount on selected categories' => 'Fixed amount discount on selected categories', + '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 title *' => 'Folder title *', 'For one ore more customers' => 'For one ore more customers', 'Full Name' => 'Full Name', @@ -190,13 +190,17 @@ return array( 'Password *' => 'Password *', 'Password confirmation' => 'Password confirmation', 'Per customer' => 'Per customer', - 'Percent Discount' => 'Percent Discount', + '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', 'Phone' => 'Phone', 'Please accept the Terms and conditions in order to register.' => 'Please accept the Terms and conditions in order to register.', 'Please check your input: %error' => 'Please check your input: %error', '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 at least one category' => 'Please select at least one category', + 'Please select at least one product' => 'Please select at least one 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', @@ -285,7 +289,10 @@ return array( 'This condition is always true' => 'This condition is always true', 'This content is online.' => 'This content is online.', 'This coupon does not exists' => 'This coupon does not exists', - 'This coupon subtracts the specified amount from the order total for each product of 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 of 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 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 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 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.', 'This email already exists.' => 'This email already exists.', @@ -316,6 +323,9 @@ return array( 'Username *' => 'Username *', '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', 'Weight' => 'Weight', diff --git a/core/lib/Thelia/Config/I18n/fr_FR.php b/core/lib/Thelia/Config/I18n/fr_FR.php index 388b3d684..a9f14dbee 100644 --- a/core/lib/Thelia/Config/I18n/fr_FR.php +++ b/core/lib/Thelia/Config/I18n/fr_FR.php @@ -85,7 +85,6 @@ return array( 'Description' => 'Description', 'Detailed description' => 'Description détaillée', 'Disabled' => 'Désactivé', - 'Discount amount' => 'Montant de la remise', 'Document deleted successfully' => 'Le document a été supprimé.', 'Document position updated' => 'La position du document a été modfiée', 'EAN Code' => 'Code EAN', @@ -112,6 +111,8 @@ return array( 'First Name' => 'Prénom', 'Firstname' => 'Prénom', 'Fixed Amount Discount' => 'Remise d\'un montant fixe', + 'Fixed amount discount for selected categories' => 'Remise fixe pour certaines catégories', + 'Fixed amount discount for selected products' => 'Remise fixe pour certains produits', 'Folder title *' => 'Titre du dossier *', 'For one ore more customers' => 'Valable pour un ou plusieurs clients', 'Full Name' => 'Nom complet', @@ -189,13 +190,17 @@ return array( 'Password *' => 'Mot de passe *', 'Password confirmation' => 'Confirmation du mot de passe.', 'Per customer' => 'Par client', - 'Percent Discount' => 'Remise en pourcentage de la commande', + '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', 'Phone' => 'Téléphone', 'Please accept the Terms and conditions in order to register.' => 'Veuillez accepter les termes et conditions pour vous inscrire.', 'Please check your input: %error' => 'Merci de vérifier votre saisie: %error', '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 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 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', @@ -284,6 +289,10 @@ return array( 'This condition is always true' => 'Cette condition est troujours vérifiée', 'This content is online.' => 'Ce contenu est en ligne.', '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 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 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.', 'This email already exists.' => 'Cette adresse email existe déjà', @@ -314,6 +323,9 @@ return array( 'Username *' => 'Nom d\'utilisateur *', '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', 'Weight' => 'Poids', diff --git a/core/lib/Thelia/Config/Resources/coupon.xml b/core/lib/Thelia/Config/Resources/coupon.xml index a8b2df45d..f08746d22 100644 --- a/core/lib/Thelia/Config/Resources/coupon.xml +++ b/core/lib/Thelia/Config/Resources/coupon.xml @@ -30,12 +30,28 @@ - + + + + + + + + + + + + + + + + + diff --git a/core/lib/Thelia/Controller/Admin/CouponController.php b/core/lib/Thelia/Controller/Admin/CouponController.php index 11abe29a3..81c6ba2f4 100644 --- a/core/lib/Thelia/Controller/Admin/CouponController.php +++ b/core/lib/Thelia/Controller/Admin/CouponController.php @@ -25,9 +25,7 @@ use Thelia\Core\Security\AccessManager; use Thelia\Coupon\CouponFactory; use Thelia\Coupon\CouponManager; use Thelia\Condition\ConditionCollection; -use Thelia\Coupon\Type\CouponAbstract; use Thelia\Coupon\Type\CouponInterface; -use Thelia\Coupon\Type\RemoveXPercent; use Thelia\Form\CouponCreationForm; use Thelia\Form\Exception\FormValidationException; use Thelia\Log\Tlog; @@ -199,6 +197,7 @@ class CouponController extends BaseAdminController } $args['couponCode'] = $coupon->getCode(); + $args['couponType'] = $coupon->getType(); $args['availableCoupons'] = $this->getAvailableCoupons(); $args['couponInputsHtml'] = $couponManager->drawBackOfficeInputs(); $args['urlAjaxAdminCouponDrawInputs'] = $this->getRoute( @@ -577,7 +576,7 @@ class CouponController extends BaseAdminController $condition['serviceId'] = $availableCoupon->getServiceId(); $condition['name'] = $availableCoupon->getName(); $condition['toolTip'] = $availableCoupon->getToolTip(); - // $condition['inputName'] = $availableCoupon->getInputName(); + $cleanedCoupons[] = $condition; } @@ -624,17 +623,23 @@ class CouponController extends BaseAdminController return $response; } - $this->checkXmlHttpRequest(); + if (! empty($couponServiceId)) { + $this->checkXmlHttpRequest(); - /** @var CouponInterface $coupon */ - $couponManager = $this->container->get($couponServiceId); + /** @var CouponInterface $coupon */ + $couponManager = $this->container->get($couponServiceId); - if (!$couponManager instanceof CouponInterface) { - $this->pageNotFound(); + if (!$couponManager instanceof CouponInterface) { + $this->pageNotFound(); + } + + $response = new ResponseRest($couponManager->drawBackOfficeInputs()); + } else { + // Return an empty response if the service ID is not defined + // Typically, when the user chooses "Please select a coupon type" + $response = new ResponseRest(''); } - $response = new ResponseRest($couponManager->drawBackOfficeInputs()); - return $response; } @@ -676,35 +681,6 @@ class CouponController extends BaseAdminController } - /** - * Add percentage logic if found in the Coupon post data - * - * @param array $effects Effect parameters to populate - * @param array $extendedInputNames Extended Inputs to manage - * - * @return array Populated effect with percentage - */ - protected function addExtendedLogic(array $effects, array $extendedInputNames) - { - /** @var Request $request */ - $request = $this->container->get('request'); - $postData = $request->request; - // Validate quantity input - - if ($postData->has(RemoveXPercent::INPUT_EXTENDED__NAME)) { - $extentedPostData = $postData->get(RemoveXPercent::INPUT_EXTENDED__NAME); - - foreach ($extendedInputNames as $extendedInputName) { - if (isset($extentedPostData[$extendedInputName])) { - $inputValue = $extentedPostData[$extendedInputName]; - $effects[$extendedInputName] = $inputValue; - } - } - } - - return $effects; - } - /** * Feed the Coupon Create or Update event with the User inputs * @@ -718,16 +694,15 @@ class CouponController extends BaseAdminController // Get the form field values $data = $form->getData(); $serviceId = $data['type']; - /** @var CouponInterface $couponManager */ - $couponManager = $this->container->get($serviceId); - $effects = [CouponAbstract::INPUT_AMOUNT_NAME => $data[CouponAbstract::INPUT_AMOUNT_NAME]]; - $effects = $this->addExtendedLogic($effects, $couponManager->getExtendedInputs()); + + /** @var CouponInterface $coupon */ + $coupon = $this->container->get($serviceId); $couponEvent = new CouponCreateOrUpdateEvent( $data['code'], $serviceId, $data['title'], - $effects, + $coupon->getEffects($data), $data['shortDescription'], $data['description'], $data['isEnabled'], diff --git a/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php b/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php index 755dbe9f7..ae2312aae 100644 --- a/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php +++ b/core/lib/Thelia/Core/Event/Coupon/CouponCreateOrUpdateEvent.php @@ -327,10 +327,9 @@ class CouponCreateOrUpdateEvent extends ActionEvent */ public function setEffects(array $effects) { - if (null === $effects['amount']) { - throw new InvalidArgumentException('Missing key \'amount\' in Coupon effect ready to be serialized array'); - } - $this->amount = $effects['amount']; + // Amount is now optionnal. + $this->amount = isset($effects['amount']) ? $effects['amount'] : 0; + $this->effects = $effects; } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php index 588cbe76d..f9f8725bd 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/DataAccessFunctions.php @@ -402,7 +402,7 @@ class DataAccessFunctions extends AbstractSmartyPlugin return $noGetterData[$keyAttribute]; } - $getter = sprintf("get%s", ucfirst($attribute)); + $getter = sprintf("get%s", $this->underscoreToCamelcase($attribute)); if (method_exists($data, $getter)) { $return = $data->$getter(); @@ -427,6 +427,27 @@ class DataAccessFunctions extends AbstractSmartyPlugin return ''; } + /** + * Transcode an underscored string into a camel-cased string, eg. default_folder into DefaultFolder + * + * @param string $str the string to convert from underscore to camel-case + * + * @return string the camel cased string. + */ + private function underscoreToCamelcase($str) + { + // Split string in words. + $words = explode('_', strtolower($str)); + + $return = ''; + + foreach ($words as $word) { + $return .= ucfirst(trim($word)); + } + + return $return; + } + /** * Define the various smarty plugins hendled by this class * diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php index aa66c114f..22ba0d14f 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Translation.php @@ -47,20 +47,26 @@ class Translation extends AbstractSmartyPlugin */ public function translate($params, &$smarty) { - // All parameters other than 'l' and 'd' are supposed to be variables. Build an array of var => value pairs + // All parameters other than 'l' and 'd' and 'js' are supposed to be variables. Build an array of var => value pairs // and pass it to the translator $vars = array(); foreach ($params as $name => $value) { - if ($name != 'l' && $name != 'd') $vars["%$name"] = $value; + if ($name != 'l' && $name != 'd' && $name != 'js') $vars["%$name"] = $value; } - return $this->translator->trans( + $str = $this->translator->trans( $this->getParam($params, 'l'), $vars, $this->getParam($params, 'd', $this->defaultTranslationDomain) ); -} + + if ($this->getParam($params, 'js', 0)) { + $str = preg_replace("/(['\"])/", "\\\\$1", $str); + } + + return $str; + } /** * Define the various smarty plugins handled by this class diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php index 03d605358..332c3fbaf 100644 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/UrlGenerator.php @@ -152,9 +152,9 @@ class UrlGenerator extends AbstractSmartyPlugin protected function getNavigateToValues() { return array( - "current" => "getCurrentUrl", - "return_to" => "getReturnToUrl", - "index" => "getIndexUrl", + "current" => "getCurrentUrl", + "previous" => "getPreviousUrl", + "index" => "getIndexUrl", ); } @@ -167,7 +167,7 @@ class UrlGenerator extends AbstractSmartyPlugin $navigateToValues = $this->getNavigateToValues(); if (!array_key_exists($to, $navigateToValues)) { - throw new \InvalidArgumentException("Incorrect value for parameter `to` in `navigate` substitution."); + throw new \InvalidArgumentException(sprintf("Incorrect value `%s` for parameter `to` in `navigate` substitution.", $to)); } return $navigateToValues[$to]; @@ -178,7 +178,7 @@ class UrlGenerator extends AbstractSmartyPlugin return $this->request->getUri(); } - protected function getReturnToUrl() + protected function getPreviousUrl() { return URL::getInstance()->absoluteUrl($this->request->getSession()->getReturnToUrl()); } diff --git a/core/lib/Thelia/Coupon/BaseFacade.php b/core/lib/Thelia/Coupon/BaseFacade.php index 0fdf0c36b..5794df673 100644 --- a/core/lib/Thelia/Coupon/BaseFacade.php +++ b/core/lib/Thelia/Coupon/BaseFacade.php @@ -118,17 +118,35 @@ class BaseFacade implements FacadeInterface * * @return float */ - public function getCartTotalPrice() + public function getCartTotalPrice($withItemsInPromo = true) { - return $this->getRequest()->getSession()->getCart()->getTotalAmount(); + $total = 0; + $cartItems = $this->getRequest()->getSession()->getCart()->getCartItems(); + + foreach ($cartItems as $cartItem) { + if ($withItemsInPromo || ! $cartItem->getPromo()) { + $total += $cartItem->getRealPrice() * $cartItem->getQuantity(); + } + } + + return $total; } - public function getCartTotalTaxPrice() + public function getCartTotalTaxPrice($withItemsInPromo = true) { $taxCountry = $this->getContainer()->get('thelia.taxEngine')->getDeliveryCountry(); + $cartItems = $this->getRequest()->getSession()->getCart()->getCartItems(); - return $this->getCart()->getTaxedAmount($taxCountry, false); + $total = 0; + + foreach ($cartItems as $cartItem) { + if ($withItemsInPromo || ! $cartItem->getPromo()) { + $total += $cartItem->getRealTaxedPrice($taxCountry) * $cartItem->getQuantity(); + } + } + + return $total; } /** diff --git a/core/lib/Thelia/Coupon/FacadeInterface.php b/core/lib/Thelia/Coupon/FacadeInterface.php index 40e4e2948..8a4d004a3 100644 --- a/core/lib/Thelia/Coupon/FacadeInterface.php +++ b/core/lib/Thelia/Coupon/FacadeInterface.php @@ -68,17 +68,19 @@ interface FacadeInterface /** * Return Products total price * CartTotalPrice = Checkout total - discount - postage + * @param bool $withItemsInPromo true (default) if item in promotion should be included in the total, false otherwise. * * @return float */ - public function getCartTotalPrice(); + public function getCartTotalPrice($withItemsInPromo = true); /** * Return Product total tax price + * @param bool $withItemsInPromo true (default) if item in promotion should be included in the total, false otherwise. * * @return float */ - public function getCartTotalTaxPrice(); + public function getCartTotalTaxPrice($withItemsInPromo = true); /** * Return the Checkout currency EUR|USD diff --git a/core/lib/Thelia/Coupon/Type/CouponAbstract.php b/core/lib/Thelia/Coupon/Type/CouponAbstract.php index 424de8323..6d57b0d38 100644 --- a/core/lib/Thelia/Coupon/Type/CouponAbstract.php +++ b/core/lib/Thelia/Coupon/Type/CouponAbstract.php @@ -17,6 +17,7 @@ use Thelia\Condition\ConditionEvaluator; use Thelia\Condition\ConditionOrganizerInterface; use Thelia\Core\Translation\Translator; use Thelia\Coupon\FacadeInterface; +use Thelia\Form\CouponCreationForm; use Thelia\Model\CouponCountry; use Thelia\Model\CouponModule; @@ -29,12 +30,21 @@ use Thelia\Model\CouponModule; */ abstract class CouponAbstract implements CouponInterface { - const INPUT_EXTENDED__NAME = 'thelia_coupon_creation_extended'; + /** + * The dataset name for all coupon specific input fields, that do not appear in the CouPonCreationForm form. + * + * In the input form, these fields have to be created like: + * + * thelia_coupon_specific[my_field, thelia_coupon_creation_extended[my_other_field] + * + * use the makeCouponField() method to do that safely. + */ + const COUPON_DATASET_NAME = 'coupon_specific'; - const INPUT_AMOUNT_NAME = 'amount'; - - /** @var array Extended Inputs to manage */ - protected $extendedInputs = array(); + /** + * A standard 'amount' filed name, thant can be used in coupons which extends this class + */ + const AMOUNT_FIELD_NAME = 'amount'; /** @var FacadeInterface Provide necessary value from Thelia */ protected $facade = null; @@ -161,7 +171,8 @@ abstract class CouponAbstract implements CouponInterface $this->facade = $facade; $this->effects = $effects; - $this->amount = $effects[self::INPUT_AMOUNT_NAME]; + // Amount is now optional. + $this->amount = isset($effects[self::AMOUNT_FIELD_NAME]) ? $effects[self::AMOUNT_FIELD_NAME] : 0; $this->freeShippingForCountries = $freeShippingForCountries; $this->freeShippingForModules = $freeShippingForModules; @@ -267,10 +278,7 @@ abstract class CouponAbstract implements CouponInterface } /** - * Return effects generated by the coupon - * A negative value - * - * @return float Amount removed from the Total Checkout + * @inheritdoc */ public function exec() { @@ -383,6 +391,19 @@ abstract class CouponAbstract implements CouponInterface return $this->conditionEvaluator->isMatching($this->conditions); } + /** + * This is the field label than will be displayed in the form. + * This method should be overridden to be useful. + * + * For backward compatibility only. + * + * @return string + */ + public function getInputName() + { + return "Please override getInputName() method"; + } + /** * Draw the input displayed in the BackOffice * allowing Admin to set its Coupon effect @@ -394,19 +415,88 @@ abstract class CouponAbstract implements CouponInterface { return $this->facade->getParser()->render('coupon/type-fragments/remove-x.html', [ 'label' => $this->getInputName(), - 'fieldName' => self::INPUT_AMOUNT_NAME, + 'fieldId' => self::AMOUNT_FIELD_NAME, + 'fieldName' => $this->makeCouponFieldName(self::AMOUNT_FIELD_NAME), 'value' => $this->amount ]); } /** - * Get all extended inputs name to manage + * This methods checks a field value. If the field has a correct value, this value is returned + * Otherwise, an InvalidArgumentException describing the problem should be thrown. * + * This method should be ovveriden to be useful. + * + * @param $fieldName + * @param $fieldValue * @return mixed + * @throws \InvalidArgumentException if the field valiue is not valid. */ - public function getExtendedInputs() + protected function checkCouponFieldValue($fieldName, $fieldValue) { - return $this->extendedInputs; + return $fieldValue; } + /** + * A helper to get the value of a standard field name + * + * @param string $fieldName the field name + * @param array $data the input form data (e.g. $form->getData()) + * @param mixed $defaultValue the default value if the field is not found. + * + * @return mixed the input value, or the default one + * + * @throws \InvalidArgumentException if the field is not found, and no default value has been defined. + */ + protected function getCouponFieldValue($fieldName, $data, $defaultValue = null) + { + if (isset($data[self::COUPON_DATASET_NAME][$fieldName])) { + return $this->checkCouponFieldValue( + $fieldName, + $data[self::COUPON_DATASET_NAME][$fieldName] + ); + } elseif (null !== $defaultValue) { + return $defaultValue; + } else { + throw new \InvalidArgumentException(sprintf("The coupon field name %s was not found in the coupon form", $fieldName)); + } + } + + /** + * A helper to create an standard field name that will be used in the coupon form + * + * @param string $fieldName the field name + * @return string the complete name, ready to be used in a form. + */ + protected function makeCouponFieldName($fieldName) + { + return sprintf("%s[%s][%s]", CouponCreationForm::COUPON_CREATION_FORM_NAME, self::COUPON_DATASET_NAME, $fieldName); + } + + /** + * Return a list of the fields name for this coupon. + * + * @return array + */ + protected function getFieldList() + { + return [self::AMOUNT_FIELD_NAME]; + } + + /** + * Create the effect array from the list of fields + * + * @param array $data the input form data (e.g. $form->getData()) + * @return array a filedName => fieldValue array + */ + public function getEffects($data) + { + $effects = []; + + foreach ($this->getFieldList() as $fieldName) { + $effects[$fieldName] = $this->getCouponFieldValue($fieldName, $data); + } + + return $effects; + } } diff --git a/core/lib/Thelia/Coupon/Type/CouponInterface.php b/core/lib/Thelia/Coupon/Type/CouponInterface.php index 69eca002a..a9382749f 100644 --- a/core/lib/Thelia/Coupon/Type/CouponInterface.php +++ b/core/lib/Thelia/Coupon/Type/CouponInterface.php @@ -220,13 +220,6 @@ interface CouponInterface */ public function drawBackOfficeInputs(); - /** - * Get all extended inputs name to manage - * - * @return mixed - */ - public function getExtendedInputs(); - /** * @return ObjectCollection list of country IDs for which shipping is free. All if empty */ @@ -236,4 +229,14 @@ interface CouponInterface * @return ObjectCollection list of module IDs for which shipping is free. All if empty */ public function getFreeShippingForModules(); + + /** + * Create the effect array from the list of fields + * + * @param array $data the input form data (e.g. $form->getData()) + * + * @return array a filedName => fieldValue array + */ + public function getEffects($data); + } diff --git a/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php b/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php index 2372dbfb4..d65b462f7 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php +++ b/core/lib/Thelia/Coupon/Type/RemoveAmountOnCategories.php @@ -12,6 +12,11 @@ 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 * @@ -26,29 +31,62 @@ class RemoveAmountOnCategories extends CouponAbstract /** @var string Service Id */ protected $serviceId = 'thelia.coupon.type.remove_amount_on_categories'; + public $category_list = array(); + /** - * Get I18n name - * - * @return string + * @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); + return $this; + } + + /** + * @inheritdoc */ public function getName() { return $this->facade ->getTranslator() - ->trans('Fixed amount discount on selected categories', array(), 'coupon'); + ->trans('Fixed amount discount for selected categories', array(), 'coupon'); } /** - * Get I18n tooltip - * - * @return string + * @inheritdoc */ public function getToolTip() { $toolTip = $this->facade ->getTranslator() ->trans( - 'This coupon subtracts the specified amount from the order total for each product of 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.', array(), 'coupon' ); @@ -57,25 +95,88 @@ class RemoveAmountOnCategories extends CouponAbstract } /** - * Return effects generated by the coupon - * A negative value - * - * @return float Amount removed from the Total Checkout + * @inheritdoc */ public function exec() { - return $this->amount; + // 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', [ - 'amount_field_name' => self::INPUT_AMOUNT_NAME, - 'amount_value' => $this->amount, - 'categories_field_name' => self::CATEGORIES_LIST, - 'categories_values' => isset($this->values[self::CATEGORIES_LIST]) ? $this->values[self::CATEGORIES_LIST] : array() + // 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 + ]); } + + /** + * @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 new file mode 100644 index 000000000..9a3fb8b93 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/RemoveAmountOnProducts.php @@ -0,0 +1,193 @@ + + * + */ +class RemoveAmountOnProducts extends CouponAbstract +{ + const CATEGORY_ID = 'category_id'; + const PRODUCTS_LIST = 'products'; + + /** @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 + ) + { + 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; + } + + /** + * Get I18n name + * + * @return string + */ + public function getName() + { + return $this->facade + ->getTranslator() + ->trans('Fixed amount discount for selected products', array(), 'coupon'); + } + + /** + * @inheritdoc + */ + public function getToolTip() + { + $toolTip = $this->facade + ->getTranslator() + ->trans( + '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.', + array(), + 'coupon' + ); + + 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; + } +} diff --git a/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php b/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php new file mode 100644 index 000000000..892984a14 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/RemovePercentageOnCategories.php @@ -0,0 +1,184 @@ + + * + */ +class RemovePercentageOnCategories extends CouponAbstract +{ + const CATEGORIES_LIST = 'categories'; + const PERCENTAGE = 'percentage'; + + /** @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 + ) + { + 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; + } + + /** + * @inheritdoc + */ + public function getName() + { + return $this->facade + ->getTranslator() + ->trans('Percentage discount for selected categories', array(), 'coupon'); + } + + /** + * @inheritdoc + */ + public function getToolTip() + { + $toolTip = $this->facade + ->getTranslator() + ->trans( + '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.', + array(), + 'coupon' + ); + + 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 + ]); + } + + /** + * @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 new file mode 100644 index 000000000..030a87c23 --- /dev/null +++ b/core/lib/Thelia/Coupon/Type/RemovePercentageOnProducts.php @@ -0,0 +1,198 @@ + + * + */ +class RemovePercentageOnProducts extends CouponAbstract +{ + const CATEGORY_ID = 'category_id'; + const PRODUCTS_LIST = 'products'; + const PERCENTAGE = 'percentage'; + + /** @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 + ) + { + 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; + } + + /** + * Get I18n name + * + * @return string + */ + public function getName() + { + return $this->facade + ->getTranslator() + ->trans('Percentage discount for selected products', array(), 'coupon'); + } + + /** + * @inheritdoc + */ + public function getToolTip() + { + $toolTip = $this->facade + ->getTranslator() + ->trans( + '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.', + array(), + 'coupon' + ); + + 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; + } +} diff --git a/core/lib/Thelia/Coupon/Type/RemoveXAmount.php b/core/lib/Thelia/Coupon/Type/RemoveXAmount.php index c644706a2..7ded8108c 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveXAmount.php +++ b/core/lib/Thelia/Coupon/Type/RemoveXAmount.php @@ -12,6 +12,8 @@ namespace Thelia\Coupon\Type; +use Thelia\Core\Translation\Translator; + /** * Allow to remove an amount from the checkout total * @@ -25,9 +27,7 @@ class RemoveXAmount extends CouponAbstract protected $serviceId = 'thelia.coupon.type.remove_x_amount'; /** - * Get I18n name - * - * @return string + * @inheritdoc */ public function getName() { @@ -37,21 +37,7 @@ class RemoveXAmount extends CouponAbstract } /** - * Get I18n amount input name - * - * @return string - */ - public function getInputName() - { - return $this->facade - ->getTranslator() - ->trans('Discount amount', array(), 'coupon'); - } - - /** - * Get I18n tooltip - * - * @return string + * @inheritdoc */ public function getToolTip() { @@ -66,12 +52,43 @@ class RemoveXAmount extends CouponAbstract return $toolTip; } + /** + * @inheritdoc + */ public function drawBackOfficeInputs() { return $this->facade->getParser()->render('coupon/type-fragments/remove-x-amount.html', [ - 'label' => $this->getInputName(), - 'fieldName' => self::INPUT_AMOUNT_NAME, + 'fieldName' => $this->makeCouponFieldName(self::AMOUNT_FIELD_NAME), 'value' => $this->amount ]); } + + /** + * @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 e5a98c5fb..554e3f5da 100644 --- a/core/lib/Thelia/Coupon/Type/RemoveXPercent.php +++ b/core/lib/Thelia/Coupon/Type/RemoveXPercent.php @@ -12,6 +12,7 @@ namespace Thelia\Coupon\Type; +use Thelia\Core\Translation\Translator; use Thelia\Coupon\FacadeInterface; /** @@ -29,11 +30,6 @@ class RemoveXPercent extends CouponAbstract /** @var float Percentage removed from the Cart */ protected $percentage = 0; - /** @var array Extended Inputs to manage */ - protected $extendedInputs = array( - self::INPUT_PERCENTAGE_NAME - ); - /** * @inheritdoc */ @@ -69,28 +65,37 @@ class RemoveXPercent extends CouponAbstract } /** - * Return effects generated by the coupon - * A negative value - * - * @throws \Thelia\Exception\MissingFacadeException - * @throws \InvalidArgumentException - * @return float + * @inheritdoc */ public function exec() { - if ($this->percentage >= 100) { - throw new \InvalidArgumentException( - 'Percentage must be inferior to 100' - ); - } - - return round($this->facade->getCartTotalTaxPrice() * $this->percentage/100, 2); + return round($this->facade->getCartTotalTaxPrice($this->isAvailableOnSpecialOffers()) * $this->percentage/100, 2); } /** - * Get I18n name - * - * @return string + * @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; + } + + /** + * @inheritdoc */ public function getName() { @@ -100,21 +105,7 @@ class RemoveXPercent extends CouponAbstract } /** - * Get I18n amount input name - * - * @return string - */ - public function getInputName() - { - return $this->facade - ->getTranslator() - ->trans('Percent Discount', array(), 'coupon'); - } - - /** - * Get I18n tooltip - * - * @return string + * @inheritdoc */ public function getToolTip() { @@ -130,19 +121,21 @@ class RemoveXPercent extends CouponAbstract } /** - * Draw the input displayed in the BackOffice - * allowing Admin to set its Coupon effect - * - * @return string HTML string + * @inheritdoc */ public function drawBackOfficeInputs() { return $this->facade->getParser()->render('coupon/type-fragments/remove-x-percent.html', [ - 'label' => $this->getInputName(), - 'typeKey' => self::INPUT_AMOUNT_NAME, - 'fieldId' => self::INPUT_PERCENTAGE_NAME, - 'fieldName' => self::INPUT_EXTENDED__NAME, + 'fieldName' => $this->makeCouponFieldName(self::INPUT_PERCENTAGE_NAME), 'value' => $this->percentage ]); } + + /** + * @inheritdoc + */ + protected function getFieldList() + { + return [self::INPUT_PERCENTAGE_NAME]; + } } diff --git a/core/lib/Thelia/Form/CouponCreationForm.php b/core/lib/Thelia/Form/CouponCreationForm.php index 688e8a6ba..acb16ce70 100644 --- a/core/lib/Thelia/Form/CouponCreationForm.php +++ b/core/lib/Thelia/Form/CouponCreationForm.php @@ -33,6 +33,8 @@ use Thelia\Module\BaseModule; */ class CouponCreationForm extends BaseForm { + const COUPON_CREATION_FORM_NAME = 'thelia_coupon_creation'; + /** * Build Coupon form * @@ -110,14 +112,6 @@ class CouponCreationForm extends BaseForm ) ) ) - ->add( - 'amount', - 'money', - array( - 'constraints' => array( - new NotBlank() - )) - ) ->add( 'isEnabled', 'text', @@ -198,7 +192,12 @@ class CouponCreationForm extends BaseForm new NotBlank() ) ) - ); + ) + ->add('coupon_specific', 'collection', array( + 'allow_add' => true, + 'allow_delete' => true, + )) + ; } /** @@ -226,6 +225,6 @@ class CouponCreationForm extends BaseForm */ public function getName() { - return 'thelia_coupon_creation'; + return self::COUPON_CREATION_FORM_NAME; } } diff --git a/core/lib/Thelia/Model/Coupon.php b/core/lib/Thelia/Model/Coupon.php index 45f701876..921078eda 100644 --- a/core/lib/Thelia/Model/Coupon.php +++ b/core/lib/Thelia/Model/Coupon.php @@ -184,7 +184,8 @@ class Coupon extends BaseCoupon */ public function getAmount() { - $amount = $this->getEffects()['amount']; + // Amount is now optional + $amount = isset($this->getEffects()['amount']) ? $this->getEffects()['amount'] : 0; return floatval($amount); } @@ -199,10 +200,6 @@ class Coupon extends BaseCoupon { $effects = $this->unserializeEffects($this->getSerializedEffects()); - if (null === $effects['amount']) { - throw new InvalidArgumentException('Missing key \'amount\' in Coupon effect coming from database'); - } - return $effects; } @@ -210,18 +207,12 @@ class Coupon extends BaseCoupon * Get the Coupon effects * * @param array $effects Effect ready to be serialized - * Needs at least the key 'amount' - * with the amount removed from the cart * * @throws Exception\InvalidArgumentException * @return $this */ public function setEffects(array $effects) { - if (null === $effects['amount']) { - throw new InvalidArgumentException('Missing key \'amount\' in Coupon effect ready to be serialized array'); - } - $this->setSerializedEffects($this->serializeEffects($effects)); return $this; diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemoveXAmountTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemoveXAmountTest.php index ed03a6fc3..730250967 100644 --- a/core/lib/Thelia/Tests/Coupon/Type/RemoveXAmountTest.php +++ b/core/lib/Thelia/Tests/Coupon/Type/RemoveXAmountTest.php @@ -196,22 +196,6 @@ class RemoveXAmountTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $actual); } - /** - * @covers Thelia\Coupon\Type\RemoveXPercent::getToolTip - */ - public function testGetInputName() - { - $inputName = 'Amount removed from the cart'; - $stubFacade = $this->generateFacadeStub(399, 'EUR', $inputName); - - /** @var FacadeInterface $stubFacade */ - $coupon = new RemoveXAmount($stubFacade); - - $actual = $coupon->getInputName(); - $expected = $inputName; - $this->assertEquals($expected, $actual); - } - /** * Tears down the fixture, for example, closes a network connection. * This method is called after a test is executed. diff --git a/core/lib/Thelia/Tests/Coupon/Type/RemoveXPercentTest.php b/core/lib/Thelia/Tests/Coupon/Type/RemoveXPercentTest.php index 2b36d27a2..5b58f59a7 100644 --- a/core/lib/Thelia/Tests/Coupon/Type/RemoveXPercentTest.php +++ b/core/lib/Thelia/Tests/Coupon/Type/RemoveXPercentTest.php @@ -152,8 +152,6 @@ class RemoveXPercentTest extends \PHPUnit_Framework_TestCase $this->assertEquals(254, $coupon->getMaxUsage()); $this->assertEquals($date, $coupon->getExpirationDate()); - $this->assertEquals(array(RemoveXPercent::INPUT_PERCENTAGE_NAME), $coupon->getExtendedInputs()); - $this->assertEquals(40.00, $coupon->exec()); } @@ -188,22 +186,6 @@ class RemoveXPercentTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $actual); } - /** - * @covers Thelia\Coupon\Type\RemoveXPercent::getToolTip - */ - public function testGetInputName() - { - $inputName = 'Percentage removed from the cart'; - $stubFacade = $this->generateFacadeStub(399, 'EUR', $inputName); - - /** @var FacadeInterface $stubFacade */ - $coupon = new RemoveXPercent($stubFacade); - - $actual = $coupon->getInputName(); - $expected = $inputName; - $this->assertEquals($expected, $actual); - } - /** * Tears down the fixture, for example, closes a network connection. * This method is called after a test is executed. diff --git a/setup/faker.php b/setup/faker.php index 6843097b7..08da3889a 100644 --- a/setup/faker.php +++ b/setup/faker.php @@ -685,7 +685,7 @@ Praesent ligula lorem, faucibus ut metus quis, fermentum iaculis erat. Pellentes Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesuada tortor vel erat volutpat tincidunt. In vehicula diam est, a convallis eros scelerisque ut. Donec aliquet venenatis iaculis. Ut a arcu gravida, placerat dui eu, iaculis nisl. Quisque adipiscing orci sit amet dui dignissim lacinia. Sed vulputate lorem non dolor adipiscing ornare. Morbi ornare id nisl id aliquam. Ut fringilla elit ante, nec lacinia enim fermentum sit amet. Aenean rutrum lorem eu convallis pharetra. Cras malesuada varius metus, vitae gravida velit. Nam a varius ipsum, ac commodo dolor. Phasellus nec elementum elit. Etiam vel adipiscing leo.'); $coupon1->setEffects(array( - RemoveXAmount::INPUT_AMOUNT_NAME => 10.00, + RemoveXAmount::AMOUNT_FIELD_NAME => 10.00, )); $coupon1->setIsUsed(true); $coupon1->setIsEnabled(true); @@ -744,8 +744,7 @@ Praesent ligula lorem, faucibus ut metus quis, fermentum iaculis erat. Pellentes Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesuada tortor vel erat volutpat tincidunt. In vehicula diam est, a convallis eros scelerisque ut. Donec aliquet venenatis iaculis. Ut a arcu gravida, placerat dui eu, iaculis nisl. Quisque adipiscing orci sit amet dui dignissim lacinia. Sed vulputate lorem non dolor adipiscing ornare. Morbi ornare id nisl id aliquam. Ut fringilla elit ante, nec lacinia enim fermentum sit amet. Aenean rutrum lorem eu convallis pharetra. Cras malesuada varius metus, vitae gravida velit. Nam a varius ipsum, ac commodo dolor. Phasellus nec elementum elit. Etiam vel adipiscing leo.'); $coupon2->setEffects(array( - RemoveXPercent::INPUT_AMOUNT_NAME => 0.00, - RemoveXPercent::INPUT_PERCENTAGE_NAME => 10.00, + RemoveXPercent::INPUT_PERCENTAGE_NAME => 10.00 )); $coupon2->setIsUsed(true); $coupon2->setIsEnabled(true); @@ -790,7 +789,6 @@ Praesent ligula lorem, faucibus ut metus quis, fermentum iaculis erat. Pellentes Sed facilisis pellentesque nisl, eu tincidunt erat scelerisque a. Nullam malesuada tortor vel erat volutpat tincidunt. In vehicula diam est, a convallis eros scelerisque ut. Donec aliquet venenatis iaculis. Ut a arcu gravida, placerat dui eu, iaculis nisl. Quisque adipiscing orci sit amet dui dignissim lacinia. Sed vulputate lorem non dolor adipiscing ornare. Morbi ornare id nisl id aliquam. Ut fringilla elit ante, nec lacinia enim fermentum sit amet. Aenean rutrum lorem eu convallis pharetra. Cras malesuada varius metus, vitae gravida velit. Nam a varius ipsum, ac commodo dolor. Phasellus nec elementum elit. Etiam vel adipiscing leo.'); $coupon3->setEffects(array( - RemoveXPercent::INPUT_AMOUNT_NAME => 0.00, RemoveXPercent::INPUT_PERCENTAGE_NAME => 10.00, )); $coupon3->setIsUsed(false); diff --git a/templates/backOffice/default/I18n/en_US.php b/templates/backOffice/default/I18n/en_US.php index 52e31dcaa..025f1fb20 100644 --- a/templates/backOffice/default/I18n/en_US.php +++ b/templates/backOffice/default/I18n/en_US.php @@ -59,8 +59,10 @@ return array( 'Alpha code 2' => 'Alpha code 2', 'Alpha code 3' => 'Alpha code 3', 'Amount' => 'Amount', + 'Amount, e.g. 12.50' => 'Amount, e.g. 12.50', 'An error occured' => 'An error occured', 'And' => 'And', + 'Applies to products :' => 'Applies to products :', 'Applies to products in categories :' => 'Applies to products in categories :', 'Apply' => 'Apply', 'Associated countries' => 'Associated countries', @@ -285,6 +287,7 @@ return array( 'Disabled' => 'Disabled', 'Discount' => 'Discount', 'Discount amount' => 'Discount amount', + 'Discount percentage' => 'Discount percentage', 'Do not use a product template' => 'Do not use a product template', 'Do you really want to add this attribute to all product templates ?' => 'Do you really want to add this attribute to all product templates ?', 'Do you really want to add this feature to all product templates ?' => 'Do you really want to add this feature to all product templates ?', @@ -637,6 +640,7 @@ return array( 'Payment information' => 'Payment information', 'Payment module' => 'Payment module', 'Payment modules' => 'Payment modules', + 'Percent Discount' => 'Percent Discount', 'Period' => 'Period', 'Phone' => 'Phone', 'Phone number' => 'Phone number', @@ -653,6 +657,7 @@ return array( 'Please select the PDF template to translate' => 'Please select the PDF template to translate', 'Please select the module component' => 'Please select the module component', 'Please select the module to translate' => 'Please select the module to translate', + 'Please select...' => 'Please select...', 'Please wait, loading' => 'Please wait, loading', 'Port' => 'Port', 'Port :' => 'Port :', @@ -763,6 +768,7 @@ return array( 'Select an attribute...' => 'Select an 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 product category :' => 'Select product category :', 'Send a mail to this customer' => 'Send a mail to this customer', 'Send files' => 'Send files', 'Sequential number of log line' => 'Sequential number of log line', diff --git a/templates/backOffice/default/assets/js/coupon.js b/templates/backOffice/default/assets/js/coupon.js index a22a968c6..dae6d9574 100644 --- a/templates/backOffice/default/assets/js/coupon.js +++ b/templates/backOffice/default/assets/js/coupon.js @@ -235,6 +235,14 @@ $(function($){ } }).done(function(data) { inputsDiv.html(data); + + // Invoke coupon setup funtion, if any + try { + couponInputFormSetup(); + } + catch (ex) { + // Ignore the error + } }); }; diff --git a/templates/backOffice/default/coupon-create.html b/templates/backOffice/default/coupon-create.html index d1a56e68b..e3b61a5d7 100644 --- a/templates/backOffice/default/coupon-create.html +++ b/templates/backOffice/default/coupon-create.html @@ -46,9 +46,9 @@ $(function($){ // Url alowing to get coupon inputs $.couponManager.urlAjaxAdminCouponDrawInputs = "{$urlAjaxAdminCouponDrawInputs}"; - $.couponManager.intlPleaseRetry = '{intl l='Something goes wrong, please try again.'}'; - $.couponManager.intlDoYouReallyWantToSetCouponAvailableForEveryOne = '{intl l='Do you really want to set this coupon available to everyone ?'}'; - $.couponManager.intlDoYouReallyWantToDeleteThisCondition = '{intl l='Do you really want to delete this condition ?'}'; + $.couponManager.intlPleaseRetry = '{intl l='Something goes wrong, please try again.' js=1}'; + $.couponManager.intlDoYouReallyWantToSetCouponAvailableForEveryOne = '{intl l='Do you really want to set this coupon available to everyone ?' js=1}'; + $.couponManager.intlDoYouReallyWantToDeleteThisCondition = '{intl l='Do you really want to delete this condition ?' js=1}'; }); diff --git a/templates/backOffice/default/coupon-update.html b/templates/backOffice/default/coupon-update.html index 3e93eeee2..03a6d08f2 100644 --- a/templates/backOffice/default/coupon-update.html +++ b/templates/backOffice/default/coupon-update.html @@ -53,12 +53,20 @@ $.couponManager.urlAjaxAdminCouponDrawInputs = '{$urlAjaxAdminCouponDrawInputs}'; $.couponManager.urlAjaxGetConditionInputFromServiceId = '{$urlAjaxGetConditionInputFromServiceId}'; $.couponManager.urlAjaxGetConditionInputFromConditionInterface = '{$urlAjaxGetConditionInputFromConditionInterface}'; - $.couponManager.intlPleaseRetry = '{intl l='Something goes wrong, please try again'}'; - $.couponManager.intlPleaseSelectAnotherCondition = '{intl l='Please select another condition'}'; - $.couponManager.intlDoYouReallyWantToSetCouponAvailableForEveryOne = '{intl l='Do you really want to set this coupon available to everyone ?'}'; - $.couponManager.intlDoYouReallyWantToDeleteThisCondition = '{intl l='Do you really want to delete this condition ?'}'; + $.couponManager.intlPleaseRetry = '{intl l='Something goes wrong, please try again' js=1}'; + $.couponManager.intlPleaseSelectAnotherCondition = '{intl l='Please select another condition' js=1}'; + $.couponManager.intlDoYouReallyWantToSetCouponAvailableForEveryOne = '{intl l='Do you really want to set this coupon available to everyone ?' js=1}'; + $.couponManager.intlDoYouReallyWantToDeleteThisCondition = '{intl l='Do you really want to delete this condition ?' js=1}'; $('#condition-save-btn').hide(); + + // Invoke the coupon setup function if defined. + try { + couponInputFormSetup(); + } + catch (ex) { + // Ignore the error + } }); {/block} diff --git a/templates/backOffice/default/coupon/form.html b/templates/backOffice/default/coupon/form.html index 19ed9254e..bdb8b7226 100644 --- a/templates/backOffice/default/coupon/form.html +++ b/templates/backOffice/default/coupon/form.html @@ -181,9 +181,7 @@ {/form_field}
- {form_field form=$form field='amount'} {$couponInputsHtml nofilter} - {/form_field}
diff --git a/templates/backOffice/default/coupon/type-fragments/ajax-products-list.html b/templates/backOffice/default/coupon/type-fragments/ajax-products-list.html new file mode 100644 index 000000000..2752f4d0f --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/ajax-products-list.html @@ -0,0 +1,3 @@ +{loop type="product" category={$smarty.post.category_id} name="list-of-products" backend_context="1"} + +{/loop} \ 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 b14a171a5..15c528331 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,19 +1,26 @@ -
- + +
+ + +
- + + {loop type="currency" name="get-symbol" default_only="true"}
{$SYMBOL}
{/loop}
-
- - {loop type="category-tree" category=0 name="list-of-categories" backend_context="1"} - + {/loop} + {intl l='Use Ctrl+click to select (or deselect) more that one category'}
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 new file mode 100644 index 000000000..57d45684c --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/remove-amount-on-products.html @@ -0,0 +1,77 @@ + +
+ + + +
+ + + {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'} +
+ + 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 new file mode 100644 index 000000000..08c3655f1 --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-categories.html @@ -0,0 +1,24 @@ + +
+ + + +
+ + +
%
+
+
+ +
+ + + + + + {intl l='Use Ctrl+click to select (or deselect) more that one category'} +
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 new file mode 100644 index 000000000..4787da2ee --- /dev/null +++ b/templates/backOffice/default/coupon/type-fragments/remove-percentage-on-products.html @@ -0,0 +1,75 @@ + +
+ + + +
+ + +
%
+
+
+ +
+ + + + + + {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'} +
+ + 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 6159af73f..fe9cf285f 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-x-amount.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-x-amount.html @@ -1,9 +1,11 @@ -
- +
+ +
- + + {loop type="currency" name="get-symbol" default_only="true"} -
{$SYMBOL}
+
{$SYMBOL}
{/loop}
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 4c7c8d9d9..f06b42a6a 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-x-percent.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-x-percent.html @@ -1,8 +1,9 @@ - -
- +
+ + +
- +
%
diff --git a/templates/backOffice/default/coupon/type-fragments/remove-x.html b/templates/backOffice/default/coupon/type-fragments/remove-x.html index 3ac1bab7b..05a606d5a 100644 --- a/templates/backOffice/default/coupon/type-fragments/remove-x.html +++ b/templates/backOffice/default/coupon/type-fragments/remove-x.html @@ -1,4 +1,4 @@ -
- - +
+ +
diff --git a/templates/backOffice/default/translations.html b/templates/backOffice/default/translations.html index c67d59fe0..a64861dcf 100644 --- a/templates/backOffice/default/translations.html +++ b/templates/backOffice/default/translations.html @@ -52,7 +52,7 @@ - +
@@ -303,6 +303,7 @@ $('#item_to_translate').change(function() { $('#item-id').val(''); + $('#item_name').val(''); $('.item-id-selector').hide(); }); @@ -325,7 +326,7 @@ translation_changed = true; ev.preventDefault(); - }) + }); $('#translation_form').submit(function(ev) { diff --git a/templates/frontOffice/default/index.html b/templates/frontOffice/default/index.html index 53aa8c0f1..f5bcfa668 100644 --- a/templates/frontOffice/default/index.html +++ b/templates/frontOffice/default/index.html @@ -12,6 +12,7 @@ {block name='breadcrumb'}{/block} {block name="main-content"} + u={viewurl view="legal"}