Merge pull request #4876 from lchrusciel/promotion-feature

[Admin] Promotions start and end date validation
This commit is contained in:
Michał Marcinkowski 2016-04-26 14:15:42 +02:00
commit 4cc16020e2
18 changed files with 341 additions and 5 deletions

View file

@ -19,6 +19,7 @@
<parameter key="sylius.behat.context.transform.coupon.class">Sylius\Behat\Context\Transform\CouponContext</parameter>
<parameter key="sylius.behat.context.transform.currency.class">Sylius\Behat\Context\Transform\CurrencyContext</parameter>
<parameter key="sylius.behat.context.transform.customer.class">Sylius\Behat\Context\Transform\CustomerContext</parameter>
<parameter key="sylius.behat.context.transform.date_time.class">Sylius\Behat\Context\Transform\DateTimeContext</parameter>
<parameter key="sylius.behat.context.transform.lexical.class">Sylius\Behat\Context\Transform\LexicalContext</parameter>
<parameter key="sylius.behat.context.transform.locale.class">Sylius\Behat\Context\Transform\LocaleContext</parameter>
<parameter key="sylius.behat.context.transform.payment.class">Sylius\Behat\Context\Transform\PaymentContext</parameter>
@ -73,6 +74,10 @@
<tag name="sylius.behat.context" />
</service>
<service id="sylius.behat.context.transform.date_time" class="%sylius.behat.context.transform.date_time.class%" scope="scenario">
<tag name="sylius.behat.context" />
</service>
<service id="sylius.behat.context.transform.lexical" class="%sylius.behat.context.transform.lexical.class%" scope="scenario">
<tag name="sylius.behat.context" />
</service>

View file

@ -9,6 +9,7 @@ default:
- sylius.behat.context.transform.addressing
- sylius.behat.context.transform.customer
- sylius.behat.context.transform.date_time
- sylius.behat.context.transform.lexical
- sylius.behat.context.transform.payment
- sylius.behat.context.transform.product

View file

@ -56,3 +56,13 @@ Feature: Adding a new promotion
And I add it
Then I should be notified that it has been successfully created
And the "Full metal promotion" promotion should be applicable for the "France" channel
@ui
Scenario: Adding a promotion with start and end date
Given I want to create a new promotion
When I specify its code as "FULL_METAL_PROMOTION"
And I name it "Full metal promotion"
And I specify that it should starts at "21.04.2017"
And I specify that it should ends at "21.05.2017"
And I add it
Then I should be notified that it has been successfully created

View file

@ -45,3 +45,12 @@ Feature: Editing promotion
And I save my changes
Then I should be notified that it has been successfully edited
And the "Christmas sale" promotion should be applicable for the "France" channel
@ui
Scenario: Adding a promotion with start and end date
Given I want to modify a "Christmas sale" promotion
And I specify that it should starts at "12.12.2017"
And I specify that it should ends at "24.12.2017"
And I save my changes
Then I should be notified that it has been successfully edited
And the "Christmas sale" promotion should be applicable from "12.12.2017" to "24.12.2017"

View file

@ -26,6 +26,16 @@ Feature: Promotion validation
Then I should be notified that name is required
And promotion with code "no_vat_promotion" should not be added
@ui
Scenario: Adding a promotion with start date set up after ends date
Given I want to create a new promotion
When I specify its code as "FULL_METAL_PROMOTION"
And I name it "Full metal promotion"
And I specify that it should starts at "24.12.2017"
And I specify that it should ends at "12.12.2017"
And I try to add it
Then I should be notified that promotion cannot end before it starts
@ui
Scenario: Trying to remove name from existing promotion
Given there is a promotion "Christmas sale"
@ -34,3 +44,12 @@ Feature: Promotion validation
And I try to save my changes
Then I should be notified that name is required
And this promotion should still be named "Christmas sale"
@ui
Scenario: Trying to add start later then ends date for existing promotion
Given there is a promotion "Christmas sale"
And I want to modify this promotion
And I specify that it should starts at "24.12.2017"
And I specify that it should ends at "12.12.2017"
And I try to save my changes
Then I should be notified that promotion cannot end before it starts

