Merge pull request #12846 from snipe/features/livewire_location_import

Location importer via Livewire Importer
This commit is contained in:
snipe 2023-05-03 11:02:06 -07:00 committed by GitHub
commit ea17fdeba5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 220 additions and 37 deletions

View file

@ -193,6 +193,9 @@ class ImportController extends Controller
case 'user':
$redirectTo = 'users.index';
break;
case 'location':
$redirectTo = 'locations.index';
break;
}
if ($errors) { //Failure

View file

@ -116,8 +116,8 @@ class Importer extends Component
static $users = [
'employee_num' => 'Employee Number',
'first_name' => 'First Name',
'jobtitle' => 'Job Title',
'last_name' => 'Last Name',
'jobtitle' => 'Job Title',
'phone_number' => 'Phone Number',
'manager_first_name' => 'Manager First Name',
'manager_last_name' => 'Manager Last Name',
@ -126,7 +126,24 @@ class Importer extends Component
'city' => 'City',
'state' => 'State',
'country' => 'Country',
'vip' => 'VIP'
'zip' => 'Zip',
'vip' => 'VIP',
'remote' => 'Remote',
];
static $locations = [
'name' => 'Name',
'address' => 'Address',
'address2' => 'Address 2',
'city' => 'City',
'state' => 'State',
'country' => 'Country',
'zip' => 'Zip',
'currency' => 'Currency',
'ldap_ou' => 'LDAP OU',
'manager_username' => 'Manager Username',
'manager' => 'Manager',
'parent_location' => 'Parent Location',
];
//array of "real fieldnames" to a list of aliases for that field
@ -150,6 +167,11 @@ class Importer extends Component
'QTY',
'Quantity'
],
'zip' =>
[
'Postal Code',
'Post Code'
],
'min_amt' =>
[
'Min Amount',
@ -159,6 +181,31 @@ class Importer extends Component
[
'Next Audit',
],
'address2' =>
[
'Address 2',
'Address2',
],
'ldap_ou' =>
[
'LDAP OU',
'OU',
],
'parent_location' =>
[
'Parent',
'Parent Location',
],
'manager' =>
[
'Managed By',
'Manager Name',
'Manager Full Name',
],
'manager_username' =>
[
'Manager Username',
],
];
@ -181,6 +228,9 @@ class Importer extends Component
case 'user':
$results = self::$general + self::$users;
break;
case 'location':
$results = self::$general + self::$locations;
break;
default:
$results = self::$general;
}
@ -252,7 +302,6 @@ class Importer extends Component
$this->authorize('import');
$this->progress = -1; // '-1' means 'don't show the progressbar'
$this->progress_bar_class = 'progress-bar-warning';
\Log::debug("Hey, we are calling MOUNT (in the importer-file) !!!!!!!!"); //fcuk
$this->importTypes = [
'asset' => trans('general.assets'),
'accessory' => trans('general.accessories'),
@ -260,6 +309,7 @@ class Importer extends Component
'component' => trans('general.components'),
'license' => trans('general.licenses'),
'user' => trans('general.users'),
'location' => trans('general.locations'),
];
$this->columnOptions[''] = $this->getColumns(''); //blank mode? I don't know what this is supposed to mean
@ -273,8 +323,7 @@ class Importer extends Component
public function selectFile($id)
{
\Log::debug("TOGGLE EVENT FIRED!");
\Log::debug("The ID we are trying to find is AS FOLLOWS: ".$id);
$this->activeFile = Import::find($id);
$this->field_map = null;
foreach($this->activeFile->header_row as $element) {
@ -284,11 +333,9 @@ class Importer extends Component
$this->field_map[] = null; // re-inject the 'nulls' if a file was imported with some 'Do Not Import' settings
}
}
//$this->field_map = $this->activeFile->field_map ? array_values($this->activeFile->field_map) : []; // this is wrong
$this->file_id = $id;
$this->import_errors = null;
$this->statusText = null;
\Log::debug("The import type we are about to try and load up is gonna be this: ".$this->activeFile->import_type);
}

View file

