diff --git a/.github/workflows/tests-mysql.yml b/.github/workflows/tests-mysql.yml index 237220b337..bc1c9275bb 100644 --- a/.github/workflows/tests-mysql.yml +++ b/.github/workflows/tests-mysql.yml @@ -76,4 +76,16 @@ jobs: DB_DATABASE: snipeit DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_USERNAME: root + LOG_CHANNEL: single + LOG_LEVEL: debug run: php artisan test + + - name: Upload Laravel logs as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }} + path: | + storage/logs/*.log + if-no-files-found: ignore + retention-days: 7 diff --git a/.github/workflows/tests-postgres.yml b/.github/workflows/tests-postgres.yml index 97379ec2bc..5d7d4d0001 100644 --- a/.github/workflows/tests-postgres.yml +++ b/.github/workflows/tests-postgres.yml @@ -75,4 +75,16 @@ jobs: DB_PORT: ${{ job.services.postgresql.ports[5432] }} DB_USERNAME: snipeit DB_PASSWORD: password + LOG_CHANNEL: single + LOG_LEVEL: debug run: php artisan test + + - name: Upload Laravel logs as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }} + path: | + storage/logs/*.log + if-no-files-found: ignore + retention-days: 7 diff --git a/.github/workflows/tests-sqlite.yml b/.github/workflows/tests-sqlite.yml index fdf4ea2ce9..e00e0f7319 100644 --- a/.github/workflows/tests-sqlite.yml +++ b/.github/workflows/tests-sqlite.yml @@ -61,4 +61,16 @@ jobs: - name: Execute tests (Unit and Feature tests) via PHPUnit env: DB_CONNECTION: sqlite + LOG_CHANNEL: single + LOG_LEVEL: debug run: php artisan test + + - name: Upload Laravel logs as artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: laravel-logs-php-${{ matrix.php-version }}-run-${{ github.run_attempt }} + path: | + storage/logs/*.log + if-no-files-found: ignore + retention-days: 7 diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 9db9e4cb2a..5e954a24c5 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -1706,5 +1706,5 @@ class Helper } } return $mismatched; - } + } } diff --git a/app/Http/Controllers/Api/ImportController.php b/app/Http/Controllers/Api/ImportController.php index 79bffd1206..69703770f3 100644 --- a/app/Http/Controllers/Api/ImportController.php +++ b/app/Http/Controllers/Api/ImportController.php @@ -69,7 +69,7 @@ class ImportController extends Controller if (function_exists('iconv')) { $file_contents = $file->getContent(); //TODO - this *does* load the whole file in RAM, but we need that to be able to 'iconv' it? $encoding = $detector->getEncoding($file_contents); - \Log::warning("Discovered encoding: $encoding in uploaded CSV"); + \Log::debug("Discovered encoding: $encoding in uploaded CSV"); $reader = null; if (strcasecmp($encoding, 'UTF-8') != 0) { $transliterated = false; @@ -103,7 +103,7 @@ class ImportController extends Controller $reader = Reader::createFromFileObject($file->openFile('r')); //file pointer leak? try { - $import->header_row = $reader->fetchOne(0); + $import->header_row = $reader->nth(0); } catch (JsonEncodingException $e) { return response()->json( Helper::formatStandardApiResponse( @@ -136,7 +136,7 @@ class ImportController extends Controller try { // Grab the first row to display via ajax as the user picks fields - $import->first_row = $reader->fetchOne(1); + $import->first_row = $reader->nth(1); } catch (JsonEncodingException $e) { return response()->json( Helper::formatStandardApiResponse( diff --git a/app/Http/Controllers/Api/LicenseSeatsController.php b/app/Http/Controllers/Api/LicenseSeatsController.php index 934261e97a..247f71ff26 100644 --- a/app/Http/Controllers/Api/LicenseSeatsController.php +++ b/app/Http/Controllers/Api/LicenseSeatsController.php @@ -128,7 +128,9 @@ class LicenseSeatsController extends Controller // nothing to update return response()->json(Helper::formatStandardApiResponse('success', $licenseSeat, trans('admin/licenses/message.update.success'))); } - + if( $touched && $licenseSeat->unreassignable_seat) { + return response()->json(Helper::formatStandardApiResponse('error', null, trans('admin/licenses/message.checkout.unavailable'))); + } // 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... if ($licenseSeat->isDirty('assigned_to')) { @@ -145,7 +147,11 @@ class LicenseSeatsController extends Controller if ($licenseSeat->save()) { if ($is_checkin) { - $licenseSeat->logCheckin($target, $request->input('notes')); + 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'))); } diff --git a/app/Http/Controllers/Licenses/LicenseCheckinController.php b/app/Http/Controllers/Licenses/LicenseCheckinController.php index 2a17c06d62..2bb2d5e68e 100644 --- a/app/Http/Controllers/Licenses/LicenseCheckinController.php +++ b/app/Http/Controllers/Licenses/LicenseCheckinController.php @@ -64,12 +64,7 @@ class LicenseCheckinController extends Controller $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 $rules = [ @@ -98,6 +93,9 @@ class LicenseCheckinController extends Controller $licenseSeat->assigned_to = null; $licenseSeat->asset_id = null; $licenseSeat->notes = $request->input('notes'); + if (! $licenseSeat->license->reassignable) { + $licenseSeat->unreassignable_seat = true; + } session()->put(['redirect_option' => $request->get('redirect_option')]); if ($request->get('redirect_option') === 'target'){ @@ -106,7 +104,7 @@ class LicenseCheckinController extends Controller // Was the asset updated? 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 Helper::getRedirectOption($request, $license->id, 'Licenses') @@ -132,21 +130,17 @@ class LicenseCheckinController extends Controller $license = License::findOrFail($licenseId); $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) ->whereNotNull('assigned_to') - ->with('user') + ->with('user', 'license') ->get(); + $license = $licenseSeatsByUser->first()?->license; foreach ($licenseSeatsByUser as $user_seat) { $user_seat->assigned_to = null; - + if ($license && ! $license->reassignable) { + $user_seat->unreassignable_seat = true; + } if ($user_seat->save()) { 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')); @@ -159,9 +153,12 @@ class LicenseCheckinController extends Controller ->get(); $count = 0; + $license = $licenseSeatsByAsset->first()?->license; foreach ($licenseSeatsByAsset as $asset_seat) { $asset_seat->asset_id = null; - + if ($license && ! $license->reassignable) { + $asset_seat->unreassignable_seat = true; + } if ($asset_seat->save()) { 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')); diff --git a/app/Http/Controllers/Licenses/LicensesController.php b/app/Http/Controllers/Licenses/LicensesController.php index 98a65f5ad4..b1728469b4 100755 --- a/app/Http/Controllers/Licenses/LicensesController.php +++ b/app/Http/Controllers/Licenses/LicensesController.php @@ -245,16 +245,25 @@ class LicensesController extends Controller $license = License::with('assignedusers')->find($license->id); $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(); - $checkedout_seats_count = ($total_seats_count - $available_seats_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); + } $this->authorize('view', $license); return view('licenses.view', compact('license')) ->with('users_count', $users_count) ->with('total_seats_count', $total_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); } diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index 89a0a07a21..4dce536f91 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -856,7 +856,7 @@ class ReportsController extends Controller } if ($request->filled('assigned_to')) { - $row[] = ($asset->checkedOutToUser() && $asset->assigned) ?? $asset->assigned->display_name; + $row[] = ($asset->checkedOutToUser() && $asset->assigned) ? $asset->assigned->display_name : ''; $row[] = ($asset->checkedOutToUser() && $asset->assigned) ? 'user' : $asset->assignedType(); } diff --git a/app/Http/Transformers/LicenseSeatsTransformer.php b/app/Http/Transformers/LicenseSeatsTransformer.php index 57f4087e9f..17025e7f9f 100644 --- a/app/Http/Transformers/LicenseSeatsTransformer.php +++ b/app/Http/Transformers/LicenseSeatsTransformer.php @@ -7,7 +7,6 @@ use App\Models\License; use App\Models\LicenseSeat; use Illuminate\Support\Facades\Gate; use Illuminate\Database\Eloquent\Collection; - class LicenseSeatsTransformer { public function transformLicenseSeats(Collection $seats, $total) @@ -52,6 +51,7 @@ class LicenseSeatsTransformer 'reassignable' => (bool) $seat->license->reassignable, 'notes' => e($seat->notes), 'user_can_checkout' => (($seat->assigned_to == '') && ($seat->asset_id == '')), + 'disabled' => $seat->unreassignable_seat, ]; $permissions_array['available_actions'] = [ diff --git a/app/Http/Transformers/LicensesTransformer.php b/app/Http/Transformers/LicensesTransformer.php index 678c491257..24822efeca 100644 --- a/app/Http/Transformers/LicensesTransformer.php +++ b/app/Http/Transformers/LicensesTransformer.php @@ -37,7 +37,7 @@ class LicensesTransformer '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, + 'free_seats_count' => (int) $license->free_seats_count - License::unReassignableCount($license), 'remaining' => (int) $license->free_seats_count, 'min_amt' => ($license->min_amt) ? (int) ($license->min_amt) : null, 'license_name' => ($license->license_name) ? e($license->license_name) : null, diff --git a/app/Importer/AssetModelImporter.php b/app/Importer/AssetModelImporter.php index 7cfd8a530d..b458742313 100644 --- a/app/Importer/AssetModelImporter.php +++ b/app/Importer/AssetModelImporter.php @@ -40,11 +40,32 @@ class AssetModelImporter extends ItemImporter { $editingAssetModel = false; - $assetModel = AssetModel::where('name', '=', $this->findCsvMatch($row, 'name'))->first(); + + /** + * This part gets a little confusing, since folks might be importing multiple models with the same name and different model numbers for the first time + * or they might be wanting to update existing models with new model numbers. + */ + + // They are not trying to update existing models, so we'll check for duplicates with model name *and* number + if (! $this->updating) { + $this->log('Finding model by name and model number: '.$this->findCsvMatch($row, 'name').' / '.$this->findCsvMatch($row, 'model_number')); + $assetModel = AssetModel::where('name', '=', $this->findCsvMatch($row, 'name'))->where('model_number', '=', $this->findCsvMatch($row, 'model_number'))->first(); + } else { + + if ($this->findCsvMatch($row, 'id')!='') { + // Override model if an ID was given + $this->log('Finding model by ID: '.$this->findCsvMatch($row, 'id')); + $assetModel = AssetModel::find($this->findCsvMatch($row, 'id')); + } else { + $this->log('Finding model by name: '.$this->findCsvMatch($row, 'name')); + $assetModel = AssetModel::where('name', '=', $this->findCsvMatch($row, 'name'))->first(); + } + } + if ($assetModel) { if (! $this->updating) { - $this->log('A matching Model '.$this->item['name'].' already exists'); + $this->log('A matching Model '.$this->item['name'].' already exists and we are not updating. Skipping.'); return; } @@ -66,6 +87,7 @@ class AssetModelImporter extends ItemImporter $this->item['fieldset'] = trim($this->findCsvMatch($row, 'fieldset')); $this->item['depreciation'] = trim($this->findCsvMatch($row, 'depreciation')); $this->item['requestable'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'requestable'))) == 1) ? 1 : 0; + $this->item['require_serial'] = trim(($this->fetchHumanBoolean($this->findCsvMatch($row, 'require_serial'))) == 1) ? 1 : 0; if (!empty($this->item['category'])) { if ($category = $this->createOrFetchCategory($this->item['category'])) { diff --git a/app/Livewire/Importer.php b/app/Livewire/Importer.php index d86b2469c1..dd80eec2cc 100644 --- a/app/Livewire/Importer.php +++ b/app/Livewire/Importer.php @@ -403,6 +403,7 @@ class Importer extends Component $this->assetmodels_fields = [ + 'id' => trans('general.id'), 'category' => trans('general.category'), 'eol' => trans('general.eol'), 'fieldset' => trans('admin/models/general.fieldset'), @@ -412,6 +413,7 @@ class Importer extends Component 'model_number' => trans('general.model_no'), 'notes' => trans('general.item_notes', ['item' => trans('admin/hardware/form.model')]), 'requestable' => trans('admin/models/general.requestable'), + 'require_serial' => trans('admin/hardware/general.require_serial'), ]; @@ -535,6 +537,10 @@ class Importer extends Component 'product key', 'key', ], + 'require_serial' => + [ + 'serial required', + ], 'model_number' => [ 'model', diff --git a/app/Models/License.php b/app/Models/License.php index ddcac30d4c..ecd1b003e3 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -534,6 +534,7 @@ class License extends Depreciable return $this->licenseSeatsRelation() ->whereNull('asset_id') ->whereNull('assigned_to') + ->where('unreassignable_seat', '=', false) ->whereNull('deleted_at'); } @@ -585,7 +586,22 @@ class License extends Depreciable 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 * @@ -593,11 +609,12 @@ class License extends Depreciable * @since [v1.0] * @return int */ - public function remaincount() + public function remaincount() : int { $total = $this->licenseSeatsCount; $taken = $this->assigned_seats_count; - $diff = ($total - $taken); + $unreassignable = self::unReassignableCount($this); + $diff = ($total - $taken - $unreassignable); return (int) $diff; } @@ -655,12 +672,11 @@ class License extends Depreciable { return $this->licenseseats() ->whereNull('deleted_at') - ->where( - function ($query) { - $query->whereNull('assigned_to') - ->whereNull('asset_id'); - } - ) + ->where('unreassignable_seat', '=', false) + ->where(function ($query) { + $query->whereNull('assigned_to') + ->whereNull('asset_id'); + }) ->orderBy('id', 'asc') ->first(); } diff --git a/app/Models/LicenseSeat.php b/app/Models/LicenseSeat.php index f98028a2f0..9ddd3fb431 100755 --- a/app/Models/LicenseSeat.php +++ b/app/Models/LicenseSeat.php @@ -22,6 +22,9 @@ class LicenseSeat extends SnipeModel implements ICompanyableChild protected $guarded = 'id'; protected $table = 'license_seats'; + protected $casts = [ + 'unreassignable_seat' => 'boolean', + ]; /** * The attributes that are mass assignable. diff --git a/database/factories/LicenseSeatFactory.php b/database/factories/LicenseSeatFactory.php index aaf75bdc2d..ee516b6dac 100644 --- a/database/factories/LicenseSeatFactory.php +++ b/database/factories/LicenseSeatFactory.php @@ -14,6 +14,7 @@ class LicenseSeatFactory extends Factory { return [ 'license_id' => License::factory(), + 'unreassignable_seat' => false, ]; } diff --git a/database/migrations/2025_01_15_190348_adds_unavailable_to_license_seats_tables.php b/database/migrations/2025_01_15_190348_adds_unavailable_to_license_seats_tables.php new file mode 100644 index 0000000000..40c8ad5367 --- /dev/null +++ b/database/migrations/2025_01_15_190348_adds_unavailable_to_license_seats_tables.php @@ -0,0 +1,26 @@ +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'); + }); + } +}; diff --git a/resources/lang/en-US/admin/licenses/message.php b/resources/lang/en-US/admin/licenses/message.php index 74e1d7af5a..9a0219d857 100644 --- a/resources/lang/en-US/admin/licenses/message.php +++ b/resources/lang/en-US/admin/licenses/message.php @@ -50,7 +50,7 @@ return array( 'checkin' => array( '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' ), diff --git a/resources/views/licenses/view.blade.php b/resources/views/licenses/view.blade.php index 8466aebb11..591870c544 100755 --- a/resources/views/licenses/view.blade.php +++ b/resources/views/licenses/view.blade.php @@ -582,20 +582,13 @@ {{ trans('admin/licenses/general.bulk.checkin_all.button') }} - @elseif (! $license->reassignable) - - - - {{ trans('admin/licenses/general.bulk.checkin_all.button') }} - - - @else - - - {{ trans('admin/licenses/general.bulk.checkin_all.button') }} - - @endif - @endcan + @else + + + {{ trans('admin/licenses/general.bulk.checkin_all.button') }} + + @endif + @endcan @can('delete', $license) diff --git a/resources/views/locations/print.blade.php b/resources/views/locations/print.blade.php index 0f7f3b8c45..1214ea0864 100644 --- a/resources/views/locations/print.blade.php +++ b/resources/views/locations/print.blade.php @@ -142,7 +142,7 @@ {{ (($asset->model) && ($asset->model->manufacturer)) ? $asset->model->manufacturer->name : '' }} {{ ($asset->model) ? $asset->model->name : '' }} {{ $asset->serial }} - {{ $asset->location->name }} + {{ ($asset->location->name) ? $asset->location->name : '' }} {{ \App\Helpers\Helper::getFormattedDateObject( $asset->last_checkout, 'datetime', false) }} {{ \App\Helpers\Helper::getFormattedDateObject( $asset->expected_checkin, 'datetime', false) }} diff --git a/resources/views/notifications/markdown/report-expiring-assets.blade.php b/resources/views/notifications/markdown/report-expiring-assets.blade.php index 4c7ac47f1f..0b760058ea 100644 --- a/resources/views/notifications/markdown/report-expiring-assets.blade.php +++ b/resources/views/notifications/markdown/report-expiring-assets.blade.php @@ -2,16 +2,24 @@ {{ trans_choice('mail.assets_warrantee_alert', $assets->count(), ['count'=>$assets->count(), 'threshold' => $threshold]) }} @component('mail::table') - - @foreach ($assets as $asset) @php $expires = Helper::getFormattedDateObject($asset->present()->warranty_expires, 'date'); $diff = round(abs(strtotime($asset->present()->warranty_expires) - strtotime(date('Y-m-d')))/86400); $icon = ($diff <= ($threshold / 2)) ? '🚨' : (($diff <= $threshold) ? '⚠️' : ' '); @endphp - +@component('mail::table') +| | | | +| ------------- | ------------- | ------------- | +| {{ $icon }} **{{ trans('mail.name') }}** | {{ $asset->display_name }}
{{trans('mail.serial').': '.$asset->serial}} | +| **{{ trans('mail.expires') }}** | {{ !is_null($expires) ? $expires['formatted'] : '' }} ({{ $diff }} {{ trans('mail.Days') }}) | +@if ($asset->supplier) +| **{{ trans('mail.supplier') }}** | {{ ($asset->supplier ? e($asset->supplier->name) : '') }} | +@endif +@if ($asset->assignedTo) +| **{{ trans('mail.assigned_to') }}** | {{ e($asset->assignedTo->present()->display_name) }} | +@endif +@endcomponent @endforeach -
 {{ trans('mail.name') }}{{ trans('mail.serial') }}{{ trans('mail.Days') }}{{ trans('mail.expires') }}{{ trans('mail.supplier') }}{{ trans('mail.assigned_to') }}
{{ $icon }} {{ $asset->display_name }}
{{trans('mail.serial').': '.$asset->serial}}
{{ $diff }} {{ trans('mail.Days') }} {{ !is_null($expires) ? $expires['formatted'] : '' }} {{ ($asset->supplier ? e($asset->supplier->name) : '') }} {{ ($asset->assignedTo ? e($asset->assignedTo->present()->display_name) : '') }}
@endcomponent @endcomponent diff --git a/resources/views/partials/bootstrap-table.blade.php b/resources/views/partials/bootstrap-table.blade.php index 8e1ba555ac..e6dbe596d3 100644 --- a/resources/views/partials/bootstrap-table.blade.php +++ b/resources/views/partials/bootstrap-table.blade.php @@ -561,13 +561,16 @@ // Checkouts need the license ID, checkins need the specific seat ID function licenseSeatInOutFormatter(value, row) { + if(row.disabled) { + 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) && ((!row.asset_id) && (!row.assigned_to))) { return '{{ trans('general.checkout') }}'; - } else { - return '{{ trans('general.checkin') }}'; } - + else { + return '{{ trans('general.checkin') }}'; + } } function genericCheckinCheckoutFormatter(destination) { diff --git a/tests/Feature/Checkins/Ui/LicenseCheckinTest.php b/tests/Feature/Checkins/Ui/LicenseCheckinTest.php index 7a6553d8ba..f1f912cad4 100644 --- a/tests/Feature/Checkins/Ui/LicenseCheckinTest.php +++ b/tests/Feature/Checkins/Ui/LicenseCheckinTest.php @@ -20,7 +20,7 @@ class LicenseCheckinTest extends TestCase ->assertForbidden(); } - public function testCannotCheckinNonReassignableLicense() + public function testNonReassignableLicenseSeatCantBeCheckedOut() { $licenseSeat = LicenseSeat::factory() ->notReassignable() @@ -28,13 +28,11 @@ class LicenseCheckinTest extends TestCase ->create(); $this->actingAs(User::factory()->checkoutLicenses()->create()) - ->post(route('licenses.checkin.save', $licenseSeat), [ - 'notes' => 'my note', - 'redirect_option' => 'index', - ]) - ->assertSessionHas('error', trans('admin/licenses/message.checkin.not_reassignable') . '.'); + ->post(route('licenses.checkin.save', $licenseSeat)); - $this->assertNotNull($licenseSeat->fresh()->assigned_to); + $licenseSeat->refresh(); + + $this->assertEquals(true, $licenseSeat->unreassignable_seat); } public function testCannotCheckinLicenseThatIsNotAssigned() diff --git a/tests/Feature/Importing/Api/ImportAssetModelsTest.php b/tests/Feature/Importing/Api/ImportAssetModelsTest.php index a3a0a9ca4d..d79062b2f6 100644 --- a/tests/Feature/Importing/Api/ImportAssetModelsTest.php +++ b/tests/Feature/Importing/Api/ImportAssetModelsTest.php @@ -113,28 +113,28 @@ class ImportAssetModelsTest extends ImportDataTestCase implements TestsPermissio #[Test] public function updateAssetModelFromImport(): void { - $assetmodel = AssetModel::factory()->create()->refresh(); - $category = Category::find($assetmodel->category->name); - $importFileBuilder = ImportFileBuilder::new(['name' => $assetmodel->name, 'model_number' => Str::random(), 'category' => $category]); + $assetmodel = AssetModel::factory()->create(); + $category = Category::find($assetmodel->category_id); + $importFileBuilder = ImportFileBuilder::new(['name' => $assetmodel->name, 'model_number' => Str::random(), 'category' => $category->name]); $row = $importFileBuilder->firstRow(); $import = Import::factory()->assetmodel()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]); $this->actingAsForApi(User::factory()->superuser()->create()); - $this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk(); + $this->importFileResponse(['import' => $import->id, 'import-update' => true]) + ->assertOk() + ->assertExactJson([ + 'payload' => null, + 'status' => 'success', + 'messages' => ['redirect_url' => route('models.index')] + ]); $updatedAssetmodel = AssetModel::query()->find($assetmodel->id); - $updatedAttributes = [ - 'name', - 'model_number' - ]; $this->assertEquals($row['model_number'], $updatedAssetmodel->model_number); + $this->assertEquals($row['name'], $updatedAssetmodel->name); - $this->assertEquals( - Arr::except($assetmodel->attributesToArray(), array_merge($updatedAttributes, $assetmodel->getDates())), - Arr::except($updatedAssetmodel->attributesToArray(), array_merge($updatedAttributes, $assetmodel->getDates())), - ); } + }