1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace hiqdev\php\billing\tests\behat\bootstrap; |
4
|
|
|
|
5
|
|
|
use DateTimeImmutable; |
6
|
|
|
use hiqdev\php\billing\bill\BillInterface; |
7
|
|
|
use hiqdev\php\billing\charge\ChargeInterface; |
8
|
|
|
use hiqdev\php\units\Unit; |
9
|
|
|
use PHPUnit\Framework\Assert; |
10
|
|
|
|
11
|
|
|
class BillingContext extends BaseContext |
12
|
|
|
{ |
13
|
|
|
protected $saleTime; |
14
|
|
|
|
15
|
|
|
protected $bill; |
16
|
|
|
|
17
|
|
|
protected array $charges = []; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @Given reseller :reseller |
21
|
|
|
*/ |
22
|
|
|
public function reseller($reseller) |
23
|
|
|
{ |
24
|
|
|
$this->builder->buildReseller($reseller); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @Given customer :customer |
29
|
|
|
*/ |
30
|
|
|
public function customer($customer) |
31
|
|
|
{ |
32
|
|
|
$this->builder->buildCustomer($customer); |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @Given manager :manager |
37
|
|
|
*/ |
38
|
|
|
public function manager($manager) |
39
|
|
|
{ |
40
|
|
|
$this->builder->buildManager($manager); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @Given /^(grouping )?(\S+) tariff plan (\S+)/ |
45
|
|
|
*/ |
46
|
|
|
public function plan($grouping, $type, $plan) |
47
|
|
|
{ |
48
|
|
|
$this->builder->buildPlan($plan, $type, !empty($grouping)); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
protected function fullPrice(array $data) |
52
|
|
|
{ |
53
|
|
|
$this->builder->buildPrice($data); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @Given /price for (\S+) is +(\S+) (\S+) per (\S+) for target (\S+)/ |
58
|
|
|
*/ |
59
|
|
|
public function priceWithTarget($type, $price, $currency, $unit, $target) |
60
|
|
|
{ |
61
|
|
|
return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'target')); |
|
|
|
|
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+)$/ |
66
|
|
|
*/ |
67
|
|
|
public function priceWithPrepaid($type, $price, $currency, $unit, $prepaid) |
68
|
|
|
{ |
69
|
|
|
$prepaid = "$prepaid $unit"; |
70
|
|
|
return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid')); |
|
|
|
|
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+) for target (\S+)$/ |
75
|
|
|
*/ |
76
|
|
|
public function priceWithPrepaidAndTarget($type, $price, $currency, $unit, $prepaid, $target) |
77
|
|
|
{ |
78
|
|
|
$prepaid = "$prepaid $unit"; |
79
|
|
|
return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid', 'target')); |
|
|
|
|
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @Given /price for (\S+) is +(\S+) (\S+) per (\S+)$/ |
84
|
|
|
*/ |
85
|
|
|
public function price($type, $price, $currency, $unit) |
86
|
|
|
{ |
87
|
|
|
return $this->fullPrice(compact('type', 'price', 'currency', 'unit')); |
|
|
|
|
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @Given /price for (\S+) is +(\S+) (\S+) per 1 (\S+) and (\S+) (\S+) per 2 (\S+) for target (\S+)/ |
92
|
|
|
*/ |
93
|
|
|
public function enumPrice($type, $price, $currency, $unit, $price2, $currency2, $unit2, $target) |
|
|
|
|
94
|
|
|
{ |
95
|
|
|
$sums = [1 => $price, 2 => $price2]; |
96
|
|
|
return $this->fullPrice(compact('type', 'sums', 'currency', 'unit', 'target')); |
|
|
|
|
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @Given /^remove and recreate tariff plan (\S+)/ |
101
|
|
|
*/ |
102
|
|
|
public function recreatePlan($plan) |
103
|
|
|
{ |
104
|
|
|
$this->builder->recreatePlan($plan); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @Given /sale (\S+) for (\S+) plan:(\S+) time:(\S+)/ |
109
|
|
|
*/ |
110
|
|
|
public function sale($id, $target, $plan, $time): void |
111
|
|
|
{ |
112
|
|
|
$this->saleTime = $this->prepareTime($time); |
113
|
|
|
$this->builder->buildSale($id, $target, $plan, $this->saleTime); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @Given /purchase target (\S+) by plan (\S+) at (.+)$/ |
118
|
|
|
*/ |
119
|
|
|
public function purchaseTarget(string $target, string $plan, string $time): void |
120
|
|
|
{ |
121
|
|
|
$time = $this->prepareTime($time); |
122
|
|
|
$this->builder->buildPurchase($target, $plan, $time); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @Given /resource consumption for (\S+) is (\d+) (\S+) for target (\S+) at (.+)$/ |
127
|
|
|
*/ |
128
|
|
|
public function setConsumption(string $type, int $amount, string $unit, string $target, string $time): void |
129
|
|
|
{ |
130
|
|
|
$time = $this->prepareTime($time); |
131
|
|
|
$this->builder->setConsumption($type, $amount, $unit, $target, $time); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* @Given /perform billing for time (\S+) for sales/ |
136
|
|
|
*/ |
137
|
|
|
public function performBilling(string $time): void |
138
|
|
|
{ |
139
|
|
|
$this->builder->performBilling($time); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @Given /bill +for (\S+) is +(\S+) (\S+) per (\S+) (\S+) for target (\S+)$/ |
144
|
|
|
*/ |
145
|
|
|
public function bill($type, $sum, $currency, $quantity, $unit, $target) |
146
|
|
|
{ |
147
|
|
|
$quantity = $this->prepareQuantity($quantity); |
148
|
|
|
$sum = $this->prepareSum($sum, $quantity); |
149
|
|
|
$bill = $this->findBill([ |
150
|
|
|
'type' => $type, |
151
|
|
|
'target' => $target, |
152
|
|
|
'sum' => "$sum $currency", |
153
|
|
|
'quantity' => "$quantity $unit", |
154
|
|
|
]); |
155
|
|
|
Assert::assertSame($type, $bill->getType()->getName()); |
156
|
|
|
Assert::assertSame($target, $bill->getTarget()->getFullName()); |
157
|
|
|
Assert::assertEquals($sum * 100, $bill->getSum()->getAmount()); |
158
|
|
|
Assert::assertSame($currency, $bill->getSum()->getCurrency()->getCode()); |
159
|
|
|
Assert::assertEquals($quantity, $bill->getQuantity()->getQuantity()); |
160
|
|
|
Assert::assertTrue(Unit::create($unit)->equals($bill->getQuantity()->getUnit())); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function findBill(array $params): BillInterface |
164
|
|
|
{ |
165
|
|
|
$bills = $this->builder->findBills($params); |
166
|
|
|
$this->bill = reset($bills); |
167
|
|
|
$this->charges = $this->bill->getCharges(); |
168
|
|
|
|
169
|
|
|
return $this->bill; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* @Given /bills number is (\d+) for (\S+) for target (\S+)/ |
174
|
|
|
*/ |
175
|
|
|
public function billsNumber($number, $type, $target) |
176
|
|
|
{ |
177
|
|
|
$count = count($this->builder->findBills([ |
178
|
|
|
'type' => $type, |
179
|
|
|
'target' => $target, |
180
|
|
|
])); |
181
|
|
|
|
182
|
|
|
Assert::assertEquals($number, $count); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @Given /charge for (\S+) is +(\S+) (\S+) per (\S+) (\S+) for target (\S+)$/ |
187
|
|
|
*/ |
188
|
|
|
public function chargeWithTarget($type, $amount, $currency, $quantity, $unit, $target) |
189
|
|
|
{ |
190
|
|
|
$quantity = $this->prepareQuantity($quantity); |
191
|
|
|
$amount = $this->prepareSum($amount, $quantity); |
192
|
|
|
$charge = $this->findCharge($type, $target); |
193
|
|
|
Assert::assertNotNull($charge); |
194
|
|
|
Assert::assertSame($type, $charge->getType()->getName()); |
195
|
|
|
Assert::assertSame($target, $charge->getTarget()->getFullName()); |
196
|
|
|
Assert::assertEquals($amount * 100, $charge->getSum()->getAmount()); |
197
|
|
|
Assert::assertSame($currency, $charge->getSum()->getCurrency()->getCode()); |
198
|
|
|
Assert::assertEquals($quantity, $charge->getUsage()->getQuantity()); |
199
|
|
|
Assert::assertTrue(Unit::create($unit)->equals($charge->getUsage()->getUnit())); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @Given /charge for (\S+) is +(\S+) (\S+) per (\S+) (\S+)$/ |
204
|
|
|
*/ |
205
|
|
|
public function charge($type, $amount, $currency, $quantity, $unit) |
206
|
|
|
{ |
207
|
|
|
$this->chargeWithTarget($type, $amount, $currency, $quantity, $unit, null); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
public function findCharge($type, $target): ?ChargeInterface |
211
|
|
|
{ |
212
|
|
|
foreach ($this->charges as $charge) { |
213
|
|
|
if ($charge->getType()->getName() !== $type) { |
214
|
|
|
continue; |
215
|
|
|
} |
216
|
|
|
if ($charge->getTarget()->getFullName() !== $target) { |
217
|
|
|
continue; |
218
|
|
|
} |
219
|
|
|
return $charge; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return null; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
public function getNextCharge(): ChargeInterface |
226
|
|
|
{ |
227
|
|
|
$charge = current($this->charges); |
228
|
|
|
next($this->charges); |
229
|
|
|
|
230
|
|
|
return $charge; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* @param string $time |
235
|
|
|
* @return string|false |
236
|
|
|
*/ |
237
|
|
|
protected function prepareTime(string $time) |
238
|
|
|
{ |
239
|
|
|
if ($time === 'midnight second day of this month') { |
240
|
|
|
return date("Y-m-02"); |
241
|
|
|
} |
242
|
|
|
if (strncmp($time, 'Y', 1) === 0) { |
243
|
|
|
return date($time); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
return $time; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
private function prepareQuantity($quantity) |
250
|
|
|
{ |
251
|
|
|
if ($quantity[0] === 's') { |
252
|
|
|
return $this->getSaleQuantity(); |
253
|
|
|
} |
254
|
|
|
return $quantity; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
private function prepareSum($sum, $quantity) |
258
|
|
|
{ |
259
|
|
|
if ($sum[0] === 's') { |
260
|
|
|
$sum = round(substr($sum, 1) * $quantity*100)/100; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
return $sum; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
public function getSaleQuantity() |
267
|
|
|
{ |
268
|
|
|
return $this->days2quantity(new DateTimeImmutable($this->saleTime)); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
private function days2quantity(DateTimeImmutable $from) |
272
|
|
|
{ |
273
|
|
|
$till = new DateTimeImmutable('first day of next month midnight'); |
274
|
|
|
$diff = $from->diff($till); |
275
|
|
|
if ($diff->m) { |
276
|
|
|
return 1; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
return $diff->d/date('t'); |
280
|
|
|
} |
281
|
|
|
} |
282
|
|
|
|
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.