View file

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Behat\Context\Transform;
use Behat\Behat\Context\Context;
/**
* @author Łukasz Chruściel <lukasz.chrusciel@lakion.com>
*/
final class DateTimeContext implements Context
{
/**
* @Transform :date
* @Transform :startsDate
* @Transform :endsDate
*/
public function getDate($date)
{
return new \DateTime($date);
}
}

View file

@ -393,6 +393,57 @@ final class ManagingPromotionsContext implements Context
);
}
/**
* @When I specify that it should starts at :date
*/
public function iSpecifyThatItShouldStartAt(\DateTime $date)
{
$currentPage = $this->currentPageResolver->getCurrentPageWithForm($this->createPage, $this->updatePage);
$currentPage->setStartsAt($date);
}
/**
* @When I specify that it should ends at :date
*/
public function iSpecifyThatItShouldEndsAt(\DateTime $date)
{
$currentPage = $this->currentPageResolver->getCurrentPageWithForm($this->createPage, $this->updatePage);
$currentPage->setEndsAt($date);
}
/**
* @Then the :promotion promotion should be applicable from :startsDate to :endsDate
*/
public function thePromotionShouldBeApplicableFromTo(PromotionInterface $promotion, \DateTime $startsDate, \DateTime $endsDate)
{
$this->iWantToModifyAPromotion($promotion);
Assert::true(
$this->updatePage->hasStartsAt($startsDate),
sprintf('Promotion %s should starts at %s, but it isn\'t.', $promotion->getName(), date('D, d M Y H:i:s', $startsDate->getTimestamp()))
);
Assert::true(
$this->updatePage->hasEndsAt($endsDate),
sprintf('Promotion %s should ends at %s, but it isn\'t.', $promotion->getName(), date('D, d M Y H:i:s', $endsDate->getTimestamp()))
);
}
/**
* @Then I should be notified that promotion cannot end before it starts
*/
public function iShouldBeNotifiedThatPromotionCannotEndBeforeItsEvenStart()
{
$currentPage = $this->currentPageResolver->getCurrentPageWithForm($this->createPage, $this->updatePage);
Assert::true(
$currentPage->checkValidationMessageFor('ends_at', 'End date cannot be set prior start date.'),
'Start date was set after ends date, but it should not be possible.'
);
}
/**
* @param string $element
* @param string $expectedMessage

View file

@ -18,6 +18,7 @@ use Sylius\Behat\Page\Admin\Crud\CreatePage as BaseCreatePage;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Łuksaz Zalewski <mateusz.zalewski@lakion.com>
*/
class CreatePage extends BaseCreatePage implements CreatePageInterface
{
@ -99,12 +100,35 @@ class CreatePage extends BaseCreatePage implements CreatePageInterface
$this->getDocument()->checkField($name);
}
/**
* {@inheritdoc}
*/
public function setStartsAt(\DateTime $dateTime)
{
$timestamp = $dateTime->getTimestamp();
$this->getDocument()->fillField('sylius_promotion_startsAt_date', date('Y-m-d', $timestamp));
$this->getDocument()->fillField('sylius_promotion_startsAt_time', date('H:i', $timestamp));
}
/**
* {@inheritdoc}
*/
public function setEndsAt(\DateTime $dateTime)
{
$timestamp = $dateTime->getTimestamp();
$this->getDocument()->fillField('sylius_promotion_endsAt_date', date('Y-m-d', $timestamp));
$this->getDocument()->fillField('sylius_promotion_endsAt_time', date('H:i', $timestamp));
}
/**
* {@inheritdoc}
*/
protected function getDefinedElements()
{
return [
'starts_at' => '#sylius_promotion_startsAt',
'actions' => '#sylius_promotion_actions',
'code' => '#sylius_promotion_code',
'name' => '#sylius_promotion_name',

View file

@ -15,6 +15,7 @@ use Sylius\Behat\Page\Admin\Crud\CreatePageInterface as BaseCreatePageInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Łukasz Chruściel <lukasz.chrusciel@lakion.com>
*/
interface CreatePageInterface extends BaseCreatePageInterface
{
@ -77,4 +78,14 @@ interface CreatePageInterface extends BaseCreatePageInterface
* @param string $name
*/
public function checkChannel($name);
/**
* @param \DateTime $dateTime
*/
public function setStartsAt(\DateTime $dateTime);
/**
* @param \DateTime $dateTime
*/
public function setEndsAt(\DateTime $dateTime);
}

View file

@ -17,6 +17,7 @@ use Sylius\Behat\Page\Admin\Crud\UpdatePage as BaseUpdatePage;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Łukasz Chruściel <lukasz.chrusciel@lakion.com>
*/
class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
{
@ -56,6 +57,50 @@ class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
$this->getDocument()->checkField($name);
}
/**
* {@inheritdoc}
*/
public function setStartsAt(\DateTime $dateTime)
{
$timestamp = $dateTime->getTimestamp();
$this->getDocument()->fillField('sylius_promotion_startsAt_date', date('Y-m-d', $timestamp));
$this->getDocument()->fillField('sylius_promotion_startsAt_time', date('H:i', $timestamp));
}
/**
* {@inheritdoc}
*/
public function setEndsAt(\DateTime $dateTime)
{
$timestamp = $dateTime->getTimestamp();
$this->getDocument()->fillField('sylius_promotion_endsAt_date', date('Y-m-d', $timestamp));
$this->getDocument()->fillField('sylius_promotion_endsAt_time', date('H:i', $timestamp));
}
/**
* {@inheritdoc}
*/
public function hasStartsAt(\DateTime $dateTime)
{
$timestamp = $dateTime->getTimestamp();
return $this->getElement('starts_at_date')->getValue() === date('Y-m-d', $timestamp)
&& $this->getElement('starts_at_time')->getValue() === date('H:i', $timestamp);
}
/**
* {@inheritdoc}
*/
public function hasEndsAt(\DateTime $dateTime)
{
$timestamp = $dateTime->getTimestamp();
return $this->getElement('ends_at_date')->getValue() === date('Y-m-d', $timestamp)
&& $this->getElement('ends_at_time')->getValue() === date('H:i', $timestamp);
}
/**
* {@inheritDoc}
*/
@ -75,6 +120,11 @@ class UpdatePage extends BaseUpdatePage implements UpdatePageInterface
'exclusive' => '#sylius_promotion_exclusive',
'name' => '#sylius_promotion_name',
'usage_limit' => '#sylius_promotion_usageLimit',
'starts_at' => '#sylius_promotion_startsAt',
'starts_at_date' => '#sylius_promotion_startsAt_date',
'starts_at_time' => '#sylius_promotion_startsAt_time',
'ends_at_date' => '#sylius_promotion_endsAt_date',
'ends_at_time' => '#sylius_promotion_endsAt_time',
];
}
}

