Skip to content

WIP: Include "child" assets in inventory email and other views #16842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/Http/Controllers/Api/AssetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ public function index(Request $request, $action = null, $upcoming_status = null)
'supplier'
); // it might be tempting to add 'assetlog' here, but don't. It blows up update-heavy users.

if ($request->input('include_child_assets') === 'true') {
$assets->with(['assignedAssets.assignedTo']);
}

if ($filter_non_deprecable_assets) {
$non_deprecable_models = AssetModel::select('id')->whereNotNull('depreciation_id')->get();
Expand Down Expand Up @@ -432,6 +435,16 @@ public function index(Request $request, $action = null, $upcoming_status = null)
$total = $assets->count();
$assets = $assets->skip($offset)->take($limit)->get();

if ($request->input('include_child_assets') === 'true') {
// Each asset might have a collection of assignedAssets on it.
// We need to move those up to the top level so the transformer runs on them.
foreach ($assets as $asset) {
if ($asset->assignedAssets->count()) {
$assets = $assets->merge($asset->assignedAssets);
}
}
}


/**
* Include additional associated relationships
Expand Down
22 changes: 22 additions & 0 deletions app/Http/Controllers/ViewAssetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public function getIndex() : View | RedirectResponse
'licenses',
)->find(auth()->id());

if (Setting::getSettings()->show_assigned_assets) {
$user->load([
'assets.assignedAssets',
'assets.assignedAssets.model',
'assets.assignedAssets.model.fieldset.fields',
]);
}

$field_array = array();

// Loop through all the custom fields that are applied to any model the user has assigned
Expand All @@ -58,6 +66,20 @@ public function getIndex() : View | RedirectResponse
}
}

foreach ($asset->assignedAssets as $assignedAsset) {
// Make sure the model has a custom fieldset before trying to loop through the associated fields
if ($assignedAsset->model->fieldset) {

foreach ($assignedAsset->model->fieldset->fields as $field) {
// check and make sure they're allowed to see the value of the custom field
if ($field->display_in_user_view == '1') {
$field_array[$field->db_column] = $field->name;
}

}
}
}

}

// Since some models may re-use the same fieldsets/fields, let's make the array unique so we don't repeat columns
Expand Down
9 changes: 9 additions & 0 deletions app/Notifications/CurrentInventory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Notifications;

use App\Models\Setting;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
Expand Down Expand Up @@ -38,12 +39,20 @@ public function via()
*/
public function toMail()
{
$this->user->load([
'assets.assignedAssets',
'accessories',
'licenses',
'consumables',
]);

$message = (new MailMessage)->markdown('notifications.markdown.user-inventory',
[
'assets' => $this->user->assets,
'accessories' => $this->user->accessories,
'licenses' => $this->user->licenses,
'consumables' => $this->user->consumables,
'show_assigned_assets' => Setting::getSettings()->show_assigned_assets,
])
->subject(trans('mail.inventory_report'));

Expand Down
7 changes: 3 additions & 4 deletions database/factories/AssetFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,11 @@ public function assignedToLocation(Location $location = null)
});
}

