mirror of
https://github.com/snipe/snipe-it.git
synced 2025-03-05 20:52:15 -08:00
Merge 1c67d6802d
into c9f55bfd94
This commit is contained in:
commit
4384e51e09
|
@ -1529,4 +1529,5 @@ class Helper
|
||||||
}
|
}
|
||||||
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
|
return redirect()->back()->with('error', trans('admin/hardware/message.checkout.error'));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,9 @@ class LicenseSeatsController extends Controller
|
||||||
// nothing to update
|
// nothing to update
|
||||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||||
}
|
}
|
||||||
|
if( $touched && $licenseSeat->unreassignable_seat) {
|
||||||
|
return response()->json(Helper::formatStandardApiResponse('error', $licenseSeat, trans('admin/licenses/message.checkout.unavailable')));
|
||||||
|
}
|
||||||
// the logging functions expect only one "target". if both asset and user are present in the request,
|
// the logging functions expect only one "target". if both asset and user are present in the request,
|
||||||
// we simply let assets take precedence over users...
|
// we simply let assets take precedence over users...
|
||||||
if ($licenseSeat->isDirty('assigned_to')) {
|
if ($licenseSeat->isDirty('assigned_to')) {
|
||||||
|
@ -136,7 +138,11 @@ class LicenseSeatsController extends Controller
|
||||||
if ($licenseSeat->save()) {
|
if ($licenseSeat->save()) {
|
||||||
|
|
||||||
if ($is_checkin) {
|
if ($is_checkin) {
|
||||||
$licenseSeat->logCheckin($target, $request->input('note'));
|
if(!$licenseSeat->license->reassignable){
|
||||||
|
$licenseSeat->unreassignable_seat = true;
|
||||||
|
$licenseSeat->save();
|
||||||
|
}
|
||||||
|
$licenseSeat->logCheckin($target, $licenseSeat->notes);
|
||||||
|
|
||||||
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success')));
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,12 +64,7 @@ class LicenseCheckinController extends Controller
|
||||||
|
|
||||||
$this->authorize('checkout', $license);
|
$this->authorize('checkout', $license);
|
||||||
|
|
||||||
if (! $license->reassignable) {
|
|
||||||
// Not allowed to checkin
|
|
||||||
Session::flash('error', trans('admin/licenses/message.checkin.not_reassignable') . '.');
|
|
||||||
|
|
||||||
return redirect()->back()->withInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare the rules for the form validation
|
// Declare the rules for the form validation
|
||||||
$rules = [
|
$rules = [
|
||||||
|
@ -95,13 +90,16 @@ class LicenseCheckinController extends Controller
|
||||||
$licenseSeat->assigned_to = null;
|
$licenseSeat->assigned_to = null;
|
||||||
$licenseSeat->asset_id = null;
|
$licenseSeat->asset_id = null;
|
||||||
$licenseSeat->notes = $request->input('notes');
|
$licenseSeat->notes = $request->input('notes');
|
||||||
|
if (! $licenseSeat->license->reassignable) {
|
||||||
|
$licenseSeat->unreassignable_seat = true;
|
||||||
|
}
|
||||||
|
|
||||||
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
session()->put(['redirect_option' => $request->get('redirect_option')]);
|
||||||
|
|
||||||
|
|
||||||
// Was the asset updated?
|
// Was the asset updated?
|
||||||
if ($licenseSeat->save()) {
|
if ($licenseSeat->save()) {
|
||||||
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $request->input('notes')));
|
event(new CheckoutableCheckedIn($licenseSeat, $return_to, auth()->user(), $licenseSeat->notes));
|
||||||
|
|
||||||
|
|
||||||
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
|
return redirect()->to(Helper::getRedirectOption($request, $license->id, 'Licenses'))->with('success', trans('admin/licenses/message.checkin.success'));
|
||||||
|
@ -126,21 +124,17 @@ class LicenseCheckinController extends Controller
|
||||||
$license = License::findOrFail($licenseId);
|
$license = License::findOrFail($licenseId);
|
||||||
$this->authorize('checkin', $license);
|
$this->authorize('checkin', $license);
|
||||||
|
|
||||||
if (! $license->reassignable) {
|
|
||||||
// Not allowed to checkin
|
|
||||||
Session::flash('error', 'License not reassignable.');
|
|
||||||
|
|
||||||
return redirect()->back()->withInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
$licenseSeatsByUser = LicenseSeat::where('license_id', '=', $licenseId)
|
$licenseSeatsByUser = LicenseSeat::where('license_id', '=', $licenseId)
|
||||||
->whereNotNull('assigned_to')
|
->whereNotNull('assigned_to')
|
||||||
->with('user')
|
->with('user', 'license')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
$license = $licenseSeatsByUser->first()?->license;
|
||||||
foreach ($licenseSeatsByUser as $user_seat) {
|
foreach ($licenseSeatsByUser as $user_seat) {
|
||||||
$user_seat->assigned_to = null;
|
$user_seat->assigned_to = null;
|
||||||
|
if ($license && ! $license->reassignable) {
|
||||||
|
$user_seat->unreassignable_seat = true;
|
||||||
|
}
|
||||||
if ($user_seat->save()) {
|
if ($user_seat->save()) {
|
||||||
Log::debug('Checking in '.$license->name.' from user '.$user_seat->username);
|
Log::debug('Checking in '.$license->name.' from user '.$user_seat->username);
|
||||||
$user_seat->logCheckin($user_seat->user, trans('admin/licenses/general.bulk.checkin_all.log_msg'));
|
$user_seat->logCheckin($user_seat->user, trans('admin/licenses/general.bulk.checkin_all.log_msg'));
|
||||||
|
@ -153,9 +147,12 @@ class LicenseCheckinController extends Controller
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
$license = $licenseSeatsByAsset->first()?->license;
|
||||||
foreach ($licenseSeatsByAsset as $asset_seat) {
|
foreach ($licenseSeatsByAsset as $asset_seat) {
|
||||||
$asset_seat->asset_id = null;
|
$asset_seat->asset_id = null;
|
||||||
|
if ($license && ! $license->reassignable) {
|
||||||
|
$asset_seat->unreassignable_seat = true;
|
||||||
|
}
|
||||||
if ($asset_seat->save()) {
|
if ($asset_seat->save()) {
|
||||||
Log::debug('Checking in '.$license->name.' from asset '.$asset_seat->asset_tag);
|
Log::debug('Checking in '.$license->name.' from asset '.$asset_seat->asset_tag);
|
||||||
$asset_seat->logCheckin($asset_seat->asset, trans('admin/licenses/general.bulk.checkin_all.log_msg'));
|
$asset_seat->logCheckin($asset_seat->asset, trans('admin/licenses/general.bulk.checkin_all.log_msg'));
|
||||||
|
|
|
@ -239,16 +239,25 @@ class LicensesController extends Controller
|
||||||
$license = License::with('assignedusers')->find($license->id);
|
$license = License::with('assignedusers')->find($license->id);
|
||||||
|
|
||||||
$users_count = User::where('autoassign_licenses', '1')->count();
|
$users_count = User::where('autoassign_licenses', '1')->count();
|
||||||
$total_seats_count = $license->totalSeatsByLicenseID();
|
|
||||||
|
$total_seats_count = (int) $license->totalSeatsByLicenseID();
|
||||||
$available_seats_count = $license->availCount()->count();
|
$available_seats_count = $license->availCount()->count();
|
||||||
|
$unreassignable_seats_count = License::unReassignableCount($license);
|
||||||
|
|
||||||
|
if(!$license->reassignable){
|
||||||
|
$checkedout_seats_count = ($total_seats_count - $available_seats_count - $unreassignable_seats_count );
|
||||||
|
}
|
||||||
|
else {
|
||||||
$checkedout_seats_count = ($total_seats_count - $available_seats_count);
|
$checkedout_seats_count = ($total_seats_count - $available_seats_count);
|
||||||
|
}
|
||||||
|
|
||||||
$this->authorize('view', $license);
|
$this->authorize('view', $license);
|
||||||
return view('licenses.view', compact('license'))
|
return view('licenses.view', compact('license'))
|
||||||
->with('users_count', $users_count)
|
->with('users_count', $users_count)
|
||||||
->with('total_seats_count', $total_seats_count)
|
->with('total_seats_count', $total_seats_count)
|
||||||
->with('available_seats_count', $available_seats_count)
|
->with('available_seats_count', $available_seats_count)
|
||||||
->with('checkedout_seats_count', $checkedout_seats_count);
|
->with('checkedout_seats_count', $checkedout_seats_count)
|
||||||
|
->with('unreassignable_seats_count', $unreassignable_seats_count);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ use App\Models\License;
|
||||||
use App\Models\LicenseSeat;
|
use App\Models\LicenseSeat;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
||||||
class LicenseSeatsTransformer
|
class LicenseSeatsTransformer
|
||||||
{
|
{
|
||||||
public function transformLicenseSeats(Collection $seats, $total)
|
public function transformLicenseSeats(Collection $seats, $total)
|
||||||
|
@ -48,6 +47,7 @@ class LicenseSeatsTransformer
|
||||||
'reassignable' => (bool) $seat->license->reassignable,
|
'reassignable' => (bool) $seat->license->reassignable,
|
||||||
'notes' => e($seat->notes),
|
'notes' => e($seat->notes),
|
||||||
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
|
'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')),
|
||||||
|
'disabled' => $seat->unreassignable_seat,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($seat_count != 0) {
|
if ($seat_count != 0) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ class LicensesTransformer
|
||||||
'notes' => Helper::parseEscapedMarkedownInline($license->notes),
|
'notes' => Helper::parseEscapedMarkedownInline($license->notes),
|
||||||
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'),
|
||||||
'seats' => (int) $license->seats,
|
'seats' => (int) $license->seats,
|
||||||
'free_seats_count' => (int) $license->free_seats_count,
|
'free_seats_count' => (int) $license->free_seats_count - License::unReassignableCount($license),
|
||||||
'min_amt' => ($license->min_amt) ? (int) ($license->min_amt) : null,
|
'min_amt' => ($license->min_amt) ? (int) ($license->min_amt) : null,
|
||||||
'license_name' => ($license->license_name) ? e($license->license_name) : null,
|
'license_name' => ($license->license_name) ? e($license->license_name) : null,
|
||||||
'license_email' => ($license->license_email) ? e($license->license_email) : null,
|
'license_email' => ($license->license_email) ? e($license->license_email) : null,
|
||||||
|
|
|
@ -533,6 +533,7 @@ class License extends Depreciable
|
||||||
return $this->licenseSeatsRelation()
|
return $this->licenseSeatsRelation()
|
||||||
->whereNull('asset_id')
|
->whereNull('asset_id')
|
||||||
->whereNull('assigned_to')
|
->whereNull('assigned_to')
|
||||||
|
->where('unreassignable_seat', '=', false)
|
||||||
->whereNull('deleted_at');
|
->whereNull('deleted_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,7 +583,22 @@ class License extends Depreciable
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Calculates the number of unreassignable seats
|
||||||
|
*
|
||||||
|
* @author G. Martinez
|
||||||
|
* @since [v7.1.15]
|
||||||
|
*/
|
||||||
|
public static function unReassignableCount($license) : int
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
if (!$license->reassignable) {
|
||||||
|
$count = licenseSeat::query()->where('unreassignable_seat', '=', true)
|
||||||
|
->where('license_id', '=', $license->id)
|
||||||
|
->count();
|
||||||
|
}
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Calculates the number of remaining seats
|
* Calculates the number of remaining seats
|
||||||
*
|
*
|
||||||
|
@ -590,11 +606,12 @@ class License extends Depreciable
|
||||||
* @since [v1.0]
|
* @since [v1.0]
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function remaincount()
|
public function remaincount() : int
|
||||||
{
|
{
|
||||||
$total = $this->licenseSeatsCount;
|
$total = $this->licenseSeatsCount;
|
||||||
$taken = $this->assigned_seats_count;
|
$taken = $this->assigned_seats_count;
|
||||||
$diff = ($total - $taken);
|
$unreassignable = self::unReassignableCount($this);
|
||||||
|
$diff = ($total - $taken - $unreassignable);
|
||||||
|
|
||||||
return (int) $diff;
|
return (int) $diff;
|
||||||
}
|
}
|
||||||
|
@ -652,6 +669,7 @@ class License extends Depreciable
|
||||||
{
|
{
|
||||||
return $this->licenseseats()
|
return $this->licenseseats()
|
||||||
->whereNull('deleted_at')
|
->whereNull('deleted_at')
|
||||||
|
->where('unreassignable_seat', '=', false)
|
||||||
->where(function ($query) {
|
->where(function ($query) {
|
||||||
$query->whereNull('assigned_to')
|
$query->whereNull('assigned_to')
|
||||||
->whereNull('asset_id');
|
->whereNull('asset_id');
|
||||||
|
|
|
@ -21,6 +21,9 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild
|
||||||
|
|
||||||
protected $guarded = 'id';
|
protected $guarded = 'id';
|
||||||
protected $table = 'license_seats';
|
protected $table = 'license_seats';
|
||||||
|
protected $casts = [
|
||||||
|
'unreassignable_seat' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
|
|
|
@ -14,6 +14,7 @@ class LicenseSeatFactory extends Factory
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'license_id' => License::factory(),
|
'license_id' => License::factory(),
|
||||||
|
'unreassignable_seat' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('license_seats', function (Blueprint $table) {
|
||||||
|
$table->addColumn('boolean', 'unreassignable_seat')->default(false)->after('assigned_to');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('license_seats', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('unreassignable_seat');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -50,7 +50,7 @@ return array(
|
||||||
|
|
||||||
'checkin' => array(
|
'checkin' => array(
|
||||||
'error' => 'There was an issue checking in the license. Please try again.',
|
'error' => 'There was an issue checking in the license. Please try again.',
|
||||||
'not_reassignable' => 'License not reassignable',
|
'not_reassignable' => 'Seat has been used',
|
||||||
'success' => 'The license was checked in successfully'
|
'success' => 'The license was checked in successfully'
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
@ -573,13 +573,6 @@
|
||||||
{{ trans('admin/licenses/general.bulk.checkin_all.button') }}
|
{{ trans('admin/licenses/general.bulk.checkin_all.button') }}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
@elseif (! $license->reassignable)
|
|
||||||
<span data-tooltip="true" title=" {{ trans('admin/licenses/general.bulk.checkin_all.disabled_tooltip_reassignable') }}">
|
|
||||||
<a href="#" class="btn btn-primary bg-purple btn-sm btn-social btn-block hidden-print disabled" style="margin-bottom: 25px;">
|
|
||||||
<x-icon type="checkin" />
|
|
||||||
{{ trans('admin/licenses/general.bulk.checkin_all.button') }}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
@else
|
@else
|
||||||
<a href="#" class="btn btn-primary bg-purple btn-sm btn-social btn-block hidden-print" style="margin-bottom: 25px;" data-toggle="modal" data-tooltip="true" data-target="#checkinFromAllModal" data-content="{{ trans('general.sure_to_delete') }} data-title="{{ trans('general.delete') }}" onClick="return false;">
|
<a href="#" class="btn btn-primary bg-purple btn-sm btn-social btn-block hidden-print" style="margin-bottom: 25px;" data-toggle="modal" data-tooltip="true" data-target="#checkinFromAllModal" data-content="{{ trans('general.sure_to_delete') }} data-title="{{ trans('general.delete') }}" onClick="return false;">
|
||||||
<x-icon type="checkin" />
|
<x-icon type="checkin" />
|
||||||
|
|
|
@ -464,13 +464,16 @@
|
||||||
// Checkouts need the license ID, checkins need the specific seat ID
|
// Checkouts need the license ID, checkins need the specific seat ID
|
||||||
|
|
||||||
function licenseSeatInOutFormatter(value, row) {
|
function licenseSeatInOutFormatter(value, row) {
|
||||||
|
if(row.disabled) {
|
||||||
|
return '<a href="{{ config('app.url') }}/licenses/' + row.id + '/checkin" class="btn btn-sm bg-maroon disabled" data-tooltip="true" title="{{ trans('general.checkin_tooltip') }}">{{ trans('general.checkout') }}</a>';
|
||||||
|
} else
|
||||||
// The user is allowed to check the license seat out and it's available
|
// The user is allowed to check the license seat out and it's available
|
||||||
if ((row.available_actions.checkout === true) && (row.user_can_checkout === true) && ((!row.asset_id) && (!row.assigned_to))) {
|
if ((row.available_actions.checkout === true) && (row.user_can_checkout === true) && ((!row.asset_id) && (!row.assigned_to))) {
|
||||||
return '<a href="{{ config('app.url') }}/licenses/' + row.license_id + '/checkout/'+row.id+'" class="btn btn-sm bg-maroon" data-tooltip="true" title="{{ trans('general.checkout_tooltip') }}">{{ trans('general.checkout') }}</a>';
|
return '<a href="{{ config('app.url') }}/licenses/' + row.license_id + '/checkout/'+row.id+'" class="btn btn-sm bg-maroon" data-tooltip="true" title="{{ trans('general.checkout_tooltip') }}">{{ trans('general.checkout') }}</a>';
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return '<a href="{{ config('app.url') }}/licenses/' + row.id + '/checkin" class="btn btn-sm bg-purple" data-tooltip="true" title="{{ trans('general.checkin_tooltip') }}">{{ trans('general.checkin') }}</a>';
|
return '<a href="{{ config('app.url') }}/licenses/' + row.id + '/checkin" class="btn btn-sm bg-purple" data-tooltip="true" title="{{ trans('general.checkin_tooltip') }}">{{ trans('general.checkin') }}</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function genericCheckinCheckoutFormatter(destination) {
|
function genericCheckinCheckoutFormatter(destination) {
|
||||||
|
|
27
tests/Feature/Checkins/Api/LicenseCheckinTest.php
Normal file
27
tests/Feature/Checkins/Api/LicenseCheckinTest.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
namespace Tests\Feature\Checkins\Api;
|
||||||
|
|
||||||
|
use App\Models\LicenseSeat;
|
||||||
|
use App\Models\User;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class LicenseCheckinTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function testUnreassignableLicenseSeatMarkedUponCheckin()
|
||||||
|
{
|
||||||
|
$licenseSeat = LicenseSeat::factory()
|
||||||
|
->notReassignable()
|
||||||
|
->assignedToUser()
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$this->assertEquals(false, $licenseSeat->unreassignable_seat);
|
||||||
|
|
||||||
|
$this->actingAs(User::factory()->checkoutLicenses()->create())
|
||||||
|
->post(route('licenses.checkin.save', $licenseSeat));
|
||||||
|
|
||||||
|
$licenseSeat->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals(true, $licenseSeat->unreassignable_seat);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ class LicenseCheckinTest extends TestCase
|
||||||
->assertForbidden();
|
->assertForbidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCannotCheckinNonReassignableLicense()
|
public function testNonReassignableLicenseSeatCantBeCheckedOut()
|
||||||
{
|
{
|
||||||
$licenseSeat = LicenseSeat::factory()
|
$licenseSeat = LicenseSeat::factory()
|
||||||
->notReassignable()
|
->notReassignable()
|
||||||
|
@ -28,13 +28,11 @@ class LicenseCheckinTest extends TestCase
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
$this->actingAs(User::factory()->checkoutLicenses()->create())
|
$this->actingAs(User::factory()->checkoutLicenses()->create())
|
||||||
->post(route('licenses.checkin.save', $licenseSeat), [
|
->post(route('licenses.checkin.save', $licenseSeat));
|
||||||
'notes' => 'my note',
|
|
||||||
'redirect_option' => 'index',
|
|
||||||
])
|
|
||||||
->assertSessionHas('error', trans('admin/licenses/message.checkin.not_reassignable') . '.');
|
|
||||||
|
|
||||||
$this->assertNotNull($licenseSeat->fresh()->assigned_to);
|
$licenseSeat->refresh();
|
||||||
|
|
||||||
|
$this->assertEquals(true, $licenseSeat->unreassignable_seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCannotCheckinLicenseThatIsNotAssigned()
|
public function testCannotCheckinLicenseThatIsNotAssigned()
|
||||||
|
|
Loading…
Reference in a new issue