Passed
Pull Request — master (#5)
by Kauri
03:35
created

PaymentPeriods::getRatePerPeriod()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0073

Importance

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