From bc8fa31eb24d540768d3231bc379eb2b1c92e28d Mon Sep 17 00:00:00 2001 From: Dmitriy Minaev Date: Tue, 28 Aug 2018 22:32:46 +0300 Subject: [PATCH] Add depreciation with half-year convention. Fixed #1237 (#6128) * Add half-year convention in depreciation for Models/Depreciable.php * Add a setting for the depreciation method * Integrate half-year convention inside working output * fix: add more checks at Depreciable.php * depreciation value rounding * Codestyle fix --- app/Http/Controllers/SettingsController.php | 2 + app/Models/Depreciable.php | 84 +++++++++++++++++++ ...42_add_depreciation_option_to_settings.php | 32 +++++++ resources/views/settings/general.blade.php | 14 ++++ 4 files changed, 132 insertions(+) create mode 100644 database/migrations/2018_08_20_204842_add_depreciation_option_to_settings.php diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 798d866983..90391a23f8 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -351,6 +351,8 @@ class SettingsController extends Controller $setting->thumbnail_max_h = $request->input('thumbnail_max_h'); $setting->privacy_policy_link = $request->input('privacy_policy_link'); + $setting->depreciation_method = $request->input('depreciation_method'); + if (Input::get('per_page')!='') { $setting->per_page = $request->input('per_page'); } else { diff --git a/app/Models/Depreciable.php b/app/Models/Depreciable.php index ee12a6506a..1ed80fdcb2 100644 --- a/app/Models/Depreciable.php +++ b/app/Models/Depreciable.php @@ -49,7 +49,29 @@ class Depreciable extends SnipeModel if ($this->get_depreciation()->months <= 0) { return $this->purchase_cost; } + $depreciation = 0; + $setting = Setting::first(); + switch($setting->depreciation_method) { + case 'half_1': + $depreciation = $this->getHalfYearDepreciatedValue(true); + break; + + case 'half_2': + $depreciation = $this->getHalfYearDepreciatedValue(false); + break; + default: + $depreciation = $this->getLinearDepreciatedValue(); + } + return $depreciation; + } + + /** + * @return float|int + */ + + public function getLinearDepreciatedValue() + { // fraction of value left $months_remaining = $this->time_until_depreciated()->m + 12*$this->time_until_depreciated()->y; //UGlY $current_value = round(($months_remaining/ $this->get_depreciation()->months) * $this->purchase_cost, 2); @@ -60,6 +82,62 @@ class Depreciable extends SnipeModel return $current_value; } + /** + * @param onlyHalfFirstYear Boolean always applied only second half of the first year + * @return float|int + */ + public function getHalfYearDepreciatedValue($onlyHalfFirstYear = false) + { + // @link http://www.php.net/manual/en/class.dateinterval.php + $current_date = $this->getDateTime(); + $purchase_date = date_create($this->purchase_date); + $currentYear = $this->get_fiscal_year( $current_date ); + $purchaseYear = $this->get_fiscal_year( $purchase_date ); + $yearsPast = $currentYear - $purchaseYear; + $deprecationYears = ceil($this->get_depreciation()->months / 12); + if( $onlyHalfFirstYear ) { + $yearsPast -= 0.5; + } + else if( !$this->is_first_half_of_year($purchase_date) ) { + $yearsPast -= 0.5; + } + if( !$this->is_first_half_of_year($current_date) ) { + $yearsPast += 0.5; + } + + if($yearsPast >= $deprecationYears) { + $yearsPast = $deprecationYears; + } + else if($yearsPast < 0) { + $yearsPast = 0; + } + return round($yearsPast / $deprecationYears * $this->purchase_cost, 2); + } + + /** + * @param \DateTime $date + * @return int + */ + protected function get_fiscal_year($date) { + $year = intval($date->format('Y')); + // also, maybe it'll have to set fiscal year date + if($date->format('nj') === '1231') { + return $year; + } + else { + return $year - 1; + } + } + + /** + * @param \DateTime $date + * @return bool + */ + protected function is_first_half_of_year($date) { + $date0m0d = intval($date->format('md')); + return ($date0m0d < 601) || ($date0m0d >= 1231); + } + public function time_until_depreciated() { // @link http://www.php.net/manual/en/class.datetime.php @@ -81,4 +159,10 @@ class Depreciable extends SnipeModel date_add($date, date_interval_create_from_date_string($this->get_depreciation()->months . ' months')); return $date; //date_format($date, 'Y-m-d'); //don't bake-in format, for internationalization } + + // it's necessary for unit tests + protected function getDateTime($time = null) + { + return new \DateTime($time); + } } diff --git a/database/migrations/2018_08_20_204842_add_depreciation_option_to_settings.php b/database/migrations/2018_08_20_204842_add_depreciation_option_to_settings.php new file mode 100644 index 0000000000..7678c45d56 --- /dev/null +++ b/database/migrations/2018_08_20_204842_add_depreciation_option_to_settings.php @@ -0,0 +1,32 @@ +char('depreciation_method', 10)->nullable()->default('default'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('depreciation_method'); + }); + } +} diff --git a/resources/views/settings/general.blade.php b/resources/views/settings/general.blade.php index 41fe09ea59..af93a7c8ec 100644 --- a/resources/views/settings/general.blade.php +++ b/resources/views/settings/general.blade.php @@ -281,6 +281,20 @@ {{ Form::checkbox('show_in_model_list[]', 'model_number', Input::old('show_in_model_list', $snipeSettings->modellistCheckedValue('model_number')),array('class' => 'minimal')) }} {{ trans('general.model_no') }}
+ + +
+
+ {{ Form::label('depreciation_method', trans('Depreciation method')) }} +
+
+ {{ Form::select('depreciation_method', array( + 'default' => 'Linear (default)', + 'half_1' => 'Half-year convention, always applied', + 'half_2' => 'Half-year convention, applied with condition', + ), Input::old('username_format', $setting->depreciation_method)) }} +
+