mirror of
https://github.com/snipe/snipe-it.git
synced 2025-01-02 09:27:45 -08:00
Merge remote-tracking branch 'origin/develop'
This commit is contained in:
commit
521fcd45b0
|
@ -905,6 +905,7 @@ class AssetsController extends Controller
|
|||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkout = null;
|
||||
$asset->last_checkin = now();
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->accepted = null;
|
||||
|
|
|
@ -68,6 +68,7 @@ class AssetCheckinController extends Controller
|
|||
|
||||
$asset->expected_checkin = null;
|
||||
$asset->last_checkout = null;
|
||||
$asset->last_checkin = now();
|
||||
$asset->assigned_to = null;
|
||||
$asset->assignedTo()->disassociate($asset);
|
||||
$asset->assigned_type = null;
|
||||
|
|
|
@ -545,6 +545,10 @@ class ReportsController extends Controller
|
|||
$header[] = trans('admin/hardware/table.checkout_date');
|
||||
}
|
||||
|
||||
if ($request->filled('checkin_date')) {
|
||||
$header[] = trans('admin/hardware/table.last_checkin_date');
|
||||
}
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$header[] = trans('admin/hardware/form.expected_checkin');
|
||||
}
|
||||
|
@ -651,6 +655,14 @@ class ReportsController extends Controller
|
|||
$assets->whereBetween('assets.last_checkout', [$checkout_start, $checkout_end]);
|
||||
}
|
||||
|
||||
if (($request->filled('checkin_date_start'))) {
|
||||
$assets->whereBetween('last_checkin', [
|
||||
Carbon::parse($request->input('checkin_date_start'))->startOfDay(),
|
||||
// use today's date is `checkin_date_end` is not provided
|
||||
Carbon::parse($request->input('checkin_date_end', now()))->endOfDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
if (($request->filled('expected_checkin_start')) && ($request->filled('expected_checkin_end'))) {
|
||||
$assets->whereBetween('assets.expected_checkin', [$request->input('expected_checkin_start'), $request->input('expected_checkin_end')]);
|
||||
}
|
||||
|
@ -835,6 +847,12 @@ class ReportsController extends Controller
|
|||
$row[] = ($asset->last_checkout) ? $asset->last_checkout : '';
|
||||
}
|
||||
|
||||
if ($request->filled('checkin_date')) {
|
||||
$row[] = ($asset->last_checkin)
|
||||
? Carbon::parse($asset->last_checkin)->format('Y-m-d')
|
||||
: '';
|
||||
}
|
||||
|
||||
if ($request->filled('expected_checkin')) {
|
||||
$row[] = ($asset->expected_checkin) ? $asset->expected_checkin : '';
|
||||
}
|
||||
|
|
|
@ -161,6 +161,7 @@ class ActionlogsTransformer
|
|||
{ $location = Location::withTrashed()->get();
|
||||
$supplier = Supplier::withTrashed()->get();
|
||||
$model = AssetModel::withTrashed()->get();
|
||||
$company = Company::withTrashed()->get();
|
||||
|
||||
|
||||
if(array_key_exists('rtd_location_id',$clean_meta)) {
|
||||
|
@ -177,17 +178,24 @@ class ActionlogsTransformer
|
|||
}
|
||||
if(array_key_exists('model_id', $clean_meta)) {
|
||||
|
||||
$clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$model->find($clean_meta['model_id']['old'])->name;
|
||||
$clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$model->find($clean_meta['model_id']['new'])->name; /* model is required at asset creation */
|
||||
$oldModel = $model->find($clean_meta['model_id']['old']);
|
||||
$oldModelName = $oldModel->name ?? trans('admin/models/message.deleted');
|
||||
|
||||
$newModel = $model->find($clean_meta['model_id']['new']);
|
||||
$newModelName = $newModel->name ?? trans('admin/models/message.deleted');
|
||||
|
||||
$clean_meta['model_id']['old'] = "[id: ".$clean_meta['model_id']['old']."] ".$oldModelName;
|
||||
$clean_meta['model_id']['new'] = "[id: ".$clean_meta['model_id']['new']."] ".$newModelName; /** model is required at asset creation */
|
||||
|
||||
$clean_meta['Model'] = $clean_meta['model_id'];
|
||||
unset($clean_meta['model_id']);
|
||||
}
|
||||
if(array_key_exists('company_id', $clean_meta)) {
|
||||
$oldCompany = Company::find($clean_meta['company_id']['old']);
|
||||
|
||||
$oldCompany = $company->find($clean_meta['company_id']['old']);
|
||||
$oldCompanyName = $oldCompany->name ?? trans('admin/companies/message.deleted');
|
||||
|
||||
$newCompany = Company::find($clean_meta['company_id']['new']);
|
||||
$newCompany = $company->find($clean_meta['company_id']['new']);
|
||||
$newCompanyName = $newCompany->name ?? trans('admin/companies/message.deleted');
|
||||
|
||||
$clean_meta['company_id']['old'] = $clean_meta['company_id']['old'] ? "[id: ".$clean_meta['company_id']['old']."] ". $oldCompanyName : trans('general.unassigned');
|
||||
|
@ -196,8 +204,15 @@ class ActionlogsTransformer
|
|||
unset($clean_meta['company_id']);
|
||||
}
|
||||
if(array_key_exists('supplier_id', $clean_meta)) {
|
||||
$clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ".$supplier->find($clean_meta['supplier_id']['old'])->name : trans('general.unassigned');
|
||||
$clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ".$supplier->find($clean_meta['supplier_id']['new'])->name : trans('general.unassigned');
|
||||
|
||||
$oldSupplier = $supplier->find($clean_meta['supplier_id']['old']);
|
||||
$oldSupplierName = $oldSupplier->name ?? trans('admin/suppliers/message.deleted');
|
||||
|
||||
$newSupplier = $supplier->find($clean_meta['supplier_id']['new']);
|
||||
$newSupplierName = $newSupplier->name ?? trans('admin/suppliers/message.deleted');
|
||||
|
||||
$clean_meta['supplier_id']['old'] = $clean_meta['supplier_id']['old'] ? "[id: ".$clean_meta['supplier_id']['old']."] ". $oldSupplierName : trans('general.unassigned');
|
||||
$clean_meta['supplier_id']['new'] = $clean_meta['supplier_id']['new'] ? "[id: ".$clean_meta['supplier_id']['new']."] ". $newSupplierName : trans('general.unassigned');
|
||||
$clean_meta['Supplier'] = $clean_meta['supplier_id'];
|
||||
unset($clean_meta['supplier_id']);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ class Asset extends Depreciable
|
|||
protected $casts = [
|
||||
'purchase_date' => 'date',
|
||||
'last_checkout' => 'datetime',
|
||||
'last_checkin' => 'datetime',
|
||||
'expected_checkin' => 'date',
|
||||
'last_audit_date' => 'datetime',
|
||||
'next_audit_date' => 'date',
|
||||
|
|
|
@ -750,4 +750,26 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo
|
|||
{
|
||||
return $this->locale;
|
||||
}
|
||||
public function getUserTotalCost(){
|
||||
$asset_cost= 0;
|
||||
$license_cost= 0;
|
||||
$accessory_cost= 0;
|
||||
foreach ($this->assets as $asset){
|
||||
$asset_cost += $asset->purchase_cost;
|
||||
$this->asset_cost = $asset_cost;
|
||||
}
|
||||
foreach ($this->licenses as $license){
|
||||
$license_cost += $license->purchase_cost;
|
||||
$this->license_cost = $license_cost;
|
||||
}
|
||||
foreach ($this->accessories as $accessory){
|
||||
$accessory_cost += $accessory->purchase_cost;
|
||||
$this->accessory_cost = $accessory_cost;
|
||||
}
|
||||
|
||||
$this->total_user_cost = ($asset_cost + $accessory_cost + $license_cost);
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddLastCheckinToAssets extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->dateTime('last_checkin')->after('last_checkout')->nullable();
|
||||
});
|
||||
|
||||
DB::statement(
|
||||
"UPDATE " . DB::getTablePrefix() . "assets SET last_checkin=(SELECT MAX(" . DB::getTablePrefix() . "action_logs.action_date) FROM " . DB::getTablePrefix() . "action_logs WHERE item_type='App\\\Models\\\Asset' AND " . DB::getTablePrefix() . "action_logs.item_id=" . DB::getTablePrefix() . "assets.id AND " . DB::getTablePrefix() . "action_logs.action_type='checkin from')"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->dropColumn('last_checkin');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ return [
|
|||
'dl_csv' => 'Download CSV',
|
||||
'eol' => 'EOL',
|
||||
'id' => 'ID',
|
||||
'last_checkin_date' => 'Last Checkin Date',
|
||||
'location' => 'Location',
|
||||
'purchase_cost' => 'Cost',
|
||||
'purchase_date' => 'Purchased',
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
return array(
|
||||
|
||||
'deleted' => 'Deleted asset model',
|
||||
'does_not_exist' => 'Model does not exist.',
|
||||
'no_association' => 'WARNING! The asset model for this item is invalid or missing!',
|
||||
'no_association_fix' => 'This will break things in weird and horrible ways. Edit this asset now to assign it a model.',
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
return array(
|
||||
|
||||
'deleted' => 'Deleted supplier',
|
||||
'does_not_exist' => 'Supplier does not exist.',
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ return array(
|
|||
'show_deleted' => 'Show Deleted Users',
|
||||
'title' => 'Title',
|
||||
'to_restore_them' => 'to restore them.',
|
||||
'total_assets_cost' => "Total Assets Cost",
|
||||
'updateuser' => 'Update User',
|
||||
'username' => 'Username',
|
||||
'user_deleted_text' => 'This user has been marked as deleted.',
|
||||
|
|
|
@ -141,6 +141,11 @@
|
|||
{{ trans('admin/hardware/table.checkout_date') }}
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('checkin_date', '1', '1') }}
|
||||
{{ trans('admin/hardware/table.last_checkin_date') }}
|
||||
</label>
|
||||
|
||||
<label class="form-control">
|
||||
{{ Form::checkbox('expected_checkin', '1', '1') }}
|
||||
{{ trans('admin/hardware/form.expected_checkin') }}
|
||||
|
@ -289,6 +294,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Last Checkin Date -->
|
||||
<div class="form-group checkin-range">
|
||||
<label for="checkin_date" class="col-md-3 control-label">{{ trans('admin/hardware/table.last_checkin_date') }}</label>
|
||||
<div class="input-daterange input-group col-md-6" id="datepicker">
|
||||
<input type="text" class="form-control" name="checkin_date_start" aria-label="checkin_date_start">
|
||||
<span class="input-group-addon">{{ strtolower(trans('general.to')) }}</span>
|
||||
<input type="text" class="form-control" name="checkin_date_end" aria-label="checkin_date_end">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Expected Checkin Date -->
|
||||
<div class="form-group expected_checkin-range">
|
||||
<label for="expected_checkin_start" class="col-md-3 control-label">{{ trans('admin/hardware/form.expected_checkin') }}</label>
|
||||
|
@ -381,6 +396,13 @@
|
|||
format: 'yyyy-mm-dd'
|
||||
});
|
||||
|
||||
$('.checkin-range .input-daterange').datepicker({
|
||||
clearBtn: true,
|
||||
todayHighlight: true,
|
||||
endDate: '0d',
|
||||
format: 'yyyy-mm-dd'
|
||||
});
|
||||
|
||||
$('.expected_checkin-range .input-daterange').datepicker({
|
||||
clearBtn: true,
|
||||
todayHighlight: true,
|
||||
|
|
|
@ -638,6 +638,27 @@
|
|||
|
||||
</div>
|
||||
@endif
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-3">
|
||||
{{ trans('admin/users/table.total_assets_cost') }}
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
{{Helper::formatCurrencyOutput($user->getUserTotalCost()->total_user_cost)}}
|
||||
<a id="optional_info" class="text-primary">
|
||||
<i class="fa fa-caret-right fa-2x" id="optional_info_icon"></i>
|
||||
<strong>{{ trans('admin/hardware/form.optional_infos') }}</strong>
|
||||
</a>
|
||||
</div>
|
||||
<div id="optional_details" class="col-md-12" style="display:none">
|
||||
<div class="col-md-3" style="border-top:none;"></div>
|
||||
<div class="col-md-9" style="border-top:none;">
|
||||
{{trans('general.assets').': '. Helper::formatCurrencyOutput($user->getUserTotalCost()->asset_cost)}}<br>
|
||||
{{trans('general.licenses').': '. Helper::formatCurrencyOutput($user->getUserTotalCost()->license_cost)}}<br>
|
||||
{{trans('general.accessories').': '.Helper::formatCurrencyOutput($user->getUserTotalCost()->accessory_cost)}}<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!--/end striped container-->
|
||||
</div> <!-- end col-md-9 -->
|
||||
|
@ -1109,6 +1130,12 @@ $(function () {
|
|||
|
||||
}
|
||||
});
|
||||
$("#optional_info").on("click",function(){
|
||||
$('#optional_details').fadeToggle(100);
|
||||
$('#optional_info_icon').toggleClass('fa-caret-right fa-caret-down');
|
||||
var optional_info_open = $('#optional_info_icon').hasClass('fa-caret-down');
|
||||
document.cookie = "optional_info_open="+optional_info_open+'; path=/';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
30
tests/Feature/Api/Assets/AssetCheckinTest.php
Normal file
30
tests/Feature/Api/Assets/AssetCheckinTest.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Api\Assets;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
use Tests\Support\InteractsWithSettings;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AssetCheckinTest extends TestCase
|
||||
{
|
||||
use InteractsWithSettings;
|
||||
|
||||
public function testLastCheckInFieldIsSetOnCheckin()
|
||||
{
|
||||
$admin = User::factory()->superuser()->create();
|
||||
$asset = Asset::factory()->create(['last_checkin' => null]);
|
||||
|
||||
$asset->checkOut(User::factory()->create(), $admin, now());
|
||||
|
||||
$this->actingAsForApi($admin)
|
||||
->postJson(route('api.asset.checkin', $asset))
|
||||
->assertOk();
|
||||
|
||||
$this->assertNotNull(
|
||||
$asset->fresh()->last_checkin,
|
||||
'last_checkin field should be set on checkin'
|
||||
);
|
||||
}
|
||||
}
|
32
tests/Feature/Assets/AssetCheckinTest.php
Normal file
32
tests/Feature/Assets/AssetCheckinTest.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Assets;
|
||||
|
||||
use App\Models\Asset;
|
||||
use App\Models\User;
|
||||
use Tests\Support\InteractsWithSettings;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AssetCheckinTest extends TestCase
|
||||
{
|
||||
use InteractsWithSettings;
|
||||
|
||||
public function testLastCheckInFieldIsSetOnCheckin()
|
||||
{
|
||||
$admin = User::factory()->superuser()->create();
|
||||
$asset = Asset::factory()->create(['last_checkin' => null]);
|
||||
|
||||
$asset->checkOut(User::factory()->create(), $admin, now());
|
||||
|
||||
$this->actingAs($admin)
|
||||
->post(route('hardware.checkin.store', [
|
||||
'assetId' => $asset->id,
|
||||
]))
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertNotNull(
|
||||
$asset->fresh()->last_checkin,
|
||||
'last_checkin field should be set on checkin'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -107,4 +107,29 @@ class CustomReportTest extends TestCase
|
|||
->assertDontSeeTextInStreamedResponse('Asset A')
|
||||
->assertSeeTextInStreamedResponse('Asset B');
|
||||
}
|
||||
|
||||
public function testCanLimitAssetsByLastCheckIn()
|
||||
{
|
||||
Asset::factory()->create(['name' => 'Asset A', 'last_checkin' => '2023-08-01']);
|
||||
Asset::factory()->create(['name' => 'Asset B', 'last_checkin' => '2023-08-02']);
|
||||
Asset::factory()->create(['name' => 'Asset C', 'last_checkin' => '2023-08-03']);
|
||||
Asset::factory()->create(['name' => 'Asset D', 'last_checkin' => '2023-08-04']);
|
||||
Asset::factory()->create(['name' => 'Asset E', 'last_checkin' => '2023-08-05']);
|
||||
|
||||
$this->actingAs(User::factory()->canViewReports()->create())
|
||||
->post('reports/custom', [
|
||||
'asset_name' => '1',
|
||||
'asset_tag' => '1',
|
||||
'serial' => '1',
|
||||
'checkin_date' => '1',
|
||||
'checkin_date_start' => '2023-08-02',
|
||||
'checkin_date_end' => '2023-08-04',
|
||||
])->assertOk()
|
||||
->assertHeader('content-type', 'text/csv; charset=UTF-8')
|
||||
->assertDontSeeTextInStreamedResponse('Asset A')
|
||||
->assertSeeTextInStreamedResponse('Asset B')
|
||||
->assertSeeTextInStreamedResponse('Asset C')
|
||||
->assertSeeTextInStreamedResponse('Asset D')
|
||||
->assertDontSeeTextInStreamedResponse('Asset E');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue