mirror of
https://github.com/snipe/snipe-it.git
synced 2026-08-18 03:06:23 +00:00
Filled in some missing searchable relations for locations, companies, maintenances, and depts
This commit is contained in:
@ -88,6 +88,7 @@ class Actionlog extends SnipeModel
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'company' => ['name'],
|
||||
'location' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'username', 'email', 'employee_num'],
|
||||
'user' => ['first_name', 'last_name', 'username', 'email', 'employee_num'],
|
||||
'assets' => ['asset_tag', 'name', 'serial', 'order_number', 'notes', 'purchase_date'],
|
||||
|
||||
@ -98,6 +98,7 @@ final class Company extends SnipeModel
|
||||
* @var array
|
||||
*/
|
||||
protected $searchableRelations = [
|
||||
'parent' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
|
||||
@ -92,6 +92,8 @@ class Department extends SnipeModel
|
||||
protected $searchableRelations = [
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
'company' => ['name'],
|
||||
'location' => ['name'],
|
||||
'manager' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
public function isDeletable()
|
||||
|
||||
@ -115,6 +115,7 @@ class Location extends SnipeModel
|
||||
protected $searchableRelations = [
|
||||
'parent' => ['name'],
|
||||
'company' => ['name'],
|
||||
'manager' => ['first_name', 'last_name', 'display_name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
];
|
||||
|
||||
|
||||
@ -107,6 +107,9 @@ class Maintenance extends SnipeModel implements ICompanyableChild
|
||||
'asset.model' => ['name', 'model_number'],
|
||||
'asset.supplier' => ['name'],
|
||||
'asset.status' => ['name'],
|
||||
'asset.company' => ['name'],
|
||||
'asset.location' => ['name'],
|
||||
'asset.defaultLoc' => ['name'],
|
||||
'supplier' => ['name'],
|
||||
'adminuser' => ['first_name', 'last_name', 'display_name'],
|
||||
'maintenanceType' => ['name'],
|
||||
|
||||
@ -40,6 +40,29 @@ class IndexCompaniesTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
public function test_search_matches_parent_company_name()
|
||||
{
|
||||
// Companies table shows parent as a column. Before adding parent
|
||||
// to Company's $searchableRelations the search silently returned
|
||||
// nothing when typing a parent company's name.
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$parent = Company::factory()->create(['name' => 'Umbrella Holdings LLC']);
|
||||
$child = Company::factory()->create(['parent_id' => $parent->id]);
|
||||
$standalone = Company::factory()->create();
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.companies.index', ['search' => 'Umbrella Holdings']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($child->id, $ids, 'Child should match on parent-company name');
|
||||
$this->assertContains($parent->id, $ids, 'Parent should still match on its own name');
|
||||
$this->assertNotContains($standalone->id, $ids);
|
||||
}
|
||||
|
||||
public function test_adheres_to_full_multiple_companies_support_scoping()
|
||||
{
|
||||
|
||||
|
||||
@ -89,6 +89,47 @@ class DepartmentsIndexTest extends TestCase
|
||||
->assertResponseContainsInRows($departmentB);
|
||||
}
|
||||
|
||||
public function test_search_matches_location_name()
|
||||
{
|
||||
// Departments table shows location as a column; before adding
|
||||
// location to Department's $searchableRelations the search
|
||||
// silently returned nothing when typing a location's name.
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$location = Location::factory()->create(['name' => 'Bratislava HQ']);
|
||||
$matchingDepartment = Department::factory()->create(['location_id' => $location->id]);
|
||||
$otherDepartment = Department::factory()->create();
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.departments.index', ['search' => 'Bratislava']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingDepartment->id, $ids);
|
||||
$this->assertNotContains($otherDepartment->id, $ids);
|
||||
}
|
||||
|
||||
public function test_search_matches_manager_name()
|
||||
{
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$manager = User::factory()->create(['first_name' => 'Ekaterina', 'last_name' => 'Volkova']);
|
||||
$matchingDepartment = Department::factory()->create(['manager_id' => $manager->id]);
|
||||
$otherDepartment = Department::factory()->create();
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.departments.index', ['search' => 'Ekaterina']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingDepartment->id, $ids);
|
||||
$this->assertNotContains($otherDepartment->id, $ids);
|
||||
}
|
||||
|
||||
public function test_department_index_filters_all_supported_exact_fields()
|
||||
{
|
||||
$user = User::factory()->superuser()->create();
|
||||
|
||||
@ -40,4 +40,26 @@ class IndexLocationsTest extends TestCase
|
||||
])
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 3)->etc());
|
||||
}
|
||||
|
||||
public function test_search_matches_manager_name()
|
||||
{
|
||||
// Locations table shows manager as a column; before adding manager
|
||||
// to Location's $searchableRelations the search silently returned
|
||||
// nothing when typing a manager's name.
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$manager = User::factory()->create(['first_name' => 'Anastasia', 'last_name' => 'Krupin']);
|
||||
$matchingLocation = Location::factory()->create(['manager_id' => $manager->id]);
|
||||
$otherLocation = Location::factory()->create();
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.locations.index', ['search' => 'Anastasia']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingLocation->id, $ids);
|
||||
$this->assertNotContains($otherLocation->id, $ids);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,4 +164,79 @@ class IndexMaintenanceTest extends TestCase
|
||||
->getJson(route('api.maintenances.index', ['sort' => 'completed_at', 'order' => 'desc']))
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function test_search_matches_asset_company_name()
|
||||
{
|
||||
// The maintenances table shows the asset's company, so searching
|
||||
// for a company name from the search box should return matching
|
||||
// rows. Before this fix, asset.company wasn't in the Maintenance
|
||||
// model's $searchableRelations, so the search silently matched
|
||||
// nothing.
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$company = \App\Models\Company::factory()->create(['name' => 'Acme Widgets Ltd']);
|
||||
$matchingAsset = Asset::factory()->create(['company_id' => $company->id]);
|
||||
$matchingMaintenance = Maintenance::factory()->create(['asset_id' => $matchingAsset->id]);
|
||||
|
||||
$otherAsset = Asset::factory()->create();
|
||||
$otherMaintenance = Maintenance::factory()->create(['asset_id' => $otherAsset->id]);
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.maintenances.index', ['search' => 'Acme Widgets']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingMaintenance->id, $ids);
|
||||
$this->assertNotContains($otherMaintenance->id, $ids);
|
||||
}
|
||||
|
||||
public function test_search_matches_asset_location_name()
|
||||
{
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$location = \App\Models\Location::factory()->create(['name' => 'Zanzibar HQ']);
|
||||
$matchingAsset = Asset::factory()->create(['location_id' => $location->id]);
|
||||
$matchingMaintenance = Maintenance::factory()->create(['asset_id' => $matchingAsset->id]);
|
||||
|
||||
$otherAsset = Asset::factory()->create();
|
||||
$otherMaintenance = Maintenance::factory()->create(['asset_id' => $otherAsset->id]);
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.maintenances.index', ['search' => 'Zanzibar']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingMaintenance->id, $ids);
|
||||
$this->assertNotContains($otherMaintenance->id, $ids);
|
||||
}
|
||||
|
||||
public function test_search_matches_asset_default_location_name()
|
||||
{
|
||||
// defaultLoc is the RTD (return-to) location on the asset. When an
|
||||
// asset is unassigned, the maintenance table shows the RTD
|
||||
// location as its location column; searching that value should
|
||||
// still find the row.
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$rtd = \App\Models\Location::factory()->create(['name' => 'Reykjavik Warehouse']);
|
||||
$matchingAsset = Asset::factory()->create(['rtd_location_id' => $rtd->id, 'location_id' => null]);
|
||||
$matchingMaintenance = Maintenance::factory()->create(['asset_id' => $matchingAsset->id]);
|
||||
|
||||
$otherAsset = Asset::factory()->create();
|
||||
$otherMaintenance = Maintenance::factory()->create(['asset_id' => $otherAsset->id]);
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.maintenances.index', ['search' => 'Reykjavik']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingMaintenance->id, $ids);
|
||||
$this->assertNotContains($otherMaintenance->id, $ids);
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,4 +188,38 @@ class ActivityReportTest extends TestCase
|
||||
->assertJson(fn (AssertableJson $json) => $json->has('rows', 7)->etc());
|
||||
|
||||
}
|
||||
|
||||
public function test_search_matches_action_log_location_name()
|
||||
{
|
||||
// Activity Report eager-loads and shows the location on each
|
||||
// action_log row (e.g. checkouts to a location). Before adding
|
||||
// location to Actionlog's $searchableRelations, typing that
|
||||
// location's name into the report search silently returned
|
||||
// nothing.
|
||||
$actor = User::factory()->superuser()->create();
|
||||
|
||||
$location = \App\Models\Location::factory()->create(['name' => 'Kraków Office']);
|
||||
$matchingLog = Actionlog::factory()->create([
|
||||
'action_type' => 'checkout',
|
||||
'item_type' => Asset::class,
|
||||
'item_id' => Asset::factory()->create()->id,
|
||||
'location_id' => $location->id,
|
||||
]);
|
||||
$otherLog = Actionlog::factory()->create([
|
||||
'action_type' => 'checkout',
|
||||
'item_type' => Asset::class,
|
||||
'item_id' => Asset::factory()->create()->id,
|
||||
'location_id' => null,
|
||||
]);
|
||||
|
||||
$ids = collect($this->actingAsForApi($actor)
|
||||
->getJson(route('api.activity.index', ['search' => 'Kraków']))
|
||||
->assertOk()
|
||||
->json('rows'))
|
||||
->pluck('id')
|
||||
->all();
|
||||
|
||||
$this->assertContains($matchingLog->id, $ids);
|
||||
$this->assertNotContains($otherLog->id, $ids);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user