Merge branch 'develop'

# Conflicts:
#	config/version.php
This commit is contained in:
snipe 2018-05-16 19:24:31 -07:00
commit 0b5bb520a7
22 changed files with 307 additions and 31 deletions

View file

@ -0,0 +1,76 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Asset;
class SyncAssetCounters extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'snipeit:counter-sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Syncs checkedout, checked in, and requested counters for assets';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$start = microtime(true);
$assets = Asset::withCount('checkins', 'checkouts', 'userRequests')
->withTrashed()->get();
if ($assets) {
if ($assets->count() > 0) {
$bar = $this->output->createProgressBar($assets->count());
foreach ($assets as $asset) {
$asset->checkin_counter = (int) $asset->checkins_count;
$asset->checkout_counter = (int) $asset->checkouts_count;
$asset->requests_counter = (int) $asset->user_requests_count;
$asset->unsetEventDispatcher();
$asset->save();
$output['info'][] = 'Asset: ' . $asset->id . ' has ' . $asset->checkin_counter . ' checkins, ' . $asset->checkout_counter . ' checkouts, and ' . $asset->requests_counter . ' requests';
$bar->advance();
}
$bar->finish();
foreach ($output['info'] as $key => $output_text) {
$this->info($output_text);
}
$time_elapsed_secs = microtime(true) - $start;
$this->info('Sync executed in ' . $time_elapsed_secs . ' seconds');
} else {
$this->info('No assets to sync');
}
}
}
}

View file

