diff --git a/app/Console/Commands/SyncAssetCounters.php b/app/Console/Commands/SyncAssetCounters.php new file mode 100644 index 0000000000..bf63eaff9d --- /dev/null +++ b/app/Console/Commands/SyncAssetCounters.php @@ -0,0 +1,76 @@ +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'); + } + + } + + + + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 9a0d3090e5..4d66056732 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -29,6 +29,7 @@ class Kernel extends ConsoleKernel Commands\ResetDemoSettings::class, Commands\SyncAssetLocations::class, Commands\RegenerateAssetTags::class, + Commands\SyncAssetCounters::class, ]; /** diff --git a/app/Http/Controllers/Api/AssetsController.php b/app/Http/Controllers/Api/AssetsController.php index ee4f45365a..02f0d12b0c 100644 --- a/app/Http/Controllers/Api/AssetsController.php +++ b/app/Http/Controllers/Api/AssetsController.php @@ -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. diff --git a/app/Http/Controllers/AssetMaintenancesController.php b/app/Http/Controllers/AssetMaintenancesController.php index 975399f275..32271fcdd8 100644 --- a/app/Http/Controllers/AssetMaintenancesController.php +++ b/app/Http/Controllers/AssetMaintenancesController.php @@ -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(); } diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index 631ed25886..af7626765e 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -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)); diff --git a/app/Http/Transformers/AssetsTransformer.php b/app/Http/Transformers/AssetsTransformer.php index 5664b5fda4..84b5af4193 100644 --- a/app/Http/Transformers/AssetsTransformer.php +++ b/app/Http/Transformers/AssetsTransformer.php @@ -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(), ]; diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 0549d80106..3198ff36f9 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -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; diff --git a/app/Models/License.php b/app/Models/License.php index 162bea4c3e..5959819c22 100755 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -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', ); diff --git a/app/Models/Loggable.php b/app/Models/Loggable.php index a325762d4a..f74f453c26 100644 --- a/app/Models/Loggable.php +++ b/app/Models/Loggable.php @@ -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); + } + } + } diff --git a/app/Presenters/AssetPresenter.php b/app/Presenters/AssetPresenter.php index 11f00e6904..d971b09e39 100644 --- a/app/Presenters/AssetPresenter.php +++ b/app/Presenters/AssetPresenter.php @@ -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, diff --git a/config/version.php b/config/version.php index cb8795c217..3a0c03c6f3 100644 --- a/config/version.php +++ b/config/version.php @@ -1,10 +1,10 @@ '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', ); diff --git a/database/factories/CategoryFactory.php b/database/factories/CategoryFactory.php index b7fe01baa7..26b59ffac2 100644 --- a/database/factories/CategoryFactory.php +++ b/database/factories/CategoryFactory.php @@ -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', + ]; +}); diff --git a/database/factories/LicenseFactory.php b/database/factories/LicenseFactory.php index 32155fdce1..b4b440f45c 100644 --- a/database/factories/LicenseFactory.php +++ b/database/factories/LicenseFactory.php @@ -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, ]; diff --git a/database/migrations/2018_05_14_215229_add_indexes.php b/database/migrations/2018_05_14_215229_add_indexes.php new file mode 100644 index 0000000000..4f917f74e2 --- /dev/null +++ b/database/migrations/2018_05_14_215229_add_indexes.php @@ -0,0 +1,39 @@ +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']); + }); + } +} diff --git a/database/migrations/2018_05_14_223646_add_indexes_to_assets.php b/database/migrations/2018_05_14_223646_add_indexes_to_assets.php new file mode 100644 index 0000000000..a7161bdb53 --- /dev/null +++ b/database/migrations/2018_05_14_223646_add_indexes_to_assets.php @@ -0,0 +1,48 @@ +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']); + }); + } +} diff --git a/database/migrations/2018_05_14_233638_denorm_counters_on_assets.php b/database/migrations/2018_05_14_233638_denorm_counters_on_assets.php new file mode 100644 index 0000000000..d7f3971f22 --- /dev/null +++ b/database/migrations/2018_05_14_233638_denorm_counters_on_assets.php @@ -0,0 +1,36 @@ +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'); + }); + } +} diff --git a/database/migrations/2018_05_16_153409_add_first_counter_totals_to_assets.php b/database/migrations/2018_05_16_153409_add_first_counter_totals_to_assets.php new file mode 100644 index 0000000000..f3f70f001c --- /dev/null +++ b/database/migrations/2018_05_16_153409_add_first_counter_totals_to_assets.php @@ -0,0 +1,34 @@ +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 } } diff --git a/resources/views/asset_maintenances/edit.blade.php b/resources/views/asset_maintenances/edit.blade.php index a7a5743e63..fc42f36cd4 100644 --- a/resources/views/asset_maintenances/edit.blade.php +++ b/resources/views/asset_maintenances/edit.blade.php @@ -41,7 +41,7 @@
- @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') diff --git a/resources/views/hardware/labels.blade.php b/resources/views/hardware/labels.blade.php index cbfb1f4449..8ed4fbc50f 100644 --- a/resources/views/hardware/labels.blade.php +++ b/resources/views/hardware/labels.blade.php @@ -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)
-
+
 
@endif @endforeach diff --git a/tests/functional/LicensesCest.php b/tests/functional/LicensesCest.php index e6542c7d41..85c90232c1 100644 --- a/tests/functional/LicensesCest.php +++ b/tests/functional/LicensesCest.php @@ -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, diff --git a/tests/unit/ImporterTest.php b/tests/unit/ImporterTest.php index 7a2803f33e..99469a8ecd 100644 --- a/tests/unit/ImporterTest.php +++ b/tests/unit/ImporterTest.php @@ -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',