Skip to content

Commit

Permalink
Merge pull request #3339 from Arminek/specify-coupon-length
Browse files Browse the repository at this point in the history
[Promotion] Making coupon code length configurable.
  • Loading branch information
Paweł Jędrzejewski committed May 19, 2016
2 parents 99e2444 + 4c9c6a0 commit bf2eb33
Show file tree
Hide file tree
Showing 29 changed files with 858 additions and 98 deletions.
3 changes: 3 additions & 0 deletions features/legacy/backend/promotions.feature
Expand Up @@ -154,13 +154,15 @@ Feature: Promotions
Given I am on the page of promotion "Press Campaign"
And I follow "Generate coupons"
When I fill in "Amount" with "50"
And I fill in "Code length" with "6"
And I press "Generate"
Then I should see "Promotion coupons have been successfully generated"

Scenario: Generating coupons with usage limit
Given I am on the page of promotion "Press Campaign"
And I follow "Generate coupons"
When I fill in "Amount" with "5"
And I fill in "Code length" with "6"
And I fill in "Usage limit" with "5"
And I press "Generate"
Then I should see "Promotion coupons have been successfully generated"
Expand All @@ -169,6 +171,7 @@ Feature: Promotions
Given I am on the page of promotion "Press Campaign"
And I follow "Generate coupons"
And I fill in "Amount" with "50"
And I fill in "Code length" with "6"
And I press "Generate"
Then I should see "Promotion coupons have been successfully generated"
And I should see "Total: 55"
Expand Down
Expand Up @@ -14,8 +14,31 @@ Feature: Coupon generate instruction validation
Scenario: Trying to generate a new coupons without specifying its amount
Given I want to generate a new coupons for this promotion
When I do not specify its amount
And I specify its code length as 6
And I limit generated coupons usage to 25 times
And I make generated coupons valid until "26.03.2017"
And I try to generate it
Then I should be notified that generate amount is required
And there should be 0 coupon related to this promotion

@ui
Scenario: Trying to generate a new coupons without specifying its code length
Given I want to generate a new coupons for this promotion
When I do not specify its code length
And I specify its amount as 4
And I limit generated coupons usage to 25 times
And I make generated coupons valid until "26.03.2017"
And I try to generate it
Then I should be notified that generate code length is required
And there should be 0 coupon related to this promotion

@ui
Scenario: Trying to generate a new coupons with amount and code length impossible to generate
Given I want to generate a new coupons for this promotion
When I specify its code length as 1
And I specify its amount as 20
And I limit generated coupons usage to 25 times
And I make generated coupons valid until "26.03.2017"
And I try to generate it
Then I should be notified that generating 20 coupons with code length equal to 1 is not possible
And there should be 0 coupon related to this promotion
Expand Up @@ -14,6 +14,7 @@ Feature: Generating a new coupons
Scenario: Generating a new coupons
Given I want to generate a new coupons for this promotion
When I specify its amount as 5
And I specify its code length as 6
And I limit generated coupons usage to 25 times
And I make generated coupons valid until "26.03.2017"
And I generate it
Expand Down
Expand Up @@ -115,6 +115,15 @@ public function iWantToGenerateANewCouponsForThisPromotion(PromotionInterface $p
$this->generatePage->open(['promotionId' => $promotion->getId()]);
}

/**
* @When /^I specify its code length as (\d+)$/
* @When I do not specify its code length
*/
public function iSpecifyItsCodeLengthAs($codeLength = null)
{
$this->generatePage->specifyCodeLength($codeLength);
}

/**
* @When /^I limit generated coupons usage to (\d+) times$/
*/
Expand All @@ -133,26 +142,11 @@ public function iMakeGeneratedCouponsValidUntil(\DateTime $date)

/**
* @When I specify its code as :code
*/
public function iSpecifyItsCodeAs($code)
{
$this->createPage->specifyCode($code);
}

/**
* @When I do not specify its code
*/
public function iDoNotSpecifyItsCode()
{
// Intentionally left blank to fulfill context expectation
}

/**
* @When I do not specify its amount
*/
public function iDoNotSpecifyItsAmount()
public function iSpecifyItsCodeAs($code = null)
{
// Intentionally left blank to fulfill context expectation
$this->createPage->specifyCode($code);
}

/**
Expand All @@ -173,8 +167,9 @@ public function iChangeItsUsageLimitTo($limit)

/**
* @When I specify its amount as :amount
* @When I do not specify its amount
*/
public function iSpecifyItsAmountAs($amount)
public function iSpecifyItsAmountAs($amount = null)
{
$this->generatePage->specifyAmount($amount);
}
Expand Down Expand Up @@ -353,6 +348,17 @@ public function iShouldBeNotifiedThatGenerateAmountIsRequired()
);
}

