Skip to content

Commit e907d40

Browse files
authored
add stopsGrowing discount modifier (#121)
1 parent c77f7bb commit e907d40

4 files changed

Lines changed: 84 additions & 2 deletions

File tree

docs/formula-and-modifiers.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ discount.since('08.2018').grows('1%').every('month').min('10 USD')
7171
discount.since('08.2000').grows('30%').every('year').max('100%')
7272
discount.since('08.2018').grows('20 USD').every('2 months').min('15 USD').max('80 USD')
7373
discount.since('08.2018').till('12.2018').grows('10pp').every('month')
74+
discount.since('08.2018').grows('10 USD').every('month').stopsGrowing('10.2018')
7475
```
7576

76-
Supports absolute (`USD`), relative (`%`), and percentage point (`pp`) steps. `min`/`max` cap the accumulated discount.
77+
Supports absolute (`USD`), relative (`%`), and percentage point (`pp`) steps. `min`/`max` cap the accumulated discount. `stopsGrowing()` caps the growth calculation at the given month while the discount continues to apply.
7778

7879
### Increase
7980

@@ -159,6 +160,7 @@ Modifiers use a composable addon system for configuration:
159160
| `MonthPeriod` / `YearPeriod` / `DayPeriod` | Time periods for `every()` and `lasts()` |
160161
| `Discount` | Discount value (absolute, relative %, or percentage point) |
161162
| `Step` | Growth step for GrowingDiscount |
163+
| `StopsGrowing` | Last month when GrowingDiscount grows |
162164
| `Reason` | Human-readable comment attached to modifier charges |
163165
| `Minimum` / `Maximum` | Bounds for accumulated discount values |
164166

src/charge/modifiers/GrowingDiscount.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use hiqdev\php\billing\charge\modifiers\addons\Period;
2020
use hiqdev\php\billing\charge\modifiers\addons\Since;
2121
use hiqdev\php\billing\charge\modifiers\addons\Step;
22+
use hiqdev\php\billing\charge\modifiers\addons\StopsGrowing;
2223
use Money\Money;
2324

2425
/**
@@ -32,6 +33,7 @@ class GrowingDiscount extends FixedDiscount
3233
public const STEP = 'step';
3334
public const MIN = 'min';
3435
public const MAX = 'max';
36+
public const STOPS_GROWING = 'stopsGrowing';
3537

3638
public function __construct($step, $min = null, array $addons = [])
3739
{
@@ -90,6 +92,16 @@ public function every($string = 1)
9092
return $this->addAddon(self::PERIOD, Period::fromString($string));
9193
}
9294

95+
public function stopsGrowing($month)
96+
{
97+
return $this->addAddon(self::STOPS_GROWING, new StopsGrowing($month));
98+
}
99+
100+
public function getStopsGrowing(): ?StopsGrowing
101+
{
102+
return $this->getAddon(self::STOPS_GROWING);
103+
}
104+
93105
#[\Override]
94106
public function calculateSum(?ChargeInterface $charge = null): Money
95107
{
@@ -126,6 +138,15 @@ protected function countPeriodsPassed(DateTimeImmutable $time)
126138
throw new \Exception('no period given for growing discount');
127139
}
128140

129-
return $period->countPeriodsPassed($since->getValue(), $time);
141+
$sinceTime = $since->getValue();
142+
$stopsGrowing = $this->getStopsGrowing();
143+
if ($stopsGrowing instanceof StopsGrowing && $stopsGrowing->getValue() < $time) {
144+
$time = $stopsGrowing->getValue();
145+
}
146+
if ($time < $sinceTime) {
147+
$time = $sinceTime;
148+
}
149+
150+
return $period->countPeriodsPassed($sinceTime, $time);
130151
}
131152
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* PHP Billing Library
7+
*
8+
* @link https://github.com/hiqdev/php-billing
9+
* @package php-billing
10+
* @license BSD-3-Clause
11+
* @copyright Copyright (c) 2017-2020, HiQDev (http://hiqdev.com/)
12+
*/
13+
14+
namespace hiqdev\php\billing\charge\modifiers\addons;
15+
16+
use hiqdev\php\billing\charge\modifiers\AddonInterface;
17+
18+
/**
19+
* Stops growing addon.
20+
*
21+
* @author Andrii Vasyliev <sol@hiqdev.com>
22+
*/
23+
class StopsGrowing extends Date implements AddonInterface
24+
{
25+
}

tests/behat/GrowingDiscount.feature

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,40 @@ Feature: Growing discount
7373
| 2019-01-01 | monthly 100 USD | discount -80 USD |
7474
| 2028-11-01 | monthly 100 USD | discount -80 USD |
7575

76+
Scenario Outline: absolute discount stops growing but keeps applying
77+
Given formula is discount.since('08.2018').grows('20 USD').every('2 months').min('15 USD').max('80 USD').stopsGrowing('10.2018')
78+
When action date is <date>
79+
Then first charge is <first>
80+
And second charge is <second>
81+
Examples:
82+
| date | first | second |
83+
| 2018-07-31 | monthly 100 USD | |
84+
| 2018-08-01 | monthly 100 USD | discount -30 USD |
85+
| 2018-09-01 | monthly 100 USD | discount -30 USD |
86+
| 2018-10-01 | monthly 100 USD | discount -70 USD |
87+
| 2018-11-01 | monthly 100 USD | discount -70 USD |
88+
| 2018-12-01 | monthly 100 USD | discount -70 USD |
89+
| 2019-01-01 | monthly 100 USD | discount -70 USD |
90+
| 2028-11-01 | monthly 100 USD | discount -70 USD |
91+
92+
Scenario Outline: relative discount stops growing but keeps applying
93+
Given formula is discount.since('08.2000').grows('30%').every('year').max('100%').stopsGrowing('08.2003')
94+
When action date is <date>
95+
Then first charge is <first>
96+
And second charge is <second>
97+
Examples:
98+
| date | first | second |
99+
| 2000-07-31 | monthly 100 USD | |
100+
| 2000-08-01 | monthly 100 USD | discount -30.00 USD |
101+
| 2001-08-01 | monthly 100 USD | discount -51.00 USD |
102+
| 2002-08-01 | monthly 100 USD | discount -65.70 USD |
103+
| 2003-01-01 | monthly 100 USD | discount -65.70 USD |
104+
| 2003-08-01 | monthly 100 USD | discount -75.99 USD |
105+
| 2004-08-01 | monthly 100 USD | discount -75.99 USD |
106+
| 2005-08-01 | monthly 100 USD | discount -75.99 USD |
107+
| 2011-08-01 | monthly 100 USD | discount -75.99 USD |
108+
| 2024-08-01 | monthly 100 USD | discount -75.99 USD |
109+
76110
Scenario Outline: discount maximum may not match step: relative step but absolute max
77111
Given formula is discount.since('08.2018').grows('20pp').every('month').min('30%').max('77 USD')
78112
When action date is <date>

0 commit comments

Comments
 (0)