Installment   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 34
eloc 81
dl 0
loc 167
rs 9.68
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A ensureIsValid() 0 10 3
A createInstallmentCharge() 0 21 3
A modifyCharge() 0 23 5
A getRemainingPeriods() 0 14 3
A isFirstMonthInInstallmentPassed() 0 12 4
B isFirstMonthAfterInstallmentPassed() 0 20 8
A getType() 0 7 2
A createInstallmentFinishingCharge() 0 17 2
A till() 0 3 1
A createInstallmentStartingCharge() 0 5 1
A buildPrice() 0 7 1
A getTarget() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * PHP Billing Library
6
 *
7
 * @link      https://github.com/hiqdev/php-billing
8
 * @package   php-billing
9
 * @license   BSD-3-Clause
10
 * @copyright Copyright (c) 2017-2020, HiQDev (http://hiqdev.com/)
11
 */
12
13
namespace hiqdev\php\billing\charge\modifiers;
14
15
use DateTimeImmutable;
16
use hiqdev\php\billing\action\ActionInterface;
17
use hiqdev\php\billing\charge\Charge;
18
use hiqdev\php\billing\charge\ChargeInterface;
19
use hiqdev\php\billing\charge\modifiers\addons\Period;
20
use hiqdev\php\billing\charge\modifiers\event\InstallmentWasFinished;
21
use hiqdev\php\billing\charge\modifiers\event\InstallmentWasStarted;
22
use hiqdev\php\billing\formula\FormulaSemanticsError;
23
use hiqdev\php\billing\price\SinglePrice;
24
use hiqdev\php\billing\target\Target;
25
use hiqdev\php\billing\type\Type;
26
use hiqdev\php\units\Quantity;
27
use Money\Money;
28
29
/**
30
 * Installment.
31
 *
32
 * @author Andrii Vasyliev <[email protected]>
33
 */
34
class Installment extends Modifier
35
{
36
    public function buildPrice(Money $sum)
37
    {
38
        $type = $this->getType();
39
        $target = $this->getTarget();
40
        $prepaid = Quantity::create('items', 0);
41
42
        return new SinglePrice(null, $type, $target, null, $prepaid, $sum);
43
    }
44
45
    public function getType()
46
    {
47
        $since = $this->getSince();
48
        if ($since->getValue() < new DateTimeImmutable('2024-01-01')) {
49
            return Type::anyId('monthly,leasing');
50
        }
51
        return Type::anyId('monthly,installment');
52
    }
53
54
    public function getTarget()
55
    {
56
        return new Target(Target::ANY, Target::ANY);
57
    }
58
59
    public function till($time)
60
    {
61
        throw new FormulaSemanticsError('till can not be defined for installment');
62
    }
63
64
    public function modifyCharge(?ChargeInterface $charge, ActionInterface $action): array
65
    {
66
        if ($charge === null) {
67
            throw new \Exception('unexpected null charge in Installment, to be implemented');
68
        }
69
70
        $this->ensureIsValid();
71
72
        $reason = $this->getReason();
73
        if ($reason) {
0 ignored issues
show
introduced by
$reason is of type hiqdev\php\billing\charge\modifiers\addons\Reason, thus it always evaluated to true.
Loading history...
74
            $charge->setComment($reason->getValue());
75
        }
76
77
        $month = $action->getTime()->modify('first day of this month midnight');
78
        if (!$this->checkPeriod($month)) {
79
            if ($this->isFirstMonthAfterInstallmentPassed($month)) {
80
                return [$this->createInstallmentFinishingCharge($charge, $month)];
81
            }
82
83
            return [];
84
        }
85
86
        return [$this->createInstallmentCharge($charge, $month)];
87
    }
88
89
    protected function ensureIsValid(): void
90
    {
91
        $since = $this->getSince();
92
        if ($since === null) {
93
            throw new FormulaSemanticsError('no since given for installment');
94
        }
95
96
        $term = $this->getTerm();
97
        if ($term === null) {
98
            throw new FormulaSemanticsError('no term given for installment');
99
        }
100
    }
101
102
    private function isFirstMonthInInstallmentPassed(DateTimeImmutable $time): bool
103
    {
104
        $since = $this->getSince();
105
        if ($since && $since->getValue() > $time) {
106
            return false;
107
        }
108
109
        if ($since->getValue()->diff($time)->format('%a') === '0') {
110
            return true;
111
        }
112
113
        return false;
114
    }
115
116
    private function isFirstMonthAfterInstallmentPassed(DateTimeImmutable $time): bool
117
    {
118
        $since = $this->getSince();
119
        if ($since && $since->getValue() > $time) {
120
            return false;
121
        }
122
123
        $till = $this->getTill();
124
        if ($till && $till->getValue() <= $time) {
125
            if ($till->getValue()->diff($time)->format('%a') === '0') {
126
                return true;
127
            }
128
        }
129
130
        $term = $this->getTerm();
131
        if ($term && $term->addTo($since->getValue())->diff($time)->format('%a') === '0') {
132
            return true;
133
        }
134
135
        return false;
136
    }
137
138
    private function createInstallmentFinishingCharge(ChargeInterface $charge, DateTimeImmutable $month): ChargeInterface
139
    {
140
        $result = new Charge(
141
            null,
142
            $this->getType(),
143
            $charge->getTarget(),
144
            $charge->getAction(),
145
            $charge->getPrice(),
146
            $charge->getUsage(),
147
            new Money(0, $charge->getSum()->getCurrency())
148
        );
149
        $result->recordThat(InstallmentWasFinished::onCharge($result, $month));
150
        if ($charge->getComment()) {
151
            $result->setComment($charge->getComment());
152
        }
153
154
        return $result;
155
    }
156
157
    private function createInstallmentStartingCharge(Charge $charge, DateTimeImmutable $month): ChargeInterface
158
    {
159
        $charge->recordThat(InstallmentWasStarted::onCharge($charge, $month));
160
161
        return $charge;
162
    }
163
164
    private function createInstallmentCharge(ChargeInterface $charge, DateTimeImmutable $month): ChargeInterface
165
    {
166
        $result = new Charge(
167
            null,
168
            $this->getType(),
169
            $charge->getTarget(),
170
            $charge->getAction(),
171
            $charge->getPrice(),
172
            $charge->getUsage(),
173
            $charge->getSum()
174
        );
175
176
        if ($charge->getComment()) {
177
            $result->setComment($charge->getComment());
178
        }
179
180
        if ($this->isFirstMonthInInstallmentPassed($month)) {
181
            return $this->createInstallmentStartingCharge($result, $month);
182
        }
183
184
        return $result;
185
    }
186
187
    public function getRemainingPeriods(DateTimeImmutable $currentDate): ?Period
188
    {
189
        $since = $this->getSince();
190
        $term = $this->getTerm();
191
192
        if ($since === null || $term === null) {
193
            return null;
194
        }
195
196
        $className = get_class($term);
197
        $passedRatio = $term->countPeriodsPassed($since->getValue(), $currentDate);
198
199
        return new $className(
200
            $term->getValue() - ($passedRatio * $term->getValue())
201
        );
202
    }
203
}
204