/**
* @Then I should be notified that generate code length is required
*/
public function iShouldBeNotifiedThatCodeLengthIsRequired()
{
Assert::true(
$this->generatePage->checkCodeLengthValidation('Please enter coupon code length.'),
'Generate code length violation message should appear on page, but it does not.'
);
}

/**
* @Then /^there should still be only one coupon with code "([^"]+)" related to (this promotion)$/
*/
Expand Down Expand Up @@ -419,4 +425,17 @@ public function couponShouldStillExistInTheRegistry(CouponInterface $coupon)
sprintf('Coupon with code %s should exist.', $coupon->getCode())
);
}

/**
* @Then /^I should be notified that generating (\d+) coupons with code length equal to (\d+) is not possible$/
*/
public function iShouldBeNotifiedThatGeneratingCouponsWithCodeLengthIsNotPossible($amount, $codeLength)
{
$message = sprintf('Invalid coupons code length or coupons amount. It is not possible to generate %d unique coupons with code length equals %d. Possible generate amount is 8.', $amount, $codeLength);

Assert::true(
$this->generatePage->checkGenerationValidation($message),
'Generate violation message should appear on page, but it does not.'
);
}
}
49 changes: 44 additions & 5 deletions src/Sylius/Behat/Page/Admin/PromotionCoupon/GeneratePage.php
Expand Up @@ -25,12 +25,23 @@ class GeneratePage extends SymfonyPage implements GeneratePageInterface
*/
public function checkAmountValidation($message)
{
$foundedElement = $this->getValidatedField($this->getElement('amount'));
if (null === $foundedElement) {
throw new ElementNotFoundException($this->getSession(), 'Element', 'css', '#sylius_promotion_coupon_instruction_amount');
}
return $this->checkValidationMessageFor('amount', $message);
}

return $message === $foundedElement->find('css', '.pointing')->getText();
/**
* {@inheritdoc}
*/
public function checkCodeLengthValidation($message)
{
return $this->checkValidationMessageFor('code_length', $message);
}

/**
* {@inheritdoc}
*/
public function checkGenerationValidation($message)
{
return false !== strpos($this->getElement('form')->find('css', '.ui.red.label')->getText(), $message);
}

public function generate()
Expand All @@ -46,6 +57,14 @@ public function specifyAmount($amount)
$this->getDocument()->fillField('Amount', $amount);
}

/**
* {@inheritdoc}
*/
public function specifyCodeLength($codeLength)
{
$this->getDocument()->fillField('Code length', $codeLength);
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -81,9 +100,29 @@ protected function getDefinedElements()
'amount' => '#sylius_promotion_coupon_generate_instruction_amount',
'usage_limit' => '#sylius_promotion_coupon_generate_instruction_usageLimit',
'expires_at' => '#sylius_promotion_coupon_generate_instruction_expiresAt',
'code_length' => '#sylius_promotion_coupon_generate_instruction_codeLength',
'form' => '.two.column.stackable.grid',
]);
}

/**
* @param string $element
* @param string $message
*
* @return bool
*
* @throws ElementNotFoundException
*/
private function checkValidationMessageFor($element, $message)
{
$foundedElement = $this->getValidatedField($this->getElement($element));
if (null === $foundedElement) {
throw new ElementNotFoundException($this->getSession(), 'Element', 'css', '#sylius_promotion_coupon_instruction_amount');
}

return $message === $foundedElement->find('css', '.pointing')->getText();
}

/**
* @param NodeElement $element
*
Expand Down
Expand Up @@ -25,6 +25,20 @@ interface GeneratePageInterface extends SymfonyPageInterface
*/
public function checkAmountValidation($message);

/**
* @param string $message
*
* @return bool
*/
public function checkCodeLengthValidation($message);

/**
* @param string $message
*
* @return bool
*/
public function checkGenerationValidation($message);

public function generate();

/**
Expand All @@ -33,12 +47,17 @@ public function generate();
public function specifyAmount($amount);

/**
* @param int $limit
* @param int $codeLength
*/
public function setUsageLimit($limit);
public function specifyCodeLength($codeLength);

/**
* @param \DateTime $date
*/
public function setExpiresAt(\DateTime $date);

