Company to logs (#2717)

* Fix the actionlog/companyables problem by adding a company_id to all actionlogs and scoping directly on that.  Works around bugs in laravel where trying to hunt down the polymorphic relationship would lead to an infinite loop

* Scope companyables in getactivityreport.  Also eager load.

* Improve reportscontroller, work on seeder to test this.

* Only show company users in checkout dialogs

* If no admin associated with log, it might be a request.  Leave blank instead of saying deleted admin

* When injecting company_id, use target instead of user if user is a superadmin

* Build up the seeder to generate users, companies, and logs.

* Eager load the log, don't scope the users log because the log should already include things only related to the user.
This commit is contained in:
Daniel Meltzer 2016-09-30 00:20:49 -05:00 committed by snipe
parent 7ca7877740
commit 2a95a95e00
13 changed files with 231 additions and 37 deletions

View file

@ -204,7 +204,7 @@ class Helper
public static function usersList() public static function usersList()
{ {
$users_list = array( '' => trans('general.select_user')) + $users_list = array( '' => trans('general.select_user')) +
User::where('deleted_at', '=', null) Company::scopeCompanyables(User::where('deleted_at', '=', null))
->where('show_in_list','=',1) ->where('show_in_list','=',1)
->orderBy('last_name', 'asc') ->orderBy('last_name', 'asc')
->orderBy('first_name', 'asc')->get() ->orderBy('first_name', 'asc')->get()

View file

@ -1156,7 +1156,7 @@ class AssetsController extends Controller
'target_id' => $item[$asset_tag][$batch_counter]['user_id'], 'target_id' => $item[$asset_tag][$batch_counter]['user_id'],
'target_type' => User::class, 'target_type' => User::class,
'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'], 'created_at' => $item[$asset_tag][$batch_counter]['checkout_date'],
'action_type' => 'checkout' 'action_type' => 'checkout',
) )
); );

View file