@ -65,19 +65,22 @@ abstract class Importer
'email' => 'email',
'username' => 'username',
'address' => 'address',
'address2' => 'address2',
'city' => 'city',
'state' => 'state',
'country' => 'country',
'zip' => 'zip',
'jobtitle' => 'job title',
'employee_num' => 'employee number',
'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',
'manager_name' => 'manager full name',
'manager_username' => 'manager username',
'min_amt' => 'minimum quantity',
'remote' => 'remote',
'vip' => 'vip',
];
/**
* Map of item fields->csv names
@ -119,7 +122,7 @@ abstract class Importer
} else {
$this->csv = Reader::createFromString($file);
}
$this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 20);
$this->tempPassword = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 40);
}
// Cached Values for import lookups
@ -198,11 +201,11 @@ abstract class Importer
$val = $default;
$key = $this->lookupCustomKey($key);
$this->log("Custom Key: ${key}");
// $this->log("Custom Key: ${key}");
if (array_key_exists($key, $array)) {
$val = Encoding::toUTF8(trim($array[$key]));
}
$this->log("${key}: ${val}");
//$this->log("${key}: ${val}");
return $val;
}
@ -280,8 +283,9 @@ abstract class Importer
* @return User Model w/ matching name
* @internal param array $user_array User details parsed from csv
*/
protected function createOrFetchUser($row)
protected function createOrFetchUser($row, $type = 'user')
{
$user_array = [
'full_name' => $this->findCsvMatch($row, 'full_name'),
'email' => $this->findCsvMatch($row, 'email'),
@ -292,31 +296,36 @@ abstract class Importer
'remote' => $this->fetchHumanBoolean(($this->findCsvMatch($row, 'remote'))),
];
// Maybe we're lucky and the user already exists.
if ($user = User::where('username', $user_array['username'])->first()) {
$this->log('User '.$user_array['username'].' already exists');
return $user;
if ($type == 'manager') {
$user_array['full_name'] = $this->findCsvMatch($row, 'manager');
$user_array['username'] = $this->findCsvMatch($row, 'manager_username');
}
// If the full name is empty, bail out--we need this to extract first name (at the very least)
if (empty($user_array['full_name'])) {
$this->log('Insufficient user data provided (Full name is required)- skipping user creation, just adding asset');
// Maybe we're lucky and the username was passed and it already exists.
if (!empty($user_array['username'])) {
if ($user = User::where('username', $user_array['username'])->first()) {
$this->log('User '.$user_array['username'].' already exists');
return $user;
}
}
// If the full name and username is empty, bail out--we need this to extract first name (at the very least)
if ((empty($user_array['username'])) && (empty($user_array['full_name']))) {
$this->log('Insufficient user data provided (Full name or username is required) - skipping user creation.');
\Log::debug(print_r($user_array, true));
\Log::debug(print_r($row, true));
return false;
}
// Is the user actually an ID?
if ($user = $this->findUserByNumber($user_array['full_name'])) {
return $user;
}
$this->log('User does not appear to be an id with number: '.$user_array['full_name'].'. Continuing through our processes');
// Populate email if it does not exist.
if (empty($user_array['email'])) {
$user_array['email'] = User::generateEmailFromFullName($user_array['full_name']);
}
// Get some fields for first name and last name based off of full name
$user_formatted_array = User::generateFormattedNameFromFullName($user_array['full_name'], Setting::getSettings()->username_format);
$user_array['first_name'] = $user_formatted_array['first_name'];
$user_array['last_name'] = $user_formatted_array['last_name'];
@ -326,14 +335,12 @@ abstract class Importer
if ($this->usernameFormat == 'email') {
$user_array['username'] = $user_array['email'];
}
}
// Does this ever actually fire??
// Check for a matching user after trying to guess username.
if ($user = User::where('username', $user_array['username'])->first()) {
$this->log('User '.$user_array['username'].' already exists');
return $user;
// Check for a matching username one more time after trying to guess username.
if ($user = User::where('username', $user_array['username'])->first()) {
$this->log('User '.$user_array['username'].' already exists');
return $user;
}
}
// If at this point we have not found a username or first name, bail out in shame.
@ -341,7 +348,7 @@ abstract class Importer
return false;
}
// No Luck, let's create one.
// No luck finding a user on username or first name, let's create one.
$user = new User;
$user->first_name = $user_array['first_name'];
$user->last_name = $user_array['last_name'];
@ -356,9 +363,9 @@ abstract class Importer
if ($user->save()) {
$this->log('User '.$user_array['username'].' created');
return $user;
}
$this->logError($user, 'User "'.$user_array['username'].'" was not able to be created.');
return false;

View file

@ -60,8 +60,8 @@ class ItemImporter extends Importer
$this->item['department_id'] = $this->createOrFetchDepartment($item_department);
}
$item_manager_first_name = $this->findCsvMatch($row, 'manage_first_name');
$item_manager_last_name = $this->findCsvMatch($row, 'manage_last_name');
$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);
@ -112,6 +112,10 @@ class ItemImporter extends Importer
return $this->createOrFetchUser($row);
}
if (get_class($this) != LocationImporter::class) {
return;
}
if (strtolower($this->item['checkout_class']) === 'location' && $this->findCsvMatch($row, 'checkout_location') != null ) {
return Location::findOrFail($this->createOrFetchLocation($this->findCsvMatch($row, 'checkout_location')));
}

View file

@ -0,0 +1,102 @@
<?php
namespace App\Importer;
use App\Models\Location;
/**
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
* Importer\Importer.php. [ALG]
*
* Class LocationImporter
*/
class LocationImporter extends ItemImporter
{
protected $locations;
public function __construct($filename)
{
parent::__construct($filename);
}
protected function handle($row)
{
parent::handle($row);
$this->createLocationIfNotExists($row);
}
/**
* Create a location if a duplicate does not exist.
* @todo Investigate how this should interact with Importer::createLocationIfNotExists
*
* @author A. Gianotto
* @since 6.1.0
* @param array $row
*/
public function createLocationIfNotExists(array $row)
{
$editingLocation = false;
$location = Location::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
if ($location) {
if (! $this->updating) {
$this->log('A matching Location '.$this->item['name'].' already exists');
return;
}
$this->log('Updating Location');
$editingLocation = true;
} else {
$this->log('No Matching Location, Create a new one');
$location = new Location;
}
// Pull the records from the CSV to determine their values
$this->item['name'] = $this->findCsvMatch($row, 'name');
$this->item['address'] = $this->findCsvMatch($row, 'address');
$this->item['address2'] = $this->findCsvMatch($row, 'address2');
$this->item['city'] = $this->findCsvMatch($row, 'city');
$this->item['state'] = $this->findCsvMatch($row, 'state');
$this->item['country'] = $this->findCsvMatch($row, 'country');
$this->item['zip'] = $this->findCsvMatch($row, 'zip');
$this->item['currency'] = $this->findCsvMatch($row, 'currency');
$this->item['ldap_ou'] = $this->findCsvMatch($row, 'ldap_ou');
$this->item['manager'] = $this->findCsvMatch($row, 'manager');
$this->item['manager_username'] = $this->findCsvMatch($row, 'manager_username');
$this->item['user_id'] = \Auth::user()->id;
if ($this->findCsvMatch($row, 'parent_location')) {
$this->item['parent_id'] = $this->createOrFetchLocation($this->findCsvMatch($row, 'parent_location'));
}
if (!empty($this->item['manager'])) {
if ($manager = $this->createOrFetchUser($row, 'manager')) {
$this->item['manager_id'] = $manager->id;
}
}
\Log::debug('Item array is: ');
\Log::debug(print_r($this->item, true));
if ($editingLocation) {
\Log::debug('Updating existing location');
$location->update($this->sanitizeItemForUpdating($location));
} else {
\Log::debug('Creating location');
$location->fill($this->sanitizeItemForStoring($location));
}
if ($location->save()) {
$this->log('Location '.$location->name.' created or updated from CSV import');
return $location;
} else {
\Log::debug($location->getErrors());
return $location->errors;
}
}
}

View file

@ -30,7 +30,7 @@ class Location extends SnipeModel
'country' => 'min:2|max:255|nullable',
'address' => 'max:80|nullable',
'address2' => 'max:80|nullable',
'zip' => 'min:3|max:10|nullable',
'zip' => 'min:3|max:12|nullable',
'manager_id' => 'exists:users,id|nullable',
'parent_id' => 'non_circular:locations,id',
];

View file

