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 $bill; |
||||
31 | |||||
32 | protected $charges = []; |
||||
33 | |||||
34 | /** |
||||
35 | * @Given reseller :reseller |
||||
36 | */ |
||||
37 | public function reseller($reseller) |
||||
38 | { |
||||
39 | $this->builder->buildReseller($reseller); |
||||
40 | } |
||||
41 | |||||
42 | /** |
||||
43 | * @Given customer :customer |
||||
44 | */ |
||||
45 | public function customer($customer) |
||||
46 | { |
||||
47 | $this->builder->buildCustomer($customer); |
||||
48 | } |
||||
49 | |||||
50 | /** |
||||
51 | * @Given manager :manager |
||||
52 | */ |
||||
53 | public function manager($manager) |
||||
54 | { |
||||
55 | $this->builder->buildManager($manager); |
||||
56 | } |
||||
57 | |||||
58 | /** |
||||
59 | * @Given /^(\S+ )?(\S+) tariff plan (\S+)/ |
||||
60 | */ |
||||
61 | public function plan($prefix, $type, $plan) |
||||
62 | { |
||||
63 | $prefix = strtr($prefix, ' ', '_'); |
||||
64 | $grouping = $prefix === 'grouping_'; |
||||
65 | $type = $grouping ? $type : $prefix.$type; |
||||
66 | $this->builder->buildPlan($plan, $type, $grouping); |
||||
67 | } |
||||
68 | |||||
69 | protected function fullPrice(array $data) |
||||
70 | { |
||||
71 | if (!empty($data['price'])) { |
||||
72 | $data['rate'] = $data['price']; |
||||
73 | } |
||||
74 | $this->builder->buildPrice($data); |
||||
75 | } |
||||
76 | |||||
77 | /** |
||||
78 | * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) for target (.+)$/ |
||||
79 | */ |
||||
80 | public function priceWithTarget($type, $price, $currency, $unit, $target) |
||||
81 | { |
||||
82 | return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'target')); |
||||
83 | } |
||||
84 | |||||
85 | /** |
||||
86 | * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+)$/ |
||||
87 | */ |
||||
88 | public function priceWithPrepaid($type, $price, $currency, $unit, $prepaid) |
||||
89 | { |
||||
90 | return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid')); |
||||
91 | } |
||||
92 | |||||
93 | /** |
||||
94 | * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+) for target (\S+)$/ |
||||
95 | */ |
||||
96 | public function priceWithPrepaidAndTarget($type, $price, $currency, $unit, $prepaid, $target) |
||||
97 | { |
||||
98 | return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid', 'target')); |
||||
0 ignored issues
–
show
|
|||||
99 | } |
||||
100 | |||||
101 | /** |
||||
102 | * @Given /price for (\S+) is +(\S+) (\S+) per (\S+)$/ |
||||
103 | */ |
||||
104 | public function price($type, $price, $currency, $unit) |
||||
105 | { |
||||
106 | return $this->fullPrice(compact('type', 'price', 'currency', 'unit')); |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * @Given /price for (\S+) is +(\S+) (\S+) per 1 (\S+) and (\S+) (\S+) per 2 (\S+) for target (\S+)/ |
||||
111 | */ |
||||
112 | public function enumPrice($type, $price, $currency, $unit, $price2, $currency2, $unit2, $target) |
||||
113 | { |
||||
114 | $sums = [1 => $price, 2 => $price2]; |
||||
115 | |||||
116 | return $this->fullPrice(compact('type', 'sums', 'currency', 'unit', 'target')); |
||||
117 | } |
||||
118 | |||||
119 | /** |
||||
120 | * @Given /^remove and recreate tariff plan (\S+)/ |
||||
121 | */ |
||||
122 | public function recreatePlan($plan) |
||||
123 | { |
||||
124 | $this->builder->recreatePlan($plan); |
||||
125 | } |
||||
126 | |||||
127 | /** |
||||
128 | * @Given /sale target (\S+) by plan (\S+) at (\S+)/ |
||||
129 | */ |
||||
130 | public function sale($target, $plan, $time): void |
||||
131 | { |
||||
132 | $this->saleTime = $this->prepareTime($time); |
||||
133 | $this->builder->buildSale($target, $plan, $this->saleTime); |
||||
134 | } |
||||
135 | /** |
||||
136 | * @When /^sale close is requested for target "([^"]*)" at "([^"]*)", assuming current time is "([^"]*)"$/ |
||||
137 | */ |
||||
138 | public function saleClose(string $target, string $time, ?string $wallTime) |
||||
139 | { |
||||
140 | throw new PendingException(); |
||||
141 | } |
||||
142 | |||||
143 | /** |
||||
144 | * @Then /^target "([^"]*)" has exactly (\d+) sale for customer$/ |
||||
145 | */ |
||||
146 | public function targetHasExactlyNSaleForCustomer(string $target, string $count) |
||||
147 | { |
||||
148 | // TODO: implement |
||||
149 | // $sales = $this->builder->findSales(['target-name' => $target]); |
||||
150 | |||||
151 | Assert::assertCount($count, $sales); |
||||
152 | } |
||||
153 | |||||
154 | /** |
||||
155 | * @Given /purchase target (\S+) by plan (\S+) at (\S+)$/ |
||||
156 | */ |
||||
157 | public function purchaseTarget(string $target, string $plan, string $time): void |
||||
158 | { |
||||
159 | $time = $this->prepareTime($time); |
||||
160 | $this->builder->buildPurchase($target, $plan, $time); |
||||
161 | } |
||||
162 | |||||
163 | /** |
||||
164 | * @Given /^purchase target "([^"]*)" by plan "([^"]*)" at "([^"]*)" with the following initial uses:$/ |
||||
165 | */ |
||||
166 | public function purchaseTargetWithInitialUses(string $target, string $plan, string $time, TableNode $usesTable): void |
||||
167 | { |
||||
168 | $time = $this->prepareTime($time); |
||||
169 | $uses = array_map(static function (array $row) { |
||||
170 | return [ |
||||
171 | 'type' => $row['type'], |
||||
172 | 'unit' => $row['unit'], |
||||
173 | 'amount' => $row['amount'], |
||||
174 | ]; |
||||
175 | }, $usesTable->getColumnsHash()); |
||||
176 | |||||
177 | $this->mayFail( |
||||
178 | fn() => $this->builder->buildPurchase($target, $plan, $time, $uses) |
||||
179 | ); |
||||
180 | } |
||||
181 | |||||
182 | /** |
||||
183 | * @Given /resource consumption for (\S+) is +(\S+) (\S+) for target (\S+) at (.+)$/ |
||||
184 | */ |
||||
185 | public function setConsumption(string $type, int $amount, string $unit, string $target, string $time): void |
||||
186 | { |
||||
187 | $time = $this->prepareTime($time); |
||||
188 | $this->builder->setConsumption($type, $amount, $unit, $target, $time); |
||||
189 | } |
||||
190 | |||||
191 | /** |
||||
192 | * @Given /perform billing at (\S+)/ |
||||
193 | */ |
||||
194 | public function performBilling(string $time): void |
||||
195 | { |
||||
196 | $this->builder->performBilling($this->prepareTime($time)); |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * @Given /action for (\S+) is +(\S+) (\S+) +for target (.+?)( +at (\S+))?$/ |
||||
201 | */ |
||||
202 | public function setAction(string $type, int $amount, string $unit, string $target, string $at = null, string $time = null): void |
||||
203 | { |
||||
204 | $time = $this->prepareTime($time); |
||||
205 | $this->builder->setAction($type, $amount, $unit, $target, $time); |
||||
206 | } |
||||
207 | |||||
208 | /** |
||||
209 | * @Given /perform calculation( at (\S+))?/ |
||||
210 | */ |
||||
211 | public function performCalculation(string $at = null, string $time = null): array |
||||
0 ignored issues
–
show
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
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...
|
|||||
212 | { |
||||
213 | $this->charges = $this->builder->performCalculation($this->prepareTime($time)); |
||||
214 | return $this->charges; |
||||
215 | } |
||||
216 | |||||
217 | /** |
||||
218 | * @Given /bill +for (\S+) is +(\S+) (\S+) per (\S+) (\S+) for target (.+?)( +at (.+))?$/ |
||||
219 | */ |
||||
220 | public function billWithTime($type, $sum, $currency, $quantity, $unit, $target, $at = null, $time = null) |
||||
221 | { |
||||
222 | $this->builder->flushEntitiesCacheByType('bill'); |
||||
223 | |||||
224 | $quantity = $this->prepareQuantity($quantity); |
||||
225 | $sum = $this->prepareSum($sum, $quantity); |
||||
226 | $time = $this->prepareTime($time); |
||||
227 | $bill = $this->findBill([ |
||||
228 | 'type' => $type, |
||||
229 | 'target' => $target, |
||||
230 | 'sum' => "$sum $currency", |
||||
231 | 'quantity' => "$quantity $unit", |
||||
232 | 'time' => $time, |
||||
233 | ]); |
||||
234 | Assert::assertSame($type, $bill->getType()->getName()); |
||||
235 | Assert::assertSame($target, $bill->getTarget()->getFullName()); |
||||
236 | Assert::assertEquals(bcmul($sum, 100), $bill->getSum()->getAmount()); |
||||
237 | Assert::assertSame($currency, $bill->getSum()->getCurrency()->getCode()); |
||||
238 | Assert::assertEquals((float)$quantity, (float)$bill->getQuantity()->getQuantity()); |
||||
239 | Assert::assertEquals(strtolower($unit), strtolower($bill->getQuantity()->getUnit()->getName())); |
||||
240 | if ($time) { |
||||
241 | Assert::assertEquals(new DateTimeImmutable($time), $bill->getTime()); |
||||
242 | } |
||||
243 | } |
||||
244 | |||||
245 | public function findBill(array $params): BillInterface |
||||
246 | { |
||||
247 | $bills = $this->builder->findBills($params); |
||||
248 | $this->bill = reset($bills); |
||||
249 | $this->charges = $this->bill->getCharges(); |
||||
250 | |||||
251 | return $this->bill; |
||||
252 | } |
||||
253 | |||||
254 | /** |
||||
255 | * @Given /bills number is (\d+) for (\S+) for target (.+?)( +at (\S+))?$/ |
||||
256 | */ |
||||
257 | public function billsNumberWithTime($number, $type, $target, $at = null, $time = null) |
||||
258 | { |
||||
259 | $count = count($this->builder->findBills(array_filter([ |
||||
260 | 'type' => $type, |
||||
261 | 'target' => $target, |
||||
262 | 'time' => $this->prepareTime($time), |
||||
263 | ]))); |
||||
264 | |||||
265 | Assert::assertEquals($number, $count); |
||||
266 | } |
||||
267 | |||||
268 | /** |
||||
269 | * @Given /charges number is (\d+)/ |
||||
270 | */ |
||||
271 | public function chargesNumber($number) |
||||
272 | { |
||||
273 | Assert::assertEquals($number, count($this->charges)); |
||||
274 | } |
||||
275 | |||||
276 | /** |
||||
277 | * @Given /charge for (\S+) is +(\S+) (\S+) per (\S+) (\S+)$/ |
||||
278 | */ |
||||
279 | public function charge($type, $amount, $currency, $quantity, $unit) |
||||
280 | { |
||||
281 | $this->chargeWithTarget($type, $amount, $currency, $quantity, $unit, null); |
||||
282 | } |
||||
283 | |||||
284 | /** |
||||
285 | * @Given /charge for (\S+) is +(\S+) (\S+) per +(\S+) (\S+) +for target (.+?)( +at (\S+))?$/ |
||||
286 | */ |
||||
287 | public function chargeWithTarget($type, $amount, $currency, $quantity, $unit, $target, $at = null, $time = null) |
||||
288 | { |
||||
289 | $quantity = $this->prepareQuantity($quantity); |
||||
290 | $amount = $this->prepareSum($amount, $quantity); |
||||
291 | $charge = $this->findCharge($type, $target); |
||||
292 | Assert::assertNotNull($charge); |
||||
293 | Assert::assertSame($type, $charge->getType()->getName()); |
||||
294 | Assert::assertSame($target, $charge->getTarget()->getFullName()); |
||||
295 | Assert::assertEquals(bcmul($amount, 100), (int)$charge->getSum()->getAmount()); |
||||
296 | Assert::assertSame($currency, $charge->getSum()->getCurrency()->getCode()); |
||||
297 | Assert::assertEquals((float)$quantity, (float)$charge->getUsage()->getQuantity()); |
||||
298 | Assert::assertEquals(strtolower($unit), strtolower($charge->getUsage()->getUnit()->getName())); |
||||
299 | } |
||||
300 | |||||
301 | public function findCharge($type, $target): ?ChargeInterface |
||||
302 | { |
||||
303 | foreach ($this->charges as $charge) { |
||||
304 | if ($charge->getType()->getName() !== $type) { |
||||
305 | continue; |
||||
306 | } |
||||
307 | if ($charge->getTarget()->getFullName() !== $target) { |
||||
308 | continue; |
||||
309 | } |
||||
310 | |||||
311 | return $charge; |
||||
312 | } |
||||
313 | |||||
314 | return null; |
||||
315 | } |
||||
316 | |||||
317 | public function getNextCharge(): ChargeInterface |
||||
318 | { |
||||
319 | $charge = current($this->charges); |
||||
320 | next($this->charges); |
||||
321 | |||||
322 | return $charge; |
||||
323 | } |
||||
324 | |||||
325 | /** |
||||
326 | * @return string|false|null |
||||
327 | */ |
||||
328 | protected function prepareTime(string $time = null) |
||||
329 | { |
||||
330 | if ($time === null) { |
||||
331 | return null; |
||||
332 | } |
||||
333 | |||||
334 | if ($time === 'midnight second day of this month') { |
||||
335 | return date('Y-m-02'); |
||||
336 | } |
||||
337 | if (strncmp($time, 'pY', 1) === 0) { |
||||
338 | return date(substr($time, 1), strtotime('-1 year')); |
||||
339 | } |
||||
340 | if (strncmp($time, 'Y', 1) === 0) { |
||||
341 | return date($time); |
||||
342 | } |
||||
343 | |||||
344 | return $time; |
||||
345 | } |
||||
346 | |||||
347 | private function prepareQuantity($quantity) |
||||
348 | { |
||||
349 | if ($quantity[0] === 's') { |
||||
350 | return $this->getSaleQuantity(); |
||||
351 | } |
||||
352 | |||||
353 | return $quantity; |
||||
354 | } |
||||
355 | |||||
356 | private function prepareSum($sum, $quantity) |
||||
357 | { |
||||
358 | if ($sum[0] === 's') { |
||||
359 | $sum = round(substr($sum, 1) * $quantity*100)/100; |
||||
360 | } |
||||
361 | |||||
362 | return $sum; |
||||
363 | } |
||||
364 | |||||
365 | public function getSaleQuantity() |
||||
366 | { |
||||
367 | return $this->days2quantity(new DateTimeImmutable($this->saleTime)); |
||||
368 | } |
||||
369 | |||||
370 | private function days2quantity(DateTimeImmutable $from) |
||||
371 | { |
||||
372 | $till = new DateTimeImmutable('first day of next month midnight'); |
||||
373 | $diff = $from->diff($till); |
||||
374 | if ($diff->m) { |
||||
375 | return 1; |
||||
376 | } |
||||
377 | |||||
378 | return $diff->d/date('t'); |
||||
379 | } |
||||
380 | |||||
381 | /** |
||||
382 | * @When /^tariff plan change is requested for target "([^"]*)" to plan "([^"]*)" at "([^"]*)"$/ |
||||
383 | */ |
||||
384 | public function tariffPlanChangeIsRequestedForTarget(string $target, string $planName, string $date) |
||||
385 | { |
||||
386 | $this->mayFail(fn () => $this->builder->targetChangePlan($target, $planName, $this->prepareTime($date))); |
||||
387 | } |
||||
388 | |||||
389 | /** |
||||
390 | * @When /^tariff plan change is requested for target "([^"]*)" to plan "([^"]*)" at "([^"]*)", assuming current time is "([^"]*)"$/ |
||||
391 | */ |
||||
392 | public function tariffPlanChangeIsRequestedForTargetAtSpecificTime(string $target, string $planName, string $date, ?string $wallTime = null) |
||||
393 | { |
||||
394 | $this->mayFail(fn () => $this->builder->targetChangePlan($target, $planName, $this->prepareTime($date), $this->prepareTime($wallTime))); |
||||
395 | } |
||||
396 | |||||
397 | /** |
||||
398 | * @Then /^target "([^"]*)" is sold to customer by plan "([^"]*)" since "([^"]*)"(?: till "([^"]*)")?$/ |
||||
399 | */ |
||||
400 | public function targetIsSoldToCustomerByPlanSinceTill(string $target, string $planName, string $saleDate, ?string $saleCloseDate = null) |
||||
401 | { |
||||
402 | $sales = $this->builder->findHistoricalSales([ |
||||
403 | 'target' => $target, |
||||
404 | ]); |
||||
405 | |||||
406 | $saleDateTime = new DateTimeImmutable('@' . strtotime($saleDate)); |
||||
407 | $saleCloseDateTime = new DateTimeImmutable('@' . ($saleCloseDate ? strtotime($saleCloseDate) : time())); |
||||
408 | |||||
409 | foreach ($sales as $sale) { |
||||
410 | /** @noinspection PhpBooleanCanBeSimplifiedInspection */ |
||||
411 | $saleExists = true |
||||
412 | && str_contains($sale->getPlan()->getName(), $planName) |
||||
413 | && $sale->getTime()->format(DATE_ATOM) === $saleDateTime->format(DATE_ATOM) |
||||
414 | && ( |
||||
415 | ($saleCloseDate === null && $sale->getCloseTime() === null) |
||||
416 | || |
||||
417 | ($saleCloseDate !== null && $sale->getCloseTime()->format(DATE_ATOM) === $saleCloseDateTime->format(DATE_ATOM)) |
||||
418 | ); |
||||
419 | |||||
420 | if ($saleExists) { |
||||
421 | return; |
||||
422 | } |
||||
423 | } |
||||
424 | |||||
425 | Assert::fail('Requested sale does not exist'); |
||||
426 | } |
||||
427 | |||||
428 | /** |
||||
429 | * @Then /^target "([^"]*)" has exactly (\d+) sales for customer$/ |
||||
430 | */ |
||||
431 | public function targetHasExactlySalesForCustomer(string $target, int $count) |
||||
432 | { |
||||
433 | $sales = $this->builder->findHistoricalSales([ |
||||
434 | 'target' => $target, |
||||
435 | ]); |
||||
436 | |||||
437 | Assert::assertCount($count, $sales); |
||||
438 | } |
||||
439 | |||||
440 | /** |
||||
441 | * @Then /^caught error is "([^"]*)"$/ |
||||
442 | */ |
||||
443 | public function caughtErrorIs(string $errorMessage): void |
||||
444 | { |
||||
445 | $this->assertCaughtExceptionMatches(\Throwable::class, $errorMessage); |
||||
446 | } |
||||
447 | |||||
448 | /** |
||||
449 | * @Given /^target "([^"]*)"$/ |
||||
450 | */ |
||||
451 | public function target(string $target) |
||||
452 | { |
||||
453 | $this->builder->buildTarget($target); |
||||
454 | } |
||||
455 | |||||
456 | /** |
||||
457 | * @Then /^flush entities cache$/ |
||||
458 | */ |
||||
459 | public function flushEntitiesCache() |
||||
460 | { |
||||
461 | $this->builder->flushEntitiesCache(); |
||||
462 | } |
||||
463 | |||||
464 | /** |
||||
465 | * @Given /^target "([^"]*)" has the following uses:$/ |
||||
466 | */ |
||||
467 | public function targetHasTheFollowingUses(string $target, TableNode $usesTable) |
||||
468 | { |
||||
469 | foreach ($usesTable->getColumnsHash() as $row) { |
||||
470 | $uses = $this->builder->findUsage($row['time'], $target, $row['type']); |
||||
471 | Assert::assertCount(1, $uses); |
||||
472 | |||||
473 | $use = reset($uses); |
||||
474 | Assert::assertSame( |
||||
475 | $row['unit'], $use['unit'], |
||||
476 | sprintf('Exptected unit to be %s, got %s instead', $row['unit'], $use['unit']) |
||||
477 | ); |
||||
478 | Assert::assertEquals( |
||||
479 | $row['amount'], $use['total'], |
||||
480 | sprintf('Exptected total to be %s, got %s instead', $row['amount'], $use['total']) |
||||
481 | ); |
||||
482 | } |
||||
483 | } |
||||
484 | } |
||||
485 |
This check looks for function or method calls that always return null and whose return value is used.
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.