@ -30,12 +30,6 @@ class DashboardController extends Controller
// Show the page // Show the page
if (Auth::user()->hasAccess('admin')) { if (Auth::user()->hasAccess('admin')) {
$recent_activity = Actionlog::latest()
->with('item')
->take(20)
->get();
$asset_stats['total'] = Asset::Hardware()->count(); $asset_stats['total'] = Asset::Hardware()->count();
$asset_stats['rtd']['total'] = Asset::Hardware()->RTD()->count(); $asset_stats['rtd']['total'] = Asset::Hardware()->RTD()->count();
@ -82,7 +76,7 @@ class DashboardController extends Controller
} }
return View::make('dashboard')->with('asset_stats', $asset_stats)->with('recent_activity', $recent_activity); return View::make('dashboard')->with('asset_stats', $asset_stats);
} else { } else {
// Redirect to the profile page // Redirect to the profile page
return redirect()->route('view-assets'); return redirect()->route('view-assets');

View file

@ -308,7 +308,7 @@ class ReportsController extends Controller
*/ */
public function getActivityReportDataTable() public function getActivityReportDataTable()
{ {
$activitylogs = Actionlog::orderBy('created_at', 'DESC'); $activitylogs = Company::scopeCompanyables(Actionlog::with('item', 'user', 'target'))->orderBy('created_at', 'DESC');
if (Input::has('search')) { if (Input::has('search')) {
$activity = $activity->TextSearch(e(Input::get('search'))); $activity = $activity->TextSearch(e(Input::get('search')));
@ -333,10 +333,10 @@ class ReportsController extends Controller
$activityCount = $activitylogs->count(); $activityCount = $activitylogs->count();
$activitylogs = $activitylogs->skip($offset)->take($limit)->get(); // dd("Offset:" . $offset . " Limit " . $limit);
$activitylogs = $activitylogs->offset($offset)->limit($limit)->get();
$rows = array(); $rows = array();
foreach ($activitylogs as $activity) { foreach ($activitylogs as $activity) {
if ($activity->itemType() == "asset") { if ($activity->itemType() == "asset") {
@ -354,24 +354,27 @@ class ReportsController extends Controller
} }
if (($activity->item) && ($activity->itemType()=="asset")) { if (($activity->item) && ($activity->itemType()=="asset")) {
$actvity_item = '<a href="'.route('view/hardware', $activity->item_id).'">'.e($activity->item->asset_tag).' - '. e($activity->item->showAssetName()).'</a>'; $activity_item = '<a href="'.route('view/hardware', $activity->item_id).'">'.e($activity->item->asset_tag).' - '. e($activity->item->showAssetName()).'</a>';
$item_type = 'asset'; $item_type = 'asset';
} elseif ($activity->item) { } elseif ($activity->item()) {
$actvity_item = '<a href="'.route('view/'. $activity->itemType(), $activity->item_id).'">'.e($activity->item->name).'</a>'; $activity_item = '<a href="'.route('view/'. $activity->itemType(), $activity->item_id).'">'.e($activity->item->name).'</a>';
$item_type = $activity->itemType(); $item_type = $activity->itemType();
} else {
$activity_item = "unkonwn";
$item_type = "null";
} }
if (($activity->userasassetlog) && ($activity->action_type=="uploaded") && ($activity->itemType()=="user")) { if (($activity->user) && ($activity->action_type=="uploaded") && ($activity->itemType()=="user")) {
$activity_target = '<a href="'.route('view/user', $activity->target_id).'">'.$activity->userasassetlog->fullName().'</a>'; $activity_target = '<a href="'.route('view/user', $activity->target_id).'">'.$activity->user->fullName().'</a>';
} elseif (($activity->item) && ($activity->target instanceof \App\Models\Asset)) { } elseif (($activity->item) && ($activity->target_type === "App\Models\Asset")) {
$activity_target = '<a href="'.route('view/hardware', $activity->target_id).'">'.$activity->target->showAssetName().'</a>'; $activity_target = '<a href="'.route('view/hardware', $activity->target_id).'">'.$activity->target->showAssetName().'</a>';
} elseif (($activity->item) && ($activity->target instanceof \App\Models\User)) { } elseif ( $activity->target_type === "App\Models\User") {
$activity_target = '<a href="'.route('view/user', $activity->target_id).'">'.$activity->target->fullName().'</a>'; $activity_target = '<a href="'.route('view/user', $activity->target_id).'">'.$activity->target->fullName().'</a>';
} elseif ($activity->action_type=='requested') { } elseif ($activity->action_type=='requested') {
$activity_target = '<a href="'.route('view/user', $activity->user_id).'">'.$activity->user->fullName().'</a>'; $activity_target = '<a href="'.route('view/user', $activity->user_id).'">'.$activity->user->fullName().'</a>';
} else { } else {
$activity_target = $activity->target; $activity_target = $activity->target->id;
} }
@ -379,9 +382,9 @@ class ReportsController extends Controller
'icon' => $activity_icons, 'icon' => $activity_icons,
'created_at' => date("M d, Y g:iA", strtotime($activity->created_at)), 'created_at' => date("M d, Y g:iA", strtotime($activity->created_at)),
'action_type' => strtolower(trans('general.'.str_replace(' ','_',$activity->action_type))), 'action_type' => strtolower(trans('general.'.str_replace(' ','_',$activity->action_type))),
'admin' => $activity->user ? (string) link_to('/admin/users/'.$activity->user_id.'/view', $activity->user->fullName()) : 'Deleted Admin', 'admin' => $activity->user ? (string) link_to('/admin/users/'.$activity->user_id.'/view', $activity->user->fullName()) : '',
'target' => $activity_target, 'target' => $activity_target,
'item' => $actvity_item, 'item' => $activity_item,
'item_type' => $item_type, 'item_type' => $item_type,
'note' => e($activity->note), 'note' => e($activity->note),

View file

@ -39,7 +39,16 @@ class ViewAssetsController extends Controller
public function getIndex() public function getIndex()
{ {
$user = User::with('assets', 'assets.model', 'consumables', 'accessories', 'licenses', 'userloc')->withTrashed()->find(Auth::user()->id); $user = User::with(
'assets',
'assets.model',
'consumables',
'accessories',
'licenses',
'userloc',
'userlog'
)->withTrashed()->find(Auth::user()->id);
$userlog = $user->userlog->load('item', 'user', 'target'); $userlog = $user->userlog->load('item', 'user', 'target');
@ -76,7 +85,7 @@ class ViewAssetsController extends Controller
{ {
$item = null; $item = null;
$fullItemType = 'App\\Models\\' . studly_case($itemType); $fullItemType = 'App\\Models\\' . studly_case($itemType);
if($itemType == "asset_model") { if ($itemType == "asset_model") {
$itemType = "model"; $itemType = "model";
} }
$item = call_user_func(array($fullItemType, 'find'), $itemId); $item = call_user_func(array($fullItemType, 'find'), $itemId);

View file

@ -2,9 +2,10 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\SoftDeletes;
/** /**
* Model for the Actionlog (the table that keeps a historical log of * Model for the Actionlog (the table that keeps a historical log of
@ -12,10 +13,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* *
* @version v1.0 * @version v1.0
*/ */
class Actionlog extends Model implements ICompanyableChild class Actionlog extends Model
{ {
use SoftDeletes; use SoftDeletes;
use CompanyableChildTrait;
protected $dates = [ 'deleted_at' ]; protected $dates = [ 'deleted_at' ];
@ -23,11 +23,19 @@ class Actionlog extends Model implements ICompanyableChild
public $timestamps = true; public $timestamps = true;
protected $fillable = [ 'created_at', 'item_type','user_id','item_id','action_type','note','target_id', 'target_type' ]; protected $fillable = [ 'created_at', 'item_type','user_id','item_id','action_type','note','target_id', 'target_type' ];
public function getCompanyableParents() // Overridden from Builder to automatically add the company
public static function boot()
{ {
return [ 'accessorylog', 'assetlog', 'licenselog', 'consumablelog' ]; parent::boot();
static::creating( function (Actionlog $actionlog) {
// If the admin is a superadmin, let's see if the target instead has a company.
if (Auth::user() && Auth::user()->isSuperUser()) {
$actionlog->company_id = $actionlog->target->company_id;
} else if (Auth::user() && Auth::user()->company) {
$actionlog->company_id = Auth::user()->company_id;
}
});
} }
// Eloquent Relationships below // Eloquent Relationships below
public function item() public function item()
{ {

View file

@ -136,7 +136,8 @@ final class Company extends Model
public static function scopeCompanyables($query, $column = 'company_id') public static function scopeCompanyables($query, $column = 'company_id')
{ {
if (!static::isFullMultipleCompanySupportEnabled() || (Auth::check() && Auth::user()->isSuperUser())) { // If not logged in and hitting this, assume we are on the command line and don't scope?'
if (!static::isFullMultipleCompanySupportEnabled() || (Auth::check() && Auth::user()->isSuperUser()) || (!Auth::check())) {
return $query; return $query;
} else { } else {
return static::scopeCompanyablesDirectly($query, $column); return static::scopeCompanyablesDirectly($query, $column);
@ -176,4 +177,27 @@ final class Company extends Model
return e($company->name); return e($company->name);
} }
} }
public function users() {
return $this->hasMany(User::class);
}
public function assets() {
return $this->hasMany(Asset::class);
}
public function licenses() {
return $this->hasMany(License::class);
}
public function accessories() {
return $this->hasMany(Accessory::class);
}
public function consumables() {
return $this->hasMany(Consumable::class);
}
public function components() {
return $this->hasMany(Component::class);
}
} }

View file

@ -25,6 +25,7 @@ $factory->defineAs(App\Models\Asset::class, 'asset', function (Faker\Generator $
'order_number' => $faker->numberBetween(1000000,50000000), 'order_number' => $faker->numberBetween(1000000,50000000),
'supplier_id' => $faker->numberBetween(1,5), 'supplier_id' => $faker->numberBetween(1,5),
'requestable' => $faker->numberBetween(0,1), 'requestable' => $faker->numberBetween(0,1),
'company_id' => \App\Models\Company::inRandomOrder()->first()->id
]; ];
}); });
@ -96,6 +97,7 @@ $factory->defineAs(App\Models\Component::class, 'component', function (Faker\Gen
'category_id' => $faker->numberBetween(21,25), 'category_id' => $faker->numberBetween(21,25),
'total_qty' => $faker->numberBetween(3, 10), 'total_qty' => $faker->numberBetween(3, 10),
'min_amt' => $faker->numberBetween($min = 1, $max = 2), 'min_amt' => $faker->numberBetween($min = 1, $max = 2),
'company_id' => \App\Models\Company::inRandomOrder()->first()->id
]; ];
}); });
@ -113,6 +115,7 @@ $factory->defineAs(App\Models\Accessory::class, 'accessory', function (Faker\Gen
'qty' => $faker->numberBetween(5, 10), 'qty' => $faker->numberBetween(5, 10),
'location_id' => $faker->numberBetween(1,5), 'location_id' => $faker->numberBetween(1,5),
'min_amt' => $faker->numberBetween($min = 1, $max = 2), 'min_amt' => $faker->numberBetween($min = 1, $max = 2),
'company_id' => \App\Models\Company::inRandomOrder()->first()->id
]; ];
}); });
@ -138,6 +141,7 @@ $factory->defineAs(App\Models\Consumable::class, 'consumable', function (Faker\G
'company_id' => $faker->numberBetween(1, 10), 'company_id' => $faker->numberBetween(1, 10),
'qty' => $faker->numberBetween(5, 10), 'qty' => $faker->numberBetween(5, 10),
'min_amt' => $faker->numberBetween($min = 1, $max = 2), 'min_amt' => $faker->numberBetween($min = 1, $max = 2),
'company_id' => \App\Models\Company::inRandomOrder()->first()->id
]; ];
}); });
@ -251,6 +255,7 @@ $factory->defineAs(App\Models\License::class, 'license', function (Faker\Generat
'purchase_date' => $faker->dateTime(), 'purchase_date' => $faker->dateTime(),
'purchase_cost' => $faker->randomFloat(2), 'purchase_cost' => $faker->randomFloat(2),
'notes' => $faker->sentence, 'notes' => $faker->sentence,
'company_id' => \App\Models\Company::inRandomOrder()->first()->id
]; ];
}); });
@ -265,19 +270,82 @@ $factory->defineAs(App\Models\LicenseSeat::class, 'license-seat', function (Fake
}); });
$factory->defineAs(App\Models\Actionlog::class, 'asset-checkout', function (Faker\Generator $faker) { $factory->defineAs(App\Models\Actionlog::class, 'asset-checkout', function (Faker\Generator $faker) {
$company = \App\Models\Company::has('users')->has('assets')->inRandomOrder()->first();
return [ return [
'user_id' => 1, 'user_id' => $company->users()->inRandomOrder()->first()->id,
'action_type' => 'checkout', 'action_type' => 'checkout',
'item_id' => $faker->numberBetween(1, 10), 'item_id' => $company->assets()->inRandomOrder()->first()->id,
'target_id' => 1, 'target_id' => $company->users()->inRandomOrder()->first()->id,
'target_type' => 'App\\Models\\User', 'target_type' => 'App\\Models\\User',
'created_at' => $faker->dateTime(), 'created_at' => $faker->dateTime(),
'item_type' => 'App\\Models\\Asset', 'item_type' => 'App\\Models\\Asset',
'note' => $faker->sentence, 'note' => $faker->sentence,
'user_id' => '1', 'company_id' => $company->id
]; ];
}); });
$factory->defineAs(App\Models\Actionlog::class, 'license-checkout-asset', function (Faker\Generator $faker) {
$company = \App\Models\Company::has('users')->has('licenses')->inRandomOrder()->first();
return [
'user_id' => $company->users()->inRandomOrder()->first()->id,
'action_type' => 'checkout',
'item_id' => $company->licenses()->whereNotNull('company_id')->inRandomOrder()->first()->id,
'target_id' => $company->assets()->inRandomOrder()->first()->id,
'target_type' => 'App\\Models\\Asset',
'created_at' => $faker->dateTime(),
'item_type' => 'App\\Models\\License',
'note' => $faker->sentence,
'company_id' => $company->id
];
});
$factory->defineAs(App\Models\Actionlog::class, 'accessory-checkout', function (Faker\Generator $faker) {
$company = \App\Models\Company::has('users')->has('accessories')->inRandomOrder()->first();
return [
'user_id' => $company->users()->inRandomOrder()->first()->id,
'action_type' => 'checkout',
'item_id' => $company->accessories()->whereNotNull('company_id')->inRandomOrder()->first()->id,
'target_id' => $company->users()->inRandomOrder()->first()->id,
'target_type' => 'App\\Models\\User',
'created_at' => $faker->dateTime(),
'item_type' => 'App\\Models\\Accessory',
'note' => $faker->sentence,
'company_id' => $company->id
];
});
$factory->defineAs(App\Models\Actionlog::class, 'consumable-checkout', function (Faker\Generator $faker) {
$company = \App\Models\Company::has('users')->has('consumables')->inRandomOrder()->first();
return [
'user_id' => $company->users()->inRandomOrder()->first()->id,
'action_type' => 'checkout',
'item_id' => $company->consumables()->whereNotNull('company_id')->inRandomOrder()->first()->id,
'target_id' => $company->users()->inRandomOrder()->first()->id,
'target_type' => 'App\\Models\\User',
'created_at' => $faker->dateTime(),
'item_type' => 'App\\Models\\Consumable',
'note' => $faker->sentence,
'company_id' => $company->id
];
});
$factory->defineAs(App\Models\Actionlog::class, 'component-checkout', function (Faker\Generator $faker) {
$company = \App\Models\Company::has('users')->has('components')->inRandomOrder()->first();
return [
'user_id' => $company->users()->inRandomOrder()->first()->id,
'action_type' => 'checkout',
'item_id' => $company->components()->whereNotNull('company_id')->inRandomOrder()->first()->id,
'target_id' => $company->users()->inRandomOrder()->first()->id,
'target_type' => 'App\\Models\\User',
'created_at' => $faker->dateTime(),
'item_type' => 'App\\Models\\Component',
'note' => $faker->sentence,
'company_id' => $company->id
];
});
$factory->defineAs(App\Models\CustomField::class, 'customfield-ip', function (Faker\Generator $faker) { $factory->defineAs(App\Models\CustomField::class, 'customfield-ip', function (Faker\Generator $faker) {
return [ return [
@ -295,5 +363,6 @@ $factory->defineAs(App\Models\User::class, 'valid-user', function (Faker\Generat
'email' => $faker->safeEmail, 'email' => $faker->safeEmail,
'password' => $faker->password, 'password' => $faker->password,
'username' => $faker->username, 'username' => $faker->username,
'company_id' => \App\Models\Company::inRandomOrder()->first()->id
]; ];
}); });

View file

@ -0,0 +1,44 @@
<?php
use App\Models\Actionlog;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddCompanyToLogs extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('action_logs', function (Blueprint $table) {
//
$table->integer('company_id')->nullable()->default(null);
});
$logs = Actionlog::with('item')->get();
foreach ($logs as $log) {
if($log->item) {
$log->company_id = $log->item->company_id;
$log->save();
} else {
var_dump($log);
}
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('action_logs', function (Blueprint $table) {
//
$table->dropColumn('company_id');
});
}
}

View file

@ -7,6 +7,10 @@ class ActionlogSeeder extends Seeder
public function run() public function run()
{ {
Actionlog::truncate(); Actionlog::truncate();
factory(Actionlog::class, 'asset-checkout',5)->create(); factory(Actionlog::class, 'asset-checkout',25)->create();
factory(Actionlog::class, 'accessory-checkout',15)->create();
factory(Actionlog::class, 'consumable-checkout', 15)->create();
factory(Actionlog::class, 'component-checkout', 15)->create();
factory(Actionlog::class, 'license-checkout-asset', 15)->create();
} }
} }

