Merge pull request #16660 from snipe/cleanup_for_scoped_locations

Small improvements to location-by-company scoping
This commit is contained in:
snipe
2025-04-08 17:57:50 +01:00
committed by GitHub
5 changed files with 64 additions and 25 deletions

View File

@@ -19,25 +19,33 @@ class TestLocationsFMCS extends Command
*
* @var string
*/
protected $description = 'Test for inconsistencies if FullMultipleCompanySupport with scoped locations will be used';
protected $description = 'Test for company ID inconsistencies if FullMultipleCompanySupport with scoped locations will be used.';
/**
* Execute the console command.
*/
public function handle()
{
$this->info('Test for inconsistencies if FullMultipleCompanySupport with scoped locations will be used');
$this->info('Depending on the database size this will take a while, output will be displayed after the complete test is over');
$this->info('This script checks for company ID inconsistencies if Full Multiple Company Support with scoped locations will be used.');
$this->info('This could take few moments if have a very large dataset.');
$this->newLine();
// if parameter location_id is set, only test this location
$location_id = null;
if ($this->option('location_id')) {
$location_id = $this->option('location_id');
}
$ret = Helper::test_locations_fmcs(true, $location_id);
foreach($ret as $output) {
$this->info($output);
}
$mismatched = Helper::test_locations_fmcs(true, $location_id);
$this->warn(trans_choice('admin/settings/message.location_scoping.mismatch', count($mismatched)));
$this->newLine();
$this->info('Edit your locations to associate them with the correct company.');
$header = ['Type', 'ID', 'Name', 'Checkout Type', 'Company ID', 'Item Company', 'Item Location', 'Location Company', 'Location Company ID'];
sort($mismatched);
$this->table($header, $mismatched);
}
}

View File

@@ -1544,7 +1544,7 @@ class Helper
* @return string []
*/
static public function test_locations_fmcs($artisan, $location_id = null, $new_company_id = null) {
$ret = [];
$mismatched = [];
if ($location_id) {
$location = Location::find($location_id);
@@ -1556,18 +1556,31 @@ class Helper
}
foreach($locations as $location) {
// in case of an update of a single location use the newly requested company_id
// in case of an update of a single location, use the newly requested company_id
if ($new_company_id) {
$location_company = $new_company_id;
} else {
$location_company = $location->company_id;
}
// depending on the relationship we must use different operations to retrieve the objects
$keywords_relation = ['many' => ['users', 'assets', 'rtd_assets', 'consumables', 'components', 'accessories', 'assignedAssets', 'assignedAccessories'],
'one' => ['parent', 'manager']];
// Depending on the relationship, we must use different operations to retrieve the objects
$keywords_relation = [
'many' => [
'accessories',
'assets',
'assignedAccessories',
'assignedAssets',
'components',
'consumables',
'rtd_assets',
'users',
],
'one' => [
'manager',
'parent',
]];
// In case of a single location the children must be checked either, becuase we don't walk every location
// In case of a single location, the children must be checked as well, because we don't walk every location
if ($location_id) {
$keywords_relation['many'][] = 'children';
}
@@ -1575,23 +1588,35 @@ class Helper
foreach ($keywords_relation as $relation => $keywords) {
foreach($keywords as $keyword) {
if ($relation == 'many') {
$items = $location->$keyword->all();
$items = $location->{$keyword}->all();
} else {
$items = collect([])->push($location->$keyword);
}
foreach ($items as $item) {
if ($item && $item->company_id != $location_company) {
$ret[] = 'type: ' . get_class($item) . ', id: ' . $item->id . ', company_id: ' . $item->company_id . ', location company_id: ' . $location_company;
// when not called from artisan command we bail out on the first error
if (!$artisan) {
return $ret;
}
$mismatched[] = [
class_basename(get_class($item)),
$item->id,
$item->name ?? $item->asset_tag ?? $item->serial ?? $item->username,
str_replace('App\\Models\\', '', $item->assigned_type) ?? null,
$item->company_id ?? null,
$item->company->name ?? null,
// $item->defaultLoc->id ?? null,
// $item->defaultLoc->name ?? null,
// $item->defaultLoc->company->id ?? null,
// $item->defaultLoc->company->name ?? null,
$item->location->name ?? null,
$item->location->company->name ?? null,
$location_company ?? null,
];
}
}
}
}
}
return $ret;
return $mismatched;
}
}

View File

@@ -325,9 +325,9 @@ class SettingsController extends Controller
// check for inconsistencies when activating scoped locations
if ($old_locations_fmcs == '0' && $setting->scope_locations_fmcs == '1') {
$ret = Helper::test_locations_fmcs(false);
if (count($ret) != 0) {
return redirect()->back()->withInput()->with('error', 'Inconsistencies with scoped locations found, please use php artisan snipeit:test-locations-fmcs for details');
$mismatched = Helper::test_locations_fmcs(false);
if (count($mismatched) != 0) {
return redirect()->back()->withInput()->with('error', trans_choice('admin/settings/message.location_scoping.mismatch', count($mismatched)).' '.trans('admin/settings/message.location_scoping.not_saved'));
}
}

View File

@@ -50,5 +50,11 @@ return [
'error_misc' => 'Something went wrong. :( ',
'webhook_fail' => ' webhook notification failed: Check to make sure the URL is still valid.',
'webhook_channel_not_found' => ' webhook channel not found.'
]
],
'location_scoping' => [
'not_saved' => 'Your settings were not saved.',
'mismatch' => 'There is 1 item in the database that need your attention before you can enable location scoping.|There are :count items in the database that need your attention before you can enable location scoping.',
],
];

View File

@@ -36,7 +36,7 @@
<div class="box-body">
<div class="col-md-12">
<div class="col-md-11">
<!-- Full Multiple Companies Support -->
<div class="form-group {{ $errors->has('full_multiple_companies_support') ? 'error' : '' }}">