From 8be785e9b37ef4e0912243594a5687459f0d2608 Mon Sep 17 00:00:00 2001 From: gmorel Date: Tue, 10 Sep 2013 20:00:43 +0200 Subject: [PATCH 01/12] WIP - Coupon Add/Edit/Delete rule AJAX --- core/lib/Thelia/Action/Coupon.php | 54 ++-- .../Rule/AvailableForTotalAmountManager.php | 144 ----------- .../Rule/AvailableForXArticlesManager.php | 123 +-------- .../Controller/Admin/CouponController.php | 200 ++++++++------- core/lib/Thelia/Core/Event/TheliaEvents.php | 16 -- core/lib/Thelia/Model/Coupon.php | 92 +++---- templates/admin/default/coupon-create.html | 2 +- templates/admin/default/coupon-update.html | 140 +++++++--- templates/admin/default/coupon/form.html | 240 +++++++++--------- .../admin/default/coupon/rule-input-ajax.html | 14 +- templates/admin/default/coupon/rules.html | 6 +- 11 files changed, 409 insertions(+), 622 deletions(-) diff --git a/core/lib/Thelia/Action/Coupon.php b/core/lib/Thelia/Action/Coupon.php index fee349585..7a7babf01 100755 --- a/core/lib/Thelia/Action/Coupon.php +++ b/core/lib/Thelia/Action/Coupon.php @@ -24,6 +24,7 @@ namespace Thelia\Action; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Thelia\Constraint\ConstraintFactory; use Thelia\Core\Event\Coupon\CouponCreateOrUpdateEvent; use Thelia\Core\Event\TheliaEvents; use Thelia\Model\Coupon as CouponModel; @@ -68,18 +69,6 @@ class Coupon extends BaseAction implements EventSubscriberInterface $this->createOrUpdate($coupon, $event); } - /** - * Occurring when a Coupon rule is about to be created - * - * @param CouponCreateOrUpdateEvent $event Event creation or update Event - */ - public function createRule(CouponCreateOrUpdateEvent $event) - { - $coupon = $event->getCoupon(); - - $this->createOrUpdate($coupon, $event); - } - /** * Occurring when a Coupon rule is about to be updated * @@ -89,19 +78,7 @@ class Coupon extends BaseAction implements EventSubscriberInterface { $coupon = $event->getCoupon(); - $this->createOrUpdate($coupon, $event); - } - - /** - * Occurring when a Coupon rule is about to be deleted - * - * @param CouponCreateOrUpdateEvent $event Event creation or update Event - */ - public function deleteRule(CouponCreateOrUpdateEvent $event) - { - $coupon = $event->getCoupon(); - - $this->createOrUpdate($coupon, $event); + $this->createOrUpdateRule($coupon, $event); } /** @@ -130,6 +107,7 @@ class Coupon extends BaseAction implements EventSubscriberInterface $event->getTitle(), $event->getAmount(), $event->getEffect(), + $event->isRemovingPostage(), $event->getShortDescription(), $event->getDescription(), $event->isEnabled(), @@ -137,7 +115,28 @@ class Coupon extends BaseAction implements EventSubscriberInterface $event->isAvailableOnSpecialOffers(), $event->isCumulative(), $event->getMaxUsage(), - $event->getRules(), + $event->getLocale() + ); + + $event->setCoupon($coupon); + } + + /** + * Call the Model and delegate the create or delete action + * Feed the Event with the updated model + * + * @param CouponModel $coupon Model to save + * @param CouponCreateOrUpdateEvent $event Event containing data + */ + protected function createOrUpdateRule(CouponModel $coupon, CouponCreateOrUpdateEvent $event) + { + $coupon->setDispatcher($this->getDispatcher()); + + /** @var ConstraintFactory $constraintFactory */ + $constraintFactory = $this->container->get('thelia.constraint.factory'); + + $coupon->createOrUpdateRules( + $constraintFactory->serializeCouponRuleCollection($event->getRules()), $event->getLocale() ); @@ -172,8 +171,7 @@ class Coupon extends BaseAction implements EventSubscriberInterface TheliaEvents::COUPON_DISABLE => array("disable", 128), TheliaEvents::COUPON_ENABLE => array("enable", 128), TheliaEvents::COUPON_CONSUME => array("consume", 128), - TheliaEvents::COUPON_RULE_UPDATE => array("updateRule", 128), - TheliaEvents::COUPON_RULE_DELETE => array("deleteRule", 128) + TheliaEvents::COUPON_RULE_UPDATE => array("updateRule", 128) ); } } diff --git a/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php b/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php index 6ab746e6c..9f1bf23a0 100644 --- a/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php +++ b/core/lib/Thelia/Constraint/Rule/AvailableForTotalAmountManager.php @@ -73,70 +73,6 @@ class AvailableForTotalAmountManager extends CouponRuleAbstract ) ); -// /** @var RuleValidator Price Validator */ -// protected $priceValidator = null; - -// /** -// * Check if backoffice inputs are relevant or not -// * -// * @throws InvalidRuleOperatorException if Operator is not allowed -// * @throws InvalidRuleValueException if Value is not allowed -// * @return bool -// */ -// public function checkBackOfficeInput() -// { -// if (!isset($this->validators) -// || empty($this->validators) -// ||!isset($this->validators[self::PARAM1_PRICE]) -// ||!isset($this->validators[self::PARAM1_PRICE]) -// ) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_PRICE); -// } -// -// /** @var RuleValidator $ruleValidator */ -// $ruleValidator = $this->validators[self::PARAM1_PRICE]; -// /** @var PriceParam $price */ -// $price = $ruleValidator->getParam(); -// -// if (!$price instanceof PriceParam) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_PRICE); -// } -// -// $this->checkBackOfficeInputsOperators(); -// -// return $this->isPriceValid($price->getPrice(), $price->getCurrency()); -// } - -// /** -// * Check if Checkout inputs are relevant or not -// * -// * @throws InvalidRuleValueException if Value is not allowed -// * @return bool -// */ -// public function checkCheckoutInput() -// { -// $currency = $this->adapter->getCheckoutCurrency(); -// if (empty($currency)) { -// throw new InvalidRuleValueException( -// get_class(), self::PARAM1_CURRENCY -// ); -// } -// -// $price = $this->adapter->getCartTotalPrice(); -// if (empty($price)) { -// throw new InvalidRuleValueException( -// get_class(), self::PARAM1_PRICE -// ); -// } -// -// $this->paramsToValidate = array( -// self::PARAM1_PRICE => $this->adapter->getCartTotalPrice(), -// self::PARAM1_CURRENCY => $this->adapter->getCheckoutCurrency() -// ); -// -// return $this->isPriceValid($price, $currency); -// } - /** * Check validators relevancy and store them * @@ -249,49 +185,6 @@ class AvailableForTotalAmountManager extends CouponRuleAbstract return false; } -// /** -// * Check if a price is valid -// * -// * @param float $price Price to check -// * @param string $currency Price currency -// * -// * @throws InvalidRuleValueException if Value is not allowed -// * @return bool -// */ -// protected function isPriceValid($price, $currency) -// { -// $priceValidator = $this->priceValidator; -// -// /** @var PriceParam $param */ -// $param = $priceValidator->getParam(); -// if ($currency == $param->getCurrency()) { -// try { -// $priceValidator->getParam()->compareTo($price); -// } catch(\InvalidArgumentException $e) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_PRICE); -// } -// } else { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_CURRENCY); -// } -// -// return true; -// } - -// /** -// * Generate current Rule param to be validated from adapter -// * -// * @return $this -// */ -// protected function setParametersToValidate() -// { -// $this->paramsToValidate = array( -// self::PARAM1_PRICE => $this->adapter->getCartTotalPrice(), -// self::PARAM1_CURRENCY => $this->adapter->getCheckoutCurrency() -// ); -// -// return $this; -// } - /** * Get I18n name * @@ -377,41 +270,4 @@ class AvailableForTotalAmountManager extends CouponRuleAbstract ); } -// /** -// * Populate a Rule from a form admin -// * -// * @param array $operators Rule Operator set by the Admin -// * @param array $values Rule Values set by the Admin -// * -// * @throws \InvalidArgumentException -// * @return $this -// */ -// public function populateFromForm(array $operators, array $values) -// { -// if ($values[self::PARAM1_PRICE] === null -// || $values[self::PARAM1_CURRENCY] === null -// ) { -// throw new \InvalidArgumentException( -// 'The Rule ' . get_class() . 'needs at least a quantity set (' . self::PARAM1_PRICE . ', ' . self::PARAM1_CURRENCY . ')' -// ); -// } -// -// $this->priceValidator = new RuleValidator( -// $operators[self::PARAM1_PRICE], -// new PriceParam( -// $this->translator, -// $values[self::PARAM1_PRICE], -// $values[self::PARAM1_CURRENCY] -// ) -// ); -// -// $this->validators = array(self::PARAM1_PRICE => $this->priceValidator); -// -// return $this; -// } - - - - - } \ No newline at end of file diff --git a/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php b/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php index 20d7eddca..26c7a8aeb 100644 --- a/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php +++ b/core/lib/Thelia/Constraint/Rule/AvailableForXArticlesManager.php @@ -63,77 +63,6 @@ class AvailableForXArticlesManager extends CouponRuleAbstract ) ); -// /** @var QuantityParam Quantity Validator */ -// protected $quantityValidator = null; - -// /** -// * Check if backoffice inputs are relevant or not -// * -// * @throws InvalidRuleOperatorException if Operator is not allowed -// * @throws InvalidRuleValueException if Value is not allowed -// * @return bool -// */ -// public function checkBackOfficeInput() -// { -// if (!isset($this->validators) -// || empty($this->validators) -// ||!isset($this->validators[self::PARAM1_QUANTITY]) -// ||!isset($this->validators[self::PARAM1_QUANTITY]) -// ) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_QUANTITY); -// } -// -// /** @var RuleValidator $ruleValidator */ -// $ruleValidator = $this->validators[self::PARAM1_QUANTITY]; -// /** @var QuantityParam $quantity */ -// $quantity = $ruleValidator->getParam(); -// -// if (!$quantity instanceof QuantityParam) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_QUANTITY); -// } -// -// $this->checkBackOfficeInputsOperators(); -// -// return $this->isQuantityValid($quantity->getInteger()); -// } - -// /** -// * Generate current Rule param to be validated from adapter -// * -// * @param CouponAdapterInterface $adapter allowing to gather -// * all necessary Thelia variables -// * -// * @return $this -// */ -// protected function setParametersToValidate() -// { -// $this->paramsToValidate = array( -// self::PARAM1_QUANTITY => $this->adapter->getNbArticlesInCart() -// ); -// -// return $this; -// } - -// /** -// * Check if Checkout inputs are relevant or not -// * -// * @throws \Thelia\Exception\InvalidRuleValueException -// * @return bool -// */ -// public function checkCheckoutInput() -// { -// if (!isset($this->paramsToValidate) -// || empty($this->paramsToValidate) -// ||!isset($this->paramsToValidate[self::PARAM1_QUANTITY]) -// ) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_QUANTITY); -// } -// -// $price = $this->paramsToValidate[self::PARAM1_QUANTITY]; -// -// return $this->isQuantityValid($price); -// } - /** * Check validators relevancy and store them * @@ -174,7 +103,7 @@ class AvailableForXArticlesManager extends CouponRuleAbstract ); } - if (!is_int($quantityValue) || $quantityValue <= 0) { + if ((int) $quantityValue <= 0) { throw new \InvalidArgumentException( 'Value for quantity field is not legit' ); @@ -210,26 +139,6 @@ class AvailableForXArticlesManager extends CouponRuleAbstract return false; } -// /** -// * Check if a quantity is valid -// * -// * @param int $quantity Quantity to check -// * -// * @throws InvalidRuleValueException if Value is not allowed -// * @return bool -// */ -// protected function isQuantityValid($quantity) -// { -// $quantityValidator = $this->quantityValidator; -// try { -// $quantityValidator->getParam()->compareTo($quantity); -// } catch(InvalidArgumentException $e) { -// throw new InvalidRuleValueException(get_class(), self::PARAM1_QUANTITY); -// } -// -// return true; -// } - /** * Get I18n name * @@ -267,36 +176,6 @@ class AvailableForXArticlesManager extends CouponRuleAbstract return $toolTip; } -// /** -// * Populate a Rule from a form admin -// * -// * @param array $operators Rule Operator set by the Admin -// * @param array $values Rule Values set by the Admin -// * -// * @throws InvalidArgumentException -// * @return $this -// */ -// public function populateFromForm(array $operators, array $values) -// { -// if ($values[self::PARAM1_QUANTITY] === null) { -// throw new InvalidArgumentException( -// 'The Rule ' . get_class() . 'needs at least a quantity set (' . self::PARAM1_QUANTITY. ')' -// ); -// } -// -// $this->quantityValidator = new RuleValidator( -// $operators[self::PARAM1_QUANTITY], -// new QuantityParam( -// $this->adapter, -// $values[self::PARAM1_QUANTITY] -// ) -// ); -// -// $this->validators = array(self::PARAM1_QUANTITY => $this->quantityValidator); -// -// return $this; -// } - /** * Generate inputs ready to be drawn * diff --git a/core/lib/Thelia/Controller/Admin/CouponController.php b/core/lib/Thelia/Controller/Admin/CouponController.php index e11fcf09e..cd8521d7a 100755 --- a/core/lib/Thelia/Controller/Admin/CouponController.php +++ b/core/lib/Thelia/Controller/Admin/CouponController.php @@ -158,6 +158,13 @@ class CouponController extends BaseAdminController ); } else { // Prepare the data that will hydrate the form + + /** @var ConstraintFactory $constraintFactory */ + $constraintFactory = $this->container->get('thelia.constraint.factory'); + $rules = $constraintFactory->unserializeCouponRuleCollection( + $coupon->getSerializedRules() + ); + $data = array( 'code' => $coupon->getCode(), 'title' => $coupon->getTitle(), @@ -171,18 +178,12 @@ class CouponController extends BaseAdminController 'isCumulative' => ($coupon->getIsCumulative() == 1), 'isRemovingPostage' => ($coupon->getIsRemovingPostage() == 1), 'maxUsage' => $coupon->getMaxUsage(), - 'rules' => new CouponRuleCollection(array()), + 'rules' => $rules, 'locale' => $coupon->getLocale(), ); $args['rulesObject'] = array(); - /** @var ConstraintFactory $constraintFactory */ - $constraintFactory = $this->container->get('thelia.constraint.factory'); - $rules = $constraintFactory->unserializeCouponRuleCollection( - $coupon->getSerializedRules() - ); - /** @var CouponRuleInterface $rule */ foreach ($rules->getRules() as $rule) { $args['rulesObject'][] = array( @@ -193,6 +194,8 @@ class CouponController extends BaseAdminController ); } + $args['rules'] = $this->cleanRuleForTemplate($rules); + // Setup the object form $changeForm = new CouponCreationForm($this->getRequest(), 'form', $data); @@ -216,100 +219,101 @@ class CouponController extends BaseAdminController $args['formAction'] = 'admin/coupon/update/' . $couponId; - return $this->render( - 'coupon-update', - $args - ); + return $this->render('coupon-update', $args); } - /** - * Manage Coupons Rule creation display - * - * @param int $couponId Coupon id - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function createRuleAction($couponId) - { - // Check current user authorization - $response = $this->checkAuth('admin.coupon.update'); - if ($response !== null) { - return $response; - } - - /** @var Coupon $coupon */ - $coupon = CouponQuery::create()->findOneById($couponId); - if (!$coupon) { - $this->pageNotFound(); - } - - // Parameters given to the template - $args = array(); - - $i18n = new I18n(); - /** @var Lang $lang */ - $lang = $this->getSession()->get('lang'); - $eventToDispatch = TheliaEvents::COUPON_RULE_CREATE; - - if ($this->getRequest()->isMethod('POST')) { - $this->validateCreateOrUpdateForm( - $i18n, - $lang, - $eventToDispatch, - 'updated', - 'update' - ); - } else { - // Prepare the data that will hydrate the form - $data = array( - 'code' => $coupon->getCode(), - 'title' => $coupon->getTitle(), - 'amount' => $coupon->getAmount(), - 'effect' => $coupon->getType(), - 'shortDescription' => $coupon->getShortDescription(), - 'description' => $coupon->getDescription(), - 'isEnabled' => ($coupon->getIsEnabled() == 1), - 'expirationDate' => $coupon->getExpirationDate($lang->getDateFormat()), - 'isAvailableOnSpecialOffers' => ($coupon->getIsAvailableOnSpecialOffers() == 1), - 'isCumulative' => ($coupon->getIsCumulative() == 1), - 'isRemovingPostage' => ($coupon->getIsRemovingPostage() == 1), - 'maxUsage' => $coupon->getMaxUsage(), - 'rules' => new CouponRuleCollection(array()), - 'locale' => $coupon->getLocale(), - ); - - /** @var CouponAdapterInterface $adapter */ - $adapter = $this->container->get('thelia.adapter'); - /** @var Translator $translator */ - $translator = $this->container->get('thelia.translator'); - - $args['rulesObject'] = array(); - /** @var CouponRuleInterface $rule */ - foreach ($coupon->getRules()->getRules() as $rule) { - $args['rulesObject'][] = array( - 'name' => $rule->getName($translator), - 'tooltip' => $rule->getToolTip($translator), - 'validators' => $rule->getValidators() - ); - } - +// /** +// * Manage Coupons Rule creation display +// * +// * @param int $couponId Coupon id +// * +// * @return \Symfony\Component\HttpFoundation\Response +// */ +// public function createRuleAction($couponId) +// { +// // Check current user authorization +// $response = $this->checkAuth('admin.coupon.update'); +// if ($response !== null) { +// return $response; +// } +// +// /** @var Coupon $coupon */ +// $coupon = CouponQuery::create()->findOneById($couponId); +// if (!$coupon) { +// $this->pageNotFound(); +// } +// +// // Parameters given to the template +// $args = array(); +// +// $i18n = new I18n(); +// /** @var Lang $lang */ +// $lang = $this->getSession()->get('lang'); +// $eventToDispatch = TheliaEvents::COUPON_RULE_CREATE; +// +// if ($this->getRequest()->isMethod('POST')) { +// $this->validateCreateOrUpdateForm( +// $i18n, +// $lang, +// $eventToDispatch, +// 'updated', +// 'update' +// ); +// } else { +// // Prepare the data that will hydrate the form +// +// /** @var ConstraintFactory $constraintFactory */ +// $constraintFactory = $this->container->get('thelia.constraint.factory'); +// +// $data = array( +// 'code' => $coupon->getCode(), +// 'title' => $coupon->getTitle(), +// 'amount' => $coupon->getAmount(), +// 'effect' => $coupon->getType(), +// 'shortDescription' => $coupon->getShortDescription(), +// 'description' => $coupon->getDescription(), +// 'isEnabled' => ($coupon->getIsEnabled() == 1), +// 'expirationDate' => $coupon->getExpirationDate($lang->getDateFormat()), +// 'isAvailableOnSpecialOffers' => ($coupon->getIsAvailableOnSpecialOffers() == 1), +// 'isCumulative' => ($coupon->getIsCumulative() == 1), +// 'isRemovingPostage' => ($coupon->getIsRemovingPostage() == 1), +// 'maxUsage' => $coupon->getMaxUsage(), +// 'rules' => $constraintFactory->unserializeCouponRuleCollection($coupon->getSerializedRules()), +// 'locale' => $coupon->getLocale(), +// ); +// +// /** @var CouponAdapterInterface $adapter */ +// $adapter = $this->container->get('thelia.adapter'); +// /** @var Translator $translator */ +// $translator = $this->container->get('thelia.translator'); +// +// $args['rulesObject'] = array(); +// /** @var CouponRuleInterface $rule */ +// foreach ($coupon->getRules()->getRules() as $rule) { +// $args['rulesObject'][] = array( +// 'name' => $rule->getName($translator), +// 'tooltip' => $rule->getToolTip($translator), +// 'validators' => $rule->getValidators() +// ); +// } +// // $args['rules'] = $this->cleanRuleForTemplate($coupon->getRules()->getRules()); - - // Setup the object form - $changeForm = new CouponCreationForm($this->getRequest(), 'form', $data); - - // Pass it to the parser - $this->getParserContext()->addForm($changeForm); - } - - $args['formAction'] = 'admin/coupon/update/' . $couponId; - - return $this->render( - 'coupon-update', - $args - ); - } +// +// // Setup the object form +// $changeForm = new CouponCreationForm($this->getRequest(), 'form', $data); +// +// // Pass it to the parser +// $this->getParserContext()->addForm($changeForm); +// } +// +// $args['formAction'] = 'admin/coupon/update/' . $couponId; +// +// return $this->render( +// 'coupon-update', +// $args +// ); +// } @@ -413,7 +417,6 @@ class CouponController extends BaseAdminController $constraintFactory = $this->container->get('thelia.constraint.factory'); $rulesReceived = json_decode($this->getRequest()->get('rules')); foreach ($rulesReceived as $ruleReceived) { - var_dump('building ', $ruleReceived->values); $rule = $constraintFactory->build( $ruleReceived->serviceId, (array) $ruleReceived->operators, @@ -442,6 +445,7 @@ class CouponController extends BaseAdminController $rules, $coupon->getLocale() ); + $couponEvent->setCoupon($coupon); $eventToDispatch = TheliaEvents::COUPON_RULE_UPDATE; // Dispatch Event to the Action diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 500803f53..de7a09e4a 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -296,22 +296,6 @@ final class TheliaEvents */ const AFTER_COUPON_RULE_UPDATE = "action.after_update_coupon_rule"; - /** - * Sent when attempting to delete Coupon Rule - */ - const COUPON_RULE_DELETE = "action.delete_coupon_rule"; - - /** - * Sent just before an attempt to delete a Coupon Rule - */ - const BEFORE_COUPON_RULE_DELETE = "action.before_delete_coupon_rule"; - - /** - * Sent just after an attempt to delete a Coupon Rule - */ - const AFTER_COUPON_RULE_DELETE = "action.after_delete_coupon_rule"; - - // -- Configuration management --------------------------------------------- const CONFIG_CREATE = "action.createConfig"; diff --git a/core/lib/Thelia/Model/Coupon.php b/core/lib/Thelia/Model/Coupon.php index f5788ff4f..7a7ce1e4a 100755 --- a/core/lib/Thelia/Model/Coupon.php +++ b/core/lib/Thelia/Model/Coupon.php @@ -49,24 +49,25 @@ class Coupon extends BaseCoupon /** - * Constructor + * Create or Update this Coupon * - * @param string $code Coupon Code - * @param string $title Coupon title - * @param float $amount Amount removed from the Total Checkout - * @param string $effect Coupon effect - * @param string $shortDescription Coupon short description - * @param string $description Coupon description - * @param boolean $isEnabled Enable/Disable - * @param \DateTime $expirationDate Coupon expiration date - * @param boolean $isAvailableOnSpecialOffers Is available on special offers - * @param boolean $isCumulative Is cumulative - * @param boolean $isRemovingPostage Is removing Postage - * @param int $maxUsage Coupon quantity - * @param CouponRuleCollection $rules CouponRuleInterface to add - * @param string $locale Coupon Language code ISO (ex: fr_FR) + * @param string $code Coupon Code + * @param string $title Coupon title + * @param float $amount Amount removed from the Total Checkout + * @param string $effect Coupon effect + * @param bool $isRemovingPostage Is removing Postage + * @param string $shortDescription Coupon short description + * @param string $description Coupon description + * @param boolean $isEnabled Enable/Disable + * @param \DateTime $expirationDate Coupon expiration date + * @param boolean $isAvailableOnSpecialOffers Is available on special offers + * @param boolean $isCumulative Is cumulative + * @param int $maxUsage Coupon quantity + * @param string $locale Coupon Language code ISO (ex: fr_FR) + * + * @throws \Exception */ - function createOrUpdate($code, $title, $amount, $effect, $shortDescription, $description, $isEnabled, $expirationDate, $isAvailableOnSpecialOffers, $isCumulative, $maxUsage, $rules, $locale = null) + function createOrUpdate($code, $title, $amount, $effect, $isRemovingPostage, $shortDescription, $description, $isEnabled, $expirationDate, $isAvailableOnSpecialOffers, $isCumulative, $maxUsage, $locale = null) { $this->setCode($code) ->setTitle($title) @@ -74,13 +75,13 @@ class Coupon extends BaseCoupon ->setDescription($description) ->setType($effect) ->setAmount($amount) + ->setIsRemovingPostage($isRemovingPostage) ->setType($amount) ->setIsEnabled($isEnabled) ->setExpirationDate($expirationDate) ->setIsAvailableOnSpecialOffers($isAvailableOnSpecialOffers) ->setIsCumulative($isCumulative) - ->setMaxUsage($maxUsage) - ->setRules($rules); + ->setMaxUsage($maxUsage); // Set object language (i18n) if (!is_null($locale)) { @@ -99,33 +100,34 @@ class Coupon extends BaseCoupon } } -// /** -// * Set the value of [serialized_rules] column. -// * Convert a CouponRuleCollection into a serialized array of SerializableRule -// * -// * @param CouponRuleCollection $rules A set of Rules -// * -// * @return \Thelia\Model\Coupon The current object (for fluent API support) -// */ -// public function setRules(CouponRuleCollection $rules) -// { -// $serializedRules = null; -// if ($rules !== null) { -// /** @var $rule CouponRuleInterface */ -// foreach ($rules->getRules() as $rule) { -// $serializedRules[] = $rule->getSerializableRule(); -// } -// -// $serializedRules = (string) base64_encode(serialize($serializedRules)); -// } -// -// if ($this->serialized_rules !== $serializedRules) { -// $this->serialized_rules = $serializedRules; -// $this->modifiedColumns[] = CouponTableMap::SERIALIZED_RULES; -// } -// -// return $this; -// } + /** + * Create or Update this coupon rule + * + * @param string $serializableRules Serialized rules ready to be saved + * @param string $locale Coupon Language code ISO (ex: fr_FR) + * + * @throws \Exception + */ + function createOrUpdateRules($serializableRules, $locale) + { + $this->setSerializedRules($serializableRules); + + // Set object language (i18n) + if (!is_null($locale)) { + $this->setLocale($locale); + } + + $con = Propel::getWriteConnection(CouponTableMap::DATABASE_NAME); + $con->beginTransaction(); + try { + $this->save($con); + $con->commit(); + + } catch(\Exception $e) { + $con->rollback(); + throw $e; + } + } diff --git a/templates/admin/default/coupon-create.html b/templates/admin/default/coupon-create.html index 7cb31ee09..e8fcb73f5 100755 --- a/templates/admin/default/coupon-create.html +++ b/templates/admin/default/coupon-create.html @@ -16,7 +16,7 @@ {form name="thelia.admin.coupon.creation"} - {include file='coupon/form.html' formAction={url path={$formAction}}} + {include file='coupon/form.html' formAction={url path={$formAction}} noRules=true} {/form} diff --git a/templates/admin/default/coupon-update.html b/templates/admin/default/coupon-update.html index 9b6387cd7..1971fafff 100755 --- a/templates/admin/default/coupon-update.html +++ b/templates/admin/default/coupon-update.html @@ -16,7 +16,7 @@ {form name="thelia.admin.coupon.creation"} - {include file='coupon/form.html' formAction={url path={$formAction}} form=$form} + {include file='coupon/form.html' formAction={url path={$formAction}} form=$form noRules=false} {/form} @@ -43,11 +43,19 @@ - + Array.prototype.clean = function(deleteValue) { + for (var i = 0; i < this.length; i++) { + if (this[i] == deleteValue) { + this.splice(i, 1); + i--; + } + } + return this; + }; // Init Rules - var initRule = function() { + var initRules = function() { var rules = []; {foreach from=$rulesObject key=k item=rule} var rule = {}; @@ -67,16 +75,13 @@ // Save Rules AJAX var saveRuleAjax = function() { + console.log('rulesToSave'); + console.log(rulesToSave); var $url = '{$urlAjaxUpdateRules}'; - console.log('save'); - console.log('{$urlAjaxUpdateRules}'); - console.log(rules); - console.log(JSON.stringify(rules)); $.ajax({ type: "POST", url: $url, - {*data: {literal}{{/literal}rules:rules{literal}}{/literal},*} - data: {literal}{{/literal}rules:JSON.stringify(rules){literal}}{/literal}, + data: {literal}{{/literal}rules:JSON.stringify(rulesToSave){literal}}{/literal}, statusCode: { 404: function() { $('#constraint-add-operators-values').html( @@ -91,20 +96,83 @@ } // Remove 1 Rule then Save Rules AJAX - var removeRuleAjax = function($id) { - rules.slice($id, 1); + var removeRuleAjax = function(id) { + // Delete rule in temporary array + delete rulesToSave[id]; + rulesToSave.clean(undefined); + + // Save saveRuleAjax(); } - // Add 1 Rule then Save Rules AJAX - var addRuleAjax = function() { - rules.push(ruleToSave); + // Add 1 Rule / or update the temporary Rules array then Save Rules via AJAX + var addRuleAjax = function(id) { + if(typeof id === 'number' && id % 1 == 0) { + rulesToSave[id] = ruleToSave; + } else { + rulesToSave.push(ruleToSave); + } + saveRuleAjax(); } + // Set rule inputs to allow editing + var updateRuleAjax = function(id) { + ruleToSave = rulesToSave[id]; + var ruleToUpdate = ruleToSave; - var rules = initRule(); - console.log(rules); + // Deleting this rule, we will reset it + delete rulesToSave[id]; + + // Set the rule seletor + $("#category-rule option").filter(function() { + return $(this).val() == ruleToSave.serviceId; + }).prop('selected', true); + + // Force rule input refresh + loadRuleInputs(ruleToSave.serviceId, ruleToUpdate, function(ruleToUpdate) { + fillInRuleInputs(ruleToUpdate); + }); + } + + // Fill in rule inputs + var fillInRuleInputs = function(ruleToUpdate) { + var valueId = null; + var operatorId = null; + + for (idName in ruleToUpdate.operators) { + // Setting idName operator input + operatorId = idName + '-operator'; + $('#' + operatorId).val(ruleToUpdate.operators[idName]); + // Setting idName value input + valueId = idName + '-value'; + $('#' + valueId).val(ruleToUpdate.values[idName]); + } + } + + + + + + // Reload rule inputs + var loadRuleInputs = function(ruleId, ruleToSave, callBack) { + $('#constraint-add-operators-values').html('
'); + var url = "{$urlAjaxGetRuleInput}"; + url = url.replace('ruleId', ruleId) + $.ajax({ + url: url, + statusCode: { + 404: function() { + $('#constraint-add-operators-values').html( + '{intl l='Please select another rule'}' + ); + } + } + }).done(function(data) { + $('#constraint-add-operators-values').html(data); + return callBack(ruleToSave); + }); + } // Save rules on click @@ -117,12 +185,24 @@ // Remove rule on click var onClickDeleteRule = function() { - $('#constraint-delete-btn').on('click', function (e) { -// removeRuleAjax(); + $('.constraint-delete-btn').on('click', function (e) { + e.preventDefault(); + var $this = $(this); + removeRuleAjax($this.attr('data-int')); }); } onClickDeleteRule(); + // Remove rule on click + var onClickUpdateRule = function() { + $('.constraint-update-btn').on('click', function (e) { + e.preventDefault(); + var $this = $(this); + updateRuleAjax($this.attr('data-int')); + }); + } + onClickUpdateRule(); + // Reload effect inputs when changing effect var onEffectChange = function() { $('#effect').on('change', function (e) { @@ -135,24 +215,18 @@ // Reload rule inputs when changing effect var onRuleChange = function() { $('#category-rule').on('change', function (e) { - $('#constraint-add-operators-values').html('
'); - var url = "{$urlAjaxGetRuleInput}"; - url = url.replace('ruleId', $(this).val()) - $.ajax({ - url: url, - statusCode: { - 404: function() { - $('#constraint-add-operators-values').html( - '{intl l='Please select another rule'}' - ); - } - } - }).done(function(data) { - $('#constraint-add-operators-values').html(data); - }); + loadRuleInputs($(this).val(), null, function(ruleToSave) {}); }); } onRuleChange(); + + + + + // Rule to save + var ruleToSave = {literal}{}{/literal}; + // Rules which will be saved + var rulesToSave = initRules(); }); diff --git a/templates/admin/default/coupon/form.html b/templates/admin/default/coupon/form.html index e22136d41..5d30d201d 100644 --- a/templates/admin/default/coupon/form.html +++ b/templates/admin/default/coupon/form.html @@ -161,134 +161,122 @@ - -
-
- - - - - - - - - - {include file='coupon/rules.html' rules=$rulesObject} - {*{foreach from=$rulesObject item=rule name=rulesForeach}*} - {**} - {**} - - {**} - {**} - {*{/foreach}*} - -
- {intl l='Rules'} -
{intl l='Conditions'}{intl l='Actions'}
*} - {*{if !$smarty.foreach.rulesForeach.first}*} - {*{intl l='And'}*} - {*{/if}*} - {*{$rule.tooltip nofilter}*} - {**} - {* {intl l='Edit'}*} - {* {intl l='Delete'}*} - {*
-
-
- -
-
- - - - -
- - - + {if $noRules} + {include file='includes/notifications.html' message={intl l='Please save your Coupon in oder to affect it some application fields'}} + {else} +
+
+ + + + + + + + + + {include file='coupon/rules.html' rules=$rules} + +
+ {intl l='Rules'} +
{intl l='Conditions'}{intl l='Actions'}
+
-
- - +
+
+ + + + +
+ + + +
+ +
+ + +
+ +
+ {**} + {*
*} + {*
*} + {**} + {*
*} + {*
*} + {**} + {**} + {*
*} + {*
*} + {**} + + {**} + {*
*} + {*
*} + {**} + {*
*} + {*
*} + {**} + {**} + {*
*} + {*
*} + + {**} + {*
*} + {*
*} + {**} + {*
*} + {*
*} + {**} + {*
*} + {*
*} + {*
*} + {*
*} + {**} + {**} + {**} + {**} + {**} + {**} + {**} + {**} + {**} + {**} + {*
Categories list
*} + {*
*} + {*
*} +
- -
- {**} - {*
*} - {*
*} - {**} - {*
*} - {*
*} - {**} - {**} - {*
*} - {*
*} - {**} - - {**} - {*
*} - {*
*} - {**} - {*
*} - {*
*} - {**} - {**} - {*
*} - {*
*} - - {**} - {*
*} - {*
*} - {**} - {*
*} - {*
*} - {**} - {*
*} - {*
*} - {*
*} - {*
*} - {**} - {**} - {**} - {**} - {**} - {**} - {**} - {**} - {**} - {**} - {*
Categories list
*} - {*
*} - {*
*} -
-
-
+ + {/if} diff --git a/templates/admin/default/coupon/rule-input-ajax.html b/templates/admin/default/coupon/rule-input-ajax.html index cf921932b..d0959a3ad 100644 --- a/templates/admin/default/coupon/rule-input-ajax.html +++ b/templates/admin/default/coupon/rule-input-ajax.html @@ -1,4 +1,3 @@ -{*{$inputs.inputs|var_dump}*} {foreach from=$inputs.inputs key=name item=input}
@@ -71,6 +70,8 @@ {*
*} \ No newline at end of file diff --git a/templates/admin/default/coupon/rules.html b/templates/admin/default/coupon/rules.html index 8dca66c49..199ee44c3 100644 --- a/templates/admin/default/coupon/rules.html +++ b/templates/admin/default/coupon/rules.html @@ -1,4 +1,4 @@ -{foreach from=$rules item=rule name=rulesForeach} +{foreach from=$rules item=rule key=i name=rulesForeach} {if !$smarty.foreach.rulesForeach.first} @@ -7,10 +7,10 @@ {$rule nofilter} - + {intl l='Edit'} - + {intl l='Delete'} From 7e2f57e6187589f724bf7055507be24e533a1c7f Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 09:48:33 +0200 Subject: [PATCH 02/12] Working - Coupon Add/Edit/Delete rule AJAX --- templates/admin/default/coupon-update.html | 24 ++++++----- .../admin/default/coupon/rule-input-ajax.html | 40 +++++++++---------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/templates/admin/default/coupon-update.html b/templates/admin/default/coupon-update.html index 1971fafff..3895475b4 100755 --- a/templates/admin/default/coupon-update.html +++ b/templates/admin/default/coupon-update.html @@ -41,8 +41,6 @@ $(function($){ miniBrowser(0, '/test_to_remove/datas_coupon_edit.json'); - - Array.prototype.clean = function(deleteValue) { for (var i = 0; i < this.length; i++) { if (this[i] == deleteValue) { @@ -53,7 +51,6 @@ return this; }; - // Init Rules var initRules = function() { var rules = []; @@ -75,8 +72,6 @@ // Save Rules AJAX var saveRuleAjax = function() { - console.log('rulesToSave'); - console.log(rulesToSave); var $url = '{$urlAjaxUpdateRules}'; $.ajax({ type: "POST", @@ -107,12 +102,14 @@ // Add 1 Rule / or update the temporary Rules array then Save Rules via AJAX var addRuleAjax = function(id) { + // If update if(typeof id === 'number' && id % 1 == 0) { rulesToSave[id] = ruleToSave; - } else { + } else { // If create rulesToSave.push(ruleToSave); } + // Save saveRuleAjax(); } @@ -193,12 +190,15 @@ } onClickDeleteRule(); - // Remove rule on click + // Update rule on click var onClickUpdateRule = function() { $('.constraint-update-btn').on('click', function (e) { e.preventDefault(); var $this = $(this); updateRuleAjax($this.attr('data-int')); + + // Hide row being updated + $this.parent().parent().remove(); }); } onClickUpdateRule(); @@ -215,21 +215,23 @@ // Reload rule inputs when changing effect var onRuleChange = function() { $('#category-rule').on('change', function (e) { - loadRuleInputs($(this).val(), null, function(ruleToSave) {}); + loadRuleInputs($(this).val(), null, function(ruleToSave) {literal}{}{/literal}); }); } onRuleChange(); + // Fill in ready to be saved rule array + // var onInputsChange = function() + // In AJAX response - // Rule to save - var ruleToSave = {literal}{}{/literal}; // Rules which will be saved var rulesToSave = initRules(); }); - + // Rule to save + var ruleToSave = {literal}{}{/literal}; {/block} diff --git a/templates/admin/default/coupon/rule-input-ajax.html b/templates/admin/default/coupon/rule-input-ajax.html index d0959a3ad..4933c6745 100644 --- a/templates/admin/default/coupon/rule-input-ajax.html +++ b/templates/admin/default/coupon/rule-input-ajax.html @@ -72,33 +72,29 @@ \ No newline at end of file From 551c132b76bf99860432ffc82faf13d73b169770 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 11 Sep 2013 11:14:54 +0200 Subject: [PATCH 03/12] syntax --- core/lib/Thelia/Core/Thelia.php | 2 +- core/lib/Thelia/TaxEngine/Calculator.php | 12 ++++++------ templates/default_save/category.html | 12 +----------- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/core/lib/Thelia/Core/Thelia.php b/core/lib/Thelia/Core/Thelia.php index 402059ddd..eb560a484 100755 --- a/core/lib/Thelia/Core/Thelia.php +++ b/core/lib/Thelia/Core/Thelia.php @@ -83,7 +83,7 @@ class Thelia extends Kernel $con = Propel::getConnection(\Thelia\Model\Map\ProductTableMap::DATABASE_NAME); $con->setAttribute(ConnectionWrapper::PROPEL_ATTR_CACHE_PREPARES, true); if ($this->isDebug()) { - //$serviceContainer->setLogger('defaultLogger', \Thelia\Log\Tlog::getInstance()); + $serviceContainer->setLogger('defaultLogger', \Thelia\Log\Tlog::getInstance()); $con->useDebug(true); } } diff --git a/core/lib/Thelia/TaxEngine/Calculator.php b/core/lib/Thelia/TaxEngine/Calculator.php index 66c4fcbbf..e077a24b8 100755 --- a/core/lib/Thelia/TaxEngine/Calculator.php +++ b/core/lib/Thelia/TaxEngine/Calculator.php @@ -67,29 +67,29 @@ class Calculator return $this; } - public function getTaxAmount($amount) + public function getTaxAmount($untaxedPrice) { if(null === $this->taxRulesGroupedCollection) { throw new TaxEngineException('Tax rules collection is empty in Calculator::getTaxAmount', TaxEngineException::UNDEFINED_TAX_RULES_COLLECTION); } - if(false === filter_var($amount, FILTER_VALIDATE_FLOAT)) { + if(false === filter_var($untaxedPrice, FILTER_VALIDATE_FLOAT)) { throw new TaxEngineException('BAD AMOUNT FORMAT', TaxEngineException::BAD_AMOUNT_FORMAT); } $totalTaxAmount = 0; foreach($this->taxRulesGroupedCollection as $taxRule) { $rateSum = $taxRule->getTaxRuleRateSum(); - $taxAmount = $amount * $rateSum * 0.01; + $taxAmount = $untaxedPrice * $rateSum * 0.01; $totalTaxAmount += $taxAmount; - $amount += $taxAmount; + $untaxedPrice += $taxAmount; } return $totalTaxAmount; } - public function getTaxedPrice($amount) + public function getTaxedPrice($untaxedPrice) { - return $amount + $this->getTaxAmount($amount); + return $untaxedPrice + $this->getTaxAmount($untaxedPrice); } } diff --git a/templates/default_save/category.html b/templates/default_save/category.html index 2e216d0c0..0a8e04460 100755 --- a/templates/default_save/category.html +++ b/templates/default_save/category.html @@ -1,9 +1,3 @@ - - - {debugbar_renderHead} - - -

Category page

@@ -148,8 +142,4 @@ {/loop} -
- - {debugbar_render} - - \ No newline at end of file + \ No newline at end of file From a9dae509c766a0ef0b2838c8cb5a81bcb68ca5c9 Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 14:40:55 +0200 Subject: [PATCH 04/12] WIP - Coupon Add/Edit/Delete rule AJAX - add tests casper JS --- .../Tests/Functionnal/casperjs/conf/dit.js | 3 + .../Tests/Functionnal/casperjs/conf/local.js | 3 + .../Tests/Functionnal/casperjs/conf/uat.js | 3 + .../Functionnal/casperjs/exe/00_parameters.js | 12 ++ .../Functionnal/casperjs/exe/10_login.js | 25 +++ .../Thelia/Tests/Functionnal/casperjs/run.sh | 26 +++ .../Tests/Functionnal/casperjs/run_ci.sh | 4 + templates/admin/default/assets/js/coupon.js | 148 ++++++++++++++++ templates/admin/default/coupon-update.html | 161 +++--------------- .../admin/default/coupon/rule-input-ajax.html | 20 ++- 10 files changed, 257 insertions(+), 148 deletions(-) create mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js create mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js create mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js create mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js create mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js create mode 100755 core/lib/Thelia/Tests/Functionnal/casperjs/run.sh create mode 100755 core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh create mode 100644 templates/admin/default/assets/js/coupon.js diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js new file mode 100644 index 000000000..b89267246 --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js @@ -0,0 +1,3 @@ +//DIT = Environnement d'intégration +var copark_base_url = 'http://ws.copark.dit.linux.thelia.fr/'; +casper.test.done(0); diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js new file mode 100644 index 000000000..366d297f2 --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js @@ -0,0 +1,3 @@ +//LOCAL = ton pc +var thelia2_base_url = 'http://www.thelia2.dev/index.php/'; +casper.test.done(0); diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js new file mode 100644 index 000000000..02b753869 --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js @@ -0,0 +1,3 @@ +//UAT = Recette client +var copark_base_url = 'http://ws.copark.uat.openstudio.fr/'; +casper.test.done(0); \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js new file mode 100644 index 000000000..81b980d41 --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js @@ -0,0 +1,12 @@ + +casper.test.comment('Please edit 00_parameters.js to add your configuration'); + +var thelia2_login_admin_url = thelia2_base_url + 'admin/login'; + + +//var findMyId = /([0-9]+)$/; +//var currentId; + +casper.test.comment('Variables are set'); + +casper.test.done(0); diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js new file mode 100644 index 000000000..c9c4095db --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js @@ -0,0 +1,25 @@ +casper.test.comment('Testing login'); + +casper.start(thelia2_login_admin_url, function() { + this.echo('\nLOGIN'); + this.test.assertTitle('Welcome - Thelia Back Office', 'Web page title OK'); + this.sendKeys('form[action*="checklogin"] input[name="thelia_admin_login[username]"]', 'thelia2'); + this.sendKeys('form[action*="checklogin"] input[name="thelia_admin_login[password]"]', 'thelia2'); + this.click('form[action*="checklogin"] input[type="submit"]'); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.echo('\nDASHBOARD'); + console.log('Now on : ' + this.getCurrentUrl()); + this.test.assertTitle('Back-office home - Thelia Back Office', 'Web page title OK'); + this.test.assertSelectorHasText('#wrapper > div', ' This is the administration home page. Put some interesting statistics here, and display useful information :) ', 'Web page main content OK'); +}); + +//RUN +casper.run(function() { + this.test.done(); +}); \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh b/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh new file mode 100755 index 000000000..6e054b152 --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# @author Guillaume MOREL + +echo "Force dropping database. All data will be lost." + +cd local/config/ + +echo -e "\n\e[01;34m[INFO] Building Models file\e[00m\n" +../../bin/propel build -v --output-dir=../../core/lib/ + +echo -e "\n\e[01;34m[INFO] Building SQL CREATE file\e[00m\n" +../../bin/propel sql:build -v --output-dir=../../install/ + +echo -e "\n\e[01;34m[INFO] Reloaded Thelia2 database\e[00m\n" +cd ../.. +rm install/sqldb.map +php Thelia thelia:dev:reloadDB + +echo -e "\n\e[01;34m[INFO] Installing fixtures\e[00m\n" +php install/faker.php + +echo -e "\n\e[01;34m[INFO] Adding admin\e[00m\n" +php Thelia thelia:create-admin --login_name thelia2 --password thelia2 --last_name thelia2 --first_name thelia2 + +# casperjs test ./exe --pre=./conf/local.js --direct + diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh b/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh new file mode 100755 index 000000000..785d8ee49 --- /dev/null +++ b/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# @author Quentin Dufour + +/usrcasperjs test ./exe --pre=./conf/dit.js --xunit=reports/report.xml diff --git a/templates/admin/default/assets/js/coupon.js b/templates/admin/default/assets/js/coupon.js new file mode 100644 index 000000000..5c52aa097 --- /dev/null +++ b/templates/admin/default/assets/js/coupon.js @@ -0,0 +1,148 @@ +$(function($){ + + // Clean array from deleteValue (undefined) keys + Array.prototype.clean = function(deleteValue) { + for (var i = 0; i < this.length; i++) { + if (this[i] == deleteValue) { + this.splice(i, 1); + i--; + } + } + return this; + }; + + // Remove 1 Rule then Save Rules AJAX + couponManager.removeRuleAjax = function(id) { + // Delete rule in temporary array + delete couponManager.rulesToSave[id]; + couponManager.rulesToSave.clean(undefined); + + // Save + couponManager.saveRuleAjax(); + }; + + // Add 1 Rule / or update the temporary Rules array then Save Rules via AJAX + couponManager.addRuleAjax = function(id) { + console.log('addRuleAjax '+ id); + // If create + if(!id) { + console.log('pushing'); + couponManager.rulesToSave.push(couponManager.ruleToSave); + } else { // else update + console.log('editing ' + id); + couponManager.rulesToSave[id] = couponManager.ruleToSave; + // reset edit mode to off + couponManager.ruleIdToUpdate = false; + } + + // Save + couponManager.saveRuleAjax(); + }; + + // Set rule inputs to allow editing + couponManager.updateRuleAjax = function(id) { + couponManager.ruleToUpdate = couponManager.rulesToSave[id]; + console.log('Set id to edit to ' + id); + couponManager.ruleIdToUpdate = id; + + // Deleting this rule, we will reset it + delete couponManager.rulesToSave[id]; + + // Set the rule selector + $("#category-rule option").filter(function() { + return $(this).val() == couponManager.ruleToUpdate.serviceId; + }).prop('selected', true); + + // Force rule input refresh + couponManager.loadRuleInputs(couponManager.ruleToUpdate.serviceId, function() { + couponManager.fillInRuleInputs(); + }); + }; + + // Fill in rule inputs + couponManager.fillInRuleInputs = function() { + console.log('fillInRuleInputs with'); + console.log(couponManager.ruleToUpdate); + var operatorId = null; + var valueId = null; + var idName = null; + + for (idName in couponManager.ruleToUpdate.operators) { + // Setting idName operator select + operatorId = idName + '-operator'; + $('#' + operatorId).val(couponManager.ruleToUpdate.operators[idName]); + + valueId = idName + '-value'; + // Setting idName value input + $('#' + valueId).val(couponManager.ruleToUpdate.values[idName]); + } + couponManager.ruleToSave = couponManager.ruleToUpdate; + + var id = couponManager.ruleIdToUpdate; + console.log('id to edit = ' + id); + if(id) { + console.log('setint rulesToSave[' + id + ']'); + console.log(couponManager.ruleToSave); + couponManager.rulesToSave[id] = couponManager.ruleToSave; + } + }; + + // Save rules on click + couponManager.onClickSaveRule = function() { + $('#constraint-save-btn').on('click', function () { + couponManager.addRuleAjax(couponManager.ruleIdToUpdate); + }); + }; + couponManager.onClickSaveRule(); + + // Remove rule on click + couponManager.onClickDeleteRule = function() { + $('.constraint-delete-btn').on('click', function (e) { + e.preventDefault(); + var $this = $(this); + couponManager.removeRuleAjax($this.attr('data-int')); + }); + }; + couponManager.onClickDeleteRule(); + + // Update rule on click + couponManager.onClickUpdateRule = function() { + $('.constraint-update-btn').on('click', function (e) { + e.preventDefault(); + var $this = $(this); + couponManager.updateRuleAjax($this.attr('data-int')); + + // Hide row being updated + $this.parent().parent().remove(); + }); + }; + couponManager.onClickUpdateRule(); + + // Reload effect inputs when changing effect + couponManager.onEffectChange = function() { + $('#effect').on('change', function () { + var optionSelected = $("option:selected", this); + $('#effectToolTip').html(optionSelected.attr("data-description")); + }); + }; + couponManager.onEffectChange(); + + // Reload rule inputs when changing effect + couponManager.onRuleChange = function() { + $('#category-rule').on('change', function () { + couponManager.loadRuleInputs($(this).val(), function(ruleToSave) {}); + }); + }; + couponManager.onRuleChange(); + + // Fill in ready to be saved rule array + // var onInputsChange = function() + // In AJAX response + +}); + +// Rule to save + +var couponManager = {}; +couponManager.ruleToSave = {}; +couponManager.ruleIdToUpdate = false; \ No newline at end of file diff --git a/templates/admin/default/coupon-update.html b/templates/admin/default/coupon-update.html index 3895475b4..925296aae 100755 --- a/templates/admin/default/coupon-update.html +++ b/templates/admin/default/coupon-update.html @@ -37,24 +37,19 @@ {/javascripts} + {javascripts file='assets/js/coupon.js'} + + {/javascripts} + {/block} diff --git a/templates/admin/default/coupon/rule-input-ajax.html b/templates/admin/default/coupon/rule-input-ajax.html index 4933c6745..cdf683d21 100644 --- a/templates/admin/default/coupon/rule-input-ajax.html +++ b/templates/admin/default/coupon/rule-input-ajax.html @@ -72,29 +72,31 @@ \ No newline at end of file From 870612f51c8a9aba45e79cdeeccb932cfe34e2f4 Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 14:43:09 +0200 Subject: [PATCH 05/12] WIP - Coupon Add/Edit/Delete rule AJAX - add tests casper JS --- core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js | 3 --- core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js | 3 --- core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh | 4 ---- 3 files changed, 10 deletions(-) delete mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js delete mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js delete mode 100755 core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js deleted file mode 100644 index b89267246..000000000 --- a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js +++ /dev/null @@ -1,3 +0,0 @@ -//DIT = Environnement d'intégration -var copark_base_url = 'http://ws.copark.dit.linux.thelia.fr/'; -casper.test.done(0); diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js deleted file mode 100644 index 02b753869..000000000 --- a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js +++ /dev/null @@ -1,3 +0,0 @@ -//UAT = Recette client -var copark_base_url = 'http://ws.copark.uat.openstudio.fr/'; -casper.test.done(0); \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh b/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh deleted file mode 100755 index 785d8ee49..000000000 --- a/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -# @author Quentin Dufour - -/usrcasperjs test ./exe --pre=./conf/dit.js --xunit=reports/report.xml From 59e1981e9ff59d52852a27b5d8109e3c5e285d2a Mon Sep 17 00:00:00 2001 From: mespeche Date: Wed, 11 Sep 2013 15:23:04 +0200 Subject: [PATCH 06/12] - Add orders view route - Add view & action for orders index --- .../Thelia/Config/Resources/routing/admin.xml | 8 + .../Controller/Admin/OrderController.php | 39 +++++ templates/admin/default/orders.html | 159 ++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 core/lib/Thelia/Controller/Admin/OrderController.php create mode 100644 templates/admin/default/orders.html diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index 8eaeb2547..5e34aec39 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -44,6 +44,14 @@ + + + + Thelia\Controller\Admin\OrderController::indexAction + + + + diff --git a/core/lib/Thelia/Controller/Admin/OrderController.php b/core/lib/Thelia/Controller/Admin/OrderController.php new file mode 100644 index 000000000..b7e2b00ce --- /dev/null +++ b/core/lib/Thelia/Controller/Admin/OrderController.php @@ -0,0 +1,39 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Controller\Admin; + +/** + * Class OrderController + * @package Thelia\Controller\Admin + * @author Manuel Raynaud + */ +class OrderController extends BaseAdminController +{ + public function indexAction() + { + if (null !== $response = $this->checkAuth("admin.orders.view")) return $response; + return $this->render("orders", array("display_order" => 20)); + } + +} \ No newline at end of file diff --git a/templates/admin/default/orders.html b/templates/admin/default/orders.html new file mode 100644 index 000000000..8693f2a33 --- /dev/null +++ b/templates/admin/default/orders.html @@ -0,0 +1,159 @@ +{extends file="admin-layout.tpl"} + +{block name="page-title"}{intl l='Orders'}{/block} + +{block name="check-permissions"}admin.orders.view{/block} + +{block name="main-content"} +
+ +
+ + + + {module_include location='orders_top'} + +
+
+
+
+ + + + + + + + + + + + {module_include location='orders_table_header'} + + + + + + + + + + + + + + + + {module_include location='orders_table_row'} + + + + + + + + + + + + + {module_include location='orders_table_row'} + + + + + + + + + + + + + {module_include location='orders_table_row'} + + + + + + + +
+ {intl l='Orders'} + {loop type="auth" name="can_create" roles="ADMIN" permissions="admin.orders.create"} + + + + {/loop} +
{intl l="Order n°"}{intl l="Date & Hour"}{intl l="Compagny"}{intl l="Name"}{intl l="Amount"}{intl l="Status"}{intl l="Actions"}
0123045012304511/09/2013 10:24:31TheliaDupont251 €Paid +
+ + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.orders.edit"} + + {/loop} + + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.orders.delete"} + + {/loop} +
+
0123045012304511/09/2013 10:24:31TheliaDupont251 €Canceled +
+ + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.orders.edit"} + + {/loop} + + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.orders.delete"} + + {/loop} +
+
0123045012304511/09/2013 10:24:31TheliaDupont251 €Current +
+ + {loop type="auth" name="can_change" roles="ADMIN" permissions="admin.orders.edit"} + + {/loop} + + {loop type="auth" name="can_delete" roles="ADMIN" permissions="admin.orders.delete"} + + {/loop} +
+
+
+
+
+
+ + {module_include location='orders_bottom'} + +
+
+ +{* Delete order confirmation dialog *} + +{capture "delete_order_dialog"} + + +{/capture} + +{include + file = "includes/generic-confirm-dialog.html" + + dialog_id = "delete_order_dialog" + dialog_title = {intl l="Delete an order"} + dialog_message = {intl l="Do you really want to delete this order ?"} + + form_action = {url path='/admin/orders/delete'} + form_content = {$smarty.capture.delete_order_dialog nofilter} +} + +{/block} \ No newline at end of file From c8b5b9e9fa9800e387e7a3b9eed5fe3af409b000 Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 15:35:25 +0200 Subject: [PATCH 07/12] Working - add tests casper JS --- .../Functionnal/casperjs/exe/10_login.js | 25 ----------------- .../casperjs/run.sh => run_casperjs.sh | 3 +-- templates/admin/default/login.html | 4 +-- .../functionnal}/casperjs/conf/local.js | 0 .../casperjs/exe/00_parameters.js | 0 tests/functionnal/casperjs/exe/10_login.js | 27 +++++++++++++++++++ 6 files changed, 30 insertions(+), 29 deletions(-) delete mode 100644 core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js rename core/lib/Thelia/Tests/Functionnal/casperjs/run.sh => run_casperjs.sh (87%) rename {core/lib/Thelia/Tests/Functionnal => tests/functionnal}/casperjs/conf/local.js (100%) rename {core/lib/Thelia/Tests/Functionnal => tests/functionnal}/casperjs/exe/00_parameters.js (100%) create mode 100644 tests/functionnal/casperjs/exe/10_login.js diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js deleted file mode 100644 index c9c4095db..000000000 --- a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js +++ /dev/null @@ -1,25 +0,0 @@ -casper.test.comment('Testing login'); - -casper.start(thelia2_login_admin_url, function() { - this.echo('\nLOGIN'); - this.test.assertTitle('Welcome - Thelia Back Office', 'Web page title OK'); - this.sendKeys('form[action*="checklogin"] input[name="thelia_admin_login[username]"]', 'thelia2'); - this.sendKeys('form[action*="checklogin"] input[name="thelia_admin_login[password]"]', 'thelia2'); - this.click('form[action*="checklogin"] input[type="submit"]'); -}); - -casper.wait(1000, function() { - this.echo("\nWaiting...."); -}); - -casper.then(function(){ - this.echo('\nDASHBOARD'); - console.log('Now on : ' + this.getCurrentUrl()); - this.test.assertTitle('Back-office home - Thelia Back Office', 'Web page title OK'); - this.test.assertSelectorHasText('#wrapper > div', ' This is the administration home page. Put some interesting statistics here, and display useful information :) ', 'Web page main content OK'); -}); - -//RUN -casper.run(function() { - this.test.done(); -}); \ No newline at end of file diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh b/run_casperjs.sh similarity index 87% rename from core/lib/Thelia/Tests/Functionnal/casperjs/run.sh rename to run_casperjs.sh index 6e054b152..88636a9d1 100755 --- a/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh +++ b/run_casperjs.sh @@ -22,5 +22,4 @@ php install/faker.php echo -e "\n\e[01;34m[INFO] Adding admin\e[00m\n" php Thelia thelia:create-admin --login_name thelia2 --password thelia2 --last_name thelia2 --first_name thelia2 -# casperjs test ./exe --pre=./conf/local.js --direct - +casperjs test ./tests/functionnal/casperjs/exe --pre=./tests/functionnal/casperjs/conf/local.js --direct diff --git a/templates/admin/default/login.html b/templates/admin/default/login.html index 8815eb982..2b994359c 100755 --- a/templates/admin/default/login.html +++ b/templates/admin/default/login.html @@ -28,13 +28,13 @@ {form_field form=$form field='username'} - + {/form_field} {form_field form=$form field='password'} - + {/form_field} diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js b/tests/functionnal/casperjs/conf/local.js similarity index 100% rename from core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js rename to tests/functionnal/casperjs/conf/local.js diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js b/tests/functionnal/casperjs/exe/00_parameters.js similarity index 100% rename from core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js rename to tests/functionnal/casperjs/exe/00_parameters.js diff --git a/tests/functionnal/casperjs/exe/10_login.js b/tests/functionnal/casperjs/exe/10_login.js new file mode 100644 index 000000000..81757168d --- /dev/null +++ b/tests/functionnal/casperjs/exe/10_login.js @@ -0,0 +1,27 @@ +casper.test.comment('Testing login'); + +casper.start(thelia2_login_admin_url, function() { + this.echo('\nLOGIN'); + this.test.assertTitle('Welcome - Thelia Back Office', 'Web page title OK'); + this.sendKeys('input#username', 'thelia2'); + this.sendKeys('input#password', 'thelia2'); + this.click('form[action*="checklogin"] button[type="submit"]'); +}); + +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + this.echo('\nDASHBOARD'); + + console.log('Now on : ' + this.getCurrentUrl()); + // @todo implement dashboard +// this.test.assertTitle('Back-office home - Thelia Back Office', 'Web page title OK'); +// this.test.assertSelectorHasText('#wrapper > div', ' This is the administration home page. Put some interesting statistics here, and display useful information :) ', 'Web page main content OK'); +}); + +//RUN +casper.run(function() { + this.test.done(); +}); \ No newline at end of file From e8a4e56324fc930455f07bf9bfe0fa0dbbdbb715 Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 11 Sep 2013 16:21:51 +0200 Subject: [PATCH 08/12] tax engine --- .../Thelia/Exception/TaxEngineException.php | 3 + core/lib/Thelia/Exception/TypeException.php | 39 ++++++ core/lib/Thelia/Model/Base/Tax.php | 132 +++++++++++++----- core/lib/Thelia/Model/Base/TaxQuery.php | 79 +++++++---- core/lib/Thelia/Model/Map/TaxTableMap.php | 46 +++--- core/lib/Thelia/Model/Tax.php | 37 ++++- core/lib/Thelia/Model/TaxRuleQuery.php | 3 - core/lib/Thelia/TaxEngine/Calculator.php | 18 ++- .../Thelia/TaxEngine/TaxType/BaseTaxType.php | 83 +++++++++++ .../TaxType/FeatureSlicePercentTaxType.php | 47 +++++++ .../TaxEngine/TaxType/PricePercentTaxType.php | 45 ++++++ .../lib/Thelia/Type/FloatToFloatArrayType.php | 56 ++++++++ core/lib/Thelia/Type/ModelType.php | 66 +++++++++ install/insert.sql | 6 +- install/thelia.sql | 3 +- local/config/schema.xml | 3 +- templates/default_save/product.html | 2 +- 17 files changed, 561 insertions(+), 107 deletions(-) create mode 100755 core/lib/Thelia/Exception/TypeException.php create mode 100755 core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php create mode 100755 core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php create mode 100755 core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php create mode 100755 core/lib/Thelia/Type/FloatToFloatArrayType.php create mode 100755 core/lib/Thelia/Type/ModelType.php diff --git a/core/lib/Thelia/Exception/TaxEngineException.php b/core/lib/Thelia/Exception/TaxEngineException.php index d56862ab6..8ce8561ef 100755 --- a/core/lib/Thelia/Exception/TaxEngineException.php +++ b/core/lib/Thelia/Exception/TaxEngineException.php @@ -27,6 +27,9 @@ class TaxEngineException extends \RuntimeException { const UNKNOWN_EXCEPTION = 0; + const BAD_RECORDED_TYPE = 101; + const BAD_RECORDED_REQUIREMENTS = 102; + const UNDEFINED_PRODUCT = 501; const UNDEFINED_COUNTRY = 502; const UNDEFINED_TAX_RULES_COLLECTION = 503; diff --git a/core/lib/Thelia/Exception/TypeException.php b/core/lib/Thelia/Exception/TypeException.php new file mode 100755 index 000000000..442fffe3e --- /dev/null +++ b/core/lib/Thelia/Exception/TypeException.php @@ -0,0 +1,39 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Exception; + +class TypeException extends \RuntimeException +{ + const UNKNOWN_EXCEPTION = 0; + + const MODEL_NOT_FOUND = 404; + + public function __construct($message, $code = null, $previous = null) + { + if ($code === null) { + $code = self::UNKNOWN_EXCEPTION; + } + parent::__construct($message, $code, $previous); + } +} diff --git a/core/lib/Thelia/Model/Base/Tax.php b/core/lib/Thelia/Model/Base/Tax.php index ed4bb40f7..da674b5cf 100644 --- a/core/lib/Thelia/Model/Base/Tax.php +++ b/core/lib/Thelia/Model/Base/Tax.php @@ -66,10 +66,16 @@ abstract class Tax implements ActiveRecordInterface protected $id; /** - * The value for the rate field. - * @var double + * The value for the type field. + * @var string */ - protected $rate; + protected $type; + + /** + * The value for the serialized_requirements field. + * @var string + */ + protected $serialized_requirements; /** * The value for the created_at field. @@ -395,14 +401,25 @@ abstract class Tax implements ActiveRecordInterface } /** - * Get the [rate] column value. + * Get the [type] column value. * - * @return double + * @return string */ - public function getRate() + public function getType() { - return $this->rate; + return $this->type; + } + + /** + * Get the [serialized_requirements] column value. + * + * @return string + */ + public function getSerializedRequirements() + { + + return $this->serialized_requirements; } /** @@ -467,25 +484,46 @@ abstract class Tax implements ActiveRecordInterface } // setId() /** - * Set the value of [rate] column. + * Set the value of [type] column. * - * @param double $v new value + * @param string $v new value * @return \Thelia\Model\Tax The current object (for fluent API support) */ - public function setRate($v) + public function setType($v) { if ($v !== null) { - $v = (double) $v; + $v = (string) $v; } - if ($this->rate !== $v) { - $this->rate = $v; - $this->modifiedColumns[] = TaxTableMap::RATE; + if ($this->type !== $v) { + $this->type = $v; + $this->modifiedColumns[] = TaxTableMap::TYPE; } return $this; - } // setRate() + } // setType() + + /** + * Set the value of [serialized_requirements] column. + * + * @param string $v new value + * @return \Thelia\Model\Tax The current object (for fluent API support) + */ + public function setSerializedRequirements($v) + { + if ($v !== null) { + $v = (string) $v; + } + + if ($this->serialized_requirements !== $v) { + $this->serialized_requirements = $v; + $this->modifiedColumns[] = TaxTableMap::SERIALIZED_REQUIREMENTS; + } + + + return $this; + } // setSerializedRequirements() /** * Sets the value of [created_at] column to a normalized version of the date/time value specified. @@ -569,16 +607,19 @@ abstract class Tax implements ActiveRecordInterface $col = $row[TableMap::TYPE_NUM == $indexType ? 0 + $startcol : TaxTableMap::translateFieldName('Id', TableMap::TYPE_PHPNAME, $indexType)]; $this->id = (null !== $col) ? (int) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 1 + $startcol : TaxTableMap::translateFieldName('Rate', TableMap::TYPE_PHPNAME, $indexType)]; - $this->rate = (null !== $col) ? (double) $col : null; + $col = $row[TableMap::TYPE_NUM == $indexType ? 1 + $startcol : TaxTableMap::translateFieldName('Type', TableMap::TYPE_PHPNAME, $indexType)]; + $this->type = (null !== $col) ? (string) $col : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 2 + $startcol : TaxTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 2 + $startcol : TaxTableMap::translateFieldName('SerializedRequirements', TableMap::TYPE_PHPNAME, $indexType)]; + $this->serialized_requirements = (null !== $col) ? (string) $col : null; + + $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : TaxTableMap::translateFieldName('CreatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } $this->created_at = (null !== $col) ? PropelDateTime::newInstance($col, null, '\DateTime') : null; - $col = $row[TableMap::TYPE_NUM == $indexType ? 3 + $startcol : TaxTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; + $col = $row[TableMap::TYPE_NUM == $indexType ? 4 + $startcol : TaxTableMap::translateFieldName('UpdatedAt', TableMap::TYPE_PHPNAME, $indexType)]; if ($col === '0000-00-00 00:00:00') { $col = null; } @@ -591,7 +632,7 @@ abstract class Tax implements ActiveRecordInterface $this->ensureConsistency(); } - return $startcol + 4; // 4 = TaxTableMap::NUM_HYDRATE_COLUMNS. + return $startcol + 5; // 5 = TaxTableMap::NUM_HYDRATE_COLUMNS. } catch (Exception $e) { throw new PropelException("Error populating \Thelia\Model\Tax object", 0, $e); @@ -852,8 +893,11 @@ abstract class Tax implements ActiveRecordInterface if ($this->isColumnModified(TaxTableMap::ID)) { $modifiedColumns[':p' . $index++] = 'ID'; } - if ($this->isColumnModified(TaxTableMap::RATE)) { - $modifiedColumns[':p' . $index++] = 'RATE'; + if ($this->isColumnModified(TaxTableMap::TYPE)) { + $modifiedColumns[':p' . $index++] = 'TYPE'; + } + if ($this->isColumnModified(TaxTableMap::SERIALIZED_REQUIREMENTS)) { + $modifiedColumns[':p' . $index++] = 'SERIALIZED_REQUIREMENTS'; } if ($this->isColumnModified(TaxTableMap::CREATED_AT)) { $modifiedColumns[':p' . $index++] = 'CREATED_AT'; @@ -875,8 +919,11 @@ abstract class Tax implements ActiveRecordInterface case 'ID': $stmt->bindValue($identifier, $this->id, PDO::PARAM_INT); break; - case 'RATE': - $stmt->bindValue($identifier, $this->rate, PDO::PARAM_STR); + case 'TYPE': + $stmt->bindValue($identifier, $this->type, PDO::PARAM_STR); + break; + case 'SERIALIZED_REQUIREMENTS': + $stmt->bindValue($identifier, $this->serialized_requirements, PDO::PARAM_STR); break; case 'CREATED_AT': $stmt->bindValue($identifier, $this->created_at ? $this->created_at->format("Y-m-d H:i:s") : null, PDO::PARAM_STR); @@ -950,12 +997,15 @@ abstract class Tax implements ActiveRecordInterface return $this->getId(); break; case 1: - return $this->getRate(); + return $this->getType(); break; case 2: - return $this->getCreatedAt(); + return $this->getSerializedRequirements(); break; case 3: + return $this->getCreatedAt(); + break; + case 4: return $this->getUpdatedAt(); break; default: @@ -988,9 +1038,10 @@ abstract class Tax implements ActiveRecordInterface $keys = TaxTableMap::getFieldNames($keyType); $result = array( $keys[0] => $this->getId(), - $keys[1] => $this->getRate(), - $keys[2] => $this->getCreatedAt(), - $keys[3] => $this->getUpdatedAt(), + $keys[1] => $this->getType(), + $keys[2] => $this->getSerializedRequirements(), + $keys[3] => $this->getCreatedAt(), + $keys[4] => $this->getUpdatedAt(), ); $virtualColumns = $this->virtualColumns; foreach($virtualColumns as $key => $virtualColumn) @@ -1043,12 +1094,15 @@ abstract class Tax implements ActiveRecordInterface $this->setId($value); break; case 1: - $this->setRate($value); + $this->setType($value); break; case 2: - $this->setCreatedAt($value); + $this->setSerializedRequirements($value); break; case 3: + $this->setCreatedAt($value); + break; + case 4: $this->setUpdatedAt($value); break; } // switch() @@ -1076,9 +1130,10 @@ abstract class Tax implements ActiveRecordInterface $keys = TaxTableMap::getFieldNames($keyType); if (array_key_exists($keys[0], $arr)) $this->setId($arr[$keys[0]]); - if (array_key_exists($keys[1], $arr)) $this->setRate($arr[$keys[1]]); - if (array_key_exists($keys[2], $arr)) $this->setCreatedAt($arr[$keys[2]]); - if (array_key_exists($keys[3], $arr)) $this->setUpdatedAt($arr[$keys[3]]); + if (array_key_exists($keys[1], $arr)) $this->setType($arr[$keys[1]]); + if (array_key_exists($keys[2], $arr)) $this->setSerializedRequirements($arr[$keys[2]]); + if (array_key_exists($keys[3], $arr)) $this->setCreatedAt($arr[$keys[3]]); + if (array_key_exists($keys[4], $arr)) $this->setUpdatedAt($arr[$keys[4]]); } /** @@ -1091,7 +1146,8 @@ abstract class Tax implements ActiveRecordInterface $criteria = new Criteria(TaxTableMap::DATABASE_NAME); if ($this->isColumnModified(TaxTableMap::ID)) $criteria->add(TaxTableMap::ID, $this->id); - if ($this->isColumnModified(TaxTableMap::RATE)) $criteria->add(TaxTableMap::RATE, $this->rate); + if ($this->isColumnModified(TaxTableMap::TYPE)) $criteria->add(TaxTableMap::TYPE, $this->type); + if ($this->isColumnModified(TaxTableMap::SERIALIZED_REQUIREMENTS)) $criteria->add(TaxTableMap::SERIALIZED_REQUIREMENTS, $this->serialized_requirements); if ($this->isColumnModified(TaxTableMap::CREATED_AT)) $criteria->add(TaxTableMap::CREATED_AT, $this->created_at); if ($this->isColumnModified(TaxTableMap::UPDATED_AT)) $criteria->add(TaxTableMap::UPDATED_AT, $this->updated_at); @@ -1157,7 +1213,8 @@ abstract class Tax implements ActiveRecordInterface */ public function copyInto($copyObj, $deepCopy = false, $makeNew = true) { - $copyObj->setRate($this->getRate()); + $copyObj->setType($this->getType()); + $copyObj->setSerializedRequirements($this->getSerializedRequirements()); $copyObj->setCreatedAt($this->getCreatedAt()); $copyObj->setUpdatedAt($this->getUpdatedAt()); @@ -1729,7 +1786,8 @@ abstract class Tax implements ActiveRecordInterface public function clear() { $this->id = null; - $this->rate = null; + $this->type = null; + $this->serialized_requirements = null; $this->created_at = null; $this->updated_at = null; $this->alreadyInSave = false; diff --git a/core/lib/Thelia/Model/Base/TaxQuery.php b/core/lib/Thelia/Model/Base/TaxQuery.php index 07316bf69..93da10f53 100644 --- a/core/lib/Thelia/Model/Base/TaxQuery.php +++ b/core/lib/Thelia/Model/Base/TaxQuery.php @@ -23,12 +23,14 @@ use Thelia\Model\Map\TaxTableMap; * * * @method ChildTaxQuery orderById($order = Criteria::ASC) Order by the id column - * @method ChildTaxQuery orderByRate($order = Criteria::ASC) Order by the rate column + * @method ChildTaxQuery orderByType($order = Criteria::ASC) Order by the type column + * @method ChildTaxQuery orderBySerializedRequirements($order = Criteria::ASC) Order by the serialized_requirements column * @method ChildTaxQuery orderByCreatedAt($order = Criteria::ASC) Order by the created_at column * @method ChildTaxQuery orderByUpdatedAt($order = Criteria::ASC) Order by the updated_at column * * @method ChildTaxQuery groupById() Group by the id column - * @method ChildTaxQuery groupByRate() Group by the rate column + * @method ChildTaxQuery groupByType() Group by the type column + * @method ChildTaxQuery groupBySerializedRequirements() Group by the serialized_requirements column * @method ChildTaxQuery groupByCreatedAt() Group by the created_at column * @method ChildTaxQuery groupByUpdatedAt() Group by the updated_at column * @@ -48,12 +50,14 @@ use Thelia\Model\Map\TaxTableMap; * @method ChildTax findOneOrCreate(ConnectionInterface $con = null) Return the first ChildTax matching the query, or a new ChildTax object populated from the query conditions when no match is found * * @method ChildTax findOneById(int $id) Return the first ChildTax filtered by the id column - * @method ChildTax findOneByRate(double $rate) Return the first ChildTax filtered by the rate column + * @method ChildTax findOneByType(string $type) Return the first ChildTax filtered by the type column + * @method ChildTax findOneBySerializedRequirements(string $serialized_requirements) Return the first ChildTax filtered by the serialized_requirements column * @method ChildTax findOneByCreatedAt(string $created_at) Return the first ChildTax filtered by the created_at column * @method ChildTax findOneByUpdatedAt(string $updated_at) Return the first ChildTax filtered by the updated_at column * * @method array findById(int $id) Return ChildTax objects filtered by the id column - * @method array findByRate(double $rate) Return ChildTax objects filtered by the rate column + * @method array findByType(string $type) Return ChildTax objects filtered by the type column + * @method array findBySerializedRequirements(string $serialized_requirements) Return ChildTax objects filtered by the serialized_requirements column * @method array findByCreatedAt(string $created_at) Return ChildTax objects filtered by the created_at column * @method array findByUpdatedAt(string $updated_at) Return ChildTax objects filtered by the updated_at column * @@ -144,7 +148,7 @@ abstract class TaxQuery extends ModelCriteria */ protected function findPkSimple($key, $con) { - $sql = 'SELECT ID, RATE, CREATED_AT, UPDATED_AT FROM tax WHERE ID = :p0'; + $sql = 'SELECT ID, TYPE, SERIALIZED_REQUIREMENTS, CREATED_AT, UPDATED_AT FROM tax WHERE ID = :p0'; try { $stmt = $con->prepare($sql); $stmt->bindValue(':p0', $key, PDO::PARAM_INT); @@ -275,44 +279,61 @@ abstract class TaxQuery extends ModelCriteria } /** - * Filter the query on the rate column + * Filter the query on the type column * * Example usage: * - * $query->filterByRate(1234); // WHERE rate = 1234 - * $query->filterByRate(array(12, 34)); // WHERE rate IN (12, 34) - * $query->filterByRate(array('min' => 12)); // WHERE rate > 12 + * $query->filterByType('fooValue'); // WHERE type = 'fooValue' + * $query->filterByType('%fooValue%'); // WHERE type LIKE '%fooValue%' * * - * @param mixed $rate The value to use as filter. - * Use scalar values for equality. - * Use array values for in_array() equivalent. - * Use associative array('min' => $minValue, 'max' => $maxValue) for intervals. + * @param string $type The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * * @return ChildTaxQuery The current query, for fluid interface */ - public function filterByRate($rate = null, $comparison = null) + public function filterByType($type = null, $comparison = null) { - if (is_array($rate)) { - $useMinMax = false; - if (isset($rate['min'])) { - $this->addUsingAlias(TaxTableMap::RATE, $rate['min'], Criteria::GREATER_EQUAL); - $useMinMax = true; - } - if (isset($rate['max'])) { - $this->addUsingAlias(TaxTableMap::RATE, $rate['max'], Criteria::LESS_EQUAL); - $useMinMax = true; - } - if ($useMinMax) { - return $this; - } - if (null === $comparison) { + if (null === $comparison) { + if (is_array($type)) { $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $type)) { + $type = str_replace('*', '%', $type); + $comparison = Criteria::LIKE; } } - return $this->addUsingAlias(TaxTableMap::RATE, $rate, $comparison); + return $this->addUsingAlias(TaxTableMap::TYPE, $type, $comparison); + } + + /** + * Filter the query on the serialized_requirements column + * + * Example usage: + * + * $query->filterBySerializedRequirements('fooValue'); // WHERE serialized_requirements = 'fooValue' + * $query->filterBySerializedRequirements('%fooValue%'); // WHERE serialized_requirements LIKE '%fooValue%' + * + * + * @param string $serializedRequirements The value to use as filter. + * Accepts wildcards (* and % trigger a LIKE) + * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL + * + * @return ChildTaxQuery The current query, for fluid interface + */ + public function filterBySerializedRequirements($serializedRequirements = null, $comparison = null) + { + if (null === $comparison) { + if (is_array($serializedRequirements)) { + $comparison = Criteria::IN; + } elseif (preg_match('/[\%\*]/', $serializedRequirements)) { + $serializedRequirements = str_replace('*', '%', $serializedRequirements); + $comparison = Criteria::LIKE; + } + } + + return $this->addUsingAlias(TaxTableMap::SERIALIZED_REQUIREMENTS, $serializedRequirements, $comparison); } /** diff --git a/core/lib/Thelia/Model/Map/TaxTableMap.php b/core/lib/Thelia/Model/Map/TaxTableMap.php index 11e5047ce..6ca89ae85 100644 --- a/core/lib/Thelia/Model/Map/TaxTableMap.php +++ b/core/lib/Thelia/Model/Map/TaxTableMap.php @@ -57,7 +57,7 @@ class TaxTableMap extends TableMap /** * The total number of columns */ - const NUM_COLUMNS = 4; + const NUM_COLUMNS = 5; /** * The number of lazy-loaded columns @@ -67,7 +67,7 @@ class TaxTableMap extends TableMap /** * The number of columns to hydrate (NUM_COLUMNS - NUM_LAZY_LOAD_COLUMNS) */ - const NUM_HYDRATE_COLUMNS = 4; + const NUM_HYDRATE_COLUMNS = 5; /** * the column name for the ID field @@ -75,9 +75,14 @@ class TaxTableMap extends TableMap const ID = 'tax.ID'; /** - * the column name for the RATE field + * the column name for the TYPE field */ - const RATE = 'tax.RATE'; + const TYPE = 'tax.TYPE'; + + /** + * the column name for the SERIALIZED_REQUIREMENTS field + */ + const SERIALIZED_REQUIREMENTS = 'tax.SERIALIZED_REQUIREMENTS'; /** * the column name for the CREATED_AT field @@ -110,12 +115,12 @@ class TaxTableMap extends TableMap * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id' */ protected static $fieldNames = array ( - self::TYPE_PHPNAME => array('Id', 'Rate', 'CreatedAt', 'UpdatedAt', ), - self::TYPE_STUDLYPHPNAME => array('id', 'rate', 'createdAt', 'updatedAt', ), - self::TYPE_COLNAME => array(TaxTableMap::ID, TaxTableMap::RATE, TaxTableMap::CREATED_AT, TaxTableMap::UPDATED_AT, ), - self::TYPE_RAW_COLNAME => array('ID', 'RATE', 'CREATED_AT', 'UPDATED_AT', ), - self::TYPE_FIELDNAME => array('id', 'rate', 'created_at', 'updated_at', ), - self::TYPE_NUM => array(0, 1, 2, 3, ) + self::TYPE_PHPNAME => array('Id', 'Type', 'SerializedRequirements', 'CreatedAt', 'UpdatedAt', ), + self::TYPE_STUDLYPHPNAME => array('id', 'type', 'serializedRequirements', 'createdAt', 'updatedAt', ), + self::TYPE_COLNAME => array(TaxTableMap::ID, TaxTableMap::TYPE, TaxTableMap::SERIALIZED_REQUIREMENTS, TaxTableMap::CREATED_AT, TaxTableMap::UPDATED_AT, ), + self::TYPE_RAW_COLNAME => array('ID', 'TYPE', 'SERIALIZED_REQUIREMENTS', 'CREATED_AT', 'UPDATED_AT', ), + self::TYPE_FIELDNAME => array('id', 'type', 'serialized_requirements', 'created_at', 'updated_at', ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, ) ); /** @@ -125,12 +130,12 @@ class TaxTableMap extends TableMap * e.g. self::$fieldKeys[self::TYPE_PHPNAME]['Id'] = 0 */ protected static $fieldKeys = array ( - self::TYPE_PHPNAME => array('Id' => 0, 'Rate' => 1, 'CreatedAt' => 2, 'UpdatedAt' => 3, ), - self::TYPE_STUDLYPHPNAME => array('id' => 0, 'rate' => 1, 'createdAt' => 2, 'updatedAt' => 3, ), - self::TYPE_COLNAME => array(TaxTableMap::ID => 0, TaxTableMap::RATE => 1, TaxTableMap::CREATED_AT => 2, TaxTableMap::UPDATED_AT => 3, ), - self::TYPE_RAW_COLNAME => array('ID' => 0, 'RATE' => 1, 'CREATED_AT' => 2, 'UPDATED_AT' => 3, ), - self::TYPE_FIELDNAME => array('id' => 0, 'rate' => 1, 'created_at' => 2, 'updated_at' => 3, ), - self::TYPE_NUM => array(0, 1, 2, 3, ) + self::TYPE_PHPNAME => array('Id' => 0, 'Type' => 1, 'SerializedRequirements' => 2, 'CreatedAt' => 3, 'UpdatedAt' => 4, ), + self::TYPE_STUDLYPHPNAME => array('id' => 0, 'type' => 1, 'serializedRequirements' => 2, 'createdAt' => 3, 'updatedAt' => 4, ), + self::TYPE_COLNAME => array(TaxTableMap::ID => 0, TaxTableMap::TYPE => 1, TaxTableMap::SERIALIZED_REQUIREMENTS => 2, TaxTableMap::CREATED_AT => 3, TaxTableMap::UPDATED_AT => 4, ), + self::TYPE_RAW_COLNAME => array('ID' => 0, 'TYPE' => 1, 'SERIALIZED_REQUIREMENTS' => 2, 'CREATED_AT' => 3, 'UPDATED_AT' => 4, ), + self::TYPE_FIELDNAME => array('id' => 0, 'type' => 1, 'serialized_requirements' => 2, 'created_at' => 3, 'updated_at' => 4, ), + self::TYPE_NUM => array(0, 1, 2, 3, 4, ) ); /** @@ -150,7 +155,8 @@ class TaxTableMap extends TableMap $this->setUseIdGenerator(true); // columns $this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null); - $this->addColumn('RATE', 'Rate', 'FLOAT', true, null, null); + $this->addColumn('TYPE', 'Type', 'VARCHAR', true, 255, null); + $this->addColumn('SERIALIZED_REQUIREMENTS', 'SerializedRequirements', 'LONGVARCHAR', true, null, null); $this->addColumn('CREATED_AT', 'CreatedAt', 'TIMESTAMP', false, null, null); $this->addColumn('UPDATED_AT', 'UpdatedAt', 'TIMESTAMP', false, null, null); } // initialize() @@ -327,12 +333,14 @@ class TaxTableMap extends TableMap { if (null === $alias) { $criteria->addSelectColumn(TaxTableMap::ID); - $criteria->addSelectColumn(TaxTableMap::RATE); + $criteria->addSelectColumn(TaxTableMap::TYPE); + $criteria->addSelectColumn(TaxTableMap::SERIALIZED_REQUIREMENTS); $criteria->addSelectColumn(TaxTableMap::CREATED_AT); $criteria->addSelectColumn(TaxTableMap::UPDATED_AT); } else { $criteria->addSelectColumn($alias . '.ID'); - $criteria->addSelectColumn($alias . '.RATE'); + $criteria->addSelectColumn($alias . '.TYPE'); + $criteria->addSelectColumn($alias . '.SERIALIZED_REQUIREMENTS'); $criteria->addSelectColumn($alias . '.CREATED_AT'); $criteria->addSelectColumn($alias . '.UPDATED_AT'); } diff --git a/core/lib/Thelia/Model/Tax.php b/core/lib/Thelia/Model/Tax.php index 21efbae6a..58144d551 100755 --- a/core/lib/Thelia/Model/Tax.php +++ b/core/lib/Thelia/Model/Tax.php @@ -2,8 +2,10 @@ namespace Thelia\Model; +use Symfony\Component\Form\Exception\Exception; use Thelia\Exception\TaxEngineException; use Thelia\Model\Base\Tax as BaseTax; +use Thelia\TaxEngine\TaxType\BaseTaxType; class Tax extends BaseTax { @@ -33,14 +35,37 @@ class Tax extends BaseTax return $taxRuleCountryPosition; } - public function getTaxRuleRateSum() + public function getTypeInstance() { - try { - $taxRuleRateSum = $this->getVirtualColumn(TaxRuleQuery::ALIAS_FOR_TAX_RATE_SUM); - } catch(PropelException $e) { - throw new PropelException("Virtual column `" . TaxRuleQuery::ALIAS_FOR_TAX_RATE_SUM . "` does not exist in Tax::getTaxRuleRateSum"); + $class = '\\Thelia\\TaxEngine\\TaxType\\' . $this->getType(); + + /* test type */ + if(!class_exists($class)) { + throw new TaxEngineException('Recorded type does not exists', TaxEngineException::BAD_RECORDED_TYPE); } - return $taxRuleRateSum; + $instance = new $class; + + if(!$instance instanceof BaseTaxType) { + throw new TaxEngineException('Recorded type does not extends BaseTaxType', TaxEngineException::BAD_RECORDED_TYPE); + } + + return $instance; + } + + public function setRequirements($requirements) + { + parent::setSerializedRequirements(base64_encode(json_encode($requirements))); + } + + public function getRequirements() + { + $requirements = json_decode(base64_decode(parent::getSerializedRequirements()), true); + + if(json_last_error() != JSON_ERROR_NONE || !is_array($requirements)) { + throw new TaxEngineException('BAD RECORDED REQUIREMENTS', TaxEngineException::BAD_RECORDED_REQUIREMENTS); + } + + return $requirements; } } diff --git a/core/lib/Thelia/Model/TaxRuleQuery.php b/core/lib/Thelia/Model/TaxRuleQuery.php index f9c6cf1e5..75a21a453 100755 --- a/core/lib/Thelia/Model/TaxRuleQuery.php +++ b/core/lib/Thelia/Model/TaxRuleQuery.php @@ -20,7 +20,6 @@ use Thelia\Model\Map\TaxTableMap; class TaxRuleQuery extends BaseTaxRuleQuery { const ALIAS_FOR_TAX_RULE_COUNTRY_POSITION = 'taxRuleCountryPosition'; - const ALIAS_FOR_TAX_RATE_SUM = 'taxRateSum'; public function getTaxCalculatorGroupedCollection(Product $product, Country $country) { @@ -29,12 +28,10 @@ class TaxRuleQuery extends BaseTaxRuleQuery TaxRuleCountryQuery::create() ->filterByCountry($country, Criteria::EQUAL) ->filterByTaxRuleId($product->getTaxRuleId()) - ->groupByPosition() ->orderByPosition() ->find() ) ->withColumn(TaxRuleCountryTableMap::POSITION, self::ALIAS_FOR_TAX_RULE_COUNTRY_POSITION) - ->withColumn('ROUND(SUM(' . TaxTableMap::RATE . '), 2)', self::ALIAS_FOR_TAX_RATE_SUM) ; return $search->find(); diff --git a/core/lib/Thelia/TaxEngine/Calculator.php b/core/lib/Thelia/TaxEngine/Calculator.php index e077a24b8..1630ad550 100755 --- a/core/lib/Thelia/TaxEngine/Calculator.php +++ b/core/lib/Thelia/TaxEngine/Calculator.php @@ -36,7 +36,7 @@ class Calculator { protected $taxRuleQuery = null; - protected $taxRulesGroupedCollection = null; + protected $taxRulesCollection = null; protected $product = null; protected $country = null; @@ -50,7 +50,7 @@ class Calculator { $this->product = null; $this->country = null; - $this->taxRulesGroupedCollection = null; + $this->taxRulesCollection = null; if($product->getId() === null) { throw new TaxEngineException('Product id is empty in Calculator::load', TaxEngineException::UNDEFINED_PRODUCT); @@ -62,14 +62,14 @@ class Calculator $this->product = $product; $this->country = $country; - $this->taxRulesGroupedCollection = $this->taxRuleQuery->getTaxCalculatorGroupedCollection($product, $country); + $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorGroupedCollection($product, $country); return $this; } public function getTaxAmount($untaxedPrice) { - if(null === $this->taxRulesGroupedCollection) { + if(null === $this->taxRulesCollection) { throw new TaxEngineException('Tax rules collection is empty in Calculator::getTaxAmount', TaxEngineException::UNDEFINED_TAX_RULES_COLLECTION); } @@ -78,9 +78,13 @@ class Calculator } $totalTaxAmount = 0; - foreach($this->taxRulesGroupedCollection as $taxRule) { - $rateSum = $taxRule->getTaxRuleRateSum(); - $taxAmount = $untaxedPrice * $rateSum * 0.01; + foreach($this->taxRulesCollection as $taxRule) { + $taxType = $taxRule->getTypeInstance(); + + $taxType->loadRequirements($taxRule->getRequirements()); + + $taxAmount = $taxType->calculate($untaxedPrice); + $totalTaxAmount += $taxAmount; $untaxedPrice += $taxAmount; } diff --git a/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php new file mode 100755 index 000000000..d155bb4c0 --- /dev/null +++ b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php @@ -0,0 +1,83 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\TaxEngine\TaxType; + +use Thelia\Type\TypeInterface; + +/** + * + * @author Etienne Roudeix + * + */ +abstract class BaseTaxType +{ + protected $requirements = null; + + public abstract function calculate($untaxedPrice); + + public abstract function getRequirementsList(); + + public function loadRequirements($requirementsValues) + { + $this->requirements = $this->getRequirementsList(); + + if(!is_array($this->requirements)) { + //@todo throw sg + exit('err_1'); + } + + foreach($this->requirements as $requirement => $requirementType) { + if(!$requirementType instanceof TypeInterface) { + //@todo throw sg + exit('err_2'); + } + + if(!array_key_exists($requirement, $requirementsValues)) { + //@todo throw sg + exit('err_3'); + } + + if(!$requirementType->isValid($requirementsValues[$requirement])) { + //@todo throw sg + exit('err_4'); + } + + $this->requirements[$requirement] = $requirementsValues[$requirement]; + } + } + + public function getRequirement($key) + { + if($this->requirements === null) { + //@todo throw sg + exit('err_5'); + } + + if(!array_key_exists($key, $this->requirements)) { + //@todo throw sg + exit('err_6'); + } + + return $this->requirements[$key]; + } +} diff --git a/core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php new file mode 100755 index 000000000..4485f1e21 --- /dev/null +++ b/core/lib/Thelia/TaxEngine/TaxType/FeatureSlicePercentTaxType.php @@ -0,0 +1,47 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\TaxEngine\TaxType; + +use Thelia\Type\FloatToFloatArrayType; +use Thelia\Type\ModelType; + +/** + * + * @author Etienne Roudeix + * + */ +class featureSlicePercentTaxType extends BaseTaxType +{ + public function calculate($untaxedPrice) + { + + } + + public function getRequirementsList() + { + return array( + 'featureId' => new ModelType('Currency'), + 'slices' => new FloatToFloatArrayType(), + ); + } +} diff --git a/core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php new file mode 100755 index 000000000..1d7152fcf --- /dev/null +++ b/core/lib/Thelia/TaxEngine/TaxType/PricePercentTaxType.php @@ -0,0 +1,45 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\TaxEngine\TaxType; + +use Thelia\Type\FloatType; + +/** + * + * @author Etienne Roudeix + * + */ +class PricePercentTaxType extends BaseTaxType +{ + public function calculate($untaxedPrice) + { + return $untaxedPrice * $this->getRequirement("percent") * 0.01; + } + + public function getRequirementsList() + { + return array( + 'percent' => new FloatType(), + ); + } +} diff --git a/core/lib/Thelia/Type/FloatToFloatArrayType.php b/core/lib/Thelia/Type/FloatToFloatArrayType.php new file mode 100755 index 000000000..5a70dba91 --- /dev/null +++ b/core/lib/Thelia/Type/FloatToFloatArrayType.php @@ -0,0 +1,56 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +/** + * + * @author Etienne Roudeix + * + */ + +class FloatToFloatArrayType implements TypeInterface +{ + public function getType() + { + return 'Float key to float value array type'; + } + + public function isValid($value) + { + if(!is_array($value)) + return false; + + foreach($value as $key => $value) { + if( filter_var($key, FILTER_VALIDATE_FLOAT) === false || filter_var($value, FILTER_VALIDATE_FLOAT) === false ) { + return false; + } + } + + return true; + } + + public function getFormattedValue($value) + { + return $this->isValid($value) ? $value : null; + } +} diff --git a/core/lib/Thelia/Type/ModelType.php b/core/lib/Thelia/Type/ModelType.php new file mode 100755 index 000000000..38df857e4 --- /dev/null +++ b/core/lib/Thelia/Type/ModelType.php @@ -0,0 +1,66 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Type; + +use Propel\Runtime\ActiveRecord\ActiveRecordInterface; +use Thelia\Exception\TypeException; + +/** + * + * @author Etienne Roudeix + * + */ +class ModelType implements TypeInterface +{ + protected $expectedModelActiveRecord = null; + + /** + * @param $expectedModelActiveRecord + * @throws TypeException + */ + public function __construct($expectedModelActiveRecord) + { + $class = '\\Thelia\\Model\\' . $expectedModelActiveRecord; + + if(!(class_exists($class) && new $class instanceof ActiveRecordInterface)) { + throw new TypeException('MODEL NOT FOUND', TypeException::MODEL_NOT_FOUND); + } + + $this->expectedModelActiveRecord = $class; + } + + public function getType() + { + return 'Model type'; + } + + public function isValid($value) + { + return $value instanceof $this->expectedModelActiveRecord; + } + + public function getFormattedValue($value) + { + return $this->isValid($value) ? $value : null; + } +} diff --git a/install/insert.sql b/install/insert.sql index da1414ce3..8ffdcb092 100755 --- a/install/insert.sql +++ b/install/insert.sql @@ -17,7 +17,7 @@ INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updat ('image_cache_dir_from_web_root', 'cache/images', 0, 0, NOW(), NOW()), ('currency_rate_update_url', 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml', 0, 0, NOW(), NOW()), ('page_not_found_view', '404.html', 0, 0, NOW(), NOW()), -('use_tax_free_amounts', 1, 1, 0, NOW(), NOW()); +('use_tax_free_amounts', 0, 1, 0, NOW(), NOW()); INSERT INTO `module` (`id`, `code`, `type`, `activate`, `position`, `full_namespace`, `created_at`, `updated_at`) VALUES @@ -1112,9 +1112,9 @@ INSERT INTO `country_i18n` (`id`, `locale`, `title`, `description`, `chapo`, `po (268, 'es_ES', 'USA - Alabama', '', '', ''), (268, 'fr_FR', 'USA - Alabama', '', '', ''); -INSERT INTO `tax` (`id`, `rate`, `created_at`, `updated_at`) +INSERT INTO `tax` (`id`, `type`, `serialized_requirements`, `created_at`, `updated_at`) VALUES - (1, '19.6', NOW(), NOW()); + (1, 'PricePercentTaxType', 'eyJwZXJjZW50IjoxOS42fQ==', NOW(), NOW()); INSERT INTO `tax_i18n` (`id`, `locale`, `title`) VALUES diff --git a/install/thelia.sql b/install/thelia.sql index 0346ffba8..e4e5fb6b7 100755 --- a/install/thelia.sql +++ b/install/thelia.sql @@ -111,7 +111,8 @@ DROP TABLE IF EXISTS `tax`; CREATE TABLE `tax` ( `id` INTEGER NOT NULL AUTO_INCREMENT, - `rate` FLOAT NOT NULL, + `type` VARCHAR(255) NOT NULL, + `serialized_requirements` TEXT NOT NULL, `created_at` DATETIME, `updated_at` DATETIME, PRIMARY KEY (`id`) diff --git a/local/config/schema.xml b/local/config/schema.xml index afb7b2c41..7590da1e4 100755 --- a/local/config/schema.xml +++ b/local/config/schema.xml @@ -86,7 +86,8 @@ - + + diff --git a/templates/default_save/product.html b/templates/default_save/product.html index 441585aba..cde8b61eb 100755 --- a/templates/default_save/product.html +++ b/templates/default_save/product.html @@ -8,7 +8,7 @@ Index : {navigate to="index"}
{ifloop rel="product"} -{loop type="product" name="product" current="true" min_price="50" max_price="100"} +{loop type="product" name="product" current="true"}

PRODUCT ({$ID}) : {$REF}

From 25756ef9dd81f414a25cacb40e9347950a8fbd8b Mon Sep 17 00:00:00 2001 From: Etienne Roudeix Date: Wed, 11 Sep 2013 16:56:16 +0200 Subject: [PATCH 09/12] tax engine --- .../Thelia/Exception/TaxEngineException.php | 6 ++++ core/lib/Thelia/Model/Tax.php | 3 +- core/lib/Thelia/Model/TaxRuleQuery.php | 2 +- core/lib/Thelia/TaxEngine/Calculator.php | 5 +++- .../Thelia/TaxEngine/TaxType/BaseTaxType.php | 19 +++++------- .../Thelia/Tests/TaxEngine/CalculatorTest.php | 30 ++++++++++--------- 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/core/lib/Thelia/Exception/TaxEngineException.php b/core/lib/Thelia/Exception/TaxEngineException.php index 8ce8561ef..93f5b8237 100755 --- a/core/lib/Thelia/Exception/TaxEngineException.php +++ b/core/lib/Thelia/Exception/TaxEngineException.php @@ -30,9 +30,15 @@ class TaxEngineException extends \RuntimeException const BAD_RECORDED_TYPE = 101; const BAD_RECORDED_REQUIREMENTS = 102; + const TAX_TYPE_BAD_ABSTRACT_METHOD = 201; + const TAX_TYPE_REQUIREMENT_NOT_FOUND = 202; + const TAX_TYPE_BAD_REQUIREMENT_VALUE = 203; + const UNDEFINED_PRODUCT = 501; const UNDEFINED_COUNTRY = 502; const UNDEFINED_TAX_RULES_COLLECTION = 503; + const UNDEFINED_REQUIREMENTS = 504; + const UNDEFINED_REQUIREMENT_VALUE = 505; const BAD_AMOUNT_FORMAT = 601; diff --git a/core/lib/Thelia/Model/Tax.php b/core/lib/Thelia/Model/Tax.php index 58144d551..738f16508 100755 --- a/core/lib/Thelia/Model/Tax.php +++ b/core/lib/Thelia/Model/Tax.php @@ -2,7 +2,6 @@ namespace Thelia\Model; -use Symfony\Component\Form\Exception\Exception; use Thelia\Exception\TaxEngineException; use Thelia\Model\Base\Tax as BaseTax; use Thelia\TaxEngine\TaxType\BaseTaxType; @@ -49,7 +48,7 @@ class Tax extends BaseTax if(!$instance instanceof BaseTaxType) { throw new TaxEngineException('Recorded type does not extends BaseTaxType', TaxEngineException::BAD_RECORDED_TYPE); } - + return $instance; } diff --git a/core/lib/Thelia/Model/TaxRuleQuery.php b/core/lib/Thelia/Model/TaxRuleQuery.php index 75a21a453..d5ce47546 100755 --- a/core/lib/Thelia/Model/TaxRuleQuery.php +++ b/core/lib/Thelia/Model/TaxRuleQuery.php @@ -21,7 +21,7 @@ class TaxRuleQuery extends BaseTaxRuleQuery { const ALIAS_FOR_TAX_RULE_COUNTRY_POSITION = 'taxRuleCountryPosition'; - public function getTaxCalculatorGroupedCollection(Product $product, Country $country) + public function getTaxCalculatorCollection(Product $product, Country $country) { $search = TaxQuery::create() ->filterByTaxRuleCountry( diff --git a/core/lib/Thelia/TaxEngine/Calculator.php b/core/lib/Thelia/TaxEngine/Calculator.php index 1630ad550..2708e88c6 100755 --- a/core/lib/Thelia/TaxEngine/Calculator.php +++ b/core/lib/Thelia/TaxEngine/Calculator.php @@ -34,6 +34,9 @@ use Thelia\Model\TaxRuleQuery; */ class Calculator { + /** + * @var TaxRuleQuery + */ protected $taxRuleQuery = null; protected $taxRulesCollection = null; @@ -62,7 +65,7 @@ class Calculator $this->product = $product; $this->country = $country; - $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorGroupedCollection($product, $country); + $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($product, $country); return $this; } diff --git a/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php index d155bb4c0..7f487bf64 100755 --- a/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php +++ b/core/lib/Thelia/TaxEngine/TaxType/BaseTaxType.php @@ -22,6 +22,7 @@ /*************************************************************************************/ namespace Thelia\TaxEngine\TaxType; +use Thelia\Exception\TaxEngineException; use Thelia\Type\TypeInterface; /** @@ -42,24 +43,20 @@ abstract class BaseTaxType $this->requirements = $this->getRequirementsList(); if(!is_array($this->requirements)) { - //@todo throw sg - exit('err_1'); + throw new TaxEngineException('getRequirementsList must return an array', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD); } foreach($this->requirements as $requirement => $requirementType) { if(!$requirementType instanceof TypeInterface) { - //@todo throw sg - exit('err_2'); + throw new TaxEngineException('getRequirementsList must return an array of TypeInterface', TaxEngineException::TAX_TYPE_BAD_ABSTRACT_METHOD); } if(!array_key_exists($requirement, $requirementsValues)) { - //@todo throw sg - exit('err_3'); + throw new TaxEngineException('Cannot load requirements : requirement value for `' . $requirement . '` not found', TaxEngineException::TAX_TYPE_REQUIREMENT_NOT_FOUND); } if(!$requirementType->isValid($requirementsValues[$requirement])) { - //@todo throw sg - exit('err_4'); + throw new TaxEngineException('Requirement value for `' . $requirement . '` does not match required type', TaxEngineException::TAX_TYPE_BAD_REQUIREMENT_VALUE); } $this->requirements[$requirement] = $requirementsValues[$requirement]; @@ -69,13 +66,11 @@ abstract class BaseTaxType public function getRequirement($key) { if($this->requirements === null) { - //@todo throw sg - exit('err_5'); + throw new TaxEngineException('Requirements are empty in BaseTaxType::getRequirement', TaxEngineException::UNDEFINED_REQUIREMENTS); } if(!array_key_exists($key, $this->requirements)) { - //@todo throw sg - exit('err_6'); + throw new TaxEngineException('Requirement value for `' . $key . '` does not exists in BaseTaxType::$requirements', TaxEngineException::UNDEFINED_REQUIREMENT_VALUE); } return $this->requirements[$key]; diff --git a/core/lib/Thelia/Tests/TaxEngine/CalculatorTest.php b/core/lib/Thelia/Tests/TaxEngine/CalculatorTest.php index fac6ca643..719d90835 100755 --- a/core/lib/Thelia/Tests/TaxEngine/CalculatorTest.php +++ b/core/lib/Thelia/Tests/TaxEngine/CalculatorTest.php @@ -83,9 +83,9 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase $calculator = new Calculator(); - $taxRuleQuery = $this->getMock('\Thelia\Model\TaxRuleQuery', array('getTaxCalculatorGroupedCollection')); + $taxRuleQuery = $this->getMock('\Thelia\Model\TaxRuleQuery', array('getTaxCalculatorCollection')); $taxRuleQuery->expects($this->once()) - ->method('getTaxCalculatorGroupedCollection') + ->method('getTaxCalculatorCollection') ->with($productQuery, $countryQuery) ->will($this->returnValue('foo')); @@ -104,7 +104,7 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase ); $this->assertEquals( 'foo', - $this->getProperty('taxRulesGroupedCollection')->getValue($calculator) + $this->getProperty('taxRulesCollection')->getValue($calculator) ); } @@ -124,35 +124,37 @@ class CalculatorTest extends \PHPUnit_Framework_TestCase */ public function testGetTaxAmountBadAmount() { - $taxRulesGroupedCollection = new ObjectCollection(); + $taxRulesCollection = new ObjectCollection(); $calculator = new Calculator(); - $rewritingUrlQuery = $this->getProperty('taxRulesGroupedCollection'); - $rewritingUrlQuery->setValue($calculator, $taxRulesGroupedCollection); + $rewritingUrlQuery = $this->getProperty('taxRulesCollection'); + $rewritingUrlQuery->setValue($calculator, $taxRulesCollection); $calculator->getTaxAmount('foo'); } public function testGetTaxAmountAndGetTaxedPrice() { - $taxRulesGroupedCollection = new ObjectCollection(); - $taxRulesGroupedCollection->setModel('\Thelia\Model\Tax'); + $taxRulesCollection = new ObjectCollection(); + $taxRulesCollection->setModel('\Thelia\Model\Tax'); $tax = new Tax(); - $tax->setVirtualColumn('taxRateSum', 10); + $tax->setType('PricePercentTaxType') + ->setRequirements(array('percent' => 10)); - $taxRulesGroupedCollection->append($tax); + $taxRulesCollection->append($tax); $tax = new Tax(); - $tax->setVirtualColumn('taxRateSum', 8); + $tax->setType('PricePercentTaxType') + ->setRequirements(array('percent' => 8)); - $taxRulesGroupedCollection->append($tax); + $taxRulesCollection->append($tax); $calculator = new Calculator(); - $rewritingUrlQuery = $this->getProperty('taxRulesGroupedCollection'); - $rewritingUrlQuery->setValue($calculator, $taxRulesGroupedCollection); + $rewritingUrlQuery = $this->getProperty('taxRulesCollection'); + $rewritingUrlQuery->setValue($calculator, $taxRulesCollection); $taxAmount = $calculator->getTaxAmount(500); $taxedPrice = $calculator->getTaxedPrice(500); From 8d71bfecc51a2f2f4096ff1c3df93fc403b2c671 Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 17:06:04 +0200 Subject: [PATCH 10/12] WIP - add tests casper JS on coupon --- tests/functionnal/casperjs/conf/local.js | 2 +- .../functionnal/casperjs/exe/00_parameters.js | 5 +++ tests/functionnal/casperjs/exe/30_coupons.js | 39 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/functionnal/casperjs/exe/30_coupons.js diff --git a/tests/functionnal/casperjs/conf/local.js b/tests/functionnal/casperjs/conf/local.js index 366d297f2..9fa930598 100644 --- a/tests/functionnal/casperjs/conf/local.js +++ b/tests/functionnal/casperjs/conf/local.js @@ -1,3 +1,3 @@ //LOCAL = ton pc -var thelia2_base_url = 'http://www.thelia2.dev/index.php/'; +var thelia2_base_url = 'http://www.thelia2.dev/index_dev.php/'; casper.test.done(0); diff --git a/tests/functionnal/casperjs/exe/00_parameters.js b/tests/functionnal/casperjs/exe/00_parameters.js index 81b980d41..a32ee89b5 100644 --- a/tests/functionnal/casperjs/exe/00_parameters.js +++ b/tests/functionnal/casperjs/exe/00_parameters.js @@ -2,6 +2,11 @@ casper.test.comment('Please edit 00_parameters.js to add your configuration'); var thelia2_login_admin_url = thelia2_base_url + 'admin/login'; +var thelia2_login_coupon_list_url = thelia2_base_url + 'admin/login'; +var thelia2_login_coupon_create_url = thelia2_base_url + 'admin/coupon/create'; +var thelia2_login_coupon_read_url = thelia2_base_url + 'admin/coupon/read/1'; +var thelia2_login_coupon_update_url = thelia2_base_url + 'admin/coupon/update/1'; + //var findMyId = /([0-9]+)$/; diff --git a/tests/functionnal/casperjs/exe/30_coupons.js b/tests/functionnal/casperjs/exe/30_coupons.js new file mode 100644 index 000000000..3f3d0ee50 --- /dev/null +++ b/tests/functionnal/casperjs/exe/30_coupons.js @@ -0,0 +1,39 @@ +casper.test.comment('Testing coupons'); + + +////LIST +// @todo implement + +////CREATE +// @todo implement + +//UPDATE COUPON RULE +casper.start(thelia2_login_coupon_update_url, function() { + console.log('Now on : ' + this.getCurrentUrl()); + this.echo('\nCOUPON RULE - EDIT'); + this.test.assertTitle('Update coupon - Thelia Back Office', 'Web page title OK'); +// this.test.assertSelectorHasText('#content-header > h1', 'Liste des pays', 'Web page main content OK'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(1)', 'If cart total amount is superior to 40 EUR','1st default rule found'); + this.test.assertSelectorHasText('tbody#constraint-list tr:nth-child(2)', 'If cart total amount is inferior to 400 EUR','2nd default rule found'); + + // Create rule + this.evaluate(function() { +// document.querySelector('select#category-rule').selectedItem = 'thelia.constraint.rule.available_for_x_articles'; + $('#category-rule').val('thelia.constraint.rule.available_for_x_articles').change(); + return true; + }); + this.capture('screenshot-category-rule.png'); +// this.click('constraint-list > tr:last-child > td > a.constraint-update-btn'); +}); + + +////EDIT CHECK +// @todo implement + +////DELETE +// @todo implement + +//RUN +casper.run(function() { + this.test.done(); +}); \ No newline at end of file From 33d6c850523849b752db83e7950edd1792b15dc3 Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 17:10:36 +0200 Subject: [PATCH 11/12] WIP - add tests casper JS on coupon --- tests/functionnal/casperjs/exe/30_coupons.js | 9 ++++++++- web/index_dev.php | 18 +++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/functionnal/casperjs/exe/30_coupons.js b/tests/functionnal/casperjs/exe/30_coupons.js index 3f3d0ee50..6031d71f8 100644 --- a/tests/functionnal/casperjs/exe/30_coupons.js +++ b/tests/functionnal/casperjs/exe/30_coupons.js @@ -22,10 +22,17 @@ casper.start(thelia2_login_coupon_update_url, function() { $('#category-rule').val('thelia.constraint.rule.available_for_x_articles').change(); return true; }); - this.capture('screenshot-category-rule.png'); + this.capture('tests/functionnal/casperjs/pictures/screenshot-category-rule.png'); // this.click('constraint-list > tr:last-child > td > a.constraint-update-btn'); }); +casper.wait(1000, function() { + this.echo("\nWaiting...."); +}); + +casper.then(function(){ + +}); ////EDIT CHECK // @todo implement diff --git a/web/index_dev.php b/web/index_dev.php index 4d0063268..587631466 100755 --- a/web/index_dev.php +++ b/web/index_dev.php @@ -32,19 +32,19 @@ $env = 'dev'; require __DIR__ . '/../core/bootstrap.php'; // List of allowed IP -$trustedIp = array( - '::1', - '127.0.0.1' -); +//$trustedIp = array( +// '::1', +// '127.0.0.1' +//); $request = Request::createFromGlobals(); $thelia = new Thelia("dev", true); -if ( false === in_array($request->getClientIp(), $trustedIp)) { - // Redirect 401 Unauthorized - $response = new Response('Unauthorized', 401); - $thelia->terminate($request, $response); -} +//if ( false === in_array($request->getClientIp(), $trustedIp)) { +// // Redirect 401 Unauthorized +// $response = new Response('Unauthorized', 401); +// $thelia->terminate($request, $response); +//} $response = $thelia->handle($request)->prepare($request)->send(); From 41cf74ccac3eb2c0029c0e6ee3d593587e96b15a Mon Sep 17 00:00:00 2001 From: gmorel Date: Wed, 11 Sep 2013 17:22:31 +0200 Subject: [PATCH 12/12] Working - add ajax loader --- .../admin/default/assets/img/ajax-loader.gif | Bin 2545 -> 13582 bytes .../default/assets/less/thelia/thelia.less | 4 ++-- templates/admin/default/coupon-update.html | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/admin/default/assets/img/ajax-loader.gif b/templates/admin/default/assets/img/ajax-loader.gif index 1321dd374af3334dbcd4e031c15a423a0882a71f..948fa9bdb33274f9338eadd930aa1bbd2752882c 100644 GIT binary patch literal 13582 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 Q?ms&a;tm!#2B>%I-{~VEIsgCw diff --git a/templates/admin/default/assets/less/thelia/thelia.less b/templates/admin/default/assets/less/thelia/thelia.less index 8576272bf..ee85c7510 100644 --- a/templates/admin/default/assets/less/thelia/thelia.less +++ b/templates/admin/default/assets/less/thelia/thelia.less @@ -251,6 +251,6 @@ .loading{ background: url("@{imgDir}/ajax-loader.gif") no-repeat; - height: 24px; - width: 24px; + height: 30px; + width: 30px; } \ No newline at end of file diff --git a/templates/admin/default/coupon-update.html b/templates/admin/default/coupon-update.html index 925296aae..16771539a 100755 --- a/templates/admin/default/coupon-update.html +++ b/templates/admin/default/coupon-update.html @@ -19,6 +19,7 @@ {include file='coupon/form.html' formAction={url path={$formAction}} form=$form noRules=false} {/form} + {/block}