/**
* @param int $limit
*/
public function setUsageLimit($limit);
}
Expand Up @@ -11,6 +11,7 @@
<div class="column">
<div class="ui segment">
<div class="one fields">
{{ form_row(form.codeLength) }}
{{ form_row(form.expiresAt) }}
</div>
</div>
Expand Down
Expand Up @@ -19,15 +19,30 @@
*/
class CouponRepository extends EntityRepository implements CouponRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function countCouponsByCodeLength($codeLength)
{
$queryBuilder = $this->createQueryBuilder('o');

$count = (int) $queryBuilder->select($queryBuilder->expr()->count('o'))
->where($queryBuilder->expr()->eq($queryBuilder->expr()->length('o.code'), $codeLength))
->getQuery()
->getSingleScalarResult()
;

return $count;
}

/**
* {@inheritdoc}
*/
public function createQueryBuilderWithPromotion($promotionId)
{
return
$this->createQueryBuilder('o')
->where('o.promotion = :promotionId')
->setParameter('promotionId', $promotionId)
;
return $this->createQueryBuilder('o')
->where('o.promotion = :promotionId')
->setParameter('promotionId', $promotionId)
;
}
}
Expand Up @@ -30,6 +30,9 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->add('amount', 'integer', [
'label' => 'sylius.form.coupon_generate_instruction.amount',
])
->add('codeLength', 'integer', [
'label' => 'sylius.form.coupon_generate_instruction.code_length',
])
->add('usageLimit', 'integer', [
'label' => 'sylius.form.coupon_generate_instruction.usage_limit',
])
Expand Down
10 changes: 10 additions & 0 deletions src/Sylius/Bundle/PromotionBundle/Resources/config/services.xml
Expand Up @@ -34,6 +34,7 @@

<parameter key="sylius.generator.instructions.class">Sylius\Component\Promotion\Generator\Instruction</parameter>
<parameter key="sylius.generator.promotion_coupon.class">Sylius\Component\Promotion\Generator\CouponGenerator</parameter>
<parameter key="sylius.generator.promotion_coupon.percentage_policy.class">Sylius\Component\Promotion\Generator\PercentageGenerationPolicy</parameter>

<parameter key="sylius.registry.promotion_rule_checker.class">Sylius\Component\Registry\ServiceRegistry</parameter>
<parameter key="sylius.registry.promotion_action.class">Sylius\Component\Registry\ServiceRegistry</parameter>
Expand All @@ -51,6 +52,7 @@
<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>
<parameter key="sylius.validator.promotion_coupon_generation_amount.class">Sylius\Bundle\PromotionBundle\Validator\CouponGenerationAmountValidator</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -145,11 +147,19 @@
<argument type="service" id="sylius.factory.promotion_coupon" />
<argument type="service" id="sylius.repository.promotion_coupon" />
<argument type="service" id="sylius.manager.promotion_coupon" />
<argument type="service" id="sylius.generator.promotion_coupon.percentage_policy" />
</service>
<service id="sylius.generator.promotion_coupon.percentage_policy" class="%sylius.generator.promotion_coupon.percentage_policy.class%">
<argument type="service" id="sylius.repository.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>
<service id="sylius.validator.promotion_coupon_generation_amount" class="%sylius.validator.promotion_coupon_generation_amount.class%">
<argument type="service" id="sylius.generator.promotion_coupon.percentage_policy" />
<tag name="validator.constraint_validator" alias="sylius_coupon_generation_amount_validator" />
</service>
</services>

</container>
12 changes: 12 additions & 0 deletions src/Sylius/Bundle/PromotionBundle/Resources/config/validation.xml
Expand Up @@ -94,6 +94,7 @@
</class>

<class name="Sylius\Component\Promotion\Generator\Instruction">
<constraint name="Sylius\Bundle\PromotionBundle\Validator\Constraints\CouponPossibleGenerationAmount" />
<property name="amount">
<constraint name="NotBlank">
<option name="message">sylius.promotion_coupon_generate_instruction.amount.not_blank</option>
Expand All @@ -109,6 +110,17 @@
<option name="minMessage">sylius.promotion_coupon_generate_instruction.usage_limit.min</option>
</constraint>
</property>
<property name="codeLength">
<constraint name="Range">
<option name="min">1</option>
<option name="minMessage">sylius.promotion_coupon_generate_instruction.code_length.min</option>
<option name="max">40</option>
<option name="maxMessage">sylius.promotion_coupon_generate_instruction.code_length.max</option>
</constraint>
<constraint name="NotBlank">
<option name="message">sylius.promotion_coupon_generate_instruction.code_length.not_blank</option>
</constraint>
</property>
</class>

</constraint-mapping>
Expand Up @@ -42,6 +42,7 @@ sylius:
amount: Amount
usage_limit: Usage limit
expires_at: Expires at
code_length: Code length
promotion:
actions: Actions
add_action: Add action
Expand Down

0 comments on commit bf2eb33

Please sign in to comment.