Features/add manager and dept to importer (#6277)
* Ignore the simlink for public storage * Added manager and department to user import * More UI importer tweaks * Fisxed typos
This commit is contained in:
parent
10bc35d604
commit
c8bff3ef38
17 changed files with 8803 additions and 99 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -50,3 +50,4 @@ tests/_support/_generated/*
|
|||
/storage/oauth-public.key
|
||||
|
||||
*.cache
|
||||
/public/storage
|
||||
|
|
|
@ -74,6 +74,7 @@ class ObjectImportCommand extends Command
|
|||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId($this->option('user_id'))
|
||||
->setUpdating($this->option('update'))
|
||||
->setShouldNotify($this->option('send-welcome'))
|
||||
->setUsernameFormat($this->option('username_format'));
|
||||
|
||||
$logFile = $this->option('logfile');
|
||||
|
|
|
@ -50,6 +50,7 @@ class ItemImportRequest extends FormRequest
|
|||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId(Auth::id())
|
||||
->setUpdating($this->has('import-update'))
|
||||
->setShouldNotify($this->has('send-welcome'))
|
||||
->setUsernameFormat('firstname.lastname')
|
||||
->setFieldMappings($fieldMappings);
|
||||
// $logFile = storage_path('logs/importer.log');
|
||||
|
@ -60,7 +61,7 @@ class ItemImportRequest extends FormRequest
|
|||
|
||||
public function log($string)
|
||||
{
|
||||
// \Log::Info($string);
|
||||
\Log::Info($string);
|
||||
}
|
||||
|
||||
public function progress($count)
|
||||
|
|
|
@ -66,6 +66,9 @@ abstract class Importer
|
|||
'phone_number' => 'phone number',
|
||||
'first_name' => 'first name',
|
||||
'last_name' => 'last name',
|
||||
'department' => 'department',
|
||||
'manager_first_name' => 'manager first name',
|
||||
'manager_last_name' => 'manager last name',
|
||||
];
|
||||
/**
|
||||
* Map of item fields->csv names
|
||||
|
@ -176,7 +179,6 @@ abstract class Importer
|
|||
{
|
||||
|
||||
$val = $default;
|
||||
|
||||
$key = $this->lookupCustomKey($key);
|
||||
|
||||
$this->log("Custom Key: ${key}");
|
||||
|
@ -198,7 +200,6 @@ abstract class Importer
|
|||
public function lookupCustomKey($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->fieldMap)) {
|
||||
// $this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]);
|
||||
return $this->fieldMap[$key];
|
||||
}
|
||||
// Otherwise no custom key, return original.
|
||||
|
@ -307,6 +308,8 @@ abstract class Importer
|
|||
$user->last_name = $user_array['last_name'];
|
||||
$user->username = $user_array['username'];
|
||||
$user->email = $user_array['email'];
|
||||
$user->manager_id = $user_array['manager_id'];
|
||||
$user->department_id = $user_array['department_id'];
|
||||
$user->activated = 1;
|
||||
$user->password = $this->tempPassword;
|
||||
|
||||
|
@ -360,6 +363,20 @@ abstract class Importer
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Are we updating items in the import.
|
||||
*
|
||||
* @param bool $updating the updating
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setShouldNotify($send_welcome)
|
||||
{
|
||||
$this->send_welcome = $send_welcome;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines mappings of csv fields
|
||||
*
|
||||
|
|
|
@ -10,6 +10,7 @@ use App\Models\Location;
|
|||
use App\Models\Manufacturer;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\Department;
|
||||
use App\Models\User;
|
||||
|
||||
class ItemImporter extends Importer
|
||||
|
@ -54,6 +55,18 @@ class ItemImporter extends Importer
|
|||
if ($this->shouldUpdateField($item_supplier)) {
|
||||
$this->item['supplier_id'] = $this->createOrFetchSupplier($item_supplier);
|
||||
}
|
||||
|
||||
$item_department = $this->findCsvMatch($row, "department");
|
||||
if ($this->shouldUpdateField($item_department)) {
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($item_department);
|
||||
}
|
||||
|
||||
$item_manager_first_name = $this->findCsvMatch($row, "manager_first_name");
|
||||
$item_manager_last_name = $this->findCsvMatch($row, "manager_last_name");
|
||||
if ($this->shouldUpdateField($item_manager_first_name)) {
|
||||
$this->item['manager_id'] = $this->fetchManager($item_manager_first_name, $item_manager_last_name);
|
||||
}
|
||||
|
||||
$this->item["name"] = $this->findCsvMatch($row, "item_name");
|
||||
$this->item["notes"] = $this->findCsvMatch($row, "notes");
|
||||
$this->item["order_number"] = $this->findCsvMatch($row, "order_number");
|
||||
|
@ -84,7 +97,7 @@ class ItemImporter extends Importer
|
|||
*/
|
||||
protected function determineCheckout($row)
|
||||
{
|
||||
// We only support checkout-to-location for asset, so short circuit otherw.
|
||||
// We only support checkout-to-location for asset, so short circuit otherwise.
|
||||
if(get_class($this) != AssetImporter::class) {
|
||||
return $this->createOrFetchUser($row);
|
||||
}
|
||||
|
@ -161,7 +174,7 @@ class ItemImporter extends Importer
|
|||
* @since 3.0
|
||||
* @param array
|
||||
* @param $category Category
|
||||
* @param $manufacturer Manufacturer
|
||||
* @param $row Manufacturer
|
||||
* @return int Id of asset model created/found
|
||||
* @internal param $asset_modelno string
|
||||
*/
|
||||
|
@ -279,6 +292,54 @@ class ItemImporter extends Importer
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an existing department, or create new if it doesn't exist
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 4.6.5
|
||||
* @param $user_department string
|
||||
* @return int id of company created/found
|
||||
*/
|
||||
public function createOrFetchDepartment($user_department_name)
|
||||
{
|
||||
$department = Department::where('name', '=', $user_department_name)->first();
|
||||
|
||||
if ($department) {
|
||||
$this->log('A matching Department ' . $user_department_name . ' already exists');
|
||||
return $department->id;
|
||||
}
|
||||
|
||||
$department = new Department();
|
||||
$department->name = $user_department_name;
|
||||
|
||||
if ($department->save()) {
|
||||
$this->log('Department ' . $user_department_name . ' was created');
|
||||
return $department->id;
|
||||
}
|
||||
$this->logError($department, 'Department');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an existing manager
|
||||
*
|
||||
* @author A. Gianotto
|
||||
* @since 4.6.5
|
||||
* @param $user_manager string
|
||||
* @return int id of company created/found
|
||||
*/
|
||||
public function fetchManager($user_manager_first_name, $user_manager_last_name)
|
||||
{
|
||||
$manager = User::where('first_name', '=', $user_manager_first_name)
|
||||
->where('last_name', '=', $user_manager_last_name)->first();
|
||||
if ($manager) {
|
||||
$this->log('A matching Manager ' . $user_manager_first_name . ' '. $user_manager_last_name . ' already exists');
|
||||
return $manager->id;
|
||||
}
|
||||
$this->log('No matching Manager ' . $user_manager_first_name . ' '. $user_manager_last_name . ' found. If their user account is being created through this import, you should re-process this file again. ');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the existing status label or create new if it doesn't exist.
|
||||
*
|
||||
|
|
|
@ -12,7 +12,6 @@ class UserImporter extends ItemImporter
|
|||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
// $this->users = User::all();
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
|
@ -31,14 +30,18 @@ class UserImporter extends ItemImporter
|
|||
*/
|
||||
public function createUserIfNotExists(array $row)
|
||||
{
|
||||
// User Specific Bits
|
||||
$this->item['username'] = $this->findCsvMatch($row, 'username');
|
||||
$this->item['first_name'] = $this->findCsvMatch($row, 'first_name');
|
||||
$this->item['last_name'] = $this->findCsvMatch($row, 'last_name');
|
||||
$this->item['email'] = $this->findCsvMatch($row, 'email');
|
||||
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
|
||||
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
|
||||
$this->item['activated'] = $this->findCsvMatch($row, 'activated');
|
||||
$this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num');
|
||||
$this->item['department_id'] = $this->createOrFetchDepartment($this->findCsvMatch($row, 'department'));
|
||||
$this->item['manager_id'] = $this->fetchManager($this->findCsvMatch($row, 'manager_first_name'), $this->findCsvMatch($row, 'manager_last_name'));
|
||||
|
||||
|
||||
$user = User::where('username', $this->item['username'])->first();
|
||||
if ($user) {
|
||||
if (!$this->updating) {
|
||||
|
@ -50,6 +53,7 @@ class UserImporter extends ItemImporter
|
|||
$user->save();
|
||||
return;
|
||||
}
|
||||
|
||||
// This needs to be applied after the update logic, otherwise we'll overwrite user passwords
|
||||
// Issue #5408
|
||||
$this->item['password'] = $this->tempPassword;
|
||||
|
@ -58,7 +62,6 @@ class UserImporter extends ItemImporter
|
|||
$user = new User();
|
||||
$user->fill($this->sanitizeItemForStoring($user));
|
||||
if ($user->save()) {
|
||||
// $user->logCreate('Imported using CSV Importer');
|
||||
$this->log("User " . $this->item["name"] . ' was created');
|
||||
if($user->email) {
|
||||
$data = [
|
||||
|
@ -70,7 +73,10 @@ class UserImporter extends ItemImporter
|
|||
];
|
||||
|
||||
// UNCOMMENT this to re-enable sending email notifications on user import
|
||||
// $user->notify(new WelcomeNotification($data));
|
||||
if ($this->send_welcome) {
|
||||
$user->notify(new WelcomeNotification($data));
|
||||
}
|
||||
|
||||
}
|
||||
$user = null;
|
||||
$this->item = null;
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
| CSV | Item | Applicable Types |
|
||||
|---------------------|------------------|-------------------------------------------|
|
||||
| activated | | User |
|
||||
| asset tag | asset_tag | Asset |
|
||||
| category | category | All |
|
||||
| company | company | All |
|
||||
| department_id | | User ? All |
|
||||
| item name | item_name | All |
|
||||
| image | image | asset |
|
||||
| image | image | Asset |
|
||||
| email | | |
|
||||
| expiration date | expiration_date | License |
|
||||
| location | location | All |
|
||||
| notes | notes | All |
|
||||
| licensed to email | license_email | License |
|
||||
| licensed to name | license_name | License |
|
||||
| maintained | maintained | License |
|
||||
| manager_id | | User |
|
||||
| manufacturer | manufacturer | All |
|
||||
| model name | asset_model | Asset |
|
||||
| model number | model_number | Asset |
|
||||
|
@ -22,12 +26,12 @@
|
|||
| reassignable | reassignable | License |
|
||||
| requestable | requestable | Asset, Accessory? |
|
||||
| seats | seats | License |
|
||||
| serial number | serial | asset, license |
|
||||
| status | status | asset ? All |
|
||||
| serial number | serial | Asset, license |
|
||||
| status | status | Asset ? All |
|
||||
| supplier | supplier | Asset ? All |
|
||||
| termination date | termination_date | License |
|
||||
| warranty months | warranty_months | asset |
|
||||
| warranty months | warranty_months | Asset |
|
||||
| User Related Fields | assigned_to | Asset |
|
||||
| name | | |
|
||||
| email | | |
|
||||
| username | | |
|
||||
| username | | |
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@ class Department extends SnipeModel
|
|||
use ValidatingTrait, UniqueUndeletedTrait;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|max:255',
|
||||
'user_id' => 'required',
|
||||
'location_id' => 'numeric|nullable',
|
||||
'company_id' => 'numeric|nullable',
|
||||
'manager_id' => 'numeric|nullable',
|
||||
'name' => 'required|max:255',
|
||||
'user_id' => 'nullable|exists:users,id',
|
||||
'location_id' => 'numeric|nullable',
|
||||
'company_id' => 'numeric|nullable',
|
||||
'manager_id' => 'numeric|nullable',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because one or more lines are too long
2
public/css/dist/all.css
vendored
2
public/css/dist/all.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
40
public/js/dist/all.js
vendored
40
public/js/dist/all.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"/js/build/vue.js": "/js/build/vue.js?id=832c22cb5b66ac81ed06",
|
||||
"/js/build/vue.js": "/js/build/vue.js?id=94cc644b10b5436da8ef",
|
||||
"/css/AdminLTE.css": "/css/AdminLTE.css?id=5e72463a66acbcc740d5",
|
||||
"/css/app.css": "/css/app.css?id=407edb63cc6b6dc62405",
|
||||
"/css/overrides.css": "/css/overrides.css?id=2d81c3704393bac77011",
|
||||
"/js/build/vue.js.map": "/js/build/vue.js.map?id=0deaf852882fe2d65263",
|
||||
"/js/build/vue.js.map": "/js/build/vue.js.map?id=d2f525f3411031bec8a0",
|
||||
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map?id=0be7790b84909dca6a0a",
|
||||
"/css/app.css.map": "/css/app.css.map?id=96b5c985e860716e6a16",
|
||||
"/css/overrides.css.map": "/css/overrides.css.map?id=f7ce9ca49027594ac402",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=98db4e9b7650453c8b00",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=9d02373ef452329336d3",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=15c1c83483171165eb54",
|
||||
"/css/build/all.css": "/css/build/all.css?id=98db4e9b7650453c8b00",
|
||||
"/js/build/all.js": "/js/build/all.js?id=9d02373ef452329336d3"
|
||||
}
|
||||
"/js/build/all.js": "/js/build/all.js?id=15c1c83483171165eb54"
|
||||
}
|
||||
|
|
|
@ -6,29 +6,50 @@ tr {
|
|||
|
||||
<template>
|
||||
<tr v-show="processDetail">
|
||||
<td colspan="3">
|
||||
<td colspan="5">
|
||||
<div class="col-md-2 text-left">
|
||||
</div>
|
||||
<div class="col-md-8 col-md-offset-2 text-center" style="padding-top: 30px; margin: 0 auto;">
|
||||
<div class="col-md-12 text-left">
|
||||
|
||||
<h4 class="modal-title">Import File:</h4>
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<div class="col-md-5 col-xs-12">
|
||||
<label for="import-type">Import Type:</label>
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<div class="col-md-7 col-xs-12">
|
||||
<select2 :options="options.importTypes" v-model="options.importType" required>
|
||||
<option disabled value="0"></option>
|
||||
</select2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<div class="col-md-5 col-xs-12">
|
||||
<label for="import-update">Update Existing Values?:</label>
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<div class="col-md-7 col-xs-12">
|
||||
<input type="checkbox" name="import-update" v-model="options.update">
|
||||
</div>
|
||||
</div>
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-5 col-xs-12">
|
||||
<label for="send-welcome">Send Welcome Email for new Users?</label>
|
||||
</div>
|
||||
<div class="col-md-7 col-xs-12">
|
||||
<input type="checkbox" name="send-welcome" v-model="options.send_welcome">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert col-md-12"
|
||||
:class="alertClass"
|
||||
style="text-align:left"
|
||||
v-if="statusText">
|
||||
{{ this.statusText }}
|
||||
</div>
|
||||
|
||||
<div class="col-md-12" style="padding-top: 30px;">
|
||||
<table class="table">
|
||||
|
||||
<div class="text-left" style="padding-top: 30px;">
|
||||
<table class="table table-striped snipe-table">
|
||||
<thead>
|
||||
<th>Header Field</th>
|
||||
<th>Import Field</th>
|
||||
|
@ -54,21 +75,25 @@ tr {
|
|||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
<div class="col-md-8 col-md-offset-2 text-right">
|
||||
<button type="button" class="btn btn-sm btn-default" @click="processDetail = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-sm btn-primary" @click="postSave">Import</button>
|
||||
<br><br>
|
||||
</div>
|
||||
|
||||
<div class="alert col-md-12" style="padding-top: 20px;"
|
||||
:class="alertClass"
|
||||
style="text-align:left"
|
||||
v-if="statusText">
|
||||
{{ this.statusText }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-default" @click="processDetail = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-sm btn-primary" @click="postSave">Import</button>
|
||||
<div
|
||||
class="alert col-md-5 col-md-offset-1"
|
||||
:class="alertClass"
|
||||
style="text-align:left"
|
||||
v-if="statusText"
|
||||
>
|
||||
{{ this.statusText }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
|
@ -143,6 +168,10 @@ tr {
|
|||
{id: 'jobtitle', text: 'Job Title' },
|
||||
{id: 'last_name', text: 'Last Name' },
|
||||
{id: 'phone_number', text: 'Phone Number' },
|
||||
{id: 'manager_first_name', text: 'Manager First Name' },
|
||||
{id: 'manager_last_name', text: 'Manager Last Name' },
|
||||
{id: 'department', text: 'Department' },
|
||||
{id: 'activated', text: 'Activated' },
|
||||
|
||||
],
|
||||
customFields: this.customFields,
|
||||
|
@ -211,6 +240,7 @@ tr {
|
|||
this.statusText = "Processing...";
|
||||
this.$http.post(route('api.imports.importFile', this.file.id), {
|
||||
'import-update': this.options.update,
|
||||
'send-welcome': this.options.send_welcome,
|
||||
'import-type': this.options.importType,
|
||||
'column-mappings': this.columnMappings
|
||||
}).then( ({body}) => {
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<td>@{{ currentFile.filesize }}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-info" @click="toggleEvent(currentFile.id)">Process</button>
|
||||
<button class="btn btn-sm btn-danger" @click="deleteFile(currentFile)"><i class="fa fa-trash icon-white"></i></button>
|
||||
<button class="btn btn-sm btn-danger" @click="deleteFile(currentFile)"><i class="fa fa-trash icon-white"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<import-file
|
||||
|
|
Loading…
Add table
Reference in a new issue