View file

@ -15,6 +15,7 @@ use Sylius\Behat\Page\Admin\Crud\UpdatePageInterface as BaseUpdatePageInterface;
/**
* @author Mateusz Zalewski <mateusz.zalewski@lakion.com>
* @author Łukasz Chruściel <lukasz.chrusciel@lakion.com>
*/
interface UpdatePageInterface extends BaseUpdatePageInterface
{
@ -48,4 +49,24 @@ interface UpdatePageInterface extends BaseUpdatePageInterface
* @param string $name
*/
public function checkChannel($name);
/**
* @param \DateTime $dateTime
*/
public function setStartsAt(\DateTime $dateTime);
/**
* @param \DateTime $dateTime
*/
public function setEndsAt(\DateTime $dateTime);
/**
* {@inheritdoc}
*/
public function hasStartsAt(\DateTime $dateTime);
/**
* {@inheritdoc}
*/
public function hasEndsAt(\DateTime $dateTime);
}

View file

@ -5,7 +5,7 @@
{{ form_row(form.code) }}
{{ form_row(form.name) }}
</div>
<div class="ui two column grid">
<div class="two field">
<div class="column">
{{ form_row(form.usageLimit) }}
{{ form_row(form.exclusive) }}
@ -19,6 +19,10 @@
{% endfor %}
</div>
</div>
<div class="two fields">
{{ form_row(form.startsAt) }}
{{ form_row(form.endsAt) }}
</div>
</div>
<div class="ui segment">
<h4 class="ui dividing header">{{ 'sylius.ui.promotion_configuration'|trans }}</h4>

View file

@ -41,13 +41,13 @@ class PromotionType extends AbstractResourceType
])
->add('startsAt', 'datetime', [
'label' => 'sylius.form.promotion.starts_at',
'empty_value' => /* @Ignore */ ['year' => '-', 'month' => '-', 'day' => '-'],
'time_widget' => 'text',
'date_widget' => 'single_text',
'time_widget' => 'single_text',
])
->add('endsAt', 'datetime', [
'label' => 'sylius.form.promotion.ends_at',
'empty_value' => /* @Ignore */ ['year' => '-', 'month' => '-', 'day' => '-'],
'time_widget' => 'text',
'date_widget' => 'single_text',
'time_widget' => 'single_text',
])
->add('couponBased', 'checkbox', [
'label' => 'sylius.form.promotion.coupon_based',

View file

@ -49,6 +49,8 @@
<parameter key="sylius.form.type.promotion_coupon_to_code.class">Sylius\Bundle\PromotionBundle\Form\Type\CouponToCodeType</parameter>
<parameter key="sylius.form.type.promotion_coupon_generate_instruction.class">Sylius\Bundle\PromotionBundle\Form\Type\CouponGenerateInstructionType</parameter>
<parameter key="sylius.form.transformer.promotion_coupon_to_code.class">Sylius\Bundle\PromotionBundle\Form\DataTransformer\CouponToCodeTransformer</parameter>
<parameter key="sylius.validator.date_range.class">Sylius\Bundle\PromotionBundle\Validator\PromotionDateRangeValidator</parameter>
</parameters>
<services>
@ -144,6 +146,10 @@
<argument type="service" id="sylius.repository.promotion_coupon" />
<argument type="service" id="sylius.manager.promotion_coupon" />
</service>
<service id="sylius.validator.date_range" class="%sylius.validator.date_range.class%">
<tag name="validator.constraint_validator" alias="sylius_promotion_date_range_validator" />
</service>
</services>
</container>

View file

@ -22,6 +22,10 @@
<option name="message">sylius.promotion.code.unique</option>
<option name="groups">sylius</option>
</constraint>
<constraint name="Sylius\Bundle\PromotionBundle\Validator\Constraints\PromotionDateRange">
<option name="message">sylius.promotion.startsAt.end_date_cannot_be_set_prior_start_date</option>
<option name="groups">sylius</option>
</constraint>
<property name="code">
<constraint name="NotBlank">
<option name="message">sylius.promotion.code.not_blank</option>

View file

@ -14,6 +14,8 @@ sylius:
max_length: Promotion name must not be longer than 1 character.|Promotion name must not be longer than {{ limit }} characters.
min_length: Promotion name must be at least 1 character long.|Promotion name must be at least {{ limit }} characters long.
not_blank: Please enter promotion name.
endsAt:
end_date_cannot_be_set_prior_start_date: End date cannot be set prior start date.
promotion_coupon:
code:
max_length: Coupon code must not be longer than 1 character.|Coupon code must not be longer than {{ limit }} characters.

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\PromotionBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @author Łukasz Chruściel <lukasz.chrusciel@lakion.com>
*/
class PromotionDateRange extends Constraint
{
public $message = 'sylius.promotion.startsAt.end_date_cannot_be_set_prior_start_date';
/**
* {@inheritdoc}
*/
public function getTargets()
{
return [self::CLASS_CONSTRAINT];
}
/**
* {@inheritdoc}
*/
public function validatedBy()
{
return 'sylius_promotion_date_range_validator';
}
}

View file

@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Bundle\PromotionBundle\Validator;
use Sylius\Component\Promotion\Model\PromotionInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;
/**
* @author Łukasz Chruściel <lukasz.chrusciel@lakion.com>
*/
class PromotionDateRangeValidator extends ConstraintValidator
{
/**
* {@inheritdoc}
*
* @param mixed $value
* @param Constraint $constraint
*/
public function validate($value, Constraint $constraint)
{
if (null === $value) {
return;
}
Assert::isInstanceOf(
$value,
PromotionInterface::class
);
if (null === $value->getStartsAt() || null === $value->getEndsAt()) {
return;
}
if ($value->getStartsAt()->getTimestamp() > $value->getEndsAt()->getTimestamp()) {
$this->context->buildViolation($constraint->message)
->atPath('endsAt')
->addViolation();
}
}
}