some refactor + tests

This commit is contained in:
spencerrlongg 2023-11-28 15:17:46 -06:00
parent 8c7edcb357
commit 75ac7f80b9
6 changed files with 280 additions and 32 deletions

View file

@ -3,15 +3,15 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Events\CheckoutableCheckedIn; use App\Events\CheckoutableCheckedIn;
use App\Http\Requests\StoreAssetRequest;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\AssetCheckoutRequest; use App\Http\Requests\AssetCheckoutRequest;
use App\Http\Transformers\AssetsTransformer; use App\Http\Transformers\AssetsTransformer;
use App\Http\Transformers\DepreciationReportTransformer;
use App\Http\Transformers\LicensesTransformer; use App\Http\Transformers\LicensesTransformer;
use App\Http\Transformers\SelectlistTransformer; use App\Http\Transformers\SelectlistTransformer;
use App\Models\Actionlog;
use App\Models\Asset; use App\Models\Asset;
use App\Models\AssetModel; use App\Models\AssetModel;
use App\Models\Company; use App\Models\Company;
@ -20,11 +20,12 @@ use App\Models\License;
use App\Models\Location; use App\Models\Location;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use Auth; use \Illuminate\Support\Facades\Auth;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Http\Requests\ImageUploadRequest; use App\Http\Requests\ImageUploadRequest;
use Illuminate\Support\Facades\Log;
use Input; use Input;
use Paginator; use Paginator;
use Slack; use Slack;
@ -533,35 +534,17 @@ class AssetsController extends Controller
* @since [v4.0] * @since [v4.0]
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
*/ */
public function store(ImageUploadRequest $request) public function store(StoreAssetRequest $request)
{ {
$this->authorize('create', Asset::class);
$asset = new Asset(); $asset = new Asset();
$asset->model()->associate(AssetModel::find((int) $request->get('model_id'))); $asset->model()->associate(AssetModel::find((int) $request->get('model_id')));
$asset->name = $request->get('name'); $asset->fill($request->validated());
$asset->serial = $request->get('serial');
$asset->company_id = Company::getIdForCurrentUser($request->get('company_id'));
$asset->model_id = $request->get('model_id');
$asset->order_number = $request->get('order_number');
$asset->notes = $request->get('notes');
$asset->asset_tag = $request->get('asset_tag', Asset::autoincrement_asset());
$asset->user_id = Auth::id(); $asset->user_id = Auth::id();
$asset->archived = '0'; $asset->archived = '0';
$asset->physical = '1'; $asset->physical = '1';
$asset->depreciate = '0'; $asset->depreciate = '0';
$asset->status_id = $request->get('status_id', 0);
$asset->warranty_months = $request->get('warranty_months', null);
$asset->purchase_cost = $request->get('purchase_cost');
$asset->asset_eol_date = $request->get('asset_eol_date', $asset->present()->eol_date());
$asset->purchase_date = $request->get('purchase_date', null);
$asset->assigned_to = $request->get('assigned_to', null);
$asset->supplier_id = $request->get('supplier_id');
$asset->requestable = $request->get('requestable', 0);
$asset->rtd_location_id = $request->get('rtd_location_id', null);
$asset->location_id = $request->get('rtd_location_id', null);
/** /**
* this is here just legacy reasons. Api\AssetController * this is here just legacy reasons. Api\AssetController
@ -585,22 +568,22 @@ class AssetsController extends Controller
// If input value is null, use custom field's default value // If input value is null, use custom field's default value
if ($field_val == null) { if ($field_val == null) {
\Log::debug('Field value for '.$field->db_column.' is null'); Log::debug('Field value for '.$field->db_column.' is null');
$field_val = $field->defaultValue($request->get('model_id')); $field_val = $field->defaultValue($request->get('model_id'));
\Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id'))); Log::debug('Use the default fieldset value of '.$field->defaultValue($request->get('model_id')));
} }
// if the field is set to encrypted, make sure we encrypt the value // if the field is set to encrypted, make sure we encrypt the value
if ($field->field_encrypted == '1') { if ($field->field_encrypted == '1') {
\Log::debug('This model field is encrypted in this fieldset.'); Log::debug('This model field is encrypted in this fieldset.');
if (Gate::allows('admin')) { if (Gate::allows('admin')) {
// If input value is null, use custom field's default value // If input value is null, use custom field's default value
if (($field_val == null) && ($request->has('model_id') != '')) { if (($field_val == null) && ($request->has('model_id') != '')) {
$field_val = \Crypt::encrypt($field->defaultValue($request->get('model_id'))); $field_val = Crypt::encrypt($field->defaultValue($request->get('model_id')));
} else { } else {
$field_val = \Crypt::encrypt($request->input($field->db_column)); $field_val = Crypt::encrypt($request->input($field->db_column));
} }
} }
} }

View file

@ -3,7 +3,7 @@
namespace App\Http\Requests; namespace App\Http\Requests;
use App\Models\Asset; use App\Models\Asset;
use Illuminate\Foundation\Http\FormRequest; use App\Models\Company;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
class StoreAssetRequest extends ImageUploadRequest class StoreAssetRequest extends ImageUploadRequest
@ -20,7 +20,10 @@ class StoreAssetRequest extends ImageUploadRequest
public function prepareForValidation(): void public function prepareForValidation(): void
{ {
// $this->merge([
'asset_tag' => $this->asset_tag ?? Asset::autoincrement_asset(),
'company_id' => Company::getIdForCurrentUser($this->company_id),
]);
} }
/** /**

View file

@ -92,7 +92,7 @@ class Asset extends Depreciable
'name' => 'max:255|nullable', 'name' => 'max:255|nullable',
'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array', 'model_id' => 'required|integer|exists:models,id,deleted_at,NULL|not_array',
'status_id' => 'required|integer|exists:status_labels,id', 'status_id' => 'required|integer|exists:status_labels,id',
'company_id' => 'integer|nullable', 'company_id' => 'integer|nullable|exists:companies,id',
'warranty_months' => 'numeric|nullable|digits_between:0,240', 'warranty_months' => 'numeric|nullable|digits_between:0,240',
'physical' => 'numeric|max:1|nullable', 'physical' => 'numeric|max:1|nullable',
'last_checkout' => 'date_format:Y-m-d H:i:s|nullable', 'last_checkout' => 'date_format:Y-m-d H:i:s|nullable',
@ -107,6 +107,10 @@ class Asset extends Depreciable
'asset_eol_date' => 'date|nullable', 'asset_eol_date' => 'date|nullable',
'eol_explicit' => 'boolean|nullable', 'eol_explicit' => 'boolean|nullable',
'byod' => 'boolean', 'byod' => 'boolean',
'order_number' => 'max:255|nullable',
'notes' => 'max:65535|nullable',
'assigned_to' => 'nullable|integer|exists:users,id',
'requestable' => 'boolean',
]; ];

View file

@ -2,7 +2,14 @@
namespace Tests\Feature\Api\Assets; namespace Tests\Feature\Api\Assets;
use App\Models\Asset;
use App\Models\AssetModel;
use App\Models\Company;
use App\Models\Location;
use App\Models\Statuslabel;
use App\Models\Supplier;
use App\Models\User; use App\Models\User;
use Carbon\Carbon;
use Tests\Support\InteractsWithSettings; use Tests\Support\InteractsWithSettings;
use Tests\TestCase; use Tests\TestCase;
@ -16,4 +23,223 @@ class AssetStoreTest extends TestCase
->postJson(route('api.assets.store')) ->postJson(route('api.assets.store'))
->assertForbidden(); ->assertForbidden();
} }
public function testAllAssetAttributesAreStored()
{
$company = Company::factory()->create();
$location = Location::factory()->create();
$model = AssetModel::factory()->create();
$rtdLocation = Location::factory()->create();
$status = Statuslabel::factory()->create();
$supplier = Supplier::factory()->create();
$user = User::factory()->createAssets()->create();
$userAssigned = User::factory()->create();
$response = $this->actingAsForApi($user)
->postJson(route('api.assets.store'), [
'archived' => true,
'asset_eol_date' => '2024-06-02',
'asset_tag' => 'random_string',
'assigned_to' => $userAssigned->id,
'company_id' => $company->id,
'depreciate' => true,
'last_audit_date' => '2023-09-03',
'location_id' => $location->id,
'model_id' => $model->id,
'name' => 'A New Asset',
'notes' => 'Some notes',
'order_number' => '5678',
'purchase_cost' => '123.45',
'purchase_date' => '2023-09-02',
'requestable' => true,
'rtd_location_id' => $rtdLocation->id,
'serial' => '1234567890',
'status_id' => $status->id,
'supplier_id' => $supplier->id,
'warranty_months' => 10,
])
->assertOk()
->assertStatusMessageIs('success')
->json();
$asset = Asset::find($response['payload']['id']);
$this->assertTrue($asset->adminuser->is($user));
// @todo: this is explicitly set 0 in the controller but they docs say they are customizable
// $this->assertTrue($asset->archived);
// @todo: This isn't in the docs but it's in the controller
$this->assertEquals('2024-06-02', $asset->asset_eol_date);
$this->assertEquals('random_string', $asset->asset_tag);
// @todo: This isn't in the docs but it's in the controller (should it be removed?)
$this->assertEquals($userAssigned->id, $asset->assigned_to);
// @todo: This is not in the docs but it's in the controller
$this->assertTrue($asset->company->is($company));
// @todo: this is explicitly set 0 in the controller but they docs say they are customizable
// $this->assertTrue($asset->depreciate);
// @todo: this is in the docs but not the controller
// $this->assertEquals('2023-09-03', $asset->last_audit_date);
// @todo: this is set to rtd_location_id in the controller but customizable in the docs
// $this->assertTrue($asset->location->is($location));
$this->assertTrue($asset->model->is($model));
$this->assertEquals('A New Asset', $asset->name);
$this->assertEquals('Some notes', $asset->notes);
$this->assertEquals('5678', $asset->order_number);
$this->assertEquals('123.45', $asset->purchase_cost);
$this->assertTrue($asset->purchase_date->is('2023-09-02'));
$this->assertEquals('1', $asset->requestable);
$this->assertTrue($asset->defaultLoc->is($rtdLocation));
$this->assertEquals('1234567890', $asset->serial);
$this->assertTrue($asset->assetstatus->is($status));
$this->assertTrue($asset->supplier->is($supplier));
$this->assertEquals(10, $asset->warranty_months);
}
public function testAssetGetsAssetTagWithAutoIncrement()
{
$model = AssetModel::factory()->create();
$status = Statuslabel::factory()->create();
$this->settings->enableAutoIncrement();
$response = $this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'model_id' => $model->id,
'status_id' => $status->id,
])
->assertOk()
->assertStatusMessageIs('success');
$asset = Asset::find($response->json()['payload']['id']);
$this->assertNotNull($asset->asset_tag);
}
public function testAssetCreationFailsWithNoAssetTagOrAutoIncrement()
{
$model = AssetModel::factory()->create();
$status = Statuslabel::factory()->create();
$this->settings->disableAutoIncrement();
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'model_id' => $model->id,
'status_id' => $status->id,
])
->assertOk()
->assertStatusMessageIs('error');
}
public function testUniqueSerialNumbersIsEnforcedWhenEnabled()
{
$model = AssetModel::factory()->create();
$status = Statuslabel::factory()->create();
$serial = '1234567890';
$this->settings->enableAutoIncrement();
$this->settings->enableUniqueSerialNumbers();
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'model_id' => $model->id,
'status_id' => $status->id,
'serial' => $serial,
])
->assertOk()
->assertStatusMessageIs('success');
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'model_id' => $model->id,
'status_id' => $status->id,
'serial' => $serial,
])
->assertOk()
->assertStatusMessageIs('error');
}
public function testUniqueSerialNumbersIsNotEnforcedWhenDisabled()
{
$model = AssetModel::factory()->create();
$status = Statuslabel::factory()->create();
$serial = '1234567890';
$this->settings->enableAutoIncrement();
$this->settings->disableUniqueSerialNumbers();
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'model_id' => $model->id,
'status_id' => $status->id,
'serial' => $serial,
])
->assertOk()
->assertStatusMessageIs('success');
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'model_id' => $model->id,
'status_id' => $status->id,
'serial' => $serial,
])
->assertOk()
->assertStatusMessageIs('success');
}
public function testAssetTagsMustBeUniqueWhenUndeleted()
{
$model = AssetModel::factory()->create();
$status = Statuslabel::factory()->create();
$asset_tag = '1234567890';
$this->settings->disableAutoIncrement();
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'asset_tag' => $asset_tag,
'model_id' => $model->id,
'status_id' => $status->id,
'deleted_at' => null,
])
->assertOk()
->assertStatusMessageIs('success');
$this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'asset_tag' => $asset_tag,
'model_id' => $model->id,
'status_id' => $status->id,
'deleted_at' => null,
])
->assertOk()
->assertStatusMessageIs('error');
}
public function testAssetTagsCanBeDuplicatedIfDeleted()
{
$model = AssetModel::factory()->create();
$status = Statuslabel::factory()->create();
$asset_tag = '1234567890';
$this->settings->disableAutoIncrement();
$response1 = $this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'asset_tag' => $asset_tag,
'model_id' => $model->id,
'status_id' => $status->id,
])
->assertOk()
->assertStatusMessageIs('success')
->json();
$asset1 = Asset::find($response1['payload']['id'])->delete();
$response2 = $this->actingAsForApi(User::factory()->superuser()->create())
->postJson(route('api.assets.store'), [
'asset_tag' => $asset_tag,
'model_id' => $model->id,
'status_id' => $status->id,
])
->assertOk()
->assertStatusMessageIs('success');
}
} }

View file

@ -74,5 +74,18 @@ trait CustomTestMacros
return $this; return $this;
} }
); );
TestResponse::macro(
'assertStatusMessageIs',
function (string $message) {
Assert::assertEquals(
$message,
$this['status'],
"Response status message was not {$message}"
);
return $this;
}
);
} }
} }

View file

@ -64,7 +64,26 @@ class Settings
'next_auto_tag_base' => '123', 'next_auto_tag_base' => '123',
'zerofill_count' => 5 'zerofill_count' => 5
]); ]);
}
public function disableAutoIncrement(): Settings
{
return $this->update([
'auto_increment_assets' => 0,
'auto_increment_prefix' => '',
'next_auto_tag_base' => '',
'zerofill_count' => 0
]);
}
public function enableUniqueSerialNumbers(): Settings
{
return $this->update(['unique_serial' => 1]);
}
public function disableUniqueSerialNumbers(): Settings
{
return $this->update(['unique_serial' => 0]);
} }
/** /**