Compare commits

...

22 Commits

Author SHA1 Message Date
snipe 46b00b6fb6 Merge remote-tracking branch 'origin/develop' 2016-10-27 22:17:52 -07:00
snipe 86ef44b43d Bumped version 2016-10-27 22:17:30 -07:00
snipe 5a835a5b6e Removed old test DB schema dump 2016-10-27 21:41:27 -07:00
snipe 0f47ddbeb1 Merge remote-tracking branch 'origin/develop' 2016-10-27 21:40:06 -07:00
snipe 066d947f1a Fixes #2535 - increased varachar limit to 2048 for serial 2016-10-27 21:39:46 -07:00
snipe 683b3bcdc1 Merge remote-tracking branch 'origin/develop' 2016-10-27 15:34:03 -07:00
snipe a929b635ff Fixes #2809 - adds serial number to components 2016-10-27 15:33:48 -07:00
snipe d7296462ac Merge remote-tracking branch 'origin/develop' 2016-10-27 15:03:03 -07:00
snipe 9c3c611b37 Fixes #2825 - columns misaligned when older display_asset_name field set to 1 2016-10-27 15:02:32 -07:00
snipe 2f72d97f24 Merge remote-tracking branch 'origin/develop' 2016-10-27 14:33:24 -07:00
snipe 3e701c6dd1 Fixes #2814 - adds job title to users listing display 2016-10-27 14:29:07 -07:00
snipe e4140f4c48 Fixes #2817 - adds EOL to custom report 2016-10-27 14:20:55 -07:00
snipe 61ecbf91bc Merge remote-tracking branch 'origin/develop' 2016-10-26 11:28:14 -07:00
snipe 691f9b621e Fixes #2821 2016-10-26 11:27:37 -07:00
snipe 52b6298098 Merge remote-tracking branch 'origin/develop' 2016-10-26 11:23:03 -07:00
snipe ccb844acd6 Fixed #2800 - use DBTablePrefix() for raw queries 2016-10-26 11:21:36 -07:00
snipe a0a6a23232 Merge remote-tracking branch 'origin/develop' 2016-10-26 10:32:50 -07:00
snipe 43404e427d Bumped version 2016-10-26 10:31:13 -07:00
snipe d7222ae1f0 Fix for defaultLoc in asset view 2016-10-26 10:12:16 -07:00
snipe ad1d6529a7 Merge branch 'master' of github.com:snipe/snipe-it 2016-10-26 10:09:59 -07:00
Daniel Meltzer 7790f9e91f Fix #2813 (#2819)
Looks like a copypasta miss.  Should fix the exception thrown in #2813.
2016-10-26 10:01:24 -07:00
Daniel Meltzer a418dece80 Better checking for empty values when updating. (#2811)
* Better checking for empty values when updating.  There's a lot of conditionals in here that we may want to look at cleaning up over time

* Fix typo.  No manfacturers here.

* Fix model update/import.  Also hardcode the status id of unset assets to the first existing one instead of an id that may not exist... Still not ideal, but better.

* Let requests to .env through the middleware.  We check to see if this is readable during setup as a warning, and as it stands it triggers an infinite loop trying to hit the file.
2016-10-25 19:51:13 -07:00
20 changed files with 202 additions and 721 deletions
+85 -57
View File
@@ -68,7 +68,7 @@ class ObjectImportCommand extends Command
$tmp_password = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$password = bcrypt($tmp_password);
$this->updating = $this->option('update');
if (!$this->option('web-importer')) {
$logFile = $this->option('logfile');
\Log::useFiles($logFile);
@@ -138,7 +138,7 @@ class ObjectImportCommand extends Command
$item_category = $this->array_smart_fetch($row, "category");
$item_company_name = $this->array_smart_fetch($row, "company");
$item_location = $this->array_smart_fetch($row, "location");
$item_manufacturer = $this->array_smart_fetch($row, "manufacturer");
$item_status_name = $this->array_smart_fetch($row, "status");
$item["item_name"] = $this->array_smart_fetch($row, "item name");
@@ -162,6 +162,7 @@ class ObjectImportCommand extends Command
}
$this->log('Category: ' . $item_category);
$this->log('Location: ' . $item_location);
$this->log('Manufacturer: ' . $item_manufacturer);
$this->log('Purchase Date: ' . $item["purchase_date"]);
$this->log('Purchase Cost: ' . $item["purchase_cost"]);
$this->log('Company Name: ' . $item_company_name);
@@ -170,12 +171,22 @@ class ObjectImportCommand extends Command
$item["user"] = $this->createOrFetchUser($row, $password);
$item["location"] = $this->createOrFetchLocation($item_location);
$item["category"] = $this->createOrFetchCategory($item_category, $item_type);
$item["manufacturer"] = $this->createOrFetchManufacturer($row);
$item["company"] = $this->createOrFetchCompany($item_company_name);
if (!($this->updating && empty($item_location))) {
$item["location"] = $this->createOrFetchLocation($item_location);
}
if (!($this->updating && empty($item_category))) {
$item["category"] = $this->createOrFetchCategory($item_category, $item_type);
}
if (!($this->updating && empty($item_manufacturer))) {
$item["manufacturer"] = $this->createOrFetchManufacturer($item_manufacturer);
}
if (!($this->updating && empty($item_company_name))) {
$item["company"] = $this->createOrFetchCompany($item_company_name);
}
$item["status_label"] = $this->createOrFetchStatusLabel($item_status_name);
if (!($this->updating && empty($item_status_name))) {
$item["status_label"] = $this->createOrFetchStatusLabel($item_status_name);
}
switch ($item_type) {
case "asset":
@@ -236,7 +247,7 @@ class ObjectImportCommand extends Command
}
// Tracks the current item for error messages
private $current_assetId;
private $updating;
// An array of errors encountered while parsing
private $errors;
@@ -333,27 +344,39 @@ class ObjectImportCommand extends Command
$this->log('Model Name: ' . $asset_model_name);
$this->log('Model No: ' . $asset_modelno);
$asset_model = null;
$editingModel = false;
foreach ($this->asset_models as $tempmodel) {
if ((strcasecmp($tempmodel->name, $asset_model_name) == 0)
&& $tempmodel->modelno == $asset_modelno
&& $tempmodel->category_id == $category->id
&& $tempmodel->manufacturer_id == $manufacturer->id ) {
$this->log('A matching model ' . $asset_model_name . ' with model number ' . $asset_modelno . ' already exists');
return $tempmodel;
if (strcasecmp($tempmodel->name, $asset_model_name) == 0
&& $tempmodel->modelno == $asset_modelno) {
$this->log('A matching model ' . $asset_model_name . ' already exists');
if (!$this->option('update')) {
return $tempmodel;
}
$this->log('Updating matching model with new values');
$editingModel = true;
$asset_model = $tempmodel;
}
}
$asset_model = new AssetModel();
$asset_model->name = $asset_model_name;
$asset_model->manufacturer_id = $manufacturer->id;
$asset_model->modelno = $asset_modelno;
$asset_model->category_id = $category->id;
if (is_null($asset_model)) {
$this->log("No Matching Model, Creating a new one");
$asset_model = new AssetModel();
}
if (($editingModel && (!$asset_model_name === "Unknown")) || (!$editingModel)) {
$asset_model->name = $asset_model_name;
}
isset($manufacturer) && $manufacturer->exists && $asset_model->manufacturer_id = $manufacturer->id;
isset($asset_modelno) && $asset_model->modelno = $asset_modelno;
if (isset($category) && $category->exists) {
$asset_model->category_id = $category->id;
}
$asset_model->user_id = $this->option('user_id');
if (!$editingModel) {
$this->asset_models->add($asset_model);
}
if (!$this->option('testrun')) {
if ($asset_model->save()) {
$this->asset_models->add($asset_model);
$this->log('Asset Model ' . $asset_model_name . ' with model number ' . $asset_modelno . ' was created');
return $asset_model;
} else {
@@ -500,23 +523,20 @@ class ObjectImportCommand extends Command
*
* @author Daniel Melzter
* @since 3.0
* @param $row array
* @param $item_manufacturer string
* @return Manufacturer
* @internal param $asset_mfgr string
*/
public function createOrFetchManufacturer(array $row)
public function createOrFetchManufacturer($item_manufacturer)
{
$asset_mfgr = $this->array_smart_fetch($row, "manufacturer");
if (empty($asset_mfgr)) {
$asset_mfgr='Unknown';
if (empty($item_manufacturer)) {
$item_manufacturer='Unknown';
}
$this->log('Manufacturer ID: ' . $asset_mfgr);
foreach ($this->manufacturers as $tempmanufacturer) {
if (strcasecmp($tempmanufacturer->name, $asset_mfgr) == 0) {
$this->log('Manufacturer ' . $asset_mfgr . ' already exists') ;
if (strcasecmp($tempmanufacturer->name, $item_manufacturer) == 0) {
$this->log('Manufacturer ' . $item_manufacturer . ' already exists') ;
return $tempmanufacturer;
}
}
@@ -524,7 +544,7 @@ class ObjectImportCommand extends Command
//Otherwise create a manufacturer.
$manufacturer = new Manufacturer();
$manufacturer->name = $asset_mfgr;
$manufacturer->name = $item_manufacturer;
$manufacturer->user_id = $this->option('user_id');
if (!$this->option('testrun')) {
@@ -604,27 +624,26 @@ class ObjectImportCommand extends Command
* @param $row array
* @return Supplier
*/
public function createOrFetchSupplier(array $row)
public function createOrFetchSupplier($item_supplier)
{
$supplier_name = $this->array_smart_fetch($row, "supplier");
if (empty($supplier_name)) {
$supplier_name='Unknown';
if (empty($item_supplier)) {
$item_supplier='Unknown';
}
foreach ($this->suppliers as $tempsupplier) {
if (strcasecmp($tempsupplier->name, $supplier_name) == 0) {
$this->log('A matching Company ' . $supplier_name . ' already exists');
if (strcasecmp($tempsupplier->name, $item_supplier) == 0) {
$this->log('A matching Supplier ' . $item_supplier . ' already exists');
return $tempsupplier;
}
}
$supplier = new Supplier();
$supplier->name = $supplier_name;
$supplier->name = $item_supplier;
$supplier->user_id = $this->option('user_id');
if (!$this->option('testrun')) {
if ($supplier->save()) {
$this->suppliers->add($supplier);
$this->log('Supplier ' . $supplier_name . ' was created');
$this->log('Supplier ' . $item_supplier . ' was created');
return $supplier;
} else {
$this->log('Supplier', $supplier->getErrors());
@@ -770,8 +789,17 @@ class ObjectImportCommand extends Command
$asset_warranty_months = null;
}
// Check for the asset model match and create it if it doesn't exist
$asset_model = $this->createOrFetchAssetModel($row, $item["category"], $item["manufacturer"]);
$supplier = $this->createOrFetchSupplier($row);
if (!($editingAsset && empty($this->array_smart_fetch($row, 'model name')))) {
// Ignore the asset_model
isset($item["category"]) || $item["category"] = new Category();
isset($item["manufacturer"]) || $item["manufacturer"] = new Manufacturer();
$asset_model = $this->createOrFetchAssetModel($row, $item["category"], $item["manufacturer"]);
}
$item_supplier = $this->array_smart_fetch($row, "supplier");
// If we're editing, only update if value isn't empty
if (!($editingAsset && empty($item_supplier))) {
$supplier = $this->createOrFetchSupplier($item_supplier);
}
$this->log('Serial No: '.$asset_serial);
$this->log('Asset Tag: '.$item['asset_tag']);
@@ -780,13 +808,13 @@ class ObjectImportCommand extends Command
if ($item["status_label"]) {
if (isset($item["status_label"])) {
$status_id = $item["status_label"]->id;
} else {
} else if (!$editingAsset) {
// Assume if we are editing, we already have a status and can ignore.
// FIXME: We're already grabbing the list of statuses, we should probably not hardcode here
$this->log("No status field found, defaulting to id 1.");
$status_id = 1;
$status_id = $this->status_labels->first()->id;
}
if (!$editingAsset) {
@@ -821,7 +849,7 @@ class ObjectImportCommand extends Command
$asset->warranty_months = $asset_warranty_months;
}
if ($asset_model) {
if (isset($asset_model)) {
$asset->model_id = $asset_model->id;
}
@@ -829,21 +857,21 @@ class ObjectImportCommand extends Command
$asset->assigned_to = $item["user"]->id;
}
if ($item["location"]) {
if (isset($item["location"])) {
$asset->rtd_location_id = $item["location"]->id;
}
$asset->user_id = $this->option('user_id');
if (!empty($status_id)) {
if (isset($status_id)) {
$asset->status_id = $status_id;
}
if ($item["company"]) {
if (isset($item["company"])) {
$asset->company_id = $item["company"]->id;
}
if ($item["order_number"]) {
$asset->order_number = $item["order_number"];
}
if ($supplier) {
if (isset($supplier)) {
$asset->supplier_id = $supplier->id;
}
if ($item["notes"]) {
@@ -913,17 +941,17 @@ class ObjectImportCommand extends Command
$accessory->purchase_cost = Helper::ParseFloat($item["purchase_cost"]);
}
if ($item["location"]) {
if (isset($item["location"])) {
$accessory->location_id = $item["location"]->id;
}
$accessory->user_id = $this->option('user_id');
if ($item["company"]) {
if (isset($item["company"])) {
$accessory->company_id = $item["company"]->id;
}
if (!empty($item["order_number"])) {
$accessory->order_number = $item["order_number"];
}
if ($item["category"]) {
if (isset($item["category"])) {
$accessory->category_id = $item["category"]->id;
}
@@ -999,17 +1027,17 @@ class ObjectImportCommand extends Command
if (!empty($item["purchase_cost"])) {
$consumable->purchase_cost = Helper::ParseFloat($item["purchase_cost"]);
}
if ($item["location"]) {
if (isset($item["location"])) {
$consumable->location_id = $item["location"]->id;
}
$consumable->user_id = $this->option('user_id');
if ($item["company"]) {
if (isset($item["company"])) {
$consumable->company_id = $item["company"]->id;
}
if (!empty($item["order_number"])) {
$consumable->order_number = $item["order_number"];
}
if ($item["category"]) {
if (isset($item["category"])) {
$consumable->category_id = $item["category"]->id;
}
// TODO:Implement
@@ -89,6 +89,7 @@ class ComponentsController extends Controller
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$component->order_number = e(Input::get('order_number'));
$component->min_amt = e(Input::get('min_amt'));
$component->serial_number = e(Input::get('serial_number'));
if (e(Input::get('purchase_date')) == '') {
$component->purchase_date = null;
@@ -174,6 +175,7 @@ class ComponentsController extends Controller
$component->company_id = Company::getIdForCurrentUser(Input::get('company_id'));
$component->order_number = e(Input::get('order_number'));
$component->min_amt = e(Input::get('min_amt'));
$component->serial_number = e(Input::get('serial_number'));
if (e(Input::get('purchase_date')) == '') {
$component->purchase_date = null;
@@ -422,7 +424,7 @@ class ComponentsController extends Controller
$limit = 50;
}
$allowed_columns = ['id','name','min_amt','order_number','purchase_date','purchase_cost','companyName','category','total_qty'];
$allowed_columns = ['id','name','min_amt','order_number','serial_number','purchase_date','purchase_cost','companyName','category','total_qty'];
$order = Input::get('order') === 'asc' ? 'asc' : 'desc';
$sort = in_array(Input::get('sort'), $allowed_columns) ? Input::get('sort') : 'created_at';
@@ -470,6 +472,7 @@ class ComponentsController extends Controller
'checkbox' =>'<div class="text-center"><input type="checkbox" name="component['.$component->id.']" class="one_required"></div>',
'id' => $component->id,
'name' => (string)link_to('admin/components/'.$component->id.'/view', e($component->name)),
'serial_number' => $component->serial_number,
'location' => ($component->location) ? e($component->location->name) : '',
'total_qty' => e($component->total_qty),
'min_amt' => e($component->min_amt),
+8 -3
View File
@@ -333,7 +333,6 @@ class ReportsController extends Controller
$activityCount = $activitylogs->count();
// dd("Offset:" . $offset . " Limit " . $limit);
$activitylogs = $activitylogs->offset($offset)->limit($limit)->get();
$rows = array();
@@ -356,7 +355,7 @@ class ReportsController extends Controller
if (($activity->item) && ($activity->itemType()=="asset")) {
$activity_item = '<a href="'.route('view/hardware', $activity->item_id).'">'.e($activity->item->asset_tag).' - '. e($activity->item->showAssetName()).'</a>';
$item_type = 'asset';
} elseif ($activity->item()) {
} elseif ($activity->item) {
$activity_item = '<a href="'.route('view/'. $activity->itemType(), $activity->item_id).'">'.e($activity->item->name).'</a>';
$item_type = $activity->itemType();
} else {
@@ -539,6 +538,9 @@ class ReportsController extends Controller
if (( e(Input::get('purchase_cost')) == '1' ) && ( e(Input::get('depreciation')) != '1' )) {
$header[] = 'Purchase Cost';
}
if (e(Input::get('eol')) == '1') {
$header[] = 'EOL';
}
if (e(Input::get('order')) == '1') {
$header[] = 'Order Number';
}
@@ -614,6 +616,9 @@ class ReportsController extends Controller
if (e(Input::get('purchase_cost')) == '1' && ( e(Input::get('depreciation')) != '1' )) {
$row[] = '"' . Helper::formatCurrencyOutput($asset->purchase_cost) . '"';
}
if (e(Input::get('eol')) == '1') {
$row[] = '"' .($asset->eol_date()) ? $asset->eol_date() : ''. '"';
}
if (e(Input::get('order')) == '1') {
if ($asset->order_number) {
$row[] = e($asset->order_number);
@@ -622,7 +627,7 @@ class ReportsController extends Controller
}
}
if (e(Input::get('supplier')) == '1') {
if ($asset->supplier_id) {
if ($asset->supplier) {
$row[] = '"' .e($asset->supplier->name) . '"';
} else {
$row[] = '';
+3 -2
View File
@@ -892,7 +892,7 @@ class UsersController extends Controller
$sort = e(Input::get('sort'));
}
$users = User::select(array('users.id','users.employee_num','users.email','users.username','users.location_id','users.manager_id','users.first_name','users.last_name','users.created_at','users.notes','users.company_id', 'users.deleted_at','users.activated'))
$users = User::select(array('users.id','users.employee_num','users.jobtitle','users.email','users.username','users.location_id','users.manager_id','users.first_name','users.last_name','users.created_at','users.notes','users.company_id', 'users.deleted_at','users.activated'))
->with('assets', 'accessories', 'consumables', 'licenses', 'manager', 'groups', 'userloc', 'company','throttle');
$users = Company::scopeCompanyables($users);
@@ -918,7 +918,7 @@ class UsersController extends Controller
default:
$allowed_columns =
[
'last_name','first_name','email','username','employee_num',
'last_name','first_name','email','jobtitle','username','employee_num',
'assets','accessories', 'consumables','licenses','groups','activated','created_at'
];
@@ -979,6 +979,7 @@ class UsersController extends Controller
'id' => $user->id,
'checkbox' => ($status!='deleted') ? '<div class="text-center hidden-xs hidden-sm"><input type="checkbox" name="edit_user['.e($user->id).']" class="one_required"></div>' : '',
'name' => '<a title="'.e($user->fullName()).'" href="../admin/users/'.e($user->id).'/view">'.e($user->fullName()).'</a>',
'jobtitle' => e($user->jobtitle),
'email' => ($user->email!='') ?
'<a href="mailto:'.e($user->email).'" class="hidden-md hidden-lg">'.e($user->email).'</a>'
.'<a href="mailto:'.e($user->email).'" class="hidden-xs hidden-sm"><i class="fa fa-envelope"></i></a>'
+1 -1
View File
@@ -23,7 +23,7 @@ class CheckForSetup
}
} else {
if (!$request->is('setup*')) {
if (!($request->is('setup*')) && !($request->is('.env'))) {
return redirect(config('app.url').'/setup')->with('Request', $request);
}
+1
View File
@@ -141,6 +141,7 @@ class Component extends Model
});
})->orWhere('components.name', 'LIKE', '%'.$search.'%')
->orWhere('components.order_number', 'LIKE', '%'.$search.'%')
->orWhere('components.serial_number', 'LIKE', '%'.$search.'%')
->orWhere('components.purchase_cost', 'LIKE', '%'.$search.'%')
->orWhere('components.purchase_date', 'LIKE', '%'.$search.'%');
}
+1
View File
@@ -382,6 +382,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
->orWhere('users.email', 'LIKE', "%$search%")
->orWhere('users.username', 'LIKE', "%$search%")
->orWhere('users.notes', 'LIKE', "%$search%")
->orWhere('users.jobtitle', 'LIKE', "%$search%")
->orWhere('users.employee_num', 'LIKE', "%$search%")
->orWhere(function ($query) use ($search) {
$query->whereHas('userloc', function ($query) use ($search) {
+2 -2
View File
@@ -1,5 +1,5 @@
<?php
return array (
'app_version' => 'v3.5.0',
'hash_version' => 'v3.5.0-30-gf9a06a1',
'app_version' => 'v3.5.2',
'hash_version' => 'v3.5.2-11-g5a835a5',
);
@@ -1,636 +0,0 @@
<?php
//
// NOTE Migration Created: 2016-02-13 07:10:06
// --------------------------------------------------
class CreateSnipeitLaravelDatabase {
//
// NOTE - Make changes to the database.
// --------------------------------------------------
public function up()
{
//
// NOTE -- accessories
// --------------------------------------------------
Schema::create('accessories', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255)->nullable();
$table->integer('category_id')->nullable();
$table->integer('user_id')->nullable();
$table->integer('qty');
$table->boolean('requestable');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->integer('location_id')->nullable();
$table->date('purchase_date')->nullable();
$table->decimal('purchase_cost', 13,4)->nullable();
$table->string('order_number', 255)->nullable();
$table->integer('company_id')->nullable()->unsigned();
});
//
// NOTE -- accessories_users
// --------------------------------------------------
Schema::create('accessories_users', function($table) {
$table->increments('id')->unsigned();
$table->integer('user_id')->nullable();
$table->integer('accessory_id')->nullable();
$table->integer('assigned_to')->nullable();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
});
//
// NOTE -- asset_logs
// --------------------------------------------------
Schema::create('asset_logs', function($table) {
$table->increments('id')->unsigned();
$table->integer('user_id');
$table->string('action_type', 255);
$table->integer('asset_id')->nullable();
$table->integer('checkedout_to')->nullable();
$table->integer('location_id')->nullable();
$table->dateTime('created_at')->default("0000-00-00 00:00:00");
$table->string('asset_type', 100)->nullable();
$table->text('note')->nullable();
$table->text('filename')->nullable();
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->dateTime('requested_at')->nullable();
$table->dateTime('accepted_at')->nullable();
$table->integer('accessory_id')->nullable();
$table->integer('accepted_id')->nullable();
$table->integer('consumable_id')->nullable();
$table->date('expected_checkin')->nullable();
$table->integer('thread_id')->nullable();
});
//
// NOTE -- asset_maintenances
// --------------------------------------------------
Schema::create('asset_maintenances', function($table) {
$table->increments('id')->unsigned();
$table->integer('asset_id')->unsigned();
$table->integer('supplier_id')->unsigned();
$table->enum('asset_maintenance_type', array('Maintenance','Repair','Upgrade'));
$table->string('title', 100);
$table->boolean('is_warranty');
$table->date('start_date');
$table->date('completion_date')->nullable();
$table->integer('asset_maintenance_time')->nullable();
$table->string('notes')->nullable();
$table->decimal('cost', 10,2)->nullable();
$table->dateTime('deleted_at')->nullable();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
});
//
// NOTE -- asset_uploads
// --------------------------------------------------
Schema::create('asset_uploads', function($table) {
$table->increments('id')->unsigned();
$table->integer('user_id');
$table->string('filename', 255);
$table->integer('asset_id');
$table->string('filenotes', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
});
//
// NOTE -- assets
// --------------------------------------------------
Schema::create('assets', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255)->nullable();
$table->string('asset_tag', 255);
$table->integer('model_id');
$table->string('serial', 255);
$table->date('purchase_date')->nullable();
$table->decimal('purchase_cost', 13,4)->default("0.0000");
$table->string('order_number', 255)->nullable();
$table->integer('assigned_to')->nullable();
$table->text('notes')->nullable();
$table->text('image')->nullable();
$table->integer('user_id');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->boolean('physical')->default("1");
$table->timestamp('deleted_at')->nullable();
$table->integer('status_id')->nullable();
$table->boolean('archived');
$table->integer('warranty_months')->nullable();
$table->boolean('depreciate');
$table->integer('supplier_id')->nullable();
$table->boolean('requestable');
$table->integer('rtd_location_id')->nullable();
$table->string('_snipeit_mac_address', 255)->nullable();
$table->enum('accepted', array('pending','accepted','rejected'))->nullable();
$table->dateTime('last_checkout')->nullable();
$table->date('expected_checkin')->nullable();
$table->integer('company_id')->nullable()->unsigned();
});
//
// NOTE -- categories
// --------------------------------------------------
Schema::create('categories', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
$table->timestamp('deleted_at')->nullable();
$table->text('eula_text')->nullable();
$table->boolean('use_default_eula');
$table->boolean('require_acceptance');
$table->string('category_type', 255)->nullable()->default("asset");
$table->boolean('checkin_email');
});
//
// NOTE -- companies
// --------------------------------------------------
Schema::create('companies', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255)->unique();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
});
//
// NOTE -- consumables
// --------------------------------------------------
Schema::create('consumables', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255)->nullable();
$table->integer('category_id')->nullable();
$table->integer('location_id')->nullable();
$table->integer('user_id')->nullable();
$table->integer('qty');
$table->boolean('requestable');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->date('purchase_date')->nullable();
$table->decimal('purchase_cost', 13,4)->nullable();
$table->string('order_number', 255)->nullable();
$table->integer('company_id')->nullable()->unsigned();
});
//
// NOTE -- consumables_users
// --------------------------------------------------
Schema::create('consumables_users', function($table) {
$table->increments('id')->unsigned();
$table->integer('user_id')->nullable();
$table->integer('consumable_id')->nullable();
$table->integer('assigned_to')->nullable();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
});
//
// NOTE -- custom_field_custom_fieldset
// --------------------------------------------------
Schema::create('custom_field_custom_fieldset', function($table) {
$table->integer('custom_field_id');
$table->integer('custom_fieldset_id');
$table->integer('order');
$table->boolean('required');
});
//
// NOTE -- custom_fields
// --------------------------------------------------
Schema::create('custom_fields', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->string('format', 255);
$table->string('element', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id')->nullable();
});
//
// NOTE -- custom_fieldsets
// --------------------------------------------------
Schema::create('custom_fieldsets', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id')->nullable();
});
//
// NOTE -- depreciations
// --------------------------------------------------
Schema::create('depreciations', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->integer('months');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
});
//
// NOTE -- groups
// --------------------------------------------------
Schema::create('groups', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255)->unique();
$table->text('permissions')->nullable();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
});
//
// NOTE -- history
// --------------------------------------------------
Schema::create('history', function($table) {
$table->increments('id')->unsigned();
$table->integer('checkedout_to');
$table->integer('location_id');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
});
//
// NOTE -- license_seats
// --------------------------------------------------
Schema::create('license_seats', function($table) {
$table->increments('id')->unsigned();
$table->integer('license_id');
$table->integer('assigned_to')->nullable();
$table->text('notes')->nullable();
$table->integer('user_id');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->integer('asset_id')->nullable();
});
//
// NOTE -- licenses
// --------------------------------------------------
Schema::create('licenses', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->text('serial')->nullable();
$table->date('purchase_date')->nullable();
$table->decimal('purchase_cost', 13,4)->nullable();
$table->string('order_number', 50)->nullable();
$table->integer('seats')->default("1");
$table->text('notes')->nullable();
$table->integer('user_id');
$table->boolean('depreciation_id');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->string('license_name', 100)->nullable();
$table->string('license_email', 120)->nullable();
$table->boolean('depreciate')->nullable();
$table->integer('supplier_id')->nullable();
$table->date('expiration_date')->nullable();
$table->string('purchase_order', 255)->nullable();
$table->date('termination_date')->nullable();
$table->boolean('maintained');
$table->boolean('reassignable')->default("1");
$table->integer('company_id')->nullable()->unsigned();
});
//
// NOTE -- locations
// --------------------------------------------------
Schema::create('locations', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->string('city', 255);
$table->string('state', 255);
$table->string('country', 2);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
$table->string('address', 80);
$table->string('address2', 255)->nullable();
$table->string('zip', 10)->nullable();
$table->timestamp('deleted_at')->nullable();
$table->integer('parent_id')->nullable();
$table->string('currency', 10)->nullable();
});
//
// NOTE -- manufacturers
// --------------------------------------------------
Schema::create('manufacturers', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
$table->timestamp('deleted_at')->nullable();
});
//
// NOTE -- models
// --------------------------------------------------
Schema::create('models', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->string('modelno', 255)->nullable();
$table->integer('manufacturer_id');
$table->integer('category_id');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('depreciation_id');
$table->integer('user_id');
$table->integer('eol')->nullable();
$table->string('image', 255)->nullable();
$table->boolean('deprecated_mac_address');
$table->timestamp('deleted_at')->nullable();
$table->integer('fieldset_id')->nullable();
$table->text('note')->nullable();
});
//
// NOTE -- requested_assets
// --------------------------------------------------
Schema::create('requested_assets', function($table) {
$table->increments('id')->unsigned();
$table->integer('asset_id');
$table->integer('user_id');
$table->dateTime('accepted_at')->nullable();
$table->dateTime('denied_at')->nullable();
$table->string('notes', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
});
//
// NOTE -- requests
// --------------------------------------------------
Schema::create('requests', function($table) {
$table->increments('id')->unsigned();
$table->integer('asset_id');
$table->integer('user_id');
$table->text('request_code');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
});
//
// NOTE -- settings
// --------------------------------------------------
Schema::create('settings', function($table) {
$table->increments('id')->unsigned();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
$table->integer('per_page')->default("20");
$table->string('site_name', 100)->default("Snipe IT Asset Management");
$table->integer('qr_code')->nullable();
$table->string('qr_text', 32)->nullable();
$table->integer('display_asset_name')->nullable();
$table->integer('display_checkout_date')->nullable();
$table->integer('display_eol')->nullable();
$table->integer('auto_increment_assets');
$table->string('auto_increment_prefix', 255);
$table->boolean('load_remote')->default("1");
$table->string('logo', 255)->nullable();
$table->string('header_color', 255)->nullable();
$table->string('alert_email', 255)->nullable();
$table->boolean('alerts_enabled')->default("1");
$table->text('default_eula_text')->nullable();
$table->string('barcode_type', 255)->nullable()->default("QRCODE");
$table->string('slack_endpoint', 255)->nullable();
$table->string('slack_channel', 255)->nullable();
$table->string('slack_botname', 255)->nullable();
$table->string('default_currency', 10)->nullable();
$table->text('custom_css')->nullable();
$table->boolean('brand')->default("1");
$table->string('ldap_enabled', 255)->nullable();
$table->string('ldap_server', 255)->nullable();
$table->string('ldap_uname', 255)->nullable();
$table->string('ldap_pword')->nullable();
$table->string('ldap_basedn', 255)->nullable();
$table->string('ldap_filter', 255)->nullable()->default("cn=*");
$table->string('ldap_username_field', 255)->nullable()->default("samaccountname");
$table->string('ldap_lname_field', 255)->nullable()->default("sn");
$table->string('ldap_fname_field', 255)->nullable()->default("givenname");
$table->string('ldap_auth_filter_query', 255)->nullable()->default("uid=samaccountname");
$table->integer('ldap_version')->nullable()->default("3");
$table->string('ldap_active_flag', 255)->nullable();
$table->string('ldap_emp_num', 255)->nullable();
$table->string('ldap_email', 255)->nullable();
$table->boolean('full_multiple_companies_support');
$table->boolean('ldap_server_cert_ignore');
});
//
// NOTE -- status_labels
// --------------------------------------------------
Schema::create('status_labels', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 100);
$table->integer('user_id');
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->boolean('deployable');
$table->boolean('pending');
$table->boolean('archived');
$table->text('notes')->nullable();
});
//
// NOTE -- suppliers
// --------------------------------------------------
Schema::create('suppliers', function($table) {
$table->increments('id')->unsigned();
$table->string('name', 255);
$table->string('address', 50)->nullable();
$table->string('address2', 50)->nullable();
$table->string('city', 255)->nullable();
$table->string('state', 32)->nullable();
$table->string('country', 2)->nullable();
$table->string('phone', 20)->nullable();
$table->string('fax', 20)->nullable();
$table->string('email', 150)->nullable();
$table->string('contact', 100)->nullable();
$table->string('notes', 255)->nullable();
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->integer('user_id');
$table->timestamp('deleted_at')->nullable();
$table->string('zip', 10)->nullable();
$table->string('url', 250)->nullable();
$table->string('image', 255)->nullable();
});
//
// NOTE -- throttle
// --------------------------------------------------
Schema::create('throttle', function($table) {
$table->increments('id')->unsigned();
$table->integer('user_id')->nullable()->unsigned();
$table->string('ip_address', 255)->nullable();
$table->integer('attempts');
$table->boolean('suspended');
$table->boolean('banned');
$table->timestamp('last_attempt_at')->nullable();
$table->timestamp('suspended_at')->nullable();
$table->timestamp('banned_at')->nullable();
});
//
// NOTE -- users
// --------------------------------------------------
Schema::create('users', function($table) {
$table->increments('id')->unsigned();
$table->string('email', 255)->nullable();
$table->string('password', 255);
$table->text('permissions')->nullable();
$table->boolean('activated');
$table->string('activation_code', 255)->nullable();
$table->timestamp('activated_at')->nullable();
$table->timestamp('last_login')->nullable();
$table->string('persist_code', 255)->nullable();
$table->string('reset_password_code', 255)->nullable();
$table->string('first_name', 255);
$table->string('last_name', 255);
$table->timestamp('created_at')->default("0000-00-00 00:00:00");
$table->timestamp('updated_at')->default("0000-00-00 00:00:00");
$table->timestamp('deleted_at')->nullable();
$table->string('website', 255)->nullable();
$table->string('country', 255)->nullable();
$table->string('gravatar', 255)->nullable();
$table->integer('location_id')->nullable();
$table->string('phone', 20)->nullable();
$table->string('jobtitle', 100)->nullable();
$table->integer('manager_id')->nullable();
$table->text('employee_num')->nullable();
$table->string('avatar', 255)->nullable();
$table->string('username', 255)->nullable();
$table->string('notes', 255)->nullable();
$table->integer('company_id')->nullable()->unsigned();
});
//
// NOTE -- users_groups
// --------------------------------------------------
Schema::create('users_groups', function($table) {
$table->increments('user_id')->unsigned();
$table->integer('group_id')->unsigned();
});
}
//
// NOTE - Revert the changes to the database.
// --------------------------------------------------
public function down()
{
Schema::drop('accessories');
Schema::drop('accessories_users');
Schema::drop('asset_logs');
Schema::drop('asset_maintenances');
Schema::drop('asset_uploads');
Schema::drop('assets');
Schema::drop('categories');
Schema::drop('companies');
Schema::drop('consumables');
Schema::drop('consumables_users');
Schema::drop('custom_field_custom_fieldset');
Schema::drop('custom_fields');
Schema::drop('custom_fieldsets');
Schema::drop('depreciations');
Schema::drop('groups');
Schema::drop('history');
Schema::drop('license_seats');
Schema::drop('licenses');
Schema::drop('locations');
Schema::drop('manufacturers');
Schema::drop('models');
Schema::drop('requested_assets');
Schema::drop('requests');
Schema::drop('settings');
Schema::drop('status_labels');
Schema::drop('suppliers');
Schema::drop('throttle');
Schema::drop('users');
Schema::drop('users_groups');
}
}
@@ -18,11 +18,11 @@ class UpdateAcceptedAtToAcceptanceId extends Migration {
$table->integer('accepted_id')->nullable()->default(NULL);
});
$results = DB::select('select invitation.id AS invitation_id, acceptance.id AS acceptance_id FROM asset_logs invitation INNER JOIN asset_logs acceptance ON (invitation.checkedout_to=acceptance.checkedout_to AND invitation.asset_id=acceptance.asset_id) WHERE invitation.action_type="checkout" AND acceptance.action_type="accepted"');
$results = DB::select('select invitation.id AS invitation_id, acceptance.id AS acceptance_id FROM '.DB::getTablePrefix().'asset_logs invitation INNER JOIN '.DB::getTablePrefix().'asset_logs acceptance ON (invitation.checkedout_to=acceptance.checkedout_to AND invitation.asset_id=acceptance.asset_id) WHERE invitation.action_type="checkout" AND acceptance.action_type="accepted"');
foreach ($results as $result) {
$update = DB::update('update asset_logs set accepted_id=? where id=?', [$result->acceptance_id, $result->invitation_id]);
$update = DB::update('update '.DB::getTablePrefix().'asset_logs set accepted_id=? where id=?', [$result->acceptance_id, $result->invitation_id]);
}
}
@@ -18,8 +18,8 @@ class AddCheckoutTimeAndExpectedCheckoutDateToAssets extends Migration {
$answer=$table->dateTime('last_checkout')->nullable();
$table->date('expected_checkin')->nullable();
});
DB::statement("UPDATE assets SET last_checkout=(SELECT MAX(created_at) FROM asset_logs WHERE asset_logs.id=assets.id AND action_type='checkout') WHERE assigned_to IS NOT NULL");
DB::statement("UPDATE assets SET expected_checkin=(SELECT expected_checkin FROM asset_logs WHERE asset_logs.id=assets.id AND action_type='checkout' ORDER BY id DESC limit 1) WHERE assigned_to IS NOT NULL");
DB::statement("UPDATE ".DB::getTablePrefix()."assets SET last_checkout=(SELECT MAX(created_at) FROM ".DB::getTablePrefix()."asset_logs WHERE ".DB::getTablePrefix()."asset_logs.id=".DB::getTablePrefix()."assets.id AND action_type='checkout') WHERE assigned_to IS NOT NULL");
DB::statement("UPDATE ".DB::getTablePrefix()."assets SET expected_checkin=(SELECT expected_checkin FROM ".DB::getTablePrefix()."asset_logs WHERE ".DB::getTablePrefix()."asset_logs.id=".DB::getTablePrefix()."assets.id AND action_type='checkout' ORDER BY id DESC limit 1) WHERE assigned_to IS NOT NULL");
}
/**
@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddSerialToComponents extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('components', function ($table) {
$table->string('serial_number')->nullable()->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('components', function ($table) {
$table->dropColumn('serial_number');
});
}
}
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class IncreaseSerialFieldCapacity extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('licenses', function ($table) {
$table->string('serial', 2048)->nullable()->default(null)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('licenses', function ($table) {
$table->string('serial', 255)->nullable()->default(null)->change();
});
}
}
@@ -35,6 +35,9 @@
<thead>
<tr role="row">
<th class="col-md-3" bSortable="true">{{ trans('admin/hardware/table.asset_model') }}</th>
@if (\App\Models\Setting::getSettings()->display_asset_name)
<th class="col-md-3" bSortable="true">{{ trans('admin/hardware/form.name') }}</th>
@endif
<th class="col-md-3" bSortable="true">{{ trans('admin/hardware/table.serial') }}</th>
<th class="col-md-2" bSortable="true">{{ trans('admin/hardware/table.location') }}</th>
<th class="col-md-2" bSortable="true">{{ trans('admin/hardware/table.status') }}</th>
+11
View File
@@ -96,6 +96,17 @@
</div>
<!-- Serial -->
<div class="form-group {{ $errors->has('serial_number') ? ' has-error' : '' }}">
{{ Form::label('name', trans('admin/hardware/form.serial'), array('class' => 'col-md-3 control-label')) }}
<div class="col-md-8">
<input class="form-control" type="text" name="serial_number" id="serial_number" value="{{ Input::old('serial_number', $component->serial_number) }}" />
{!! $errors->first('serial_number', '<span class="alert-msg"><i class="fa fa-times"></i> :message</span>') !!}
</div>
</div>
<!-- Company -->
@if (\App\Models\Company::isCurrentUserAuthorized())
@@ -52,6 +52,7 @@
<th data-sortable="true" data-field="id" data-visible="false">{{ trans('general.id') }}</th>
<th data-switchable="true" data-visible="false" data-searchable="true" data-sortable="true" data-field="companyName">{{ trans('admin/companies/table.title') }}</th>
<th data-sortable="true" data-searchable="true" data-field="name">{{ trans('admin/components/table.title') }}</th>
<th data-sortable="true" data-searchable="true" data-field="serial_number" data-visible="false">{{ trans('admin/hardware/form.serial') }}</th>
<th data-searchable="true" data-sortable="true" data-field="location">{{ trans('general.location') }}</th>
<th data-searchable="true" data-sortable="true" data-field="category">{{ trans('general.category') }}</th>
<th data-switchable="false" data-searchable="false" data-sortable="false" data-field="total_qty"> {{ trans('admin/components/general.total') }}</th>
@@ -74,6 +74,12 @@
<!-- side address column -->
<div class="col-md-3">
@if ($component->serial_number!='')
<div class="col-md-12" style="padding-bottom: 5px;"><strong>{{ trans('admin/hardware/form.serial') }}: </strong>
{{ $component->serial_number }} </div>
@endif
@if ($component->purchase_date)
<div class="col-md-12" style="padding-bottom: 5px;"><strong>{{ trans('admin/components/general.date') }}: </strong>
{{ $component->purchase_date }} </div>
+1 -1
View File
@@ -314,7 +314,7 @@
</tr>
@endif
@if ($asset->assetloc)
@if ($asset->defaultLoc)
<tr>
<td>{{ trans('admin/hardware/form.default_location') }}</td>
<td>
+6 -14
View File
@@ -41,20 +41,6 @@
</label>
</div>
<div class="checkbox col-md-12">
<label>
{{ Form::checkbox('company_name', '1') }}
{{ trans('general.company') }}
</label>
</div>
<div class="checkbox col-md-12">
<label>
{{ Form::checkbox('company_name', '1') }}
{{ trans('general.company') }}
</label>
</div>
<div class="checkbox col-md-12">
<label>
{{ Form::checkbox('asset_tag', '1') }}
@@ -104,6 +90,12 @@
{{ trans('admin/hardware/form.cost') }}
</label>
</div>
<div class="checkbox col-md-12">
<label>
{{ Form::checkbox('eol', '1') }}
{{ trans('admin/hardware/table.eol') }}
</label>
</div>
<div class="checkbox col-md-12">
<label>
{{ Form::checkbox('order', '1') }}
+1
View File
@@ -82,6 +82,7 @@
<th data-switchable="true" data-sortable="false" data-field="companyName" data-visible="false">{{ trans('admin/companies/table.title') }}</th>
<th data-switchable="true" data-sortable="true" data-field="employee_num" data-visible="false">{{ trans('admin/users/table.employee_num') }}</th>
<th data-sortable="true" data-field="name">{{ trans('admin/users/table.name') }}</th>
<th data-switchable="true" data-sortable="true" data-field="jobtitle" data-visible="false">{{ trans('admin/users/table.title') }}</th>
<th data-sortable="true" data-field="email">
<span class="hidden-md hidden-lg">{{ trans('admin/users/table.email') }}</span>
<span class="hidden-xs"><i class="fa fa-envelope fa-lg"></i></span>