public function assignedToAsset()
public function assignedToAsset(Asset $asset = null)
{
return $this->state(function () {
return $this->state(function () use ($asset) {
return [
'model_id' => 1,
'assigned_to' => Asset::factory(),
'assigned_to' => $asset->id ?? Asset::factory(),
'assigned_type' => Asset::class,
];
});
Expand Down
71 changes: 6 additions & 65 deletions resources/views/account/view-assets.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -455,73 +455,14 @@ class="table table-striped snipe-table"

</thead>
<tbody>
@php
$counter = 1
@endphp
@foreach ($user->assets as $asset)
<tr>
<td>{{ $counter }}</td>
<td>
@if (($asset->image) && ($asset->image!=''))
<img src="{{ Storage::disk('public')->url(app('assets_upload_path').e($asset->image)) }}" style="max-height: 30px; width: auto" class="img-responsive" alt="">
@elseif (($asset->model) && ($asset->model->image!=''))
<img src="{{ Storage::disk('public')->url(app('models_upload_path').e($asset->model->image)) }}" style="max-height: 30px; width: auto" class="img-responsive" alt="">
@endif
</td>
<td>
@if (($asset->model) && ($asset->model->category))
{{ $asset->model->category->name }}
@endif
</td>
<td>
{{ $asset->asset_tag }}
</td>
<td>
{{ $asset->name }}
</td>
<td>
{{ $asset->model->name }}
</td>
<td>
{{ $asset->model->model_number }}
</td>
<td>
{{ $asset->serial }}
</td>
<td>
{{ ($asset->defaultLoc) ? $asset->defaultLoc->name : '' }}
</td>
<td>
{{ ($asset->location) ? $asset->location->name : '' }}
</td>
@can('self.view_purchase_cost')
<td>
{!! Helper::formatCurrencyOutput($asset->purchase_cost) !!}
</td>
@endcan

<td>
{{ ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date', false) : null }}
</td>

<td>
{{ Helper::getFormattedDateObject($asset->last_audit_date, 'datetime', false) }}
</td>
<td>
{{ Helper::getFormattedDateObject($asset->next_audit_date, 'date', false) }}
</td>

@foreach ($field_array as $db_column => $field_value)
<td>
{{ $asset->{$db_column} }}
</td>
@endforeach

</tr>
<x-asset-table-row :asset="$asset" :field_array="$field_array" :counter="$loop->iteration" />

@php
$counter++
@endphp
@if ($settings->show_assigned_assets && $asset->assignedAssets->count())
@foreach($asset->assignedAssets as $assignedAsset)
<x-asset-table-row :asset="$assignedAsset" :field_array="$field_array" :counter="$loop->parent->iteration . '.' . $loop->iteration" />
@endforeach
@endif
@endforeach
</tbody>
</table>
Expand Down
68 changes: 68 additions & 0 deletions resources/views/blade/asset-table-row.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@props([
'asset',
'field_array',
'counter',
])

{{--
This component was extracted for the account.view_assets view. Not guaranteed to work in other views.
--}}

<tr>
<td>{{ $counter }}</td>
<td>
@if (($asset->image) && ($asset->image!=''))
<img src="{{ Storage::disk('public')->url(app('assets_upload_path').e($asset->image)) }}" style="max-height: 30px; width: auto" class="img-responsive" alt="">
@elseif (($asset->model) && ($asset->model->image!=''))
<img src="{{ Storage::disk('public')->url(app('models_upload_path').e($asset->model->image)) }}" style="max-height: 30px; width: auto" class="img-responsive" alt="">
@endif
</td>
<td>
@if (($asset->model) && ($asset->model->category))
{{ $asset->model->category->name }}
@endif
</td>
<td>
{{ $asset->asset_tag }}
</td>
<td>
{{ $asset->name }}
</td>
<td>
{{ $asset->model->name }}
</td>
<td>
{{ $asset->model->model_number }}
</td>
<td>
{{ $asset->serial }}
</td>
<td>
{{ ($asset->defaultLoc) ? $asset->defaultLoc->name : '' }}
</td>
<td>
{{ ($asset->location) ? $asset->location->name : '' }}
</td>
@can('self.view_purchase_cost')
<td>
{!! Helper::formatCurrencyOutput($asset->purchase_cost) !!}
</td>
@endcan

<td>
{{ ($asset->asset_eol_date != '') ? Helper::getFormattedDateObject($asset->asset_eol_date, 'date', false) : null }}
</td>

<td>
{{ Helper::getFormattedDateObject($asset->last_audit_date, 'datetime', false) }}
</td>
<td>
{{ Helper::getFormattedDateObject($asset->next_audit_date, 'date', false) }}
</td>

@foreach ($field_array as $db_column => $field_value)
<td>
{{ $asset->{$db_column} }}
</td>
@endforeach
</tr>
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
## {{ $assets->count() }} {{ trans('general.assets') }}

<table width="100%">
<tr><th align="left">{{ trans('mail.name') }} </th><th align="left">{{ trans('mail.asset_tag') }}</th><th align="left">{{ trans('admin/hardware/table.serial') }}</th><th align="left">{{ trans('general.category') }}</th> <th></th> </tr>
<tr><th align="left">#</th><th align="left">{{ trans('mail.name') }} </th><th align="left">{{ trans('mail.asset_tag') }}</th><th align="left">{{ trans('admin/hardware/table.serial') }}</th><th align="left">{{ trans('general.category') }}</th> <th></th> </tr>


@foreach($assets as $asset)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $asset->present()->name }}</td>
<td> {{ $asset->asset_tag }} </td>
<td> {{ $asset->serial }} </td>
Expand All @@ -24,6 +25,22 @@
</td>
@endif
</tr>
@if ($show_assigned_assets && $asset->assignedAssets->count())
@foreach($asset->assignedAssets as $assignedAsset)
<tr>
<td>{{ $loop->parent->iteration }}.{{ $loop->iteration }}</td>
<td>{{ $assignedAsset->present()->name }}</td>
<td> {{ $assignedAsset->asset_tag }} </td>
<td> {{ $assignedAsset->serial }} </td>
<td> {{ $assignedAsset->model->category->name }}</td>
@if (($snipeSettings->show_images_in_email =='1') && $assignedAsset->getImageUrl())
<td>
<img src="{{ asset($assignedAsset->getImageUrl()) }}" alt="Asset" style="max-width: 64px;">
</td>
@endif
</tr>
@endforeach
@endif
@endforeach
</table>
@endif
Expand Down
6 changes: 5 additions & 1 deletion resources/views/users/view.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,11 @@
data-bulk-form-id="#assetsBulkForm"
id="userAssetsListingTable"
class="table table-striped snipe-table"
data-url="{{ route('api.assets.index',['assigned_to' => e($user->id), 'assigned_type' => 'App\Models\User']) }}"
data-url="{{ route('api.assets.index', [
'assigned_to' => e($user->id),
'assigned_type' => 'App\Models\User',
'include_child_assets' => $snipeSettings->show_assigned_assets ? 'true' : 'false',
]) }}"
data-export-options='{
"fileName": "export-{{ str_slug($user->present()->fullName()) }}-assets-{{ date('Y-m-d') }}",
"ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"]
Expand Down
33 changes: 33 additions & 0 deletions tests/Feature/Assets/Api/AssetIndexTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,37 @@ public function testAssetApiIndexAdheresToCompanyScoping()
->assertResponseDoesNotContainInRows($assetA, 'asset_tag')
->assertResponseContainsInRows($assetB, 'asset_tag');
}

public function testCanIncludeChildAssetsForGivenUser()
{
$user = User::factory()->create();

$parentAsset = Asset::factory()->assignedToUser($user)->create(['asset_tag' => 'parent-asset-tag']);
$childAsset = Asset::factory()->assignedToAsset($parentAsset)->create(['asset_tag' => 'child-asset-tag']);
Asset::factory()->assignedToAsset($childAsset)->create(['asset_tag' => 'grandchild-asset-tag']);

$this->actingAsForApi(User::factory()->superuser()->create())
->getJson(
route('api.assets.index', [
'sort' => 'name',
'order' => 'asc',
'offset' => '0',
'limit' => '20',
'assigned_to' => $user->id,
'assigned_type' => 'App\Models\User',
'include_child_assets' => 'true',
]))
->assertOk()
->assertJsonStructure([
'total',
'rows',
])
->assertJson(function (AssertableJson $json) {
return $json->has('rows', 3)
->where('rows.0.asset_tag', 'parent-asset-tag')
->where('rows.1.asset_tag', 'child-asset-tag')
->where('rows.1.asset_tag', 'grandchild-asset-tag')
->etc();
});
}
}
Loading
Loading