mirror of
https://github.com/snipe/snipe-it.git
synced 2024-12-25 05:34:06 -08:00
Merge remote-tracking branch 'origin/develop'
This commit is contained in:
commit
0e6eda5e8a
|
@ -83,11 +83,19 @@ class ReportsController extends Controller
|
||||||
$offset = ($request->input('offset') > $total) ? $total : app('api_offset_value');
|
$offset = ($request->input('offset') > $total) ? $total : app('api_offset_value');
|
||||||
$limit = app('api_limit_value');
|
$limit = app('api_limit_value');
|
||||||
|
|
||||||
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
|
||||||
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
|
$order = ($request->input('order') == 'asc') ? 'asc' : 'desc';
|
||||||
|
|
||||||
|
switch ($request->input('sort')) {
|
||||||
|
case 'admin':
|
||||||
|
$actionlogs->OrderAdmin($order);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$sort = in_array($request->input('sort'), $allowed_columns) ? e($request->input('sort')) : 'created_at';
|
||||||
|
$actionlogs = $actionlogs->orderBy($sort, $order);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$actionlogs = $actionlogs->orderBy($sort, $order)->skip($offset)->take($limit)->get();
|
$actionlogs = $actionlogs->skip($offset)->take($limit)->get();
|
||||||
|
|
||||||
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
|
return response()->json((new ActionlogsTransformer)->transformActionlogs($actionlogs, $total), 200, ['Content-Type' => 'application/json;charset=utf8'], JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use App\Presenters\Presentable;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model for the Actionlog (the table that keeps a historical log of
|
* Model for the Actionlog (the table that keeps a historical log of
|
||||||
|
@ -17,10 +16,12 @@ use Illuminate\Support\Facades\Auth;
|
||||||
*/
|
*/
|
||||||
class Actionlog extends SnipeModel
|
class Actionlog extends SnipeModel
|
||||||
{
|
{
|
||||||
|
use CompanyableTrait;
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
// This is to manually set the source (via setActionSource()) for determineActionSource()
|
// This is to manually set the source (via setActionSource()) for determineActionSource()
|
||||||
protected ?string $source = null;
|
protected ?string $source = null;
|
||||||
|
protected $with = ['admin'];
|
||||||
|
|
||||||
protected $presenter = \App\Presenters\ActionlogPresenter::class;
|
protected $presenter = \App\Presenters\ActionlogPresenter::class;
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
@ -66,7 +67,7 @@ class Actionlog extends SnipeModel
|
||||||
'company' => ['name'],
|
'company' => ['name'],
|
||||||
'admin' => ['first_name','last_name','username', 'email'],
|
'admin' => ['first_name','last_name','username', 'email'],
|
||||||
'user' => ['first_name','last_name','username', 'email'],
|
'user' => ['first_name','last_name','username', 'email'],
|
||||||
'assets' => ['asset_tag','name'],
|
'assets' => ['asset_tag','name','model', 'model_number'],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -372,4 +373,9 @@ class Actionlog extends SnipeModel
|
||||||
{
|
{
|
||||||
$this->source = $source;
|
$this->source = $source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeOrderAdmin($query, $order)
|
||||||
|
{
|
||||||
|
return $query->leftJoin('users as admin_sort', 'action_logs.user_id', '=', 'admin_sort.id')->select('action_logs.*')->orderBy('admin_sort.first_name', $order)->orderBy('admin_sort.last_name', $order);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Events\AssetCheckedOut;
|
|
||||||
use App\Events\CheckoutableCheckedOut;
|
use App\Events\CheckoutableCheckedOut;
|
||||||
use App\Exceptions\CheckoutNotAllowed;
|
use App\Exceptions\CheckoutNotAllowed;
|
||||||
use App\Helpers\Helper;
|
use App\Helpers\Helper;
|
||||||
|
@ -31,6 +30,7 @@ class Asset extends Depreciable
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $presenter = AssetPresenter::class;
|
protected $presenter = AssetPresenter::class;
|
||||||
|
protected $with = ['model', 'admin'];
|
||||||
|
|
||||||
use CompanyableTrait;
|
use CompanyableTrait;
|
||||||
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
use HasFactory, Loggable, Requestable, Presentable, SoftDeletes, ValidatingTrait, UniqueUndeletedTrait;
|
||||||
|
@ -716,7 +716,7 @@ class Asset extends Depreciable
|
||||||
* @since [v1.0]
|
* @since [v1.0]
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
* @return \Illuminate\Database\Eloquent\Relations\Relation
|
||||||
*/
|
*/
|
||||||
public function adminuser()
|
public function admin()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(\App\Models\User::class, 'user_id');
|
return $this->belongsTo(\App\Models\User::class, 'user_id');
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<th class="col-sm-3" data-field="item.serial" data-visible="false">{{ trans('admin/hardware/table.serial') }}</th>
|
<th class="col-sm-3" data-field="item.serial" data-visible="false">{{ trans('admin/hardware/table.serial') }}</th>
|
||||||
<th class="col-sm-3" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
<th class="col-sm-3" data-field="item" data-formatter="polymorphicItemFormatter">{{ trans('general.item') }}</th>
|
||||||
<th class="col-sm-2" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.to') }}</th>
|
<th class="col-sm-2" data-field="target" data-formatter="polymorphicItemFormatter">{{ trans('general.to') }}</th>
|
||||||
<th class="col-sm-1" data-field="note">{{ trans('general.notes') }}</th>
|
<th class="col-sm-1" data-field="note" data-sortable="true">{{ trans('general.notes') }}</th>
|
||||||
<th class="col-sm-2" data-field="log_meta" data-visible="false" data-formatter="changeLogFormatter">{{ trans('general.changed') }}</th>
|
<th class="col-sm-2" data-field="log_meta" data-visible="false" data-formatter="changeLogFormatter">{{ trans('general.changed') }}</th>
|
||||||
<th data-field="remote_ip" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_ip') }}</th>
|
<th data-field="remote_ip" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_ip') }}</th>
|
||||||
<th data-field="user_agent" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_user_agent') }}</th>
|
<th data-field="user_agent" data-visible="false" data-sortable="true">{{ trans('admin/settings/general.login_user_agent') }}</th>
|
||||||
|
|
|
@ -61,7 +61,7 @@ class StoreAssetTest extends TestCase
|
||||||
|
|
||||||
$asset = Asset::find($response['payload']['id']);
|
$asset = Asset::find($response['payload']['id']);
|
||||||
|
|
||||||
$this->assertTrue($asset->adminuser->is($user));
|
$this->assertTrue($asset->admin->is($user));
|
||||||
|
|
||||||
$this->assertEquals('2024-06-02', $asset->asset_eol_date);
|
$this->assertEquals('2024-06-02', $asset->asset_eol_date);
|
||||||
$this->assertEquals('random_string', $asset->asset_tag);
|
$this->assertEquals('random_string', $asset->asset_tag);
|
||||||
|
@ -456,7 +456,7 @@ class StoreAssetTest extends TestCase
|
||||||
|
|
||||||
$asset = Asset::find($response['payload']['id']);
|
$asset = Asset::find($response['payload']['id']);
|
||||||
|
|
||||||
$this->assertTrue($asset->adminuser->is($user));
|
$this->assertTrue($asset->admin->is($user));
|
||||||
$this->assertTrue($asset->checkedOutToUser());
|
$this->assertTrue($asset->checkedOutToUser());
|
||||||
$this->assertTrue($asset->assignedTo->is($userAssigned));
|
$this->assertTrue($asset->assignedTo->is($userAssigned));
|
||||||
}
|
}
|
||||||
|
@ -482,7 +482,7 @@ class StoreAssetTest extends TestCase
|
||||||
|
|
||||||
$asset = Asset::find($response['payload']['id']);
|
$asset = Asset::find($response['payload']['id']);
|
||||||
|
|
||||||
$this->assertTrue($asset->adminuser->is($user));
|
$this->assertTrue($asset->admin->is($user));
|
||||||
$this->assertTrue($asset->checkedOutToLocation());
|
$this->assertTrue($asset->checkedOutToLocation());
|
||||||
$this->assertTrue($asset->location->is($location));
|
$this->assertTrue($asset->location->is($location));
|
||||||
}
|
}
|
||||||
|
@ -508,7 +508,7 @@ class StoreAssetTest extends TestCase
|
||||||
|
|
||||||
$apiAsset = Asset::find($response['payload']['id']);
|
$apiAsset = Asset::find($response['payload']['id']);
|
||||||
|
|
||||||
$this->assertTrue($apiAsset->adminuser->is($user));
|
$this->assertTrue($apiAsset->admin->is($user));
|
||||||
$this->assertTrue($apiAsset->checkedOutToAsset());
|
$this->assertTrue($apiAsset->checkedOutToAsset());
|
||||||
// I think this makes sense, but open to a sanity check
|
// I think this makes sense, but open to a sanity check
|
||||||
$this->assertTrue($asset->assignedAssets()->find($response['payload']['id'])->is($apiAsset));
|
$this->assertTrue($asset->assignedAssets()->find($response['payload']['id'])->is($apiAsset));
|
||||||
|
|
|
@ -165,7 +165,7 @@ class BulkEditAssetsTest extends TestCase
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBulkEditAssetsRequiresAdminUserToUpdateEncryptedCustomFields()
|
public function testBulkEditAssetsRequiresadminToUpdateEncryptedCustomFields()
|
||||||
{
|
{
|
||||||
$this->markIncompleteIfMySQL('Custom Fields tests do not work on mysql');
|
$this->markIncompleteIfMySQL('Custom Fields tests do not work on mysql');
|
||||||
$edit_user = User::factory()->editAssets()->create();
|
$edit_user = User::factory()->editAssets()->create();
|
||||||
|
|
81
tests/Feature/Reporting/ActivityReportTest.php
Normal file
81
tests/Feature/Reporting/ActivityReportTest.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Reporting;
|
||||||
|
|
||||||
|
use App\Models\Asset;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Actionlog;
|
||||||
|
use Database\Factories\ActionlogFactory;
|
||||||
|
use Illuminate\Testing\Fluent\AssertableJson;
|
||||||
|
use Illuminate\Testing\TestResponse;
|
||||||
|
use League\Csv\Reader;
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ActivityReportTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testRequiresPermissionToViewActivity()
|
||||||
|
{
|
||||||
|
$this->actingAsForApi(User::factory()->create())
|
||||||
|
->getJson(route('api.activity.index'))
|
||||||
|
->assertForbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecordsAreScopedToCompanyWhenMultipleCompanySupportEnabled()
|
||||||
|
{
|
||||||
|
$this->markTestIncomplete('This test returns strange results. Need to figure out why.');
|
||||||
|
$this->settings->enableMultipleFullCompanySupport();
|
||||||
|
|
||||||
|
$companyA = Company::factory()->create();
|
||||||
|
$companyB = Company::factory()->create();
|
||||||
|
|
||||||
|
$superUser = User::factory()->superuser()->make();
|
||||||
|
|
||||||
|
$userInCompanyA = User::factory()
|
||||||
|
->viewUsers()
|
||||||
|
->viewAssets()
|
||||||
|
->canViewReports()
|
||||||
|
->create(['company_id' => $companyA->id]);
|
||||||
|
|
||||||
|
$userInCompanyB = User::factory()
|
||||||
|
->viewUsers()
|
||||||
|
->viewAssets()
|
||||||
|
->canViewReports()
|
||||||
|
->create(['company_id' => $companyB->id]);
|
||||||
|
|
||||||
|
Asset::factory()->count(5)->create(['company_id' => $companyA->id]);
|
||||||
|
Asset::factory()->count(4)->create(['company_id' => $companyB->id]);
|
||||||
|
Asset::factory()->count(3)->create();
|
||||||
|
|
||||||
|
Actionlog::factory()->userUpdated()->count(5)->create(['company_id' => $companyA->id]);
|
||||||
|
Actionlog::factory()->userUpdated()->count(4)->create(['company_id' => $companyB->id]);
|
||||||
|
Actionlog::factory()->userUpdated()->count(3)->create(['company_id' => $companyB->id]);
|
||||||
|
|
||||||
|
// I don't love this, since it doesn't test that we're actually storing the company ID appropriately
|
||||||
|
// but it's better than what we had
|
||||||
|
$response = $this->actingAsForApi($userInCompanyA)
|
||||||
|
->getJson(route('api.activity.index'))
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonStructure([
|
||||||
|
'rows',
|
||||||
|
])
|
||||||
|
->assertJson(fn(AssertableJson $json) => $json->has('rows', 5)->etc());
|
||||||
|
|
||||||
|
|
||||||
|
$this->actingAsForApi($userInCompanyB)
|
||||||
|
->getJson(
|
||||||
|
route('api.activity.index'))
|
||||||
|
->assertOk()
|
||||||
|
->assertJsonStructure([
|
||||||
|
'rows',
|
||||||
|
])
|
||||||
|
->assertJson(fn(AssertableJson $json) => $json->has('rows', 7)->etc());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue