Passed
Pull Request — master (#73)
by
unknown
16:36 queued 01:36
created

BillingContext::chargeWithTarget()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
cc 1
eloc 10
c 5
b 1
f 0
nc 1
nop 8
dl 0
loc 12
rs 9.9332

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * PHP Billing Library
4
 *
5
 * @link      https://github.com/hiqdev/php-billing
6
 * @package   php-billing
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2020, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\tests\behat\bootstrap;
12
13
use Behat\Gherkin\Node\TableNode;
14
use BehatExpectException\ExpectException;
15
use DateTimeImmutable;
16
use hiqdev\php\billing\bill\BillInterface;
17
use hiqdev\php\billing\charge\ChargeInterface;
18
use PHPUnit\Framework\Assert;
19
20
class BillingContext extends BaseContext
21
{
22
    use ExpectException {
23
        mayFail as protected;
24
        shouldFail as protected;
25
        assertCaughtExceptionMatches as protected;
26
    }
27
28
    protected $saleTime;
29
30
    protected $saleCloseTime;
31
32
    protected $bill;
33
34
    protected $charges = [];
35
36
    protected array $progressivePrice = [];
37
38
    /**
39
     * @Given reseller :reseller
40
     */
41
    public function reseller($reseller)
42
    {
43
        $this->builder->buildReseller($reseller);
44
    }
45
46
    /**
47
     * @Given customer :customer
48
     */
49
    public function customer($customer)
50
    {
51
        $this->builder->buildCustomer($customer);
52
    }
53
54
    /**
55
     * @Given manager :manager
56
     */
57
    public function manager($manager)
58
    {
59
        $this->builder->buildManager($manager);
0 ignored issues
show
Bug introduced by
The method buildManager() does not exist on hiqdev\php\billing\tests...tstrap\BuilderInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

59
        $this->builder->/** @scrutinizer ignore-call */ 
60
                        buildManager($manager);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
60
    }
61
62
    /**
63
     * @Given /^(\S+ )?(\S+) tariff plan (\S+)/
64
     */
65
    public function plan($prefix, $type, $plan)
66
    {
67
        $prefix = strtr($prefix, ' ', '_');
68
        $grouping = $prefix === 'grouping_';
69
        $type = $grouping ? $type : $prefix.$type;
70
        $this->builder->buildPlan($plan, $type, $grouping);
71
    }
72
73
    protected function fullPrice(array $data)
74
    {
75
        if (!empty($data['price'])) {
76
            $data['rate'] = $data['price'];
77
        }
78
        $this->builder->buildPrice($data);
79
    }
80
81
    /**
82
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) for target (.+)$/
83
     */
84
    public function priceWithTarget($type, $price, $currency, $unit, $target)
85
    {
86
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'target'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...cy', 'unit', 'target')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
87
    }
88
89
    /**
90
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+)$/
91
     */
92
    public function priceWithPrepaid($type, $price, $currency, $unit, $prepaid)
93
    {
94
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...y', 'unit', 'prepaid')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
95
    }
96
97
    /**
98
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+) for target (\S+)$/
99
     */
100
    public function priceWithPrepaidAndTarget($type, $price, $currency, $unit, $prepaid, $target)
101
    {
102
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid', 'target'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact..., 'prepaid', 'target')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
103
    }
104
105
    /**
106
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+)$/
107
     */
108
    public function price($type, $price, $currency, $unit)
109
    {
110
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...', 'currency', 'unit')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
111
    }
112
113
    /**
114
     * @Given /price for (\S+) is +(\S+) (\S+) per 1 (\S+) and (\S+) (\S+) per 2 (\S+) for target (\S+)/
115
     */
116
    public function enumPrice($type, $price, $currency, $unit, $price2, $currency2, $unit2, $target)
0 ignored issues
show
Unused Code introduced by
The parameter $unit2 is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

116
    public function enumPrice($type, $price, $currency, $unit, $price2, $currency2, /** @scrutinizer ignore-unused */ $unit2, $target)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $currency2 is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

116
    public function enumPrice($type, $price, $currency, $unit, $price2, /** @scrutinizer ignore-unused */ $currency2, $unit2, $target)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
117
    {
118
        $sums = [1 => $price, 2 => $price2];
119
120
        return $this->fullPrice(compact('type', 'sums', 'currency', 'unit', 'target'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...cy', 'unit', 'target')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
121
    }
122
123
    /**
124
     * @Given /progressive price for (\S+) is +(\S+) (\S+) per (\S+) (\S+) (\S+) (\S+)$/
125
     */
126
    public function progressivePrice($type, $price, $currency, $unit, $sign, $quantity, $perUnit): void
0 ignored issues
show
Unused Code introduced by
The parameter $perUnit is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

126
    public function progressivePrice($type, $price, $currency, $unit, $sign, $quantity, /** @scrutinizer ignore-unused */ $perUnit): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $sign is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

126
    public function progressivePrice($type, $price, $currency, $unit, /** @scrutinizer ignore-unused */ $sign, $quantity, $perUnit): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
127
    {
128
        if (empty($this->progressivePrice[$type])) {
129
            $this->progressivePrice[$type] = [
130
                'price' => 0,
131
                'currency' => $currency,
132
                'unit' => $unit,
133
                'thresholds' =>[
134
                    [
135
                        'price' => $price,
136
                        'currency' => $currency,
137
                        'quantity' => $quantity,
138
                        'unit' => $unit,
139
                    ],
140
                ] ,
141
            ];
142
        } else {
143
            array_push(
144
                $this->progressivePrice[$type]['thresholds'],
145
                [
146
                    'price' => $price,
147
                    'currency' => $currency,
148
                    'quantity' => $quantity,
149
                    'unit' => $unit,
150
                ]
151
            );
152
        }
153
    }
154
155
    /**
156
     * @Given /^remove and recreate tariff plan (\S+)/
157
     */
158
    public function recreatePlan($plan)
159
    {
160
        $this->builder->recreatePlan($plan);
161
    }
162
163
    /**
164
     * @Given /sale target (\S+) by plan (\S+) at (\S+)/
165
     */
166
    public function sale($target, $plan, $time): void
167
    {
168
        $this->saleTime = $this->prepareTime($time);
169
        $this->sale = $this->builder->buildSale($target, $plan, $this->saleTime);
0 ignored issues
show
Bug introduced by
It seems like $this->saleTime can also be of type null; however, parameter $time of hiqdev\php\billing\tests...rInterface::buildSale() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

169
        $this->sale = $this->builder->buildSale($target, $plan, /** @scrutinizer ignore-type */ $this->saleTime);
Loading history...
Bug Best Practice introduced by
The property sale does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
170
    }
171
172
    /**
173
     * @Given /sale target (\S+) by plan (\S+) at (\S+) and close at (\S+)/
174
     */
175
    public function saleWithCloseTime($target, $plan, $time, $closeTime): void
176
    {
177
        $this->saleTime = $this->prepareTime($time);
178
        $this->saleCloseTime = $this->prepareTime($closeTime);
179
        $this->builder->buildSale($target, $plan, $this->saleTime, $this->saleCloseTime);
0 ignored issues
show
Bug introduced by
It seems like $this->saleTime can also be of type null; however, parameter $time of hiqdev\php\billing\tests...rInterface::buildSale() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

179
        $this->builder->buildSale($target, $plan, /** @scrutinizer ignore-type */ $this->saleTime, $this->saleCloseTime);
Loading history...
180
    }
181
182
    /**
183
     * @When /^sale close is requested for target "([^"]*)" at "([^"]*)", assuming current time is "([^"]*)"$/
184
     */
185
    public function saleClose(string $target, string $time, ?string $wallTime)
0 ignored issues
show
Unused Code introduced by
The parameter $wallTime is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

185
    public function saleClose(string $target, string $time, /** @scrutinizer ignore-unused */ ?string $wallTime)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $target is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

185
    public function saleClose(/** @scrutinizer ignore-unused */ string $target, string $time, ?string $wallTime)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $time is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

185
    public function saleClose(string $target, /** @scrutinizer ignore-unused */ string $time, ?string $wallTime)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
186
    {
187
        throw new PendingException();
0 ignored issues
show
Bug introduced by
The type hiqdev\php\billing\tests...tstrap\PendingException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
188
    }
189
190
    /**
191
     * @Then /^target "([^"]*)" has exactly (\d+) sale for customer$/
192
     */
193
    public function targetHasExactlyNSaleForCustomer(string $target, string $count)
0 ignored issues
show
Unused Code introduced by
The parameter $target is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

193
    public function targetHasExactlyNSaleForCustomer(/** @scrutinizer ignore-unused */ string $target, string $count)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
194
    {
195
        // TODO: implement
196
        // $sales = $this->builder->findSales(['target-name' => $target]);
197
198
        Assert::assertCount($count, $sales);
0 ignored issues
show
Bug introduced by
$count of type string is incompatible with the type integer expected by parameter $expectedCount of PHPUnit\Framework\Assert::assertCount(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

198
        Assert::assertCount(/** @scrutinizer ignore-type */ $count, $sales);
Loading history...
Comprehensibility Best Practice introduced by
The variable $sales seems to be never defined.
Loading history...
199
    }
200
201
    /**
202
     * @Given /purchase target (\S+) by plan (\S+) at ([-:\w\s]+)$/
203
     */
204
    public function purchaseTarget(string $target, string $plan, string $time): void
205
    {
206
        $time = $this->prepareTime($time);
207
        $this->builder->buildPurchase($target, $plan, $time);
208
    }
209
210
    /**
211
     * @Given /^purchase target "([^"]*)" by plan "([^"]*)" at "([^"]*)" with the following initial uses:$/
212
     */
213
    public function purchaseTargetWithInitialUses(string $target, string $plan, string $time, TableNode $usesTable): void
214
    {
215
        $time = $this->prepareTime($time);
216
        $uses = array_map(static function (array $row) {
217
            return [
218
                'type' => $row['type'],
219
                'unit' => $row['unit'],
220
                'amount' => $row['amount'],
221
            ];
222
        }, $usesTable->getColumnsHash());
223
224
        $this->mayFail(
225
            fn() => $this->builder->buildPurchase($target, $plan, $time, $uses)
226
        );
227
    }
228
229
    /**
230
     * @Given /resource consumption for (\S+) is +(\S+) (\S+) for target (\S+) at (.+)$/
231
     */
232
    public function setConsumption(string $type, int $amount, string $unit, string $target, string $time): void
233
    {
234
        $time = $this->prepareTime($time);
235
        $this->builder->setConsumption($type, $amount, $unit, $target, $time);
0 ignored issues
show
Bug introduced by
The method setConsumption() does not exist on hiqdev\php\billing\tests...tstrap\BuilderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to hiqdev\php\billing\tests...tstrap\BuilderInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

235
        $this->builder->/** @scrutinizer ignore-call */ 
236
                        setConsumption($type, $amount, $unit, $target, $time);
Loading history...
236
    }
237
238
    /**
239
     * @Given /recalculate autotariff for target (\S+)( +at (\S+))?$/
240
     */
241
    public function recalculateAutoTariff(string $target, string $time = null): void
242
    {
243
        $this->builder->clientSetAutoTariff($target, $time);
0 ignored issues
show
Bug introduced by
The method clientSetAutoTariff() does not exist on hiqdev\php\billing\tests...tstrap\BuilderInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

243
        $this->builder->/** @scrutinizer ignore-call */ 
244
                        clientSetAutoTariff($target, $time);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
244
    }
245
246
    /**
247
     * @Given /perform billing at (\S+)/
248
     */
249
    public function performBilling(string $time): void
250
    {
251
        $this->builder->performBilling($this->prepareTime($time));
252
    }
253
254
    /**
255
     * @Given /action for (\S+) is +(\S+) (\S+) +for target (.+?)( +at (\S+))?$/
256
     */
257
    public function setAction(string $type, int $amount, string $unit, string $target, string $at = null, string $time = null): void
0 ignored issues
show
Unused Code introduced by
The parameter $at is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

257
    public function setAction(string $type, int $amount, string $unit, string $target, /** @scrutinizer ignore-unused */ string $at = null, string $time = null): void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
258
    {
259
        $time = $this->prepareTime($time);
260
        $this->builder->setAction($type, $amount, $unit, $target, $time);
0 ignored issues
show
Bug introduced by
It seems like $time can also be of type null; however, parameter $time of hiqdev\php\billing\tests...rInterface::setAction() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

260
        $this->builder->setAction($type, $amount, $unit, $target, /** @scrutinizer ignore-type */ $time);
Loading history...
261
    }
262
263
    /**
264
     * @Given /perform calculation( at (\S+))?/
265
     */
266
    public function performCalculation(string $at = null, string $time = null): array
0 ignored issues
show
Unused Code introduced by
The parameter $at is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

266
    public function performCalculation(/** @scrutinizer ignore-unused */ string $at = null, string $time = null): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
267
    {
268
        $this->charges = $this->builder->performCalculation($this->prepareTime($time));
0 ignored issues
show
Bug introduced by
It seems like $this->prepareTime($time) can also be of type null; however, parameter $time of hiqdev\php\billing\tests...e::performCalculation() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

268
        $this->charges = $this->builder->performCalculation(/** @scrutinizer ignore-type */ $this->prepareTime($time));
Loading history...
269
        return $this->charges;
270
    }
271
272
    /**
273
     * @Given /bill +for (\S+) is +(\S+) (\S+) per (\S+) (\S+) for target (.+?)( +at (.+))?$/
274
     */
275
    public function billWithTime($type, $sum, $currency, $quantity, $unit, $target, $at = null, $time = null)
0 ignored issues
show
Unused Code introduced by
The parameter $at is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

275
    public function billWithTime($type, $sum, $currency, $quantity, $unit, $target, /** @scrutinizer ignore-unused */ $at = null, $time = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
276
    {
277
        $this->builder->flushEntitiesCacheByType('bill');
278
279
        $quantity = $this->prepareQuantity($quantity);
280
        $sum = $this->prepareSum($sum, $quantity);
281
        $time = $this->prepareTime($time);
282
        $bill = $this->findBill([
283
            'type' => $type,
284
            'target' => $target,
285
            'sum' => "$sum $currency",
286
            'quantity' => "$quantity $unit",
287
            'time' => $time,
288
        ]);
289
        Assert::assertSame($type, $bill->getType()->getName(), "Bill type mismatch: expected $type, got {$bill->getType()->getName()}");
290
        Assert::assertSame($target, $bill->getTarget()->getFullName(), "Bill target mismatch: expected $target, got {$bill->getTarget()->getFullName()}");
291
        Assert::assertEquals(bcmul($sum, 100), $bill->getSum()->getAmount(), "Bill sum mismatch: expected $sum, got {$bill->getSum()->getAmount()}");
292
        Assert::assertSame($currency, $bill->getSum()->getCurrency()->getCode(), "Bill currency mismatch: expected $currency, got {$bill->getSum()->getCurrency()->getCode()}");
293
        Assert::assertEquals((float)$quantity, (float)$bill->getQuantity()->getQuantity(), "Bill quantity mismatch: expected $quantity, got {$bill->getQuantity()->getQuantity()}");
294
        Assert::assertEquals(strtolower($unit), strtolower($bill->getQuantity()->getUnit()->getName()), "Bill unit mismatch: expected $unit, got {$bill->getQuantity()->getUnit()->getName()}");
295
        if ($time) {
296
            Assert::assertEquals(new DateTimeImmutable($time), $bill->getTime(), "Bill time mismatch: expected $time, got {$bill->getTime()->format(DATE_ATOM)}");
297
        }
298
    }
299
300
    public function findBill(array $params): BillInterface
301
    {
302
        $bills = $this->builder->findBills($params);
303
        $this->bill = reset($bills);
304
        $this->charges = $this->bill->getCharges();
305
306
        return $this->bill;
307
    }
308
309
    /**
310
     * @Given /bills number is (\d+) for (\S+) for target (.+?)( +at (\S+))?$/
311
     */
312
    public function billsNumberWithTime($number, $type, $target, $at = null, $time = null)
0 ignored issues
show
Unused Code introduced by
The parameter $at is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

312
    public function billsNumberWithTime($number, $type, $target, /** @scrutinizer ignore-unused */ $at = null, $time = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
313
    {
314
        $count = count($this->builder->findBills(array_filter([
315
            'type' => $type,
316
            'target' => $target,
317
            'time' => $this->prepareTime($time),
318
        ])));
319
320
        Assert::assertEquals($number, $count);
321
    }
322
323
    /**
324
     * @Given /charges number is (\d+)/
325
     */
326
    public function chargesNumber($number)
327
    {
328
        Assert::assertEquals($number, count($this->charges));
329
    }
330
331
    /**
332
     * @Given /charge for (\S+) is +(\S+) (\S+) per (\S+) (\S+)$/
333
     */
334
    public function charge($type, $amount, $currency, $quantity, $unit)
335
    {
336
        $this->chargeWithTarget($type, $amount, $currency, $quantity, $unit, null);
337
    }
338
339
    /**
340
     * @Given /charge for (\S+) is +(\S+) (\S+) per +(\S+) (\S+) +for target (.+?)( +at (\S+))?$/
341
     */
342
    public function chargeWithTarget($type, $amount, $currency, $quantity, $unit, $target, $at = null, $time = null)
0 ignored issues
show
Unused Code introduced by
The parameter $at is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

342
    public function chargeWithTarget($type, $amount, $currency, $quantity, $unit, $target, /** @scrutinizer ignore-unused */ $at = null, $time = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $time is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

342
    public function chargeWithTarget($type, $amount, $currency, $quantity, $unit, $target, $at = null, /** @scrutinizer ignore-unused */ $time = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
343
    {
344
        $quantity = $this->prepareQuantity($quantity);
345
        $amount = $this->prepareSum($amount, $quantity);
346
        $charge = $this->findCharge($type, $target);
347
        Assert::assertNotNull($charge);
348
        Assert::assertSame($type, $charge->getType()->getName());
349
        Assert::assertSame($target, $charge->getTarget()->getFullName());
350
        Assert::assertEquals(bcmul($amount, 100), (int)$charge->getSum()->getAmount());
351
        Assert::assertSame($currency, $charge->getSum()->getCurrency()->getCode());
352
        Assert::assertEquals((float)$quantity, (float)$charge->getUsage()->getQuantity());
353
        Assert::assertEquals(strtolower($unit), strtolower($charge->getUsage()->getUnit()->getName()));
354
    }
355
356
    public function findCharge($type, $target): ?ChargeInterface
357
    {
358
        foreach ($this->charges as $charge) {
359
            if ($charge->getType()->getName() !== $type) {
360
                continue;
361
            }
362
            if ($charge->getTarget()->getFullName() !== $target) {
363
                continue;
364
            }
365
366
            return $charge;
367
        }
368
369
        return null;
370
    }
371
372
    public function getNextCharge(): ChargeInterface
373
    {
374
        $charge = current($this->charges);
375
        next($this->charges);
376
377
        return $charge;
378
    }
379
380
    /**
381
     * @return string|false|null
382
     */
383
    protected function prepareTime(string $time = null)
384
    {
385
        if ($time === null) {
386
            return null;
387
        }
388
389
        if ($time === 'midnight second day of this month') {
390
            return date('Y-m-02');
391
        }
392
        if (strncmp($time, 'pY', 1) === 0) {
393
            return date(substr($time, 1), strtotime('-1 year'));
394
        }
395
        if (str_contains($time, 'nm')) {
396
            $format = str_replace('nm', 'm', $time);
397
            return date($format, strtotime('next month'));
398
        }
399
        if (str_contains($time, 'pm')) {
400
            $time = str_replace('pm', 'm', $time);
401
            $time = date($time, strtotime('-1 month'));
402
        }
403
        if (strncmp($time, 'Y', 1) === 0) {
404
            return date($time);
405
        }
406
407
        return $time;
408
    }
409
410
    private function prepareQuantity($quantity)
411
    {
412
        if ($quantity[0] === 's') {
413
            return $this->getSaleQuantity();
414
        }
415
416
        return $quantity;
417
    }
418
419
    private function prepareSum($sum, $quantity)
420
    {
421
        if ($sum[0] === 's') {
422
            $sum = round(substr($sum, 1) * $quantity*100)/100;
423
        }
424
425
        return $sum;
426
    }
427
428
    public function getSaleQuantity()
429
    {
430
        return $this->days2quantity(new DateTimeImmutable($this->saleTime));
431
    }
432
433
    private function days2quantity(DateTimeImmutable $from)
434
    {
435
        $till = new DateTimeImmutable('first day of next month midnight');
436
        $diff = $from->diff($till);
437
        if ($diff->m) {
438
            return 1;
439
        }
440
441
        return $diff->d/date('t');
442
    }
443
444
    /**
445
     * @When /^tariff plan change is requested for target "([^"]*)" to plan "([^"]*)" at "([^"]*)"$/
446
     */
447
    public function tariffPlanChangeIsRequestedForTarget(string $target, string $planName, string $date)
448
    {
449
        $this->mayFail(fn () => $this->builder->targetChangePlan($target, $planName, $this->prepareTime($date)));
450
    }
451
452
    /**
453
     * @When /^tariff plan change is requested for target "([^"]*)" to plan "([^"]*)" at "([^"]*)", assuming current time is "([^"]*)"$/
454
     */
455
    public function tariffPlanChangeIsRequestedForTargetAtSpecificTime(string $target, string $planName, string $date, ?string $wallTime = null)
456
    {
457
        $this->mayFail(fn () => $this->builder->targetChangePlan($target, $planName, $this->prepareTime($date), $this->prepareTime($wallTime)));
458
    }
459
460
    /**
461
     * @Then /^target "([^"]*)" is sold to customer by plan "([^"]*)" since "([^"]*)"(?: till "([^"]*)")?$/
462
     */
463
    public function targetIsSoldToCustomerByPlanSinceTill(string $target, string $planName, string $saleDate, ?string $saleCloseDate = null)
464
    {
465
        $sales = $this->builder->findHistoricalSales([
466
            'target' => $target,
467
        ]);
468
469
        $saleDateTime = new DateTimeImmutable('@' . strtotime($this->prepareTime($saleDate)));
470
        $saleCloseDateTime = $saleCloseDate ? new DateTimeImmutable('@' . strtotime($this->prepareTime($saleCloseDate))) : null;
471
472
        foreach ($sales as $sale) {
473
            /** @noinspection PhpBooleanCanBeSimplifiedInspection */
474
            $saleExists = true
475
                && str_contains($sale->getPlan()->getName(), $planName)
476
                && $sale->getTime()->format(DATE_ATOM) === $saleDateTime->format(DATE_ATOM)
477
                && (
478
                    ($saleCloseDate === null && $sale->getCloseTime() === null)
479
                    ||
480
                    ($saleCloseDate !== null && $sale->getCloseTime()->format(DATE_ATOM) === $saleCloseDateTime->format(DATE_ATOM))
481
                );
482
483
            if ($saleExists) {
484
                return;
485
            }
486
        }
487
488
        Assert::fail('Requested sale does not exist');
489
    }
490
491
    /**
492
     * @Then /^target "([^"]*)" has exactly (\d+) sales for customer$/
493
     */
494
    public function targetHasExactlySalesForCustomer(string $target, int $count)
495
    {
496
        $sales = $this->builder->findHistoricalSales([
497
            'target' => $target,
498
        ]);
499
500
        Assert::assertCount($count, $sales);
501
    }
502
503
    /**
504
     * @Then /^caught error is "([^"]*)"$/
505
     */
506
    public function caughtErrorIs(string $errorMessage): void
507
    {
508
        $this->assertCaughtExceptionMatches(\Throwable::class, $errorMessage);
509
    }
510
511
    /**
512
     * @Given /^target "([^"]*)"$/
513
     */
514
    public function target(string $target)
515
    {
516
        $this->builder->buildTarget($target);
517
    }
518
519
    /**
520
     * @Then /^flush entities cache$/
521
     */
522
    public function flushEntitiesCache()
523
    {
524
        $this->builder->flushEntitiesCache();
525
    }
526
527
    /**
528
     * @Given /^target "([^"]*)" has the following uses:$/
529
     */
530
    public function targetHasTheFollowingUses(string $target, TableNode $usesTable)
531
    {
532
        foreach ($usesTable->getColumnsHash() as $row) {
533
            $uses = $this->builder->findUsage($row['time'], $target, $row['type']);
534
            Assert::assertCount(1, $uses);
535
536
            $use = reset($uses);
537
            Assert::assertSame(
538
                $row['unit'], $use['unit'],
539
                sprintf('Exptected unit to be %s, got %s instead', $row['unit'], $use['unit'])
540
            );
541
            Assert::assertEquals(
542
                $row['amount'], $use['total'],
543
                sprintf('Exptected total to be %s, got %s instead', $row['amount'], $use['total'])
544
            );
545
        }
546
    }
547
}
548