<?php
|
|
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
|
|
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
|
class Amortization
|
{
|
/**
|
* AMORDEGRC.
|
*
|
* Returns the depreciation for each accounting period.
|
* This function is provided for the French accounting system. If an asset is purchased in
|
* the middle of the accounting period, the prorated depreciation is taken into account.
|
* The function is similar to AMORLINC, except that a depreciation coefficient is applied in
|
* the calculation depending on the life of the assets.
|
* This function will return the depreciation until the last period of the life of the assets
|
* or until the cumulated value of depreciation is greater than the cost of the assets minus
|
* the salvage value.
|
*
|
* Excel Function:
|
* AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
|
*
|
* @param mixed $cost The float cost of the asset
|
* @param mixed $purchased Date of the purchase of the asset
|
* @param mixed $firstPeriod Date of the end of the first period
|
* @param mixed $salvage The salvage value at the end of the life of the asset
|
* @param mixed $period the period (float)
|
* @param mixed $rate rate of depreciation (float)
|
* @param mixed $basis The type of day count to use (int).
|
* 0 or omitted US (NASD) 30/360
|
* 1 Actual/actual
|
* 2 Actual/360
|
* 3 Actual/365
|
* 4 European 30/360
|
*
|
* @return float|string (string containing the error type if there is an error)
|
*/
|
public static function AMORDEGRC(
|
mixed $cost,
|
mixed $purchased,
|
mixed $firstPeriod,
|
mixed $salvage,
|
mixed $period,
|
mixed $rate,
|
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
): string|float {
|
$cost = Functions::flattenSingleValue($cost);
|
$purchased = Functions::flattenSingleValue($purchased);
|
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
|
$salvage = Functions::flattenSingleValue($salvage);
|
$period = Functions::flattenSingleValue($period);
|
$rate = Functions::flattenSingleValue($rate);
|
$basis = ($basis === null)
|
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
: Functions::flattenSingleValue($basis);
|
|
try {
|
$cost = FinancialValidations::validateFloat($cost);
|
$purchased = FinancialValidations::validateDate($purchased);
|
$firstPeriod = FinancialValidations::validateDate($firstPeriod);
|
$salvage = FinancialValidations::validateFloat($salvage);
|
$period = FinancialValidations::validateInt($period);
|
$rate = FinancialValidations::validateFloat($rate);
|
$basis = FinancialValidations::validateBasis($basis);
|
} catch (Exception $e) {
|
return $e->getMessage();
|
}
|
|
$yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
|
if (is_string($yearFracx)) {
|
return $yearFracx;
|
}
|
/** @var float $yearFrac */
|
$yearFrac = $yearFracx;
|
|
$amortiseCoeff = self::getAmortizationCoefficient($rate);
|
|
$rate *= $amortiseCoeff;
|
$rate = (float) (string) $rate; // ugly way to avoid rounding problem
|
$fNRate = round($yearFrac * $rate * $cost, 0);
|
$cost -= $fNRate;
|
$fRest = $cost - $salvage;
|
|
for ($n = 0; $n < $period; ++$n) {
|
$fNRate = round($rate * $cost, 0);
|
$fRest -= $fNRate;
|
|
if ($fRest < 0.0) {
|
return match ($period - $n) {
|
1 => round($cost * 0.5, 0),
|
default => 0.0,
|
};
|
}
|
$cost -= $fNRate;
|
}
|
|
return $fNRate;
|
}
|
|
/**
|
* AMORLINC.
|
*
|
* Returns the depreciation for each accounting period.
|
* This function is provided for the French accounting system. If an asset is purchased in
|
* the middle of the accounting period, the prorated depreciation is taken into account.
|
*
|
* Excel Function:
|
* AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
|
*
|
* @param mixed $cost The cost of the asset as a float
|
* @param mixed $purchased Date of the purchase of the asset
|
* @param mixed $firstPeriod Date of the end of the first period
|
* @param mixed $salvage The salvage value at the end of the life of the asset
|
* @param mixed $period The period as a float
|
* @param mixed $rate Rate of depreciation as float
|
* @param mixed $basis Integer indicating the type of day count to use.
|
* 0 or omitted US (NASD) 30/360
|
* 1 Actual/actual
|
* 2 Actual/360
|
* 3 Actual/365
|
* 4 European 30/360
|
*
|
* @return float|string (string containing the error type if there is an error)
|
*/
|
public static function AMORLINC(
|
mixed $cost,
|
mixed $purchased,
|
mixed $firstPeriod,
|
mixed $salvage,
|
mixed $period,
|
mixed $rate,
|
mixed $basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
): string|float {
|
$cost = Functions::flattenSingleValue($cost);
|
$purchased = Functions::flattenSingleValue($purchased);
|
$firstPeriod = Functions::flattenSingleValue($firstPeriod);
|
$salvage = Functions::flattenSingleValue($salvage);
|
$period = Functions::flattenSingleValue($period);
|
$rate = Functions::flattenSingleValue($rate);
|
$basis = ($basis === null)
|
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
|
: Functions::flattenSingleValue($basis);
|
|
try {
|
$cost = FinancialValidations::validateFloat($cost);
|
$purchased = FinancialValidations::validateDate($purchased);
|
$firstPeriod = FinancialValidations::validateDate($firstPeriod);
|
$salvage = FinancialValidations::validateFloat($salvage);
|
$period = FinancialValidations::validateFloat($period);
|
$rate = FinancialValidations::validateFloat($rate);
|
$basis = FinancialValidations::validateBasis($basis);
|
} catch (Exception $e) {
|
return $e->getMessage();
|
}
|
|
$fOneRate = $cost * $rate;
|
$fCostDelta = $cost - $salvage;
|
// Note, quirky variation for leap years on the YEARFRAC for this function
|
$purchasedYear = DateTimeExcel\DateParts::year($purchased);
|
$yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
|
if (is_string($yearFracx)) {
|
return $yearFracx;
|
}
|
/** @var float $yearFrac */
|
$yearFrac = $yearFracx;
|
|
if (
|
$basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
|
&& $yearFrac < 1
|
&& DateTimeExcel\Helpers::isLeapYear(Functions::scalar($purchasedYear))
|
) {
|
$yearFrac *= 365 / 366;
|
}
|
|
$f0Rate = $yearFrac * $rate * $cost;
|
$nNumOfFullPeriods = (int) (($cost - $salvage - $f0Rate) / $fOneRate);
|
|
if ($period == 0) {
|
return $f0Rate;
|
} elseif ($period <= $nNumOfFullPeriods) {
|
return $fOneRate;
|
} elseif ($period == ($nNumOfFullPeriods + 1)) {
|
return $fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate;
|
}
|
|
return 0.0;
|
}
|
|
private static function getAmortizationCoefficient(float $rate): float
|
{
|
// The depreciation coefficients are:
|
// Life of assets (1/rate) Depreciation coefficient
|
// Less than 3 years 1
|
// Between 3 and 4 years 1.5
|
// Between 5 and 6 years 2
|
// More than 6 years 2.5
|
$fUsePer = 1.0 / $rate;
|
|
if ($fUsePer < 3.0) {
|
return 1.0;
|
} elseif ($fUsePer < 4.0) {
|
return 1.5;
|
} elseif ($fUsePer <= 6.0) {
|
return 2.0;
|
}
|
|
return 2.5;
|
}
|
}
|