1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial; |
4
|
|
|
|
5
|
|
|
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; |
6
|
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Exception; |
7
|
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants; |
8
|
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions; |
9
|
|
|
|
10
|
|
|
class Amortization |
11
|
|
|
{ |
12
|
|
|
/** |
13
|
|
|
* AMORDEGRC. |
14
|
|
|
* |
15
|
|
|
* Returns the depreciation for each accounting period. |
16
|
|
|
* This function is provided for the French accounting system. If an asset is purchased in |
17
|
|
|
* the middle of the accounting period, the prorated depreciation is taken into account. |
18
|
|
|
* The function is similar to AMORLINC, except that a depreciation coefficient is applied in |
19
|
|
|
* the calculation depending on the life of the assets. |
20
|
|
|
* This function will return the depreciation until the last period of the life of the assets |
21
|
|
|
* or until the cumulated value of depreciation is greater than the cost of the assets minus |
22
|
|
|
* the salvage value. |
23
|
|
|
* |
24
|
|
|
* Excel Function: |
25
|
|
|
* AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis]) |
26
|
|
|
* |
27
|
|
|
* @param mixed $cost The float cost of the asset |
28
|
|
|
* @param mixed $purchased Date of the purchase of the asset |
29
|
|
|
* @param mixed $firstPeriod Date of the end of the first period |
30
|
|
|
* @param mixed $salvage The salvage value at the end of the life of the asset |
31
|
|
|
* @param mixed $period the period (float) |
32
|
|
|
* @param mixed $rate rate of depreciation (float) |
33
|
|
|
* @param mixed $basis The type of day count to use (int). |
34
|
|
|
* 0 or omitted US (NASD) 30/360 |
35
|
|
|
* 1 Actual/actual |
36
|
|
|
* 2 Actual/360 |
37
|
|
|
* 3 Actual/365 |
38
|
|
|
* 4 European 30/360 |
39
|
|
|
* |
40
|
|
|
* @return float|string (string containing the error type if there is an error) |
41
|
|
|
*/ |
42
|
24 |
|
public static function AMORDEGRC( |
43
|
|
|
mixed $cost, |
44
|
|
|
mixed $purchased, |
45
|
|
|
mixed $firstPeriod, |
46
|
|
|
mixed $salvage, |
47
|
|
|
mixed $period, |
48
|
|
|
mixed $rate, |
49
|
|
|
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD |
50
|
|
|
): string|float { |
51
|
24 |
|
$cost = Functions::flattenSingleValue($cost); |
52
|
24 |
|
$purchased = Functions::flattenSingleValue($purchased); |
53
|
24 |
|
$firstPeriod = Functions::flattenSingleValue($firstPeriod); |
54
|
24 |
|
$salvage = Functions::flattenSingleValue($salvage); |
55
|
24 |
|
$period = Functions::flattenSingleValue($period); |
56
|
24 |
|
$rate = Functions::flattenSingleValue($rate); |
57
|
24 |
|
$basis = ($basis === null) |
58
|
1 |
|
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD |
59
|
23 |
|
: Functions::flattenSingleValue($basis); |
60
|
|
|
|
61
|
|
|
try { |
62
|
24 |
|
$cost = FinancialValidations::validateFloat($cost); |
63
|
23 |
|
$purchased = FinancialValidations::validateDate($purchased); |
64
|
22 |
|
$firstPeriod = FinancialValidations::validateDate($firstPeriod); |
65
|
21 |
|
$salvage = FinancialValidations::validateFloat($salvage); |
66
|
20 |
|
$period = FinancialValidations::validateInt($period); |
67
|
19 |
|
$rate = FinancialValidations::validateFloat($rate); |
68
|
18 |
|
$basis = FinancialValidations::validateBasis($basis); |
69
|
8 |
|
} catch (Exception $e) { |
70
|
8 |
|
return $e->getMessage(); |
71
|
|
|
} |
72
|
|
|
|
73
|
16 |
|
$yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis); |
74
|
16 |
|
if (is_string($yearFracx)) { |
75
|
|
|
return $yearFracx; |
76
|
|
|
} |
77
|
|
|
/** @var float $yearFrac */ |
78
|
16 |
|
$yearFrac = $yearFracx; |
79
|
|
|
|
80
|
16 |
|
$amortiseCoeff = self::getAmortizationCoefficient($rate); |
81
|
|
|
|
82
|
16 |
|
$rate *= $amortiseCoeff; |
83
|
16 |
|
$rate = (float) (string) $rate; // ugly way to avoid rounding problem |
84
|
16 |
|
$fNRate = round($yearFrac * $rate * $cost, 0); |
85
|
16 |
|
$cost -= $fNRate; |
86
|
16 |
|
$fRest = $cost - $salvage; |
87
|
|
|
|
88
|
16 |
|
for ($n = 0; $n < $period; ++$n) { |
89
|
16 |
|
$fNRate = round($rate * $cost, 0); |
90
|
16 |
|
$fRest -= $fNRate; |
91
|
|
|
|
92
|
16 |
|
if ($fRest < 0.0) { |
93
|
1 |
|
return match ($period - $n) { |
94
|
1 |
|
1 => round($cost * 0.5, 0), |
95
|
1 |
|
default => 0.0, |
96
|
1 |
|
}; |
97
|
|
|
} |
98
|
15 |
|
$cost -= $fNRate; |
99
|
|
|
} |
100
|
|
|
|
101
|
15 |
|
return $fNRate; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* AMORLINC. |
106
|
|
|
* |
107
|
|
|
* Returns the depreciation for each accounting period. |
108
|
|
|
* This function is provided for the French accounting system. If an asset is purchased in |
109
|
|
|
* the middle of the accounting period, the prorated depreciation is taken into account. |
110
|
|
|
* |
111
|
|
|
* Excel Function: |
112
|
|
|
* AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis]) |
113
|
|
|
* |
114
|
|
|
* @param mixed $cost The cost of the asset as a float |
115
|
|
|
* @param mixed $purchased Date of the purchase of the asset |
116
|
|
|
* @param mixed $firstPeriod Date of the end of the first period |
117
|
|
|
* @param mixed $salvage The salvage value at the end of the life of the asset |
118
|
|
|
* @param mixed $period The period as a float |
119
|
|
|
* @param mixed $rate Rate of depreciation as float |
120
|
|
|
* @param mixed $basis Integer indicating the type of day count to use. |
121
|
|
|
* 0 or omitted US (NASD) 30/360 |
122
|
|
|
* 1 Actual/actual |
123
|
|
|
* 2 Actual/360 |
124
|
|
|
* 3 Actual/365 |
125
|
|
|
* 4 European 30/360 |
126
|
|
|
* |
127
|
|
|
* @return float|string (string containing the error type if there is an error) |
128
|
|
|
*/ |
129
|
17 |
|
public static function AMORLINC( |
130
|
|
|
mixed $cost, |
131
|
|
|
mixed $purchased, |
132
|
|
|
mixed $firstPeriod, |
133
|
|
|
mixed $salvage, |
134
|
|
|
mixed $period, |
135
|
|
|
mixed $rate, |
136
|
|
|
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD |
137
|
|
|
): string|float { |
138
|
17 |
|
$cost = Functions::flattenSingleValue($cost); |
139
|
17 |
|
$purchased = Functions::flattenSingleValue($purchased); |
140
|
17 |
|
$firstPeriod = Functions::flattenSingleValue($firstPeriod); |
141
|
17 |
|
$salvage = Functions::flattenSingleValue($salvage); |
142
|
17 |
|
$period = Functions::flattenSingleValue($period); |
143
|
17 |
|
$rate = Functions::flattenSingleValue($rate); |
144
|
17 |
|
$basis = ($basis === null) |
145
|
1 |
|
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD |
146
|
16 |
|
: Functions::flattenSingleValue($basis); |
147
|
|
|
|
148
|
|
|
try { |
149
|
17 |
|
$cost = FinancialValidations::validateFloat($cost); |
150
|
16 |
|
$purchased = FinancialValidations::validateDate($purchased); |
151
|
15 |
|
$firstPeriod = FinancialValidations::validateDate($firstPeriod); |
152
|
14 |
|
$salvage = FinancialValidations::validateFloat($salvage); |
153
|
13 |
|
$period = FinancialValidations::validateFloat($period); |
154
|
12 |
|
$rate = FinancialValidations::validateFloat($rate); |
155
|
11 |
|
$basis = FinancialValidations::validateBasis($basis); |
156
|
8 |
|
} catch (Exception $e) { |
157
|
8 |
|
return $e->getMessage(); |
158
|
|
|
} |
159
|
|
|
|
160
|
9 |
|
$fOneRate = $cost * $rate; |
161
|
9 |
|
$fCostDelta = $cost - $salvage; |
162
|
|
|
// Note, quirky variation for leap years on the YEARFRAC for this function |
163
|
9 |
|
$purchasedYear = DateTimeExcel\DateParts::year($purchased); |
164
|
9 |
|
$yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis); |
165
|
9 |
|
if (is_string($yearFracx)) { |
166
|
|
|
return $yearFracx; |
167
|
|
|
} |
168
|
|
|
/** @var float $yearFrac */ |
169
|
9 |
|
$yearFrac = $yearFracx; |
170
|
|
|
|
171
|
|
|
if ( |
172
|
9 |
|
$basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL |
173
|
9 |
|
&& $yearFrac < 1 |
174
|
9 |
|
) { |
175
|
|
|
$temp = Functions::scalar($purchasedYear); |
176
|
2 |
|
if (is_int($temp) || is_string($temp)) { |
177
|
|
|
if (DateTimeExcel\Helpers::isLeapYear($temp)) { |
178
|
|
|
$yearFrac *= 365 / 366; |
179
|
9 |
|
} |
180
|
9 |
|
} |
181
|
|
|
} |
182
|
9 |
|
|
183
|
1 |
|
$f0Rate = $yearFrac * $rate * $cost; |
184
|
8 |
|
$nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate); |
185
|
6 |
|
|
186
|
2 |
|
if ($period == 0) { |
187
|
1 |
|
return $f0Rate; |
188
|
|
|
} elseif ($period <= $nNumOfFullPeriods) { |
189
|
|
|
return $fOneRate; |
190
|
1 |
|
} elseif ($period == ($nNumOfFullPeriods + 1)) { |
191
|
|
|
return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate; |
192
|
|
|
} |
193
|
16 |
|
|
194
|
|
|
return 0.0; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
private static function getAmortizationCoefficient(float $rate): float |
198
|
|
|
{ |
199
|
|
|
// The depreciation coefficients are: |
200
|
|
|
// Life of assets (1/rate) Depreciation coefficient |
201
|
16 |
|
// Less than 3 years 1 |
202
|
|
|
// Between 3 and 4 years 1.5 |
203
|
16 |
|
// Between 5 and 6 years 2 |
204
|
1 |
|
// More than 6 years 2.5 |
205
|
15 |
|
$fUsePer = 1.0 / $rate; |
206
|
3 |
|
|
207
|
12 |
|
if ($fUsePer < 3.0) { |
208
|
8 |
|
return 1.0; |
209
|
|
|
} elseif ($fUsePer < 4.0) { |
210
|
|
|
return 1.5; |
211
|
4 |
|
} elseif ($fUsePer <= 6.0) { |
212
|
|
|
return 2.0; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
return 2.5; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|