@@ -37,10 +37,14 @@ class LocationsController extends Controller
|
||||
'address',
|
||||
'address2',
|
||||
'assets_count',
|
||||
'assets_count',
|
||||
'assigned_assets_count',
|
||||
'rtd_assets_count',
|
||||
'accessories_count',
|
||||
'assigned_accessories_count',
|
||||
'assigned_assets_count',
|
||||
'assigned_assets_count',
|
||||
'components_count',
|
||||
'consumables_count',
|
||||
'users_count',
|
||||
'children_count',
|
||||
'city',
|
||||
'country',
|
||||
'created_at',
|
||||
@@ -54,7 +58,6 @@ class LocationsController extends Controller
|
||||
'rtd_assets_count',
|
||||
'state',
|
||||
'updated_at',
|
||||
'users_count',
|
||||
'zip',
|
||||
'notes',
|
||||
];
|
||||
@@ -231,8 +234,13 @@ class LocationsController extends Controller
|
||||
])
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('assignedAccessories as assigned_accessories_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->withCount('consumables as consumables_count')
|
||||
->withCount('components as components_count')
|
||||
->findOrFail($id);
|
||||
|
||||
return (new LocationsTransformer)->transformLocation($location);
|
||||
@@ -327,11 +335,15 @@ class LocationsController extends Controller
|
||||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
$location = Location::withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('assignedAccessories as assigned_accessories_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('consumables as consumables_count')
|
||||
->withCount('components as components_count')
|
||||
->findOrFail($id);
|
||||
|
||||
if (! $location->isDeletable()) {
|
||||
|
||||
@@ -189,7 +189,18 @@ class LocationsController extends Controller
|
||||
{
|
||||
$this->authorize('delete', Location::class);
|
||||
|
||||
if (is_null($location = Location::find($locationId))) {
|
||||
$location = Location::withCount('assignedAssets as assigned_assets_count')
|
||||
->withCount('assets as assets_count')
|
||||
->withCount('assignedAccessories as assigned_accessories_count')
|
||||
->withCount('accessories as accessories_count')
|
||||
->withCount('rtd_assets as rtd_assets_count')
|
||||
->withCount('children as children_count')
|
||||
->withCount('users as users_count')
|
||||
->withCount('consumables as consumables_count')
|
||||
->withCount('components as components_count')
|
||||
->find($locationId);
|
||||
|
||||
if (!$location) {
|
||||
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist'));
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ class LocationsTransformer
|
||||
'assets_count' => (int) $location->assets_count,
|
||||
'rtd_assets_count' => (int) $location->rtd_assets_count,
|
||||
'users_count' => (int) $location->users_count,
|
||||
'consumables_count' => (int) $location->consumables_count,
|
||||
'components_count' => (int) $location->components_count,
|
||||
'currency' => ($location->currency) ? e($location->currency) : null,
|
||||
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
|
||||
'notes' => Helper::parseEscapedMarkedownInline($location->notes),
|
||||
|
||||
@@ -44,7 +44,6 @@ class Location extends SnipeModel
|
||||
];
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Whether the model should inject its identifier to the unique
|
||||
* validation rules before attempting validation. If this property
|
||||
@@ -115,16 +114,16 @@ class Location extends SnipeModel
|
||||
{
|
||||
|
||||
return Gate::allows('delete', $this)
|
||||
&& ($this->deleted_at == '')
|
||||
&& ($this->assigned_assets_count == 0)
|
||||
&& ($this->assets_count == 0)
|
||||
&& ($this->assigned_accessories_count == 0)
|
||||
&& ($this->accessories_count == 0)
|
||||
&& ($this->rtd_assets_count == 0)
|
||||
&& ($this->children_count == 0)
|
||||
&& ($this->consumables_count == 0)
|
||||
&& ($this->componments_count == 0)
|
||||
&& ($this->users_count == 0);
|
||||
&& ($this->deleted_at == '')
|
||||
&& (($this->assets_count ?? $this->assets()->count()) === 0)
|
||||
&& (($this->assigned_assets_count ?? $this->assignedAssets()->count()) === 0)
|
||||
&& (($this->accessories_count ?? $this->accessories()->count()) === 0)
|
||||
&& (($this->assigned_accessories_count ?? $this->assignedAccessories()->count()) === 0)
|
||||
&& (($this->children_count ?? $this->children()->count()) === 0)
|
||||
&& (($this->components_count ?? $this->components()->count()) === 0)
|
||||
&& (($this->consumables_count ?? $this->consumables()->count()) === 0)
|
||||
&& (($this->rtd_assets_count ?? $this->rtd_assets()->count()) === 0)
|
||||
&& (($this->users_count ?? $this->users()->count()) === 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -61,6 +61,15 @@ class LocationPresenter extends Presenter
|
||||
'title' => trans('admin/locations/table.parent'),
|
||||
'visible' => true,
|
||||
'formatter' => 'locationsLinkObjFormatter',
|
||||
], [
|
||||
'field' => 'users_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.people'),
|
||||
'titleTooltip' => trans('general.people'),
|
||||
'visible' => true,
|
||||
'class' => 'css-house-user',
|
||||
], [
|
||||
'field' => 'assets_count',
|
||||
'searchable' => false,
|
||||
@@ -98,7 +107,7 @@ class LocationPresenter extends Presenter
|
||||
'titleTooltip' => trans('general.accessories'),
|
||||
'visible' => true,
|
||||
'class' => 'css-accessory',
|
||||
], [
|
||||
],[
|
||||
'field' => 'assigned_accessories_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
@@ -108,15 +117,25 @@ class LocationPresenter extends Presenter
|
||||
'visible' => true,
|
||||
'class' => 'css-accessory-alt',
|
||||
], [
|
||||
'field' => 'users_count',
|
||||
'field' => 'components_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.people'),
|
||||
'titleTooltip' => trans('general.people'),
|
||||
'title' => trans('general.components'),
|
||||
'titleTooltip' => trans('general.components'),
|
||||
'visible' => true,
|
||||
'class' => 'css-house-user',
|
||||
], [
|
||||
'class' => 'css-component',
|
||||
],
|
||||
[
|
||||
'field' => 'consumables_count',
|
||||
'searchable' => false,
|
||||
'sortable' => true,
|
||||
'switchable' => true,
|
||||
'title' => trans('general.consumables'),
|
||||
'titleTooltip' => trans('general.consumables'),
|
||||
'visible' => true,
|
||||
'class' => 'css-consumable',
|
||||
], [
|
||||
'field' => 'currency',
|
||||
'searchable' => true,
|
||||
'sortable' => true,
|
||||
|
||||
@@ -498,10 +498,12 @@
|
||||
{{ trans('general.delete') }}
|
||||
</button>
|
||||
@else
|
||||
<a href="#" class="btn btn-block btn-sm btn-danger btn-social hidden-print disabled" data-tooltip="true" data-placement="top" data-title="{{ trans('general.cannot_be_deleted') }}">
|
||||
<span data-placement="top" data-tooltip="true" data-title="{{ trans('admin/locations/message.assoc_users') }}">
|
||||
<a href="#" class="btn btn-block btn-sm btn-danger btn-social hidden-print disabled" data-tooltip="true">
|
||||
<x-icon type="delete" />
|
||||
{{ trans('general.delete') }}
|
||||
</a>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
@else
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace Tests\Feature\Locations\Api;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
@@ -24,11 +27,12 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
|
||||
public function testErrorReturnedViaApiIfLocationDoesNotExist()
|
||||
{
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->deleteJson(route('api.users.destroy', 'invalid-id'))
|
||||
->deleteJson(route('api.locations.destroy', 'invalid-id'))
|
||||
->assertOk()
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
|
||||
}
|
||||
|
||||
public function testErrorReturnedViaApiIfLocationIsAlreadyDeleted()
|
||||
@@ -55,9 +59,10 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testDisallowUserDeletionViaApiIfStillHasChildLocations()
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasChildLocations()
|
||||
{
|
||||
$parent = Location::factory()->create();
|
||||
Location::factory()->count(5)->create(['parent_id' => $parent->id]);
|
||||
@@ -69,9 +74,10 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
$this->assertNotSoftDeleted($parent);
|
||||
}
|
||||
|
||||
public function testDisallowUserDeletionViaApiIfStillHasAssetsAssigned()
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasAssetsAssigned()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Asset::factory()->count(5)->assignedToLocation($location)->create();
|
||||
@@ -84,9 +90,10 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testDisallowUserDeletionViaApiIfStillHasAssetsAsLocation()
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasAssetsAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Asset::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
@@ -99,6 +106,73 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasConsumablesAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Consumable::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->assertFalse($location->isDeletable());
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->deleteJson(route('api.locations.destroy', $location->id))
|
||||
->assertOk()
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasComponentsAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Component::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->assertFalse($location->isDeletable());
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->deleteJson(route('api.locations.destroy', $location->id))
|
||||
->assertOk()
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasAccessoriesAssigned()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Accessory::factory()->count(5)->checkedOutToLocation($location)->create();
|
||||
|
||||
$this->assertFalse($location->isDeletable());
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->deleteJson(route('api.locations.destroy', $location->id))
|
||||
->assertOk()
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testDisallowLocationDeletionViaApiIfStillHasAccessoriesAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Accessory::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->assertFalse($location->isDeletable());
|
||||
|
||||
$this->actingAsForApi(User::factory()->superuser()->create())
|
||||
->deleteJson(route('api.locations.destroy', $location->id))
|
||||
->assertOk()
|
||||
->assertStatus(200)
|
||||
->assertStatusMessageIs('error')
|
||||
->json();
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testCanDeleteLocation()
|
||||
|
||||
139
tests/Feature/Locations/Ui/DeleteLocationsTest.php
Normal file
139
tests/Feature/Locations/Ui/DeleteLocationsTest.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Users\Ui;
|
||||
|
||||
use App\Models\Consumable;
|
||||
use Tests\TestCase;
|
||||
use App\Models\Location;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\User;
|
||||
use App\Models\Asset;
|
||||
|
||||
class DeleteLocationsTest extends TestCase
|
||||
{
|
||||
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->actingAs(User::factory()->create())
|
||||
->delete(route('locations.destroy', Location::factory()->create()))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function testCanDeleteLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('success')
|
||||
->assertStatus(302)
|
||||
->assertRedirect(route('locations.index'));
|
||||
|
||||
$this->assertSoftDeleted($location);
|
||||
}
|
||||
|
||||
|
||||
public function testCannotDeleteLocationWithAssetsAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Asset::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testCannotDeleteLocationWithAssetsAssigned()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Asset::factory()->count(5)->assignedToLocation($location)->create();
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testCannotDeleteLocationWithChildren()
|
||||
{
|
||||
$parent = Location::factory()->create();
|
||||
Location::factory()->count(5)->create(['parent_id' => $parent->id]);
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $parent))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($parent);
|
||||
}
|
||||
|
||||
public function testCannotDeleteLocationWithConsumableAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Consumable::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testCannotDeleteLocationWithAccessoriesAssigned()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Accessory::factory()->count(5)->checkedOutToLocation($location)->create();
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testCannotDeleteLocationWithAccessoriesAsLocation()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
Accessory::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
public function testCannotDeleteLocationWithPeople()
|
||||
{
|
||||
$location = Location::factory()->create();
|
||||
User::factory()->count(5)->create(['location_id' => $location->id]);
|
||||
|
||||
$this->actingAs(User::factory()->deleteLocations()->create())
|
||||
->delete(route('locations.destroy', $location))
|
||||
->assertStatus(302)
|
||||
->assertRedirectToRoute('locations.index')
|
||||
->assertSessionHas('error');
|
||||
|
||||
$this->assertNotSoftDeleted($location);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user