diff --git a/app/Http/Controllers/Api/LicensesController.php b/app/Http/Controllers/Api/LicensesController.php index db39f987aa..105a20bd86 100644 --- a/app/Http/Controllers/Api/LicensesController.php +++ b/app/Http/Controllers/Api/LicensesController.php @@ -26,6 +26,12 @@ class LicensesController extends Controller $licenses = License::with('company', 'manufacturer', 'supplier','category', 'adminuser')->withCount('freeSeats as free_seats_count'); + if ($request->input('status')=='inactive') { + $licenses->ExpiredLicenses(); + } else { + $licenses->ActiveLicenses(); + } + if ($request->filled('company_id')) { $licenses->where('licenses.company_id', '=', $request->input('company_id')); } @@ -94,6 +100,8 @@ class LicensesController extends Controller $licenses->onlyTrashed(); } + + // Make sure the offset and limit are actually integers and do not exceed system limits $offset = ($request->input('offset') > $licenses->count()) ? $licenses->count() : app('api_offset_value'); $limit = app('api_limit_value'); diff --git a/app/Http/Controllers/Licenses/LicenseCheckoutController.php b/app/Http/Controllers/Licenses/LicenseCheckoutController.php index 5b2d344ba5..db378b8f0f 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckoutController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckoutController.php @@ -25,7 +25,7 @@ class LicenseCheckoutController extends Controller * @author [A. Gianotto] [] * @since [v1.0] * @param $id - * @return \Illuminate\Contracts\View\View + * @return \Illuminate\Contracts\View\View |\Illuminate\Http\RedirectResponse * @throws \Illuminate\Auth\Access\AuthorizationException */ public function create(License $license) @@ -39,6 +39,11 @@ class LicenseCheckoutController extends Controller return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats')); } + // Make sure the license is expired or terminated + if ($license->isInactive()) { + return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.license_is_inactive')); + } + // We don't currently allow checking out licenses to locations, so we'll reset that to user if needed if (session()->get('checkout_to_type') == 'location') { session()->put(['checkout_to_type' => 'user']); @@ -70,8 +75,19 @@ class LicenseCheckoutController extends Controller return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.not_found')); } + $this->authorize('checkout', $license); + // Make sure there is at least one available to checkout + if ($license->availCount()->count() < 1) { + return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats')); + } + + // Make sure the license is expired or terminated + if ($license->isInactive()) { + return redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.license_is_inactive')); + } + $licenseSeat = $this->findLicenseSeatToCheckout($license, $seatId); $licenseSeat->created_by = auth()->id(); $licenseSeat->notes = $request->input('notes'); @@ -114,6 +130,7 @@ class LicenseCheckoutController extends Controller throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.not_enough_seats'))); } + if (! $licenseSeat->license->is($license)) { throw new \Illuminate\Http\Exceptions\HttpResponseException(redirect()->route('licenses.index')->with('error', trans('admin/licenses/message.checkout.mismatch'))); } diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php index 39079c6ec5..c360834c1c 100644 --- a/app/Http/Transformers/LicensesTransformer.php +++ b/app/Http/Transformers/LicensesTransformer.php @@ -31,11 +31,11 @@ class LicensesTransformer 'purchase_order' => ($license->purchase_order) ? e($license->purchase_order) : null, 'purchase_date' => Helper::getFormattedDateObject($license->purchase_date, 'date'), 'termination_date' => Helper::getFormattedDateObject($license->termination_date, 'date'), + 'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'), 'depreciation' => ($license->depreciation) ? ['id' => (int) $license->depreciation->id,'name'=> e($license->depreciation->name)] : null, 'purchase_cost' => Helper::formatCurrencyOutput($license->purchase_cost), 'purchase_cost_numeric' => $license->purchase_cost, 'notes' => Helper::parseEscapedMarkedownInline($license->notes), - 'expiration_date' => Helper::getFormattedDateObject($license->expiration_date, 'date'), 'seats' => (int) $license->seats, 'free_seats_count' => (int) $license->free_seats_count - License::unReassignableCount($license), 'remaining' => (int) $license->free_seats_count, diff --git a/app/Models/License.php b/app/Models/License.php index d0a0faf8d9..855995b43c 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -299,16 +299,36 @@ class License extends Depreciable } public function isInactive(): bool -{ - $day = now()->startOfDay(); + { + $day = now()->startOfDay(); - $expired = $this->expiration_date && $this->asDateTime($this->expiration_date)->startofDay()->lessThanOrEqualTo($day); + $expired = $this->expiration_date && $this->asDateTime($this->expiration_date)->startofDay()->lessThanOrEqualTo($day); - $terminated = $this->termination_date && $this->asDateTime($this->termination_date)->startofDay()->lessThanOrEqualTo($day); + $terminated = $this->termination_date && $this->asDateTime($this->termination_date)->startofDay()->lessThanOrEqualTo($day); - return $expired || $terminated; -} + return $this->isExpired() || $this->isTerminated(); + } + + public function isExpired(): bool + { + $day = now()->startOfDay(); + + $expired = $this->expiration_date && $this->asDateTime($this->expiration_date)->startofDay()->lessThanOrEqualTo($day); + + return $expired; + } + + public function isTerminated(): bool + { + $day = now()->startOfDay(); + + $terminated = $this->termination_date && $this->asDateTime($this->termination_date)->startofDay()->lessThanOrEqualTo($day); + + + return $terminated; + } + /** * Sets free_seat_count attribute * @@ -750,6 +770,38 @@ class License extends Depreciable ->get(); } + public function scopeActiveLicenses($query) + { + + return $query->whereNull('deleted_at') + + // The termination date is null or within range + ->where(function ($query) { + $query->whereNull('termination_date') + ->orWhereDate('termination_date', '>', [Carbon::now()]); + }) + ->where(function ($query) { + $query->whereNull('expiration_date') + ->orWhereDate('expiration_date', '>', [Carbon::now()]); + }); + } + + public function scopeExpiredLicenses($query) + { + + return $query->whereNull('deleted_at') + + // The termination date is null or within range + ->where(function ($query) { + $query->whereNull('termination_date') + ->orWhereDate('termination_date', '<=', [Carbon::now()]); + }) + ->orWhere(function ($query) { + $query->whereNull('expiration_date') + ->orWhereDate('expiration_date', '<=', [Carbon::now()]); + }); + } + /** * Query builder scope to order on manufacturer * diff --git a/app/Providers/BreadcrumbsServiceProvider.php b/app/Providers/BreadcrumbsServiceProvider.php index a5092b3ded..07c912bfb7 100644 --- a/app/Providers/BreadcrumbsServiceProvider.php +++ b/app/Providers/BreadcrumbsServiceProvider.php @@ -349,10 +349,19 @@ class BreadcrumbsServiceProvider extends ServiceProvider /** * Licenses Breadcrumbs */ - Breadcrumbs::for('licenses.index', fn (Trail $trail) => - $trail->parent('home', route('home')) - ->push(trans('general.licenses'), route('licenses.index')) - ); + if ((request()->is('licenses*')) && (request()->status=='inactive')) { + Breadcrumbs::for('licenses.index', fn (Trail $trail) => + $trail->parent('home', route('home')) + ->push(trans('general.licenses'), route('licenses.index')) + ->push(trans('general.show_inactive'), route('licenses.index')) + ); + } else { + Breadcrumbs::for('licenses.index', fn (Trail $trail) => + $trail->parent('home', route('home')) + ->push(trans('general.licenses'), route('licenses.index')) + ); + } + Breadcrumbs::for('licenses.create', fn (Trail $trail) => $trail->parent('licenses.index', route('licenses.index')) diff --git a/database/factories/LicenseFactory.php b/database/factories/LicenseFactory.php index 1f5b105f42..591a36da1f 100644 --- a/database/factories/LicenseFactory.php +++ b/database/factories/LicenseFactory.php @@ -33,9 +33,9 @@ class LicenseFactory extends Factory 'seats' => $this->faker->numberBetween(1, 10), 'purchase_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get())->format('Y-m-d'), 'order_number' => $this->faker->numberBetween(1000000, 50000000), - 'expiration_date' => $this->faker->dateTimeBetween('now', '+3 years', date_default_timezone_get())->format('Y-m-d H:i:s'), + 'expiration_date' => null, 'reassignable' => $this->faker->boolean(), - 'termination_date' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get())->format('Y-m-d H:i:s'), + 'termination_date' => null, 'supplier_id' => Supplier::factory(), 'category_id' => Category::factory(), ]; diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index a57c7c9f3a..a3b6eb37e6 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -589,6 +589,7 @@ return [ 'components' => ':count Component|:count Components', ], + 'show_inactive' => 'Expired or Terminated', 'more_info' => 'More Info', 'quickscan_bulk_help' => 'Checking this box will edit the asset record to reflect this new location. Leaving it unchecked will simply note the location in the audit log. Note that if this asset is checked out, it will not change the location of the person, asset or location it is checked out to.', 'whoops' => 'Whoops!', diff --git a/resources/views/licenses/index.blade.php b/resources/views/licenses/index.blade.php index c4efc93593..8012cc2bbe 100755 --- a/resources/views/licenses/index.blade.php +++ b/resources/views/licenses/index.blade.php @@ -27,7 +27,7 @@ id="licensesTable" data-buttons="licenseButtons" class="table table-striped snipe-table" - data-url="{{ route('api.licenses.index') }}" + data-url="{{ route('api.licenses.index', ['status' => e(request('status'))]) }}" data-export-options='{ "fileName": "export-licenses-{{ date('Y-m-d') }}", "ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"] diff --git a/resources/views/licenses/view.blade.php b/resources/views/licenses/view.blade.php index 591870c544..43d2c697c0 100755 --- a/resources/views/licenses/view.blade.php +++ b/resources/views/licenses/view.blade.php @@ -249,6 +249,11 @@
+ @if ($license->isExpired()) + + + + @endif {{ Helper::getFormattedDateObject($license->expiration_date, 'date', false) }}
@@ -262,6 +267,12 @@
+ @if ($license->isTerminated()) + + + + @endif + {{ Helper::getFormattedDateObject($license->termination_date, 'date', false) }}
@@ -545,7 +556,7 @@ @can('checkout', $license) - @if ($license->availCount()->count() > 0) + @if (($license->availCount()->count() > 0) && (!$license->isInactive())) @@ -558,17 +569,13 @@ @else - - + {{ trans('general.checkout') }} - - - + {{ trans('admin/licenses/general.bulk.checkout_all.button') }} - @endif @endcan diff --git a/resources/views/models/index.blade.php b/resources/views/models/index.blade.php index 95351e768d..41d402de0d 100755 --- a/resources/views/models/index.blade.php +++ b/resources/views/models/index.blade.php @@ -36,7 +36,7 @@ id="asssetModelsTable" data-buttons="modelButtons" class="table table-striped snipe-table" - data-url="{{ route('api.models.index', ['status' => request('status')]) }}" + data-url="{{ route('api.models.index', ['status' => e(request('status'))]) }}" data-export-options='{ "fileName": "export-models-{{ date('Y-m-d') }}", "ignoreColumn": ["actions","image","change","checkbox","checkincheckout","icon"] diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 7c3468537d..8ebbc9fb16 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -557,12 +557,17 @@ } function licenseInOutFormatter(value, row) { - if(row.disabled || row.user_can_checkout === false) { - return '{{ trans('general.checkout') }}'; + + // check that checkin is not disabled + if (row.user_can_checkout === false) { + return '{{ trans('general.checkout') }}'; + } else if (row.disabled === true) { + return '{{ trans('general.checkout') }}'; + } else // 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)) { - return '{{ trans('general.checkout') }}'; + if ((row.available_actions.checkout === true) && (row.user_can_checkout === true) && (row.disabled === false)) { + return '{{ trans('general.checkout') }}'; } } // We need a special formatter for license seats, since they don't work exactly the same @@ -572,7 +577,7 @@ if (row.disabled && (row.assigned_user || row.assigned_asset)) { return '{{ trans('general.checkin') }}'; } - if(row.disabled) { + if (row.disabled) { return '{{ trans('general.checkout') }}'; } // The user is allowed to check the license seat out and it's available @@ -580,6 +585,11 @@ return '{{ trans('general.checkout') }}'; } + // The user is allowed to check the license seat in and it's available + if ((row.available_actions.checkin === true) && ((row.assigned_asset) || (row.assigned_user))) { + return '{{ trans('general.checkin') }}'; + } + } function genericCheckinCheckoutFormatter(destination) { @@ -1775,7 +1785,19 @@ attributes: { title: '{{ trans('general.export') }}' } - } + }, + + btnShowInactive: { + text: '{{ (request()->input('status') == "inactive") ? trans('general.list_all') : trans('general.show_inactive') }}', + icon: 'fas fa-clock {{ (request()->input('status') == "inactive") ? ' text-danger' : '' }}', + event () { + window.location.href = '{{ (request()->input('status') == "inactive") ? route('licenses.index') : route('licenses.index', ['status' => 'inactive']) }}'; + }, + attributes: { + title: '{{ (request()->input('status') == "inactive") ? trans('general.list_all') : trans('general.show_inactive') }}', + + } + }, }); diff --git a/resources/views/users/view.blade.php b/resources/views/users/view.blade.php index 6ce7118791..0b0be323ff 100755 --- a/resources/views/users/view.blade.php +++ b/resources/views/users/view.blade.php @@ -897,7 +897,7 @@ @can('update', $license) - {{ trans('general.checkin') }} + {{ trans('general.checkin') }} @endcan