diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php
index 3f3709f52..7a333e708 100644
--- a/app/Http/Controllers/Api/AssetsController.php
+++ b/app/Http/Controllers/Api/AssetsController.php
@@ -7,6 +7,7 @@ use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Requests\AssetCheckoutRequest;
use App\Http\Transformers\AssetsTransformer;
+use App\Http\Transformers\DepreciationReportTransformer;
use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer;
use App\Models\Actionlog;
@@ -29,6 +30,7 @@ use Slack;
use Str;
use TCPDF;
use Validator;
+use Route;
/**
@@ -49,10 +51,32 @@ class AssetsController extends Controller
* @since [v4.0]
* @return JsonResponse
*/
- public function index(Request $request, $audit = null)
+ public function index(Request $request, $audit = null)
{
- $this->authorize('index', Asset::class);
+ \Log::debug(Route::currentRouteName());
+
+
+ /**
+ * This looks MAD janky (and it is), but the AssetsController@index does a LOT of heavy lifting throughout the
+ * app. This bit here just makes sure that someone without permission to view assets doesn't
+ * end up with priv escalations because they asked for a different endpoint.
+ *
+ * Since we never gave the specification for which transformer to use before, it should default
+ * gracefully to just use the AssetTransformer by default, which shouldn't break anything.
+ *
+ * It was either this mess, or repeating ALL of the searching and sorting and filtering code,
+ * which would have been far worse of a mess. *sad face* - snipe (Sept 1, 2021)
+ */
+ if (Route::currentRouteName()=='api.depreciation-report.index') {
+ $transformer = 'App\Http\Transformers\DepreciationReportTransformer';
+ $this->authorize('reports.view');
+ } else {
+ $transformer = 'App\Http\Transformers\AssetsTransformer';
+ $this->authorize('index', Asset::class);
+ }
+
+
$settings = Setting::getSettings();
$allowed_columns = [
@@ -295,8 +319,12 @@ class AssetsController extends Controller
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();
- // dd($assets);
- return (new AssetsTransformer)->transformAssets($assets, $total);
+
+ /**
+ * Here we're just determining which Transformer (via $transformer) to use based on the
+ * variables we set earlier on in this method - we default to AssetsTransformer.
+ */
+ return (new $transformer)->transformAssets($assets, $total);
}
diff --git a/app/Http/Transformers/DepreciationReportTransformer.php b/app/Http/Transformers/DepreciationReportTransformer.php
new file mode 100644
index 000000000..dfa5adc98
--- /dev/null
+++ b/app/Http/Transformers/DepreciationReportTransformer.php
@@ -0,0 +1,119 @@
+]
+ * @since [v5.2.0]
+ */
+class DepreciationReportTransformer
+{
+ public function transformAssets(Collection $assets, $total)
+ {
+ $array = array();
+ foreach ($assets as $asset) {
+ $array[] = self::transformAsset($asset);
+ }
+ return (new DatatablesTransformer)->transformDatatables($array, $total);
+ }
+
+
+ public function transformAsset(Asset $asset)
+ {
+
+ /**
+ * Set some default values here
+ */
+ $purchase_cost = null;
+ $depreciated_value = null;
+ $monthly_depreciation = null;
+ $diff = null;
+ $checkout_target = null;
+
+ /**
+ * If there is a location set and a currency set, use that for display
+ */
+ if ($asset->location && $asset->location->currency) {
+ $purchase_cost_currency = $asset->location->currency;
+ } else {
+ $purchase_cost_currency = \App\Models\Setting::getSettings()->default_currency;
+ }
+
+ /**
+ * If there is a NOT an empty purchase cost (meaning not null or '' but it *could* be zero),
+ * format the purchase cost. We coould do this inline in the transformer, but we need that value
+ * for the other calculations that come after, like diff, etc.
+ */
+ if ($asset->purchase_cost!='') {
+ $purchase_cost = $purchase_cost_currency . ' ' . \App\Helpers\Helper::formatCurrencyOutput($asset->purchase_cost);
+ }
+
+
+ /**
+ * Override the previously set null values if there is a valid model and associated depreciation
+ */
+ if (($asset->model) && ($asset->model->depreciation)) {
+ $depreciated_value = $purchase_cost_currency . ' ' . \App\Helpers\Helper::formatCurrencyOutput($asset->getDepreciatedValue());
+ $monthly_depreciation = $purchase_cost_currency . ' ' .\App\Helpers\Helper::formatCurrencyOutput(($asset->model->eol > 0 ? ($asset->purchase_cost / $asset->model->eol) : 0));
+ $diff = $purchase_cost_currency . ' ' .\App\Helpers\Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue()));
+ }
+
+ if ($asset->assigned) {
+ $checkout_target = $asset->assigned->name;
+ if ($asset->checkedOutToUser()) {
+ $checkout_target = $asset->assigned->getFullNameAttribute();
+ }
+
+ }
+
+ $array = [
+
+ 'company' => ($asset->company) ? e($asset->company->name) : null,
+ 'name' => e($asset->name),
+ 'asset_tag' => e($asset->asset_tag),
+ 'serial' => e($asset->serial),
+ 'model' => ($asset->model) ? e($asset->model->name) : null,
+ 'model_number' => (($asset->model) && ($asset->model->model_number)) ? e($asset->model->model_number) : null,
+ 'eol' => ($asset->purchase_date!='') ? Helper::getFormattedDateObject($asset->present()->eol_date(), 'date') : null ,
+ 'status_label' => ($asset->assetstatus) ? e($asset->assetstatus->name) : null,
+ 'status' => ($asset->assetstatus) ? e($asset->present()->statusMeta) : null,
+ 'category' => (($asset->model) && ($asset->model->category)) ? e($asset->model->category->name) : null,
+ 'manufacturer' => (($asset->model) && ($asset->model->manufacturer)) ? e($asset->model->manufacturer->name) : null,
+ 'supplier' => ($asset->supplier) ? e($asset->supplier->name) : null,
+ 'notes' => ($asset->notes) ? e($asset->notes) : null,
+ 'order_number' => ($asset->order_number) ? e($asset->order_number) : null,
+ 'location' => ($asset->location) ? e($asset->location->name) : null,
+ 'warranty_expires' => ($asset->warranty_months > 0) ? Helper::getFormattedDateObject($asset->warranty_expires, 'date') : null,
+ 'purchase_date' => Helper::getFormattedDateObject($asset->purchase_date, 'date'),
+ 'purchase_cost' => $purchase_cost,
+ 'book_value' => $depreciated_value,
+ 'monthly_depreciation' => $monthly_depreciation,
+ 'checked_out_to' => $checkout_target,
+ 'diff' => $diff,
+ 'number_of_months' => ($asset->model && $asset->model->depreciation) ? e($asset->model->depreciation->months) : null,
+ 'depreciation' => (($asset->model) && ($asset->model->depreciation)) ? e($asset->model->depreciation->name) : null,
+
+
+
+ ];
+
+ return $array;
+ }
+
+ public function transformAssetsDatatable($assets)
+ {
+ return (new DatatablesTransformer)->transformDatatables($assets);
+ }
+
+
+
+}
diff --git a/app/Presenters/DepreciationReportPresenter.php b/app/Presenters/DepreciationReportPresenter.php
new file mode 100644
index 000000000..d271220b8
--- /dev/null
+++ b/app/Presenters/DepreciationReportPresenter.php
@@ -0,0 +1,393 @@
+ "company",
+ "searchable" => true,
+ "sortable" => true,
+ "switchable" => true,
+ "title" => trans('general.company'),
+ "visible" => false,
+ ], [
+ "field" => "category",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('general.category'),
+ "visible" => true,
+ ], [
+ "field" => "name",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/hardware/form.name'),
+ "visible" => false,
+ ], [
+ "field" => "asset_tag",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('general.asset_tag'),
+ "visible" => true,
+ ],[
+ "field" => "model",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('general.asset_model'),
+ "visible" => true,
+ ], [
+ "field" => "model",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/hardware/form.model'),
+ "visible" => true,
+ ], [
+ "field" => "model_number",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/models/table.modelnumber'),
+ "visible" => false
+ ], [
+ "field" => "serial",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/hardware/form.serial'),
+ "visible" => true,
+ ], [
+ "field" => "depreciation",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('general.depreciation'),
+ "visible" => true,
+ ], [
+ "field" => "number_of_months",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/depreciations/general.number_of_months'),
+ "visible" => true,
+ ], [
+ "field" => "status",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/hardware/table.status'),
+ "visible" => true,
+ ], [
+ "field" => "checked_out_to",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/hardware/table.checkoutto'),
+ "visible" => false,
+ ], [
+ "field" => "location",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('admin/hardware/table.location'),
+ "visible" => true,
+ ], [
+ "field" => "manufacturer",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('general.manufacturer'),
+ "visible" => false,
+ ],[
+ "field" => "supplier",
+ "searchable" => true,
+ "sortable" => true,
+ "title" => trans('general.supplier'),
+ "visible" => false,
+ ], [
+ "field" => "purchase_date",
+ "searchable" => true,
+ "sortable" => true,
+ "visible" => true,
+ "title" => trans('general.purchase_date'),
+ "formatter" => "dateDisplayFormatter"
+ ], [
+ "field" => "purchase_cost",
+ "searchable" => true,
+ "sortable" => true,
+ "visible" => true,
+ "title" => trans('general.purchase_cost'),
+ "footerFormatter" => 'sumFormatter',
+ ], [
+ "field" => "order_number",
+ "searchable" => true,
+ "sortable" => true,
+ "visible" => false,
+ "title" => trans('general.order_number'),
+ ], [
+ "field" => "eol",
+ "searchable" => false,
+ "sortable" => false,
+ "visible" => false,
+ "title" => trans('general.eol'),
+ "formatter" => "dateDisplayFormatter"
+ ], [
+ "field" => "book_value",
+ "searchable" => true,
+ "sortable" => true,
+ "visible" => true,
+ "title" => trans('admin/hardware/table.book_value')
+ ], [
+ "field" => "monthly_depreciation",
+ "searchable" => true,
+ "sortable" => true,
+ "visible" => true,
+ "title" => trans('admin/hardware/table.monthly_depreciation')
+ ],[
+ "field" => "diff",
+ "searchable" => false,
+ "sortable" => false,
+ "visible" => true,
+ "title" => trans('admin/hardware/table.diff')
+ ],[
+ "field" => "warranty_expires",
+ "searchable" => false,
+ "sortable" => false,
+ "visible" => false,
+ "title" => trans('admin/hardware/form.warranty_expires'),
+ "formatter" => "dateDisplayFormatter"
+ ],
+ ];
+
+ return json_encode($layout);
+ }
+
+
+
+ /**
+ * Generate html link to this items name.
+ * @return string
+ */
+ public function nameUrl()
+ {
+ return (string) link_to_route('hardware.show', e($this->name), $this->id);
+ }
+
+ public function modelUrl()
+ {
+ if ($this->model->model) {
+ return $this->model->model->present()->nameUrl();
+ }
+ return '';
+ }
+
+ /**
+ * Generate img tag to this items image.
+ * @return mixed|string
+ */
+ public function imageUrl()
+ {
+ $imagePath = '';
+ if ($this->image && !empty($this->image)) {
+ $imagePath = $this->image;
+ $imageAlt = $this->name;
+ } elseif ($this->model && !empty($this->model->image)) {
+ $imagePath = $this->model->image;
+ $imageAlt = $this->model->name;
+ }
+ $url = config('app.url');
+ if (!empty($imagePath)) {
+ $imagePath = '';
+ }
+ return $imagePath;
+ }
+
+ /**
+ * Generate img tag to this items image.
+ * @return mixed|string
+ */
+ public function imageSrc()
+ {
+ $imagePath = '';
+ if ($this->image && !empty($this->image)) {
+ $imagePath = $this->image;
+ } elseif ($this->model && !empty($this->model->image)) {
+ $imagePath = $this->model->image;
+ }
+ if (!empty($imagePath)) {
+ return config('app.url').'/uploads/assets/'.$imagePath;
+ }
+ return $imagePath;
+ }
+
+ /**
+ * Get Displayable Name
+ * @return string
+ *
+ * @todo this should be factored out - it should be subsumed by fullName (below)
+ *
+ **/
+ public function name()
+ {
+ return $this->fullName;
+ }
+
+ /**
+ * Helper for notification polymorphism.
+ * @return mixed
+ */
+ public function fullName()
+ {
+ $str = '';
+
+ // Asset name
+ if ($this->model->name) {
+ $str .= $this->model->name;
+ }
+
+ // Asset tag
+ if ($this->asset_tag) {
+ $str .= ' ('.$this->model->asset_tag.')';
+ }
+
+ // Asset Model name
+ if ($this->model->model) {
+ $str .= ' - '.$this->model->model->name;
+ }
+ return $str;
+ }
+ /**
+ * Returns the date this item hits EOL.
+ * @return false|string
+ */
+ public function eol_date()
+ {
+
+ if (( $this->purchase_date ) && ( $this->model->model ) && ($this->model->model->eol) ) {
+ $date = date_create($this->purchase_date);
+ date_add($date, date_interval_create_from_date_string($this->model->model->eol . ' months'));
+ return date_format($date, 'Y-m-d');
+ }
+
+ }
+
+ /**
+ * How many months until this asset hits EOL.
+ * @return null
+ */
+ public function months_until_eol()
+ {
+
+ $today = date("Y-m-d");
+ $d1 = new DateTime($today);
+ $d2 = new DateTime($this->eol_date());
+
+ if ($this->eol_date() > $today) {
+ $interval = $d2->diff($d1);
+ } else {
+ $interval = null;
+ }
+
+ return $interval;
+ }
+
+ /**
+ * @return string
+ * This handles the status label "meta" status of "deployed" if
+ * it's assigned. Should maybe deprecate.
+ */
+ public function statusMeta()
+ {
+ if ($this->model->assigned) {
+ return 'deployed';
+ }
+ return $this->model->assetstatus->getStatuslabelType();
+ }
+
+ /**
+ * @return string
+ * This handles the status label "meta" status of "deployed" if
+ * it's assigned. Should maybe deprecate.
+ */
+ public function statusText()
+ {
+ if ($this->model->assigned) {
+ return trans('general.deployed');
+ }
+ return $this->model->assetstatus->name;
+ }
+
+ /**
+ * @return string
+ * This handles the status label "meta" status of "deployed" if
+ * it's assigned. Results look like:
+ *
+ * (if assigned and the status label is "Ready to Deploy"):
+ * (Deployed)
+ *
+ * (f assigned and status label is not "Ready to Deploy":)
+ * Deployed (Another Status Label)
+ *
+ * (if not deployed:)
+ * Another Status Label
+ */
+ public function fullStatusText() {
+ // Make sure the status is valid
+ if ($this->assetstatus) {
+
+ // If the status is assigned to someone or something...
+ if ($this->model->assigned) {
+
+ // If it's assigned and not set to the default "ready to deploy" status
+ if ($this->assetstatus->name != trans('general.ready_to_deploy')) {
+ return trans('general.deployed'). ' (' . $this->model->assetstatus->name.')';
+ }
+
+ // If it's assigned to the default "ready to deploy" status, just
+ // say it's deployed - otherwise it's confusing to have a status that is
+ // both "ready to deploy" and deployed at the same time.
+ return trans('general.deployed');
+ }
+
+ // Return just the status name
+ return $this->model->assetstatus->name;
+ }
+
+ // This status doesn't seem valid - either data has been manually edited or
+ // the status label was deleted.
+ return 'Invalid status';
+ }
+
+ /**
+ * Date the warantee expires.
+ * @return false|string
+ */
+ public function warrantee_expires()
+ {
+ if (($this->purchase_date) && ($this->warranty_months)) {
+ $date = date_create($this->purchase_date);
+ date_add($date, date_interval_create_from_date_string($this->warranty_months . ' months'));
+ return date_format($date, 'Y-m-d');
+ }
+
+ return false;
+ }
+
+ /**
+ * Url to view this item.
+ * @return string
+ */
+ public function viewUrl()
+ {
+ return route('hardware.show', $this->id);
+ }
+
+ public function glyph()
+ {
+ return '';
+ }
+}
diff --git a/resources/views/reports/depreciation.blade.php b/resources/views/reports/depreciation.blade.php
index 611a06887..c1449ce13 100644
--- a/resources/views/reports/depreciation.blade.php
+++ b/resources/views/reports/depreciation.blade.php
@@ -16,156 +16,30 @@
@if (($depreciations) && ($depreciations->count() > 0))
-
{{ trans('admin/companies/table.title') }} | -{{ trans('admin/categories/general.category_name') }} | -{{ trans('admin/hardware/table.asset_tag') }} | -{{ trans('admin/hardware/table.title') }} | - @if ($snipeSettings->display_asset_name) -{{ trans('general.name') }} | - @endif -{{ trans('admin/hardware/table.serial') }} | -{{ trans('admin/depreciations/general.depreciation_name') }} | -{{ trans('admin/depreciations/general.number_of_months') }} | -{{ trans('admin/hardware/table.status') }} | -{{ trans('admin/hardware/table.checkoutto') }} | -{{ trans('admin/hardware/table.location') }} | -{{ trans('admin/hardware/table.purchase_date') }} | -{{ trans('admin/hardware/table.eol') }} | -{{ trans('admin/hardware/table.purchase_cost') }} | -{{ trans('admin/hardware/table.book_value') }} | -{{ trans('admin/hardware/table.monthly_depreciation') }} | -{{ trans('admin/hardware/table.diff') }} | -||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
{{ is_null($asset->company) ? '' : $asset->company->name }} | -- @if ($asset->model) - {{ $asset->model->category->name }} - @endif - | -
- @if ($asset->deleted_at!='')
- |
- {{ $asset->model->name }} | - @if ($snipeSettings->display_asset_name) -{{ $asset->name }} | - @endif -{{ $asset->serial }} | -- @if ($asset->model->depreciation) - {{ $asset->model->depreciation->name }} - @endif - | -- @if ($asset->model->depreciation) - {{ $asset->model->depreciation->months }} - @endif - | -- {{ $asset->assetstatus->name }} - ({{ $asset->present()->statusMeta }}) - | -- @if (($asset->checkedOutToUser()) && ($asset->assigned)) - {{ $asset->assigned->getFullNameAttribute() }} - @else - - @if ($asset->assigned) - {{ $asset->assigned->name }} - @endif - @endif - | -- @if ($asset->location) - {{ $asset->location->name }} - @elseif ($asset->defaultloc) - {{ $asset->defaultloc->name }} - @endif - | -- {{ \Carbon\Carbon::parse($asset->purchase_date)->format('Y-m-d') }} - | - -- @if ($asset->model->eol) {{ $asset->present()->eol_date() }} - @endif - | - - @if ($asset->purchase_cost > 0) -- @if ($asset->location && $asset->location->currency) - {{ $asset->location->currency }} - @else - {{ $snipeSettings->default_currency }} - @endif - {{ \App\Helpers\Helper::formatCurrencyOutput($asset->purchase_cost) }} - | -- @if ($asset->location && $asset->location->currency) - {{ $asset->location->currency }} - @else - {{ $snipeSettings->default_currency }} - @endif - - {{ \App\Helpers\Helper::formatCurrencyOutput($asset->getDepreciatedValue()) }} - | -- @if ($asset->model->depreciation) - @if ($asset->location && $asset->location->currency) - {{ $asset->location->currency }} - @else - {{ $snipeSettings->default_currency }} - @endif - - {{ \App\Helpers\Helper::formatCurrencyOutput(($asset->model->eol > 0 ? ($asset->purchase_cost / $asset->model->eol) : 0)) }} - @endif - | -- @if ($asset->location && $asset->location->currency) - {{ $asset->location->currency }} - @else - {{ $snipeSettings->default_currency }} - @endif - - -{{ \App\Helpers\Helper::formatCurrencyOutput(($asset->purchase_cost - $asset->getDepreciatedValue())) }} - | - @else -- | - | - | - @endif - |