Passed
Pull Request — master (#2)
by Kauri
02:48
created

PaymentPeriods::getNumberOfRemainingPeriods()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 13
cts 13
cp 1
rs 8.9197
c 0
b 0
f 0
cc 4
eloc 18
nc 4
nop 2
crap 4
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Kauri\Loan;
6
7
/**
8
 * Class PaymentPeriods
9
 * @package Kauri\Loan
10
 */
11
class PaymentPeriods implements PaymentPeriodsInterface
12
{
13
    /**
14
     * Exact payment with exact interest
15
     */
16
    const CALCULATION_TYPE_EXACT = 1;
17
    /**
18
     * Annuity payment with exact interest
19
     */
20
    const CALCULATION_TYPE_EXACT_INTEREST = 2;
21
    /**
22
     * Annuity payment with annuity interest
23
     */
24
    const CALCULATION_TYPE_ANNUITY = 3;
25
26
    /**
27
     * @var array
28
     */
29
    private $periods = array();
30
    /**
31
     * @var int
32
     */
33
    private $totalLength = 0;
34
    /**
35
     * @var int
36
     */
37
    private $averagePeriod;
38
    /**
39
     * @var int
40
     */
41
    private $averageTotalLength;
42
43
    /**
44
     * PaymentPeriods constructor.
45
     * @param int $averagePeriod
46
     */
47 9
    public function __construct(int $averagePeriod)
48
    {
49 9
        $this->averagePeriod = $averagePeriod;
50 9
    }
51
52
    /**
53
     * @param PeriodInterface $period
54
     * @param int|null $sequenceNo
55
     */
56 7
    public function add(PeriodInterface $period, int $sequenceNo = null): void
57
    {
58 7
        if (is_null($sequenceNo)) {
59 4
            $sequenceNo = $this->getNoOfPeriods() + 1;
60
        }
61
62 7
        $period->setSequenceNo($sequenceNo);
63
64 7
        $this->periods[$sequenceNo] = $period;
65 7
        $this->totalLength = $this->totalLength + $period->getLength();
66 7
        $this->averageTotalLength = count($this->periods) * $this->averagePeriod;
67 7
    }
68
69
    /**
70
     * @param PeriodInterface $period
71
     * @param float $yearlyInterestRate
72
     * @param int $calculationType
73
     * @return float
74
     * @throws \Exception
75
     */
76 3
    public function getRatePerPeriod(
77
        PeriodInterface $period,
78
        float $yearlyInterestRate,
79
        int $calculationType = self::CALCULATION_TYPE_ANNUITY
80
    ): float {
81
        switch ($calculationType) {
82 3
            case self::CALCULATION_TYPE_EXACT:
83 3
            case self::CALCULATION_TYPE_EXACT_INTEREST:
84 2
                $currentPeriod = $period->getLength();
85 2
                break;
86 3
            case self::CALCULATION_TYPE_ANNUITY:
87 2
                $currentPeriod = $this->averagePeriod;
88 2
                break;
89
            default:
90 1
                throw new \Exception('Calculation type not implemented');
91
        }
92
93 2
        $ratePerPeriod = $yearlyInterestRate / 360 * $currentPeriod;
94
95 2
        return $ratePerPeriod;
96
    }
97
98
    /**
99
     * @param PeriodInterface $period
100
     * @param int $calculationType
101
     * @return float|int
102
     * @throws \Exception
103
     */
104 6
    public function getNumberOfRemainingPeriods(
105
        PeriodInterface $period,
106
        int $calculationType = self::CALCULATION_TYPE_ANNUITY
107
    ): float {
108
        switch ($calculationType) {
109 6
            case self::CALCULATION_TYPE_EXACT:
110 5
                $currentPeriod = $period->getLength();
111 5
                $totalPeriods = $this->getExactRemainingPeriodsLength($period);
112 5
                break;
113 6
            case self::CALCULATION_TYPE_EXACT_INTEREST:
114 6
            case self::CALCULATION_TYPE_ANNUITY:
115 5
                $currentPeriod = $this->averagePeriod;
116 5
                $totalPeriods = $this->getAverageRemainingPeriodsLength($period);
117 5
                break;
118
            default:
119 1
                throw new \Exception('Calculation type not implemented');
120
        }
121
122 5
        $numberOfPeriods = ($currentPeriod + $totalPeriods) / $currentPeriod;
123
124 5
        return $numberOfPeriods;
125
    }
126
127
    /**
128
     * @param PeriodInterface $currentPeriod
129
     * @return int
130
     */
131 7
    public function getExactRemainingPeriodsLength(PeriodInterface $currentPeriod)
132
    {
133 7
        $followingPeriods = $this->getFollowingPeriods($currentPeriod->getSequenceNo());
134 7
        $remainingPeriodsLength = 0;
135
136
137 7
        foreach ($followingPeriods as $period) {
138 7
            $remainingPeriodsLength += $period->getLength();
139
        }
140
141 7
        return $remainingPeriodsLength;
142
    }
143
144
    /**
145
     * @param PeriodInterface $currentPeriod
146
     * @return int
147
     */
148 7
    public function getAverageRemainingPeriodsLength(PeriodInterface $currentPeriod)
149
    {
150 7
        $followingPeriods = $this->getFollowingPeriods($currentPeriod->getSequenceNo());
151 7
        $remainingPeriodsLength = $this->averagePeriod * count($followingPeriods);
152
153 7
        return $remainingPeriodsLength;
154
    }
155
156
    /**
157
     * @param int $currentPeriodSequenceNo
158
     * @return array
159
     */
160 7
    private function getFollowingPeriods(int $currentPeriodSequenceNo): array
161
    {
162 7
        $followingPeriods = array();
163
164 7
        foreach ($this->getPeriods() as $period) {
165 7
            if ($period->getSequenceNo() > $currentPeriodSequenceNo) {
166 7
                array_push($followingPeriods, $period);
167
            }
168
        }
169
170 7
        return $followingPeriods;
171
    }
172
173
    /**
174
     * @return array
175
     */
176 7
    public function getPeriods(): array
177
    {
178 7
        return $this->periods;
179
    }
180
181
    /**
182
     * @return int
183
     */
184 4
    public function getNoOfPeriods(): int
185
    {
186 4
        return count($this->periods);
187
    }
188
189
190
}
191