Passed
Push — master ( 817bfa...9a0237 )
by Kauri
02:53
created

PaymentPeriods::getFollowingPeriods()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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