View file

@ -0,0 +1,19 @@
<?php
use App\Models\Company;
use Illuminate\Database\Seeder;
class CompanySeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
Company::truncate();
factory(Company::class, 'company', 4)->create();
}
}

View file

@ -14,9 +14,12 @@ class DatabaseSeeder extends Seeder
{ {
Model::unguard(); Model::unguard();
$this->call(CompanySeeder::class);
$this->call(UserSeeder::class);
$this->call(AssetModelSeeder::class); $this->call(AssetModelSeeder::class);
$this->call(AccessorySeeder::class); $this->call(AccessorySeeder::class);
$this->call(AssetSeeder::class); $this->call(AssetSeeder::class);
$this->call(ComponentSeeder::class);
$this->call(ConsumableSeeder::class); $this->call(ConsumableSeeder::class);
$this->call(StatuslabelSeeder::class); $this->call(StatuslabelSeeder::class);
$this->call(SupplierSeeder::class); $this->call(SupplierSeeder::class);
@ -27,7 +30,6 @@ class DatabaseSeeder extends Seeder
$this->call(ManufacturerSeeder::class); $this->call(ManufacturerSeeder::class);
$this->call(LocationSeeder::class); $this->call(LocationSeeder::class);
$this->call(CustomFieldSeeder::class); $this->call(CustomFieldSeeder::class);
$this->call(ComponentSeeder::class);
Model::reguard(); Model::reguard();
} }

View file

@ -0,0 +1,18 @@
<?php
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Don't truncate the user column, that might suck.
factory(User::class, 'valid-user', 10)->create();
}
}