@ -0,0 +1,20 @@
name,address,address2,city,state,country,zip,manager,manager username,currency
Peace River,8 Brentwood Court,,Birendranagar,AB,CA,T8S,Danika Mostyn,dmostyn0,CAD
Airdrie,14 Summer Ridge Court,306 Buhler Parkway,Poniatowa,AB,CA,T4B,Clementina Van Halen,cvan1,CAD
Calgary,3 Fieldstone Drive,,Iwanai,AB,CA,,Harwilll Heffernan,hheffernan2,CAD
High Prairie,1906 Weeping Birch Park,,Lopar,AB,CA,,Christian Pache,cpache3,CAD
Sundre,20 Summer Ridge Court,,Burujul,AB,CA,,,,
Athabasca,22 Browning Drive,424 Rieder Court,Itambacuri,AB,CA,,Alphonso Ashbridge,aashbridge5,CAD
Drayton Valley,56064 Onsgard Center,,Bahía Honda,AB,CA,,,,
Crossfield,0 Lighthouse Bay Place,,Bengras,AB,CA,,Vania Dufton,vdufton7,CAD
Beaverlodge,6 Katie Terrace,,Zhajin,AB,CA,,Papageno Baldi,pbaldi8,CAD
Grande Prairie,0 Ridgeview Parkway,,Yunxi,AB,CA,R3J,Selia Biggadike,sbiggadike9,CAD
Sherwood Park,263 Aberg Alley,,El Paso,AB,CA,,,,
Vegreville,9039 Shoshone Parkway,,Huazhou,AB,CA,,Georgy Eversfield,geversfieldb,CAD
Rocky Mountain House,8617 Arapahoe Parkway,,Paraipaba,AB,CA,,Mara Gilfoyle,mgilfoylec,CAD
Calmar,14 Green Ridge Circle,,Medveditskiy,AB,CA,S0G,Paulette Rylatt,prylattd,CAD
Rocky Mountain House,517 Bowman Terrace,,Viana,AB,CA,,Neal Gabitis,ngabitise,CAD
Pincher Creek,6054 Anzinger Hill,,Chlumec,AB,CA,,Bonnee Fowle,bfowlef,CAD
Airdrie,8 Lien Drive,,Reims,AB,CA,,Kerry Aherne,kaherneg,CAD
Camrose,4 Summit Parkway,,Xinqiao,AB,CA,T4V,Sherlock Stobbart,sstobbarth,CAD
Lamont,12 Ilene Park,,Huangtang,AB,CA,N2E,Karlotta Pinckstone,kpinckstonei,CAD
1 name address address2 city state country zip manager manager username currency
2 Peace River 8 Brentwood Court Birendranagar AB CA T8S Danika Mostyn dmostyn0 CAD
3 Airdrie 14 Summer Ridge Court 306 Buhler Parkway Poniatowa AB CA T4B Clementina Van Halen cvan1 CAD
4 Calgary 3 Fieldstone Drive Iwanai AB CA Harwilll Heffernan hheffernan2 CAD
5 High Prairie 1906 Weeping Birch Park Lopar AB CA Christian Pache cpache3 CAD
6 Sundre 20 Summer Ridge Court Burujul AB CA
7 Athabasca 22 Browning Drive 424 Rieder Court Itambacuri AB CA Alphonso Ashbridge aashbridge5 CAD
8 Drayton Valley 56064 Onsgard Center Bahía Honda AB CA
9 Crossfield 0 Lighthouse Bay Place Bengras AB CA Vania Dufton vdufton7 CAD
10 Beaverlodge 6 Katie Terrace Zhajin AB CA Papageno Baldi pbaldi8 CAD
11 Grande Prairie 0 Ridgeview Parkway Yunxi AB CA R3J Selia Biggadike sbiggadike9 CAD
12 Sherwood Park 263 Aberg Alley El Paso AB CA
13 Vegreville 9039 Shoshone Parkway Huazhou AB CA Georgy Eversfield geversfieldb CAD
14 Rocky Mountain House 8617 Arapahoe Parkway Paraipaba AB CA Mara Gilfoyle mgilfoylec CAD
15 Calmar 14 Green Ridge Circle Medveditskiy AB CA S0G Paulette Rylatt prylattd CAD
16 Rocky Mountain House 517 Bowman Terrace Viana AB CA Neal Gabitis ngabitise CAD
17 Pincher Creek 6054 Anzinger Hill Chlumec AB CA Bonnee Fowle bfowlef CAD
18 Airdrie 8 Lien Drive Reims AB CA Kerry Aherne kaherneg CAD
19 Camrose 4 Summit Parkway Xinqiao AB CA T4V Sherlock Stobbart sstobbarth CAD
20 Lamont 12 Ilene Park Huangtang AB CA N2E Karlotta Pinckstone kpinckstonei CAD