@ -29,6 +29,7 @@ class Kernel extends ConsoleKernel
Commands\ResetDemoSettings::class,
Commands\SyncAssetLocations::class,
Commands\RegenerateAssetTags::class,
Commands\SyncAssetCounters::class,
];
/**

View file

@ -77,9 +77,9 @@ class AssetsController extends Controller
'last_audit_date',
'next_audit_date',
'warranty_months',
'checkouts_count',
'checkins_count',
'user_requests_count',
'checkout_counter',
'checkin_counter',
'requests_counter',
];
$filter = array();
@ -95,7 +95,7 @@ class AssetsController extends Controller
$assets = Company::scopeCompanyables(Asset::select('assets.*'),"company_id","assets")
->with('location', 'assetstatus', 'assetlog', 'company', 'defaultLoc','assignedTo',
'model.category', 'model.manufacturer', 'model.fieldset','supplier')->withCount('checkins', 'checkouts', 'userRequests');
'model.category', 'model.manufacturer', 'model.fieldset','supplier');
// These are used by the API to query against specific ID numbers.

View file

@ -113,7 +113,7 @@ class AssetMaintenancesController extends Controller
$assetMaintenance->notes = e($request->input('notes'));
$asset = Asset::find(e($request->input('asset_id')));
if (!Company::isCurrentUserHasAccess($asset)) {
if ((!Company::isCurrentUserHasAccess($asset)) && ($asset!=null)) {
return static::getInsufficientPermissionsRedirect();
}

View file

@ -181,6 +181,8 @@ class ViewAssetsController extends Controller
// If it's already requested, cancel the request.
if ($asset->isRequestedBy(Auth::user())) {
$asset->cancelRequest();
$asset->decrement('requests_counter', 1);
$logaction->logaction('request canceled');
$settings->notify(new RequestAssetCancelationNotification($data));
return redirect()->route('requestable-assets')
@ -188,8 +190,8 @@ class ViewAssetsController extends Controller
} else {
$logaction->logaction('requested');
$asset->request();
$asset->increment('requests_counter', 1);
$settings->notify(new RequestAssetNotification($data));

View file

@ -77,9 +77,9 @@ class AssetsTransformer
'last_checkout' => Helper::getFormattedDateObject($asset->last_checkout, 'datetime'),
'expected_checkin' => Helper::getFormattedDateObject($asset->expected_checkin, 'date'),
'purchase_cost' => Helper::formatCurrencyOutput($asset->purchase_cost),
'checkins_count' => (int) $asset->checkins_count,
'checkouts_count' => (int) $asset->checkouts_count,
'user_requests_count' => (int) $asset->user_requests_count,
'checkin_counter' => (int) $asset->checkin_counter,
'checkout_counter' => (int) $asset->checkout_counter,
'requests_counter' => (int) $asset->requests_counter,
'user_can_checkout' => (bool) $asset->availableForCheckout(),
];

View file

@ -199,6 +199,8 @@ class Asset extends Depreciable
if ($this->save()) {
$this->logCheckout($note, $target);
\Log::debug('Increment the checkout count for asset: '.$this->id);
$this->increment('checkout_counter', 1);
return true;
}
return false;

View file

@ -49,7 +49,7 @@ class License extends Depreciable
'license_email' => 'email|nullable|max:120',
'license_name' => 'string|nullable|max:100',
'notes' => 'string|nullable',
'category_id' => 'integer',
'category_id' => 'required|exists:categories,id',
'company_id' => 'integer|nullable',
);

View file

@ -55,7 +55,7 @@ trait Loggable
if ($log->target_type == Location::class) {
$log->location_id = $target->id;
} elseif ($log->target_type == Asset::class) {
$log->location_id = $target->rtd_location_id;
$log->location_id = $target->location_id;
} else {
$log->location_id = $target->location_id;
}
@ -121,8 +121,17 @@ trait Loggable
$log->item_type = License::class;
$log->item_id = $this->license_id;
} else {
$log->item_type = static::class;
$log->item_id = $this->id;
if (static::class == Asset::class) {
if ($asset = Asset::find($log->item_id)) {
\Log::debug('Increment the checkin count for asset: '.$log->item_id);
$asset->increment('checkin_counter', 1);
}
}
}

View file

@ -182,21 +182,21 @@ class AssetPresenter extends Presenter
"title" => trans('general.notes'),
], [
"field" => "checkouts_count",
"field" => "checkout_counter",
"searchable" => false,
"sortable" => true,
"visible" => false,
"title" => trans('general.checkouts_count')
],[
"field" => "checkins_count",
"field" => "checkin_counter",
"searchable" => false,
"sortable" => true,
"visible" => false,
"title" => trans('general.checkins_count')
], [
"field" => "user_requests_count",
"field" => "requests_counter",
"searchable" => false,
"sortable" => true,
"visible" => false,

View file

@ -1,10 +1,10 @@
<?php
return array (
'app_version' => 'v4.3.0',
'full_app_version' => 'v4.3.0 - build 3586-gec1fa8e90',
'build_version' => '3586',
'full_app_version' => 'v4.3.0 - build 3610-g4ba9792fb',
'build_version' => '3610',
'prerelease_version' => '',
'hash_version' => 'gec1fa8e90',
'full_hash' => 'v4.3.0-4-gec1fa8e90',
'hash_version' => 'g4ba9792fb',
'full_hash' => 'v4.3.0-28-g4ba9792fb',
'branch' => 'master',
);

View file

@ -114,3 +114,18 @@ $factory->state(App\Models\Category::class, 'consumable-ink-category', function
];
});
$factory->state(App\Models\Category::class, 'license-graphics-category', function ($faker) {
return [
'name' => 'Graphics Software',
'category_type' => 'license',
];
});
$factory->state(App\Models\Category::class, 'license-office-category', function ($faker) {
return [
'name' => 'Office Software',
'category_type' => 'license',
];
});

View file

@ -34,7 +34,8 @@ $factory->state(App\Models\License::class, 'photoshop', function ($faker) {
'purchase_cost' => '299.99',
'seats' => 10,
'purchase_order' => '13503Q',
'maintained' => true
'maintained' => true,
'category_id' => 14,
];
return $data;
@ -49,6 +50,7 @@ $factory->state(App\Models\License::class, 'acrobat', function ($faker) {
'manufacturer_id' => 9,
'purchase_cost' => '29.99',
'seats' => 10,
'category_id' => 14,
];
@ -62,6 +64,7 @@ $factory->state(App\Models\License::class, 'indesign', function ($faker) {
'manufacturer_id' => 9,
'purchase_cost' => '199.99',
'seats' => 10,
'category_id' => 14,
];
@ -76,6 +79,7 @@ $factory->state(App\Models\License::class, 'office', function ($faker) {
'manufacturer_id' => 2,
'purchase_cost' => '49.99',
'seats' => 20,
'category_id' => 15,
];

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddIndexes extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->index(['target_id', 'target_type']);
$table->index('created_at');
$table->index(['item_type', 'item_id', 'action_type']);
$table->index(['target_type', 'target_id', 'action_type']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('action_logs', function (Blueprint $table) {
$table->dropIndex(['target_id', 'target_type']);
$table->dropIndex(['created_at']);
$table->dropIndex(['item_type', 'item_id', 'action_type']);
$table->dropIndex(['target_type', 'target_id', 'action_type']);
});
}
}

View file

@ -0,0 +1,48 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddIndexesToAssets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('assets', function (Blueprint $table) {
$table->index('created_at');
$table->index(['deleted_at', 'status_id']);
$table->index(['deleted_at', 'model_id']);
$table->index(['deleted_at', 'assigned_type', 'assigned_to']);
$table->index(['deleted_at', 'supplier_id']);
$table->index(['deleted_at', 'location_id']);
$table->index(['deleted_at', 'rtd_location_id']);
$table->index(['deleted_at', 'asset_tag']);
$table->index(['deleted_at', 'name']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('assets', function (Blueprint $table) {
$table->dropIndex(['created_at']);
$table->dropIndex(['deleted_at', 'status_id']);
$table->dropIndex(['deleted_at', 'model_id']);
$table->dropIndex(['deleted_at', 'assigned_type', 'assigned_to']);
$table->dropIndex(['deleted_at', 'supplier_id']);
$table->dropIndex(['deleted_at', 'location_id']);
$table->dropIndex(['deleted_at', 'rtd_location_id']);
$table->dropIndex(['deleted_at', 'asset_tag']);
$table->dropIndex(['deleted_at', 'name']);
});
}
}

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class DenormCountersOnAssets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('assets', function (Blueprint $table) {
$table->integer('checkin_counter')->default(0);
$table->integer('checkout_counter')->default(0);
$table->integer('requests_counter')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('assets', function (Blueprint $table) {
$table->dropColumn('checkin_counter');
$table->dropColumn('checkout_counter');
$table->dropColumn('requests_counter');
});
}
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Models\Asset;
class AddFirstCounterTotalsToAssets extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// This artisan call may take a while
\Log::info('This could take a while.... ');
Artisan::call('snipeit:counter-sync');
$output = Artisan::output();
\Log::info($output);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View file

@ -21,6 +21,8 @@ class CategorySeeder extends Seeder
factory(Category::class, 1)->states('consumable-ink-category')->create(); // 11
factory(Category::class, 1)->states('component-hdd-category')->create(); // 12
factory(Category::class, 1)->states('component-ram-category')->create(); // 13
factory(Category::class, 1)->states('license-graphics-category')->create(); // 14
factory(Category::class, 1)->states('license-office-category')->create(); // 15
}
}

View file

@ -41,7 +41,7 @@
</div><!-- /.box-header -->
<div class="box-body">
@include ('partials.forms.edit.asset-select', ['translated_name' => trans('admin/asset_maintenances/table.asset_name'), 'fieldname' => 'asset_id'])
@include ('partials.forms.edit.asset-select', ['translated_name' => trans('admin/asset_maintenances/table.asset_name'), 'fieldname' => 'asset_id', 'required' => 'true'])
@include ('partials.forms.edit.supplier-select', ['translated_name' => trans('general.supplier'), 'fieldname' => 'supplier_id', 'required' => 'true'])
@include ('partials.forms.edit.maintenance_type')

View file

@ -82,6 +82,7 @@
}
.next-padding {
margin: {{ $settings->labels_pmargin_top }}in {{ $settings->labels_pmargin_right }}in {{ $settings->labels_pmargin_bottom }}in {{ $settings->labels_pmargin_left }}in;
font-size: 0;
}
}
@ -152,7 +153,7 @@
@if ($count % $settings->labels_per_page == 0)
<div class="page-break"></div>
<div class="next-padding"></div>
<div class="next-padding">&nbsp;</div>
@endif
@endforeach

View file

@ -31,6 +31,7 @@ class LicensesCest
$I->seeElement('.alert-danger');
$I->see('The name field is required.', '.alert-msg');
$I->see('The seats field is required.', '.alert-msg');
$I->see('The category id field is required.', '.alert-msg');
}
public function failsShortValidation(FunctionalTester $I)
@ -58,6 +59,7 @@ class LicensesCest
'license_name' => $license->license_name,
'maintained' => true,
'manufacturer_id' => $license->manufacturer_id,
'category_id' => $license->category_id,
'name' => $license->name,
'notes' => $license->notes,
'order_number' => $license->order_number,

View file

@ -488,8 +488,8 @@ EOT;
{
$this->signIn();
$csv = <<<'EOT'
Name,Email,Username,Item name,serial,manufacturer,purchase date,purchase cost,purchase order,order number,Licensed To Name,Licensed to Email,expiration date,maintained,reassignable,seats,company,supplier,notes
Helen Anderson,cspencer0@privacy.gov.au,cspencer0,Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",07/13/2012,$79.66,53008,386436062-5,Cynthia Spencer,cspencer0@gov.uk,01/27/2016,false,no,80,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Sed ante. Vivamus tortor. Duis mattis egestas metus.
Name,Email,Username,Item name,serial,manufacturer,purchase date,purchase cost,purchase order,order number,Licensed To Name,Licensed to Email,expiration date,maintained,reassignable,seats,company,supplier,category,notes
Helen Anderson,cspencer0@privacy.gov.au,cspencer0,Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",07/13/2012,$79.66,53008,386436062-5,Cynthia Spencer,cspencer0@gov.uk,01/27/2016,false,no,80,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Graphics Software,Sed ante. Vivamus tortor. Duis mattis egestas metus.
EOT;
$this->import(new LicenseImporter($csv));
// dd($this->tester->grabRecord('licenses'));
@ -522,22 +522,26 @@ EOT;
'name' => 'Haag, Schmidt and Farrell'
]);
$this->tester->seeRecord('categories', [
'name' => 'Graphics Software'
]);
$this->tester->seeNumRecords(80, 'license_seats');
}
public function testDefaultLicenseUpdate()
{
$csv = <<<'EOT'
Name,Email,Username,Item name,serial,manufacturer,purchase date,purchase cost,purchase order,order number,Licensed To Name,Licensed to Email,expiration date,maintained,reassignable,seats,company,supplier,notes
Helen Anderson,cspencer0@privacy.gov.au,cspencer0,Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",07/13/2012,$79.66,53008,386436062-5,Cynthia Spencer,cspencer0@gov.uk,01/27/2016,false,no,80,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Sed ante. Vivamus tortor. Duis mattis egestas metus.
Name,Email,Username,Item name,serial,manufacturer,purchase date,purchase cost,purchase order,order number,Licensed To Name,Licensed to Email,expiration date,maintained,reassignable,seats,company,supplier,category,notes
Helen Anderson,cspencer0@privacy.gov.au,cspencer0,Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",07/13/2012,$79.66,53008,386436062-5,Cynthia Spencer,cspencer0@gov.uk,01/27/2016,false,no,80,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Graphics Software,Sed ante. Vivamus tortor. Duis mattis egestas metus.
EOT;
$this->import(new LicenseImporter($csv));
$this->tester->seeNumRecords(1, 'licenses');
$updatedCSV = <<<'EOT'
Item name,serial,manufacturer,purchase date,purchase cost,purchase order,order number,Licensed To Name,Licensed to Email,expiration date,maintained,reassignable,seats,company,supplier,notes
Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",05/15/2019,$1865.34,63 ar,18334,A Legend,Legendary@gov.uk,04/27/2016,yes,true,64,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Sed ante. Vivamus tortor. Duis mattis egestas metus.
Item name,serial,manufacturer,purchase date,purchase cost,purchase order,order number,Licensed To Name,Licensed to Email,expiration date,maintained,reassignable,seats,company,supplier,category,notes
Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",05/15/2019,$1865.34,63 ar,18334,A Legend,Legendary@gov.uk,04/27/2016,yes,true,64,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Graphics Software,Sed ante. Vivamus tortor. Duis mattis egestas metus.
EOT;
$importer = new LicenseImporter($updatedCSV);
$importer->setUserId(1)
@ -546,7 +550,8 @@ EOT;
// At this point we should still only have one record.
$this->tester->seeNumRecords(1, 'licenses');
// But instead these.
// dd($this->tester->grabRecord('licenses'));
\Log::debug($this->tester->grabRecord('licenses'));
$this->tester->seeRecord('licenses', [
'name' => 'Argentum Malachite Athletes Foot Relief',
'purchase_date' => '2019-05-15 00:00:01',
@ -569,8 +574,8 @@ EOT;
public function testCustomLicenseImport()
{
$csv = <<<'EOT'
Name,Email,Username,Object name,serial num,manuf,pur date,pur cost,purc order,order num,Licensed To,Licensed Email,expire date,maint,reass,seat,comp,supplier,note
Helen Anderson,cspencer0@privacy.gov.au,cspencer0,Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",07/13/2012,$79.66,53008,386436062-5,Cynthia Spencer,cspencer0@gov.uk,01/27/2016,false,no,80,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Sed ante. Vivamus tortor. Duis mattis egestas metus.
Name,Email,Username,Object name,serial num,manuf,pur date,pur cost,purc order,order num,Licensed To,Licensed Email,expire date,maint,reass,seat,comp,supplier,category,note
Helen Anderson,cspencer0@privacy.gov.au,cspencer0,Argentum Malachite Athletes Foot Relief,1aa5b0eb-79c5-40b2-8943-5472a6893c3c,"Beer, Leannon and Lubowitz",07/13/2012,$79.66,53008,386436062-5,Cynthia Spencer,cspencer0@gov.uk,01/27/2016,false,no,80,"Haag, Schmidt and Farrell","Hegmann, Mohr and Cremin",Custom Graphics Software,Sed ante. Vivamus tortor. Duis mattis egestas metus.
EOT;
$customFieldMap = [
@ -591,9 +596,9 @@ EOT;
'requestable' => 'Request',
'seats' => 'seat',
'serial' => 'serial num',
'category' => 'category',
];
$this->import(new LicenseImporter($csv), $customFieldMap);
// dd($this->tester->grabRecord('licenses'));
$this->tester->seeRecord('licenses', [
'name' => 'Argentum Malachite Athletes Foot Relief',
'purchase_date' => '2012-07-13 00:00:01',