FMCS/Floater: Refactor logic into the companyable trait
This commit is contained in:
@@ -10,7 +10,6 @@ use App\Http\Traits\CheckInOutTrait;
|
||||
use App\Models\Accessory;
|
||||
use App\Models\AccessoryCheckout;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\View\View;
|
||||
@@ -67,31 +66,18 @@ class AccessoryCheckoutController extends Controller
|
||||
$target = $this->determineCheckoutTarget();
|
||||
session()->put(['checkout_to_type' => $target]);
|
||||
|
||||
if (Setting::getSettings()->full_multiple_companies_support == '1' && $accessory->company_id) {
|
||||
if ($target instanceof User) {
|
||||
// Users may belong to multiple companies; canReceiveFromCompany() checks the pivot.
|
||||
$mismatch = ! $target->canReceiveFromCompany($accessory->company_id);
|
||||
} elseif (is_null($target->company_id)) {
|
||||
// Target has no company — only a mismatch when floater mode is off.
|
||||
$mismatch = ! Setting::getSettings()->null_company_is_floater;
|
||||
} else {
|
||||
// Both sides have a company; require an exact match.
|
||||
$mismatch = (int) $target->company_id !== (int) $accessory->company_id;
|
||||
}
|
||||
if (! $accessory->canCheckoutTo($target)) {
|
||||
$targetType = match (class_basename($target)) {
|
||||
'User' => trans('general.user'),
|
||||
'Location' => trans('general.location'),
|
||||
default => trans('general.asset'),
|
||||
};
|
||||
|
||||
if ($mismatch) {
|
||||
$targetType = match (class_basename($target)) {
|
||||
'User' => trans('general.user'),
|
||||
'Location' => trans('general.location'),
|
||||
default => trans('general.asset'),
|
||||
};
|
||||
|
||||
return redirect()->back()->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.accessory').' "'.$accessory->name.'"',
|
||||
'item_company' => $accessory->company?->name ?? trans('general.unassigned'),
|
||||
'target' => $targetType.' "'.($target->name ?? $target->username ?? $target->id).'"',
|
||||
]));
|
||||
}
|
||||
return redirect()->back()->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.accessory').' "'.$accessory->name.'"',
|
||||
'item_company' => $accessory->company?->name ?? trans('general.unassigned'),
|
||||
'target' => $targetType.' "'.($target->name ?? $target->username ?? $target->id).'"',
|
||||
]));
|
||||
}
|
||||
|
||||
$accessory->checkout_qty = $request->input('checkout_qty', 1);
|
||||
|
||||
@@ -9,7 +9,6 @@ use App\Http\Requests\AssetCheckoutRequest;
|
||||
use App\Http\Traits\CheckInOutTrait;
|
||||
use App\Models\Asset;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
@@ -119,35 +118,18 @@ class AssetCheckoutController extends Controller
|
||||
// Add any custom fields that should be included in the checkout
|
||||
$asset->customFieldsForCheckinCheckout('display_checkout');
|
||||
|
||||
$settings = Setting::getSettings();
|
||||
if (! $asset->canCheckoutTo($target)) {
|
||||
$targetType = match (class_basename($target)) {
|
||||
'User' => trans('general.user'),
|
||||
'Location' => trans('general.location'),
|
||||
default => trans('general.asset'),
|
||||
};
|
||||
|
||||
// Locations have no company, so we only enforce FMCS when both sides have a company_id.
|
||||
// For users with multiple companies, check all their associated companies via the pivot.
|
||||
if ($settings->full_multiple_companies_support && ! is_null($asset->company_id)) {
|
||||
if ($target instanceof User) {
|
||||
// Users may belong to multiple companies; canReceiveFromCompany() checks the pivot.
|
||||
$mismatch = ! $target->canReceiveFromCompany((int) $asset->company_id);
|
||||
} elseif (is_null($target->company_id)) {
|
||||
// Target has no company — only a mismatch when floater mode is off.
|
||||
$mismatch = ! $settings->null_company_is_floater;
|
||||
} else {
|
||||
// Both sides have a company; require an exact match.
|
||||
$mismatch = (int) $target->company_id !== (int) $asset->company_id;
|
||||
}
|
||||
|
||||
if ($mismatch) {
|
||||
$targetType = match (class_basename($target)) {
|
||||
'User' => trans('general.user'),
|
||||
'Location' => trans('general.location'),
|
||||
default => trans('general.asset'),
|
||||
};
|
||||
|
||||
return redirect()->route('hardware.checkout.create', $asset)->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.asset').' "'.$asset->display_name.'"',
|
||||
'item_company' => $asset->company?->name ?? trans('general.unassigned'),
|
||||
'target' => $targetType.' "'.($target->name ?? $target->username ?? $target->id).'"',
|
||||
]));
|
||||
}
|
||||
return redirect()->route('hardware.checkout.create', $asset)->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.asset').' "'.$asset->display_name.'"',
|
||||
'item_company' => $asset->company?->name ?? trans('general.unassigned'),
|
||||
'target' => $targetType.' "'.($target->name ?? $target->username ?? $target->id).'"',
|
||||
]));
|
||||
}
|
||||
|
||||
session()->put([
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Helpers\Helper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CheckoutAcceptance;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Contracts\View\View;
|
||||
@@ -97,11 +96,7 @@ class ConsumableCheckoutController extends Controller
|
||||
return redirect()->route('consumables.checkout.show', $consumable)->with('error', trans('admin/consumables/message.checkout.user_does_not_exist'))->withInput();
|
||||
}
|
||||
|
||||
if (
|
||||
Setting::getSettings()->full_multiple_companies_support == '1'
|
||||
&& $consumable->company_id
|
||||
&& ! $user->canReceiveFromCompany($consumable->company_id)
|
||||
) {
|
||||
if (! $consumable->canCheckoutTo($user)) {
|
||||
return redirect()->back()->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.consumable').' "'.$consumable->name.'"',
|
||||
'item_company' => $consumable->company?->name ?? trans('general.unassigned'),
|
||||
|
||||
@@ -99,25 +99,16 @@ class LicenseCheckoutController extends Controller
|
||||
if (Setting::getSettings()->full_multiple_companies_support == '1') {
|
||||
if ($request->filled('asset_id')) {
|
||||
$fmcsTarget = Asset::find($request->input('asset_id'));
|
||||
if ($fmcsTarget && $license->company_id) {
|
||||
if (is_null($fmcsTarget->company_id)) {
|
||||
// Target asset has no company — only a mismatch when floater mode is off.
|
||||
$mismatch = ! Setting::getSettings()->null_company_is_floater;
|
||||
} else {
|
||||
// Both sides have a company; require an exact match.
|
||||
$mismatch = $license->company_id !== $fmcsTarget->company_id;
|
||||
}
|
||||
if ($mismatch) {
|
||||
return redirect()->route('licenses.index')->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.license').' "'.$license->name.'"',
|
||||
'item_company' => $license->company?->name ?? trans('general.unassigned'),
|
||||
'target' => trans('general.asset').' "'.($fmcsTarget->name ?? $fmcsTarget->asset_tag).'"',
|
||||
]));
|
||||
}
|
||||
if ($fmcsTarget && ! $license->canCheckoutTo($fmcsTarget)) {
|
||||
return redirect()->route('licenses.index')->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.license').' "'.$license->name.'"',
|
||||
'item_company' => $license->company?->name ?? trans('general.unassigned'),
|
||||
'target' => trans('general.asset').' "'.$fmcsTarget->display_name.'"',
|
||||
]));
|
||||
}
|
||||
} elseif ($request->filled('assigned_to')) {
|
||||
$fmcsTarget = User::find($request->input('assigned_to'));
|
||||
if ($fmcsTarget && $license->company_id && ! $fmcsTarget->canReceiveFromCompany($license->company_id)) {
|
||||
if ($fmcsTarget && ! $license->canCheckoutTo($fmcsTarget)) {
|
||||
return redirect()->route('licenses.index')->with('error', trans('general.error_checkout_company_mismatch', [
|
||||
'item' => trans('general.license').' "'.$license->name.'"',
|
||||
'item_company' => $license->company?->name ?? trans('general.unassigned'),
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
namespace App\Models\Traits;
|
||||
|
||||
use App\Models\CompanyableScope;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
trait CompanyableTrait
|
||||
{
|
||||
@@ -18,4 +21,37 @@ trait CompanyableTrait
|
||||
{
|
||||
static::addGlobalScope(new CompanyableScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this item may be checked out to the given target under FMCS rules.
|
||||
*
|
||||
* Returns true when:
|
||||
* - FMCS is disabled, OR
|
||||
* - this item has no company (uncompanied items are unrestricted), OR
|
||||
* - target is a User whose company pivot includes this item's company, OR
|
||||
* - target has no company and null_company_is_floater is enabled, OR
|
||||
* - target's company_id exactly matches this item's company_id.
|
||||
*/
|
||||
public function canCheckoutTo(Model $target): bool
|
||||
{
|
||||
$settings = Setting::getSettings();
|
||||
|
||||
if (! $settings->full_multiple_companies_support) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $this->company_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($target instanceof User) {
|
||||
return $target->canReceiveFromCompany((int) $this->company_id);
|
||||
}
|
||||
|
||||
if (is_null($target->company_id)) {
|
||||
return (bool) $settings->null_company_is_floater;
|
||||
}
|
||||
|
||||
return (int) $target->company_id === (int) $this->company_id;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user