mirror of
https://github.com/snipe/snipe-it.git
synced 2024-09-19 23:37:38 -07:00
Merge branch 'develop' of github.com:snipe/snipe-it into develop
# Conflicts: # composer.lock
This commit is contained in:
commit
9e0c5e50b6
|
@ -22,9 +22,9 @@ before_script:
|
|||
- php artisan migrate --env=testing-ci --database=mysql --force
|
||||
- ./vendor/bin/codecept build
|
||||
- php artisan --env=testing-ci key:generate
|
||||
- php artisan --env=testing-ci snipeit:travisci-install
|
||||
- php artisan --env=testing-ci db:seed --database=mysql --force
|
||||
- php artisan --env=testing-ci snipeit:create-admin --first_name=Alison --last_name=Foobar --email=me@example.com --username=snipe --password=password
|
||||
- php artisan --env=testing-ci snipeit:travisci-install
|
||||
- php artisan --env=testing-ci passport:install
|
||||
- php artisan serve --env=testing-ci --port=8000 --host=localhost &
|
||||
- sleep 5
|
||||
|
|
|
@ -1,398 +0,0 @@
|
|||
<?php
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use League\Csv\Reader;
|
||||
use App\Models\User;
|
||||
use App\Models\Supplier;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Asset;
|
||||
|
||||
class LicenseImportCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'snipeit:license-import';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import Licenses from CSV';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
$filename = $this->argument('filename');
|
||||
|
||||
|
||||
if (!$this->option('testrun')=='true') {
|
||||
$this->comment('======= Importing Licenses from '.$filename.' =========');
|
||||
} else {
|
||||
$this->comment('====== TEST ONLY License Import for '.$filename.' ====');
|
||||
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
|
||||
}
|
||||
|
||||
if (! ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
$csv = Reader::createFromPath($this->argument('filename'));
|
||||
$csv->setNewline("\r\n");
|
||||
$csv->setOffset(1);
|
||||
$duplicates = '';
|
||||
|
||||
// Loop through the records
|
||||
$nbInsert = $csv->each(function ($row) use ($duplicates) {
|
||||
$status_id = 1;
|
||||
|
||||
// Let's just map some of these entries to more user friendly words
|
||||
|
||||
if (array_key_exists('0', $row)) {
|
||||
$user_name = trim($row[0]);
|
||||
} else {
|
||||
$user_name = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('1', $row)) {
|
||||
$user_email = trim($row[1]);
|
||||
} else {
|
||||
$user_email = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('2', $row)) {
|
||||
$user_username = trim($row[2]);
|
||||
} else {
|
||||
$user_username = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('3', $row)) {
|
||||
$user_license_name = trim($row[3]);
|
||||
} else {
|
||||
$user_license_name = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('4', $row)) {
|
||||
$user_license_serial = trim($row[4]);
|
||||
} else {
|
||||
$user_license_serial = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('5', $row)) {
|
||||
$user_licensed_to_name = trim($row[5]);
|
||||
} else {
|
||||
$user_licensed_to_name = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('6', $row)) {
|
||||
$user_licensed_to_email = trim($row[6]);
|
||||
} else {
|
||||
$user_licensed_to_email = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('7', $row)) {
|
||||
$user_license_seats = trim($row[7]);
|
||||
} else {
|
||||
$user_license_seats = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('8', $row)) {
|
||||
$user_license_reassignable = trim($row[8]);
|
||||
if ($user_license_reassignable!='') {
|
||||
if ((strtolower($user_license_reassignable)=='yes') || (strtolower($user_license_reassignable)=='true') || ($user_license_reassignable=='1')) {
|
||||
$user_license_reassignable = 1;
|
||||
}
|
||||
} else {
|
||||
$user_license_reassignable = 0;
|
||||
}
|
||||
} else {
|
||||
$user_license_reassignable = 0;
|
||||
}
|
||||
|
||||
if (array_key_exists('9', $row)) {
|
||||
$user_license_supplier = trim($row[9]);
|
||||
} else {
|
||||
$user_license_supplier = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('10', $row)) {
|
||||
$user_license_maintained = trim($row[10]);
|
||||
|
||||
if ($user_license_maintained!='') {
|
||||
if ((strtolower($user_license_maintained)=='yes') || (strtolower($user_license_maintained)=='true') || ($user_license_maintained=='1')) {
|
||||
$user_license_maintained = 1;
|
||||
}
|
||||
} else {
|
||||
$user_license_maintained = 0;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
$user_license_maintained = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('11', $row)) {
|
||||
$user_license_notes = trim($row[11]);
|
||||
} else {
|
||||
$user_license_notes = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('12', $row)) {
|
||||
if ($row[12]!='') {
|
||||
$user_license_purchase_date = date("Y-m-d 00:00:01", strtotime($row[12]));
|
||||
} else {
|
||||
$user_license_purchase_date = '';
|
||||
}
|
||||
} else {
|
||||
$user_license_purchase_date = 0;
|
||||
}
|
||||
|
||||
if (array_key_exists('13', $row)) {
|
||||
$user_licensed_to_asset = trim($row[13]);
|
||||
} else {
|
||||
$user_licensed_to_asset = '';
|
||||
}
|
||||
|
||||
// A number was given instead of a name
|
||||
if (is_numeric($user_name)) {
|
||||
$this->comment('User '.$user_name.' is not a name - assume this user already exists');
|
||||
$user_username = '';
|
||||
// No name was given
|
||||
|
||||
} elseif ($user_name=='') {
|
||||
$this->comment('No user data provided - skipping user creation, just adding license');
|
||||
$first_name = '';
|
||||
$last_name = '';
|
||||
$user_username = '';
|
||||
|
||||
} else {
|
||||
|
||||
$name = explode(" ", $user_name);
|
||||
$first_name = $name[0];
|
||||
$email_last_name = '';
|
||||
$email_prefix = $first_name;
|
||||
|
||||
if (!array_key_exists(1, $name)) {
|
||||
$last_name='';
|
||||
$email_last_name = $last_name;
|
||||
$email_prefix = $first_name;
|
||||
} else {
|
||||
$last_name = str_replace($first_name, '', $user_name);
|
||||
|
||||
if ($this->option('email_format')=='filastname') {
|
||||
$email_last_name.=str_replace(' ', '', $last_name);
|
||||
$email_prefix = $first_name[0].$email_last_name;
|
||||
|
||||
} elseif ($this->option('email_format')=='firstname.lastname') {
|
||||
$email_last_name.=str_replace(' ', '', $last_name);
|
||||
$email_prefix = $first_name.'.'.$email_last_name;
|
||||
|
||||
} elseif ($this->option('email_format')=='firstname') {
|
||||
$email_last_name.=str_replace(' ', '', $last_name);
|
||||
$email_prefix = $first_name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
$user_username = $email_prefix;
|
||||
|
||||
// Generate an email based on their name if no email address is given
|
||||
if ($user_email=='') {
|
||||
if ($first_name=='Unknown') {
|
||||
$status_id = 7;
|
||||
}
|
||||
$email = strtolower($email_prefix).'@'.$this->option('domain');
|
||||
$user_email = str_replace("'", '', $email);
|
||||
}
|
||||
}
|
||||
|
||||
$this->comment('Full Name: '.$user_name);
|
||||
$this->comment('First Name: '.$first_name);
|
||||
$this->comment('Last Name: '.$last_name);
|
||||
$this->comment('Username: '.$user_username);
|
||||
$this->comment('Email: '.$user_email);
|
||||
$this->comment('License Name: '.$user_license_name);
|
||||
$this->comment('Serial No: '.$user_license_serial);
|
||||
$this->comment('Licensed To Name: '.$user_licensed_to_name);
|
||||
$this->comment('Licensed To Email: '.$user_licensed_to_email);
|
||||
$this->comment('Seats: '.$user_license_seats);
|
||||
$this->comment('Reassignable: '.$user_license_reassignable);
|
||||
$this->comment('Supplier: '.$user_license_supplier);
|
||||
$this->comment('Maintained: '.$user_license_maintained);
|
||||
$this->comment('Notes: '.$user_license_notes);
|
||||
$this->comment('Purchase Date: '.$user_license_purchase_date);
|
||||
$this->comment('Asset ID: '.$user_licensed_to_asset);
|
||||
|
||||
$this->comment('------------- Action Summary ----------------');
|
||||
|
||||
if ($user_username!='') {
|
||||
if ($user = User::where('username', $user_username)->whereNotNull('username')->first()) {
|
||||
$this->comment('User '.$user_username.' already exists');
|
||||
} else {
|
||||
|
||||
$user = new \App\Models\User;
|
||||
$password = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
|
||||
|
||||
$user->first_name = $first_name;
|
||||
$user->last_name = $last_name;
|
||||
$user->username = $user_username;
|
||||
$user->email = $user_email;
|
||||
$user->permissions = '{user":1}';
|
||||
$user->password = bcrypt($password);
|
||||
$user->activated = 1;
|
||||
if ($user->save()) {
|
||||
$this->comment('User '.$first_name.' created');
|
||||
} else {
|
||||
$this->error('ERROR CREATING User '.$first_name.' '.$last_name);
|
||||
$this->error($user->getErrors());
|
||||
}
|
||||
|
||||
$this->comment('User '.$first_name.' created');
|
||||
}
|
||||
} else {
|
||||
$user = new User;
|
||||
$user->user_id = null;
|
||||
}
|
||||
|
||||
|
||||
// Check for the supplier match and create it if it doesn't exist
|
||||
if ($supplier = Supplier::where('name', $user_license_supplier)->first()) {
|
||||
$this->comment('Supplier '.$user_license_supplier.' already exists');
|
||||
} else {
|
||||
$supplier = new Supplier();
|
||||
$supplier->name = e($user_license_supplier);
|
||||
$supplier->user_id = 1;
|
||||
|
||||
if ($supplier->save()) {
|
||||
$this->comment('Supplier '.$user_license_supplier.' was created');
|
||||
} else {
|
||||
$this->comment('Something went wrong! Supplier '.$user_license_supplier.' was NOT created');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Add the license
|
||||
$license = new License();
|
||||
$license->name = e($user_license_name);
|
||||
if ($user_license_purchase_date!='') {
|
||||
$license->purchase_date = $user_license_purchase_date;
|
||||
} else {
|
||||
$license->purchase_date = null;
|
||||
}
|
||||
$license->serial = e($user_license_serial);
|
||||
$license->seats = e($user_license_seats);
|
||||
$license->supplier_id = $supplier->id;
|
||||
$license->user_id = 1;
|
||||
if ($user_license_purchase_date!='') {
|
||||
$license->purchase_date = $user_license_purchase_date;
|
||||
} else {
|
||||
$license->purchase_date = null;
|
||||
}
|
||||
$license->license_name = $user_licensed_to_name;
|
||||
$license->license_email = $user_licensed_to_email;
|
||||
$license->notes = e($user_license_notes);
|
||||
|
||||
if ($license->save()) {
|
||||
$this->comment('License '.$user_license_name.' with serial number '.$user_license_serial.' was created');
|
||||
|
||||
|
||||
$license_seat_created = 0;
|
||||
|
||||
for ($x = 0; $x < $user_license_seats; $x++) {
|
||||
// Create the license seat entries
|
||||
$license_seat = new LicenseSeat();
|
||||
$license_seat->license_id = $license->id;
|
||||
|
||||
// Only assign the first seat to the user
|
||||
if ($x==0) {
|
||||
$license_seat->assigned_to = $user->id;
|
||||
} else {
|
||||
$license_seat->assigned_to = null;
|
||||
}
|
||||
|
||||
if($user_licensed_to_asset) {
|
||||
$asset = Asset::where('asset_tag', $user_licensed_to_asset)->first();
|
||||
if($asset) {
|
||||
$license_seat->asset_id = $asset->id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($license_seat->save()) {
|
||||
$license_seat_created++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($license_seat_created > 0) {
|
||||
$this->comment($license_seat_created.' seats were created');
|
||||
} else {
|
||||
$this->comment('Something went wrong! NO seats for '.$user_license_name.' were created');
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
$this->comment('Something went wrong! License '.$user_license_name.' was NOT created');
|
||||
}
|
||||
|
||||
|
||||
$this->comment('=====================================');
|
||||
|
||||
return true;
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console command arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return array(
|
||||
array('filename', InputArgument::REQUIRED, 'File for the CSV import.'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the console command options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
array('domain', null, InputOption::VALUE_REQUIRED, 'Email domain for generated email addresses.', null),
|
||||
array('email_format', null, InputOption::VALUE_REQUIRED, 'The format of the email addresses that should be generated. Options are firstname.lastname, firstname, filastname', null),
|
||||
array('testrun', null, InputOption::VALUE_REQUIRED, 'Test the output without writing to the database or not.', null),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -80,10 +80,10 @@ class ObjectImportCommand extends Command
|
|||
$logFile = $this->option('logfile');
|
||||
\Log::useFiles($logFile);
|
||||
if ($this->option('testrun')) {
|
||||
$this->comment('====== TEST ONLY Asset Import for '.$filename.' ====');
|
||||
$this->comment('====== TEST ONLY Item Import for '.$filename.' ====');
|
||||
$this->comment('============== NO DATA WILL BE WRITTEN ==============');
|
||||
} else {
|
||||
$this->comment('======= Importing Assets from '.$filename.' =========');
|
||||
$this->comment('======= Importing Items from '.$filename.' =========');
|
||||
}
|
||||
$importer->import();
|
||||
|
||||
|
@ -175,7 +175,7 @@ class ObjectImportCommand extends Command
|
|||
array('username_format', null, InputOption::VALUE_REQUIRED, 'The format of the username that should be generated. Options are firstname.lastname, firstname, filastname, email', null),
|
||||
array('testrun', null, InputOption::VALUE_NONE, 'If set, will parse and output data without adding to database', null),
|
||||
array('logfile', null, InputOption::VALUE_REQUIRED, 'The path to log output to. storage/logs/importer.log by default', storage_path('logs/importer.log') ),
|
||||
array('item-type', null, InputOption::VALUE_REQUIRED, 'Item Type To import. Valid Options are Asset, Consumable, Or Accessory', 'Asset'),
|
||||
array('item-type', null, InputOption::VALUE_REQUIRED, 'Item Type To import. Valid Options are Asset, Consumable, Accessory, License, or User', 'Asset'),
|
||||
array('web-importer', null, InputOption::VALUE_NONE, 'Internal: packages output for use with the web importer'),
|
||||
array('user_id', null, InputOption::VALUE_REQUIRED, 'ID of user creating items', 1),
|
||||
array('update', null, InputOption::VALUE_NONE, 'If a matching item is found, update item information'),
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use DB;
|
||||
|
||||
use App\Models\Accessory;
|
||||
use App\Models\Asset;
|
||||
use App\Models\AssetModel;
|
||||
|
@ -12,14 +9,18 @@ use App\Models\Category;
|
|||
use App\Models\Company;
|
||||
use App\Models\Component;
|
||||
use App\Models\Consumable;
|
||||
use App\Models\Department;
|
||||
use App\Models\Depreciation;
|
||||
use App\Models\Group;
|
||||
use App\Models\Import;
|
||||
use App\Models\License;
|
||||
use App\Models\LicenseSeat;
|
||||
use App\Models\Location;
|
||||
use App\Models\Manufacturer;
|
||||
use App\Models\Statuslabel;
|
||||
use App\Models\Supplier;
|
||||
use DB;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class PaveIt extends Command
|
||||
{
|
||||
|
@ -63,6 +64,7 @@ class PaveIt extends Command
|
|||
Company::getQuery()->delete();
|
||||
Component::getQuery()->delete();
|
||||
Consumable::getQuery()->delete();
|
||||
Department::getQuery()->delete();
|
||||
Depreciation::getQuery()->delete();
|
||||
License::getQuery()->delete();
|
||||
LicenseSeat::getQuery()->delete();
|
||||
|
@ -108,6 +110,7 @@ class PaveIt extends Command
|
|||
\DB::statement('drop table IF EXISTS custom_fields');
|
||||
\DB::statement('drop table IF EXISTS custom_fieldsets');
|
||||
\DB::statement('drop table IF EXISTS depreciations');
|
||||
\DB::statement('drop table IF EXISTS departments');
|
||||
\DB::statement('drop table IF EXISTS groups');
|
||||
\DB::statement('drop table IF EXISTS history');
|
||||
\DB::statement('drop table IF EXISTS components');
|
||||
|
|
|
@ -17,7 +17,6 @@ class Kernel extends ConsoleKernel
|
|||
Commands\CreateAdmin::class,
|
||||
Commands\SendExpirationAlerts::class,
|
||||
Commands\SendInventoryAlerts::class,
|
||||
Commands\LicenseImportCommand::class,
|
||||
Commands\ObjectImportCommand::class,
|
||||
Commands\Versioning::class,
|
||||
Commands\SystemBackup::class,
|
||||
|
|
|
@ -11,6 +11,7 @@ use App\Models\Import;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use League\Csv\Reader;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
|
||||
class ImportController extends Controller
|
||||
|
@ -29,7 +30,7 @@ class ImportController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
* Process and store a CSV upload file.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
|
@ -65,17 +66,21 @@ class ImportController extends Controller
|
|||
$results['error'].= ' ' . $exception->getMessage();
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, $results['error']), 500);
|
||||
|
||||
}
|
||||
$file_name = date('Y-m-d-his').'-'.$fixed_filename;
|
||||
$import->file_path = $file_name;
|
||||
$import->filesize = filesize($path.'/'.$file_name);
|
||||
//TODO: is there a lighter way to do this?
|
||||
$reader = Reader::createFromPath("{$path}/{$file_name}");
|
||||
$import->header_row = $reader->fetchOne(0);
|
||||
// Grab the first row to display via ajax as the user picks fields
|
||||
$import->first_row = $reader->fetchOne(1);
|
||||
$import->save();
|
||||
$results[] = $import;
|
||||
}
|
||||
$results = (new ImportsTransformer)->transformImports($results);
|
||||
return [
|
||||
'files' => $results
|
||||
'files' => $results,
|
||||
];
|
||||
}
|
||||
return response()->json(Helper::formatStandardApiResponse('error', null, trans('general.feature_disabled')), 500);
|
||||
|
@ -90,7 +95,7 @@ class ImportController extends Controller
|
|||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$errors = $request->import(Import::find($import_id));
|
||||
$redirectTo = "hardware";
|
||||
$redirectTo = "hardware.index";
|
||||
switch ($request->get('import-type')) {
|
||||
case "asset":
|
||||
$redirectTo = "hardware.index";
|
||||
|
@ -107,6 +112,9 @@ class ImportController extends Controller
|
|||
case "license":
|
||||
$redirectTo = "licenses.index";
|
||||
break;
|
||||
case "user":
|
||||
$redirectTo = "users.index";
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errors) { //Failure
|
||||
|
|
|
@ -12,6 +12,7 @@ use App\Models\Asset;
|
|||
use App\Models\AssetModel;
|
||||
use App\Models\Company;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Import;
|
||||
use App\Models\Location;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
|
@ -673,84 +674,6 @@ class AssetsController extends Controller
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Asset import upload page.
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getImportUpload()
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
return view('hardware/import');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map the fields for import
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v4.0]
|
||||
* @return View
|
||||
*/
|
||||
public function getImportMap()
|
||||
{
|
||||
|
||||
$this->authorize('create', Asset::class);
|
||||
|
||||
// This is currently hardcoded for testing - should use a post variable or something to dynamically select the correct file.
|
||||
$file = storage_path().'/private_uploads/imports/2017-06-08-072329-sample-assets.csv';
|
||||
$reader = Reader::createFromPath($file);
|
||||
$header_rows = $reader->fetchOne(0);
|
||||
|
||||
// Grab the first row to display via ajax as the user picks fields
|
||||
$first_row = $reader->fetchOne(1);
|
||||
|
||||
// Grab all of the custom fields to we can map those too.
|
||||
$custom_fields = CustomField::all();
|
||||
|
||||
return view('importer/fieldmapper')->with('header_rows', $header_rows)->with('first_row',$first_row)->with('custom_fields',$custom_fields);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the uploaded file
|
||||
*
|
||||
* @author [A. Gianotto] [<snipe@snipe.net>]
|
||||
* @since [v2.0]
|
||||
* @return Redirect
|
||||
*/
|
||||
public function postProcessImportFile(ItemImportRequest $request)
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$errors = $request->import();
|
||||
|
||||
// We use hardware instead of asset in the url
|
||||
$redirectTo = "hardware";
|
||||
switch (request('import-type')) {
|
||||
case "asset":
|
||||
$redirectTo = "hardware.index";
|
||||
break;
|
||||
case "accessory":
|
||||
$redirectTo = "accessories.index";
|
||||
break;
|
||||
case "consumable":
|
||||
$redirectTo = "consumables.index";
|
||||
break;
|
||||
case "component":
|
||||
$redirectTo = "components.index";
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errors) { //Failure
|
||||
return redirect()->back()->with('import_errors', json_decode(json_encode($errors)))->with('error', trans('admin/hardware/message.import.error'));
|
||||
}
|
||||
return redirect()->to(route($redirectTo))->with('success', trans('admin/hardware/message.import.success'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view that presents a form to clone an asset.
|
||||
*
|
||||
|
|
18
app/Http/Controllers/ImportsController.php
Normal file
18
app/Http/Controllers/ImportsController.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Transformers\ImportsTransformer;
|
||||
use App\Models\Import;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ImportsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('create', Asset::class);
|
||||
$imports = Import::latest()->get();
|
||||
$imports = (new ImportsTransformer)->transformImports($imports);
|
||||
return view('importer/import')->with('imports', $imports);
|
||||
}
|
||||
}
|
|
@ -32,22 +32,35 @@ class ItemImportRequest extends FormRequest
|
|||
|
||||
public function import(Import $import)
|
||||
{
|
||||
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
|
||||
ini_set('memory_limit', '500M');
|
||||
$filename = config('app.private_uploads') . '/imports/' . $import->file_path;
|
||||
$class = title_case($this->input('import-type'));
|
||||
$import->import_type = $this->input('import-type');
|
||||
$class = title_case($import->import_type);
|
||||
$classString = "App\\Importer\\{$class}Importer";
|
||||
$importer = new $classString($filename);
|
||||
$import->field_map = request('column-mappings');
|
||||
$import->save();
|
||||
$fieldMappings=[];
|
||||
if ($import->field_map) {
|
||||
// We submit as csv field: column, but the importer is happier if we flip it here.
|
||||
$fieldMappings = array_change_key_case(array_flip($import->field_map), CASE_LOWER);
|
||||
// dd($fieldMappings);
|
||||
}
|
||||
$importer->setCallbacks([$this, 'log'], [$this, 'progress'], [$this, 'errorCallback'])
|
||||
->setUserId(Auth::id())
|
||||
->setUpdating($this->has('import-update'))
|
||||
->setUsernameFormat('firstname.lastname');
|
||||
|
||||
->setUsernameFormat('firstname.lastname')
|
||||
->setFieldMappings($fieldMappings);
|
||||
// $logFile = storage_path('logs/importer.log');
|
||||
// \Log::useFiles($logFile);
|
||||
$importer->import();
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function log($string)
|
||||
{
|
||||
return; // FUTURE IMPLEMENTATION
|
||||
// \Log::Info($string);
|
||||
}
|
||||
|
||||
public function progress($count)
|
||||
|
@ -58,7 +71,6 @@ class ItemImportRequest extends FormRequest
|
|||
public function errorCallback($item, $field, $errorString)
|
||||
{
|
||||
$this->errors[$item->name][$field] = $errorString;
|
||||
// $this->errors[$item->name] = $errorString;
|
||||
}
|
||||
|
||||
private $errors;
|
||||
|
|
|
@ -26,6 +26,9 @@ class ImportsTransformer
|
|||
'name' => $import->name,
|
||||
'import_type' => $import->import_type,
|
||||
'created_at' => $import->created_at->diffForHumans(),
|
||||
'header_row' => $import->header_row,
|
||||
'first_row' => $import->first_row,
|
||||
'field_map' => $import->field_map,
|
||||
|
||||
];
|
||||
|
||||
|
|
|
@ -7,11 +7,9 @@ use App\Models\Accessory;
|
|||
|
||||
class AccessoryImporter extends ItemImporter
|
||||
{
|
||||
protected $accessories;
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
$this->accessories = Accessory::all();
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
|
@ -28,17 +26,14 @@ class AccessoryImporter extends ItemImporter
|
|||
*/
|
||||
public function createAccessoryIfNotExists()
|
||||
{
|
||||
$accessoryId = $this->accessories->search(function ($key) {
|
||||
return strcasecmp($key->name, $this->item['name']) == 0;
|
||||
});
|
||||
if ($accessoryId !== false) {
|
||||
$accessory = Accessory::where('name', $this->item['name'])->first();
|
||||
if ($accessory) {
|
||||
if (!$this->updating) {
|
||||
$this->log('A matching Accessory ' . $this->item["name"] . ' already exists. ');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log('Updating Accessory');
|
||||
$accessory = $this->accessories[$accessoryId];
|
||||
$accessory->update($this->sanitizeItemForUpdating($accessory));
|
||||
if (!$this->testRun) {
|
||||
$accessory->save();
|
||||
|
@ -57,7 +52,8 @@ class AccessoryImporter extends ItemImporter
|
|||
if ($accessory->save()) {
|
||||
$accessory->logCreate('Imported using CSV Importer');
|
||||
$this->log('Accessory ' . $this->item["name"] . ' was created');
|
||||
return;
|
||||
}
|
||||
$this->jsonError($accessory, 'Accessory');
|
||||
$this->logError($accessory, 'Accessory');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,10 +45,11 @@ class AssetImporter extends ItemImporter
|
|||
public function createAssetIfNotExists(array $row)
|
||||
{
|
||||
$editingAsset = false;
|
||||
$asset = Asset::where(['asset_tag'=> $this->item['asset_tag']])->first();
|
||||
$asset_tag = $this->findCsvMatch($row, "asset_tag");
|
||||
$asset = Asset::where(['asset_tag'=> $asset_tag])->first();
|
||||
if ($asset) {
|
||||
if (!$this->updating) {
|
||||
$this->log('A matching Asset ' . $this->item['asset_tag'] . ' already exists');
|
||||
$this->log('A matching Asset ' . $asset_tag . ' already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,24 +59,19 @@ class AssetImporter extends ItemImporter
|
|||
$this->log("No Matching Asset, Creating a new one");
|
||||
$asset = new Asset;
|
||||
}
|
||||
$this->item['serial'] = $this->array_smart_fetch($row, "serial number");
|
||||
$this->item['image'] = $this->array_smart_fetch($row, "image");
|
||||
$this->item['warranty_months'] = intval($this->array_smart_fetch($row, "warranty months"));
|
||||
if ($this->item['asset_model'] = $this->createOrFetchAssetModel($row)) {
|
||||
$this->item['model_id'] = $this->item['asset_model']->id;
|
||||
}
|
||||
if (isset($this->item["status_label"])) {
|
||||
$this->item['status_id'] = $this->item["status_label"]->id;
|
||||
} elseif (!$editingAsset) {
|
||||
// Assume if we are editing, we already have a status and can ignore.
|
||||
|
||||
$this->item['image'] = $this->findCsvMatch($row, "image");
|
||||
$this->item['warranty_months'] = intval($this->findCsvMatch($row, "warranty months"));
|
||||
$this->item['model_id'] = $this->createOrFetchAssetModel($row);
|
||||
if (!$this->item['status_id'] && !$editingAsset) {
|
||||
$this->log("No status field found, defaulting to first status.");
|
||||
$this->item['status_id'] = $this->defaultStatusLabelId;
|
||||
}
|
||||
|
||||
|
||||
$this->item['asset_tag'] = $asset_tag;
|
||||
// By default we're set this to location_id in the item.
|
||||
$item = $this->sanitizeItemForStoring($asset, $editingAsset);
|
||||
if (isset($this->item["location"])) {
|
||||
if (isset($this->item["location_id"])) {
|
||||
$item['rtd_location_id'] = $this->item['location_id'];
|
||||
unset($item['location_id']);
|
||||
}
|
||||
|
@ -95,7 +91,7 @@ class AssetImporter extends ItemImporter
|
|||
$this->log('Asset ' . $this->item["name"] . ' with serial number ' . $this->item['serial'] . ' was created');
|
||||
return;
|
||||
}
|
||||
$this->jsonError($asset, 'Asset "' . $this->item['name'].'"');
|
||||
$this->logError($asset, 'Asset "' . $this->item['name'].'"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,9 @@ use App\Models\Component;
|
|||
|
||||
class ComponentImporter extends ItemImporter
|
||||
{
|
||||
protected $components;
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
$this->components = Component::all();
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
|
@ -31,11 +29,9 @@ class ComponentImporter extends ItemImporter
|
|||
$component = null;
|
||||
$editingComponent = false;
|
||||
$this->log("Creating Component");
|
||||
$componentId = $this->components->search(function ($key) {
|
||||
return strcasecmp($key->name, $this->item['name']) == 0;
|
||||
});
|
||||
$component = Component::where('name', $this->item['name']);
|
||||
|
||||
if ($componentId !== false) {
|
||||
if ($component) {
|
||||
$editingComponent = true;
|
||||
$this->log('A matching Component ' . $this->item["name"] . ' already exists. ');
|
||||
if (!$this->updating) {
|
||||
|
@ -75,6 +71,6 @@ class ComponentImporter extends ItemImporter
|
|||
}
|
||||
return;
|
||||
}
|
||||
$this->jsonError($component, 'Component');
|
||||
$this->logError($component, 'Component');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,9 @@ use App\Models\Consumable;
|
|||
|
||||
class ConsumableImporter extends ItemImporter
|
||||
{
|
||||
protected $consumables;
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
$this->consumables = Consumable::all();
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
|
@ -28,16 +26,13 @@ class ConsumableImporter extends ItemImporter
|
|||
*/
|
||||
public function createConsumableIfNotExists()
|
||||
{
|
||||
$consumableId = $this->consumables->search(function ($key) {
|
||||
return strcasecmp($key->name, $this->item['name']) == 0;
|
||||
});
|
||||
if ($consumableId !== false) {
|
||||
$consumable = Consumable::where('name', $this->item['name'])->first();
|
||||
if ($consumable) {
|
||||
if (!$this->updating) {
|
||||
$this->log('A matching Consumable ' . $this->item["name"] . ' already exists. ');
|
||||
return;
|
||||
}
|
||||
$this->log('Updating Consumable');
|
||||
$consumable = $this->consumables[$consumableId];
|
||||
$consumable->update($this->sanitizeItemForUpdating($consumable));
|
||||
if (!$this->testRun) {
|
||||
$consumable->save();
|
||||
|
@ -57,7 +52,7 @@ class ConsumableImporter extends ItemImporter
|
|||
$this->log("Consumable " . $this->item["name"] . ' was created');
|
||||
return;
|
||||
}
|
||||
$this->jsonError($consumable, 'Consumable');
|
||||
$this->logError($consumable, 'Consumable');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,11 @@ abstract class Importer
|
|||
* @var bool
|
||||
*/
|
||||
protected $updating;
|
||||
/**
|
||||
* Map of item fields->csv names
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldMap = [];
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
|
@ -71,7 +76,7 @@ abstract class Importer
|
|||
|
||||
public function import()
|
||||
{
|
||||
$results = $this->normalizeInputArray($this->csv->fetchAssoc());
|
||||
$results = $this->csv->fetchAssoc();
|
||||
$this->customFields = CustomField::All(['name']);
|
||||
DB::transaction(function () use (&$results) {
|
||||
Model::unguard();
|
||||
|
@ -87,20 +92,6 @@ abstract class Importer
|
|||
|
||||
abstract protected function handle($row);
|
||||
|
||||
/**
|
||||
* @param $results
|
||||
* @return array
|
||||
*/
|
||||
public function normalizeInputArray($results)
|
||||
{
|
||||
$newArray = [];
|
||||
|
||||
foreach ($results as $index => $arrayToNormalize) {
|
||||
$newArray[$index] = array_change_key_case($arrayToNormalize);
|
||||
}
|
||||
return $newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the given key exists in the array, and trim excess white space before returning it
|
||||
*
|
||||
|
@ -111,17 +102,38 @@ abstract class Importer
|
|||
* @param $default string
|
||||
* @return string
|
||||
*/
|
||||
public function array_smart_fetch(array $array, $key, $default = '')
|
||||
public function findCsvMatch(array $array, $key, $default = '')
|
||||
{
|
||||
$val = $default;
|
||||
if (array_key_exists(trim($key), $array)) {
|
||||
// dd($array);
|
||||
if($customKey = $this->lookupCustomKey($key)) {
|
||||
$key = $customKey;
|
||||
}
|
||||
$this->log("Custom Key: ${key}");
|
||||
if (array_key_exists($key, $array)) {
|
||||
$val = e(Encoding::toUTF8(trim($array[ $key ])));
|
||||
}
|
||||
$key = title_case($key);
|
||||
// $this->log("${key}: ${val}");
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up A custom key in the custom field map
|
||||
*
|
||||
* @author Daniel Melzter
|
||||
* @since 4.0
|
||||
* @param $key string
|
||||
* @return string|null
|
||||
*/
|
||||
public function lookupCustomKey($key)
|
||||
{
|
||||
// dd($this->fieldMap);
|
||||
if (array_key_exists($key, $this->fieldMap)) {
|
||||
// $this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]);
|
||||
return $key = $this->fieldMap[$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Figure out the fieldname of the custom field
|
||||
*
|
||||
|
@ -141,7 +153,7 @@ abstract class Importer
|
|||
call_user_func($this->logCallback, $string);
|
||||
}
|
||||
|
||||
protected function jsonError($item, $field)
|
||||
protected function logError($item, $field)
|
||||
{
|
||||
call_user_func($this->errorCallback, $item, $field, $item->getErrors());
|
||||
}
|
||||
|
@ -160,9 +172,9 @@ abstract class Importer
|
|||
*/
|
||||
protected function createOrFetchUser($row)
|
||||
{
|
||||
$user_name = $this->array_smart_fetch($row, "name");
|
||||
$user_email = $this->array_smart_fetch($row, "email");
|
||||
$user_username = $this->array_smart_fetch($row, "username");
|
||||
$user_name = $this->findCsvMatch($row, "name");
|
||||
$user_email = $this->findCsvMatch($row, "email");
|
||||
$user_username = $this->findCsvMatch($row, "username");
|
||||
$first_name = '';
|
||||
$last_name = '';
|
||||
// A number was given instead of a name
|
||||
|
@ -214,7 +226,7 @@ abstract class Importer
|
|||
if ($user->save()) {
|
||||
$this->log('User '.$first_name.' created');
|
||||
} else {
|
||||
$this->jsonError($user, 'User "' . $first_name . '"');
|
||||
$this->logError($user, 'User "' . $first_name . '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,6 +291,22 @@ abstract class Importer
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines mappings of csv fields
|
||||
*
|
||||
* @param bool $updating the updating
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setFieldMappings($fields)
|
||||
{
|
||||
// Some initial sanitization.
|
||||
|
||||
$this->fieldMap = $fields;
|
||||
$this->log($this->fieldMap);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callbacks for the import
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Importer\UserImporter;
|
||||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
|
@ -20,61 +21,58 @@ class ItemImporter extends Importer
|
|||
|
||||
protected function handle($row)
|
||||
{
|
||||
// TODO: CHeck if this interferes with checkout to user.. it shouldn't..
|
||||
$this->item["user_id"] = $this->user_id;
|
||||
$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_supplier = $this->array_smart_fetch($row, "supplier");
|
||||
$this->item["name"] = $this->array_smart_fetch($row, "item name");
|
||||
$this->item["purchase_date"] = null;
|
||||
if ($this->array_smart_fetch($row, "purchase date")!='') {
|
||||
$this->item["purchase_date"] = date("Y-m-d 00:00:01", strtotime($this->array_smart_fetch($row, "purchase date")));
|
||||
}
|
||||
|
||||
$this->item["purchase_cost"] = $this->array_smart_fetch($row, "purchase cost");
|
||||
$this->item["order_number"] = $this->array_smart_fetch($row, "order number");
|
||||
$this->item["notes"] = $this->array_smart_fetch($row, "notes");
|
||||
$this->item["qty"] = $this->array_smart_fetch($row, "quantity");
|
||||
$this->item["requestable"] = $this->array_smart_fetch($row, "requestable");
|
||||
$this->item["asset_tag"] = $this->array_smart_fetch($row, "asset tag");
|
||||
|
||||
if ($this->item["user"] = $this->createOrFetchUser($row)) {
|
||||
$this->item['assigned_to'] = $this->item['user']->id;
|
||||
}
|
||||
|
||||
if ($this->shouldUpdateField($item_location)) {
|
||||
if ($this->item["location"] = $this->createOrFetchLocation($item_location)) {
|
||||
$this->item["location_id"] = $this->item["location"]->id;
|
||||
}
|
||||
}
|
||||
$item_category = $this->findCsvMatch($row, "category");
|
||||
if ($this->shouldUpdateField($item_category)) {
|
||||
if ($this->item["category"] = $this->createOrFetchCategory($item_category)) {
|
||||
$this->item["category_id"] = $this->item["category"]->id;
|
||||
}
|
||||
}
|
||||
if ($this->shouldUpdateField($item_manufacturer)) {
|
||||
if ($this->item["manufacturer"] = $this->createOrFetchManufacturer($item_manufacturer)) {
|
||||
$this->item["manufacturer_id"] = $this->item["manufacturer"]->id;
|
||||
}
|
||||
$this->item["category_id"] = $this->createOrFetchCategory($item_category);
|
||||
}
|
||||
|
||||
$item_company_name = $this->findCsvMatch($row, "company");
|
||||
if ($this->shouldUpdateField($item_company_name)) {
|
||||
if ($this->item["company"] = $this->createOrFetchCompany($item_company_name)) {
|
||||
$this->item["company_id"] = $this->item["company"]->id;
|
||||
}
|
||||
$this->item["company_id"] = $this->createOrFetchCompany($item_company_name);
|
||||
}
|
||||
|
||||
$item_location = $this->findCsvMatch($row, "location");
|
||||
if ($this->shouldUpdateField($item_location)) {
|
||||
$this->item["location_id"] = $this->createOrFetchLocation($item_location);
|
||||
}
|
||||
|
||||
$item_manufacturer = $this->findCsvMatch($row, "manufacturer");
|
||||
if ($this->shouldUpdateField($item_manufacturer)) {
|
||||
$this->item["manufacturer_id"] = $this->createOrFetchManufacturer($item_manufacturer);
|
||||
}
|
||||
|
||||
$item_status_name = $this->findCsvMatch($row, "status");
|
||||
if ($this->shouldUpdateField($item_status_name)) {
|
||||
if ($this->item["status_label"] = $this->createOrFetchStatusLabel($item_status_name)) {
|
||||
$this->item["status_label_id"] = $this->item["status_label"]->id;
|
||||
}
|
||||
$this->item["status_id"] = $this->createOrFetchStatusLabel($item_status_name);
|
||||
}
|
||||
|
||||
$item_supplier = $this->findCsvMatch($row, "supplier");
|
||||
if ($this->shouldUpdateField($item_supplier)) {
|
||||
if ($this->item['supplier'] = $this->createOrFetchSupplier($item_supplier)) {
|
||||
$this->item['supplier_id'] = $this->item['supplier']->id;
|
||||
$this->item['supplier_id'] = $this->createOrFetchSupplier($item_supplier);
|
||||
}
|
||||
|
||||
$this->item["name"] = $this->findCsvMatch($row, "item_name");
|
||||
$this->item["notes"] = $this->findCsvMatch($row, "notes");
|
||||
$this->item["order_number"] = $this->findCsvMatch($row, "order_number");
|
||||
$this->item["purchase_cost"] = $this->findCsvMatch($row, "purchase_cost");
|
||||
|
||||
$this->item["purchase_date"] = null;
|
||||
if ($this->findCsvMatch($row, "purchase date")!='') {
|
||||
$this->item["purchase_date"] = date("Y-m-d 00:00:01", strtotime($this->findCsvMatch($row, "purchase date")));
|
||||
}
|
||||
|
||||
$this->item["qty"] = $this->findCsvMatch($row, "quantity");
|
||||
$this->item["requestable"] = $this->findCsvMatch($row, "requestable");
|
||||
$this->item["user_id"] = $this->user_id;
|
||||
$this->item['serial'] = $this->findCsvMatch($row, "serial number");
|
||||
// NO need to call this method if we're running the user import.
|
||||
// TODO: Merge these methods.
|
||||
if(get_class($this) !== UserImporter::class) {
|
||||
if ($this->item["user"] = $this->createOrFetchUser($row)) {
|
||||
$this->item['assigned_to'] = $this->item['user']->id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,8 +92,8 @@ class ItemImporter extends Importer
|
|||
// Create a collection for all manipulations to come.
|
||||
$item = collect($this->item);
|
||||
// First Filter the item down to the model's fillable fields
|
||||
|
||||
$item = $item->only($model->getFillable());
|
||||
|
||||
// Then iterate through the item and, if we are updating, remove any blank values.
|
||||
if ($updating) {
|
||||
$item = $item->reject(function ($value) {
|
||||
|
@ -108,8 +106,8 @@ class ItemImporter extends Importer
|
|||
|
||||
/**
|
||||
* Convenience function for updating that strips the empty values.
|
||||
*
|
||||
*
|
||||
* @param $model SnipeModel Model that's being updated.
|
||||
* @return array
|
||||
*/
|
||||
protected function sanitizeItemForUpdating($model)
|
||||
{
|
||||
|
@ -142,13 +140,13 @@ class ItemImporter extends Importer
|
|||
* @param array
|
||||
* @param $category Category
|
||||
* @param $manufacturer Manufacturer
|
||||
* @return AssetModel
|
||||
* @return int Id of asset model created/found
|
||||
* @internal param $asset_modelno string
|
||||
*/
|
||||
public function createOrFetchAssetModel(array $row)
|
||||
{
|
||||
$asset_model_name = $this->array_smart_fetch($row, "model name");
|
||||
$asset_modelNumber = $this->array_smart_fetch($row, "model number");
|
||||
$asset_model_name = $this->findCsvMatch($row, "asset_model");
|
||||
$asset_modelNumber = $this->findCsvMatch($row, "model_number");
|
||||
// TODO: At the moment, this means we can't update the model number if the model name stays the same.
|
||||
if (!$this->shouldUpdateField($asset_model_name)) {
|
||||
return;
|
||||
|
@ -162,10 +160,9 @@ class ItemImporter extends Importer
|
|||
$asset_model = AssetModel::where(['name' => $asset_model_name, 'model_number' => $asset_modelNumber])->first();
|
||||
|
||||
if ($asset_model) {
|
||||
|
||||
if (!$this->updating) {
|
||||
$this->log("A matching model already exists, returning it.");
|
||||
return $asset_model;
|
||||
return $asset_model->id;
|
||||
}
|
||||
$this->log("Matching Model found, updating it.");
|
||||
$item = $this->sanitizeItemForStoring($asset_model, $editingModel);
|
||||
|
@ -175,7 +172,8 @@ class ItemImporter extends Importer
|
|||
if (!$this->testRun) {
|
||||
$asset_model->save();
|
||||
}
|
||||
return $asset_model;
|
||||
$this->log("Asset Model Updated");
|
||||
return $asset_model->id;
|
||||
}
|
||||
$this->log("No Matching Model, Creating a new one");
|
||||
|
||||
|
@ -185,18 +183,18 @@ class ItemImporter extends Importer
|
|||
$item['model_number'] = $asset_modelNumber;
|
||||
|
||||
$asset_model->fill($item);
|
||||
|
||||
$item = null;
|
||||
if ($this->testRun) {
|
||||
$this->log('TEST RUN - asset_model ' . $asset_model->name . ' not created');
|
||||
return $asset_model;
|
||||
return $asset_model->id;
|
||||
}
|
||||
|
||||
if ($asset_model->save()) {
|
||||
$this->log('Asset Model ' . $asset_model_name . ' with model number ' . $asset_modelNumber . ' was created');
|
||||
return $asset_model;
|
||||
return $asset_model->id;
|
||||
}
|
||||
$this->jsonError($asset_model, 'Asset Model "' . $asset_model_name . '"');
|
||||
return;
|
||||
$this->logError($asset_model, 'Asset Model "' . $asset_model_name . '"');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,7 +203,7 @@ class ItemImporter extends Importer
|
|||
* @author Daniel Melzter
|
||||
* @since 3.0
|
||||
* @param $asset_category string
|
||||
* @return Category
|
||||
* @return int Id of category created/found
|
||||
* @internal param string $item_type
|
||||
*/
|
||||
public function createOrFetchCategory($asset_category)
|
||||
|
@ -213,6 +211,7 @@ class ItemImporter extends Importer
|
|||
// Magic to transform "AssetImporter" to "asset" or similar.
|
||||
$classname = class_basename(get_class($this));
|
||||
$item_type = strtolower(substr($classname, 0, strpos($classname, 'Importer')));
|
||||
|
||||
if (empty($asset_category)) {
|
||||
$asset_category = 'Unnamed Category';
|
||||
}
|
||||
|
@ -220,7 +219,7 @@ class ItemImporter extends Importer
|
|||
|
||||
if ($category) {
|
||||
$this->log("A matching category: " . $asset_category . " already exists");
|
||||
return $category;
|
||||
return $category->id;
|
||||
}
|
||||
|
||||
$category = new Category();
|
||||
|
@ -229,14 +228,15 @@ class ItemImporter extends Importer
|
|||
$category->user_id = $this->user_id;
|
||||
|
||||
if ($this->testRun) {
|
||||
return $category;
|
||||
return $category->id;
|
||||
}
|
||||
if ($category->save()) {
|
||||
$this->log('Category ' . $asset_category . ' was created');
|
||||
return $category;
|
||||
return $category->id;
|
||||
}
|
||||
|
||||
$this->jsonError($category, 'Category "'. $asset_category. '"');
|
||||
$this->logError($category, 'Category "'. $asset_category. '"');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -245,28 +245,28 @@ class ItemImporter extends Importer
|
|||
* @author Daniel Melzter
|
||||
* @since 3.0
|
||||
* @param $asset_company_name string
|
||||
* @return Company
|
||||
* @return int id of company created/found
|
||||
*/
|
||||
public function createOrFetchCompany($asset_company_name)
|
||||
{
|
||||
$company = Company::where(['name' => $asset_company_name])->first();
|
||||
if ($company) {
|
||||
$this->log('A matching Company ' . $asset_company_name . ' already exists');
|
||||
return $company;
|
||||
return $company->id;
|
||||
}
|
||||
$company = new Company();
|
||||
$company->name = $asset_company_name;
|
||||
|
||||
if ($this->testRun) {
|
||||
|
||||
return $company;
|
||||
return $company->id;
|
||||
}
|
||||
if ($company->save()) {
|
||||
$this->log('Company ' . $asset_company_name . ' was created');
|
||||
return $company;
|
||||
return $company->id;
|
||||
}
|
||||
$this->jsonError($company, 'Company');
|
||||
return;
|
||||
$this->logError($company, 'Company');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -286,7 +286,7 @@ class ItemImporter extends Importer
|
|||
|
||||
if ($status) {
|
||||
$this->log('A matching Status ' . $asset_statuslabel_name . ' already exists');
|
||||
return $status;
|
||||
return $status->id;
|
||||
}
|
||||
$this->log("Creating a new status");
|
||||
$status = new Statuslabel();
|
||||
|
@ -297,16 +297,16 @@ class ItemImporter extends Importer
|
|||
$status->archived = 0;
|
||||
|
||||
if ($this->testRun) {
|
||||
return $status;
|
||||
return $status->id;
|
||||
}
|
||||
|
||||
if ($status->save()) {
|
||||
$this->log('Status ' . $asset_statuslabel_name . ' was created');
|
||||
return $status;
|
||||
return $status->id;
|
||||
}
|
||||
|
||||
$this->jsonError($status, 'Status "'. $asset_statuslabel_name . '"');
|
||||
return;
|
||||
$this->logError($status, 'Status "'. $asset_statuslabel_name . '"');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,7 +328,7 @@ class ItemImporter extends Importer
|
|||
|
||||
if ($manufacturer) {
|
||||
$this->log('Manufacturer ' . $item_manufacturer . ' already exists') ;
|
||||
return $manufacturer;
|
||||
return $manufacturer->id;
|
||||
}
|
||||
|
||||
//Otherwise create a manufacturer.
|
||||
|
@ -337,14 +337,14 @@ class ItemImporter extends Importer
|
|||
$manufacturer->user_id = $this->user_id;
|
||||
|
||||
if ($this->testRun) {
|
||||
return $manufacturer;
|
||||
return $manufacturer->id;
|
||||
}
|
||||
if ($manufacturer->save()) {
|
||||
$this->log('Manufacturer ' . $manufacturer->name . ' was created');
|
||||
return $manufacturer;
|
||||
return $manufacturer->id;
|
||||
}
|
||||
$this->jsonError($manufacturer, 'Manufacturer "'. $manufacturer->name . '"');
|
||||
return;
|
||||
$this->logError($manufacturer, 'Manufacturer "'. $manufacturer->name . '"');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -363,9 +363,9 @@ class ItemImporter extends Importer
|
|||
}
|
||||
$location = Location::where(['name' => $asset_location])->first();
|
||||
|
||||
if ($location !== false) {
|
||||
if ($location) {
|
||||
$this->log('Location ' . $asset_location . ' already exists');
|
||||
return $location;
|
||||
return $location->id;
|
||||
}
|
||||
// No matching locations in the collection, create a new one.
|
||||
$location = new Location();
|
||||
|
@ -377,14 +377,14 @@ class ItemImporter extends Importer
|
|||
$location->user_id = $this->user_id;
|
||||
|
||||
if ($this->testRun) {
|
||||
return $location;
|
||||
return $location->id;
|
||||
}
|
||||
if ($location->save()) {
|
||||
$this->log('Location ' . $asset_location . ' was created');
|
||||
return $location;
|
||||
return $location->id;
|
||||
}
|
||||
$this->jsonError($location, 'Location');
|
||||
return;
|
||||
$this->logError($location, 'Location');
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -405,7 +405,7 @@ class ItemImporter extends Importer
|
|||
|
||||
if ($supplier) {
|
||||
$this->log('Supplier ' . $item_supplier . ' already exists');
|
||||
return $supplier;
|
||||
return $supplier->id;
|
||||
}
|
||||
|
||||
$supplier = new Supplier();
|
||||
|
@ -413,13 +413,13 @@ class ItemImporter extends Importer
|
|||
$supplier->user_id = $this->user_id;
|
||||
|
||||
if ($this->testRun) {
|
||||
return $supplier;
|
||||
return $supplier->id;
|
||||
}
|
||||
if ($supplier->save()) {
|
||||
$this->log('Supplier ' . $item_supplier . ' was created');
|
||||
return $supplier;
|
||||
return $supplier->id;
|
||||
}
|
||||
$this->jsonError($supplier, 'Supplier');
|
||||
return;
|
||||
$this->logError($supplier, 'Supplier');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,9 @@ class LicenseImporter extends ItemImporter
|
|||
public function createLicenseIfNotExists(array $row)
|
||||
{
|
||||
$editingLicense = false;
|
||||
$license = new License;
|
||||
$license_id = $this->licenses->search(function ($key) {
|
||||
return strcasecmp($key->name, $this->item['name']) == 0;
|
||||
});
|
||||
if ($license_id !== false) {
|
||||
$license = License::where('name', $this->item['name'])->first();
|
||||
|
||||
if ($license) {
|
||||
if (!$this->updating) {
|
||||
$this->log('A matching License ' . $this->item['name'] . ' already exists');
|
||||
return;
|
||||
|
@ -48,30 +46,26 @@ class LicenseImporter extends ItemImporter
|
|||
|
||||
$this->log("Updating License");
|
||||
$editingLicense = true;
|
||||
$license = $this->licenses[$license_id];
|
||||
} else {
|
||||
$this->log("No Matching License, Creating a new one");
|
||||
}
|
||||
|
||||
$asset_tag = $this->item['asset_tag'] = $this->array_smart_fetch($row, 'asset_tag'); // used for checkout out to an asset.
|
||||
$this->item['expiration_date'] = $this->array_smart_fetch($row, 'expiration date');
|
||||
$this->item['license_email'] = $this->array_smart_fetch($row, "licensed to email");
|
||||
$this->item['license_name'] = $this->array_smart_fetch($row, "licensed to name");
|
||||
$this->item['maintained'] = $this->array_smart_fetch($row, 'maintained');
|
||||
$this->item['purchase_order'] = $this->array_smart_fetch($row, 'purchase_order');
|
||||
$this->item['reassignable'] = $this->array_smart_fetch($row, 'reassignable');
|
||||
$this->item['serial'] = $this->array_smart_fetch($row, "serial number");
|
||||
$this->item['termination_date'] = $this->array_smart_fetch($row, 'termination_date');
|
||||
$this->item['seats'] = $this->array_smart_fetch($row, 'seats');
|
||||
$license = new License;
|
||||
$asset_tag = $this->item['asset_tag'] = $this->findCsvMatch($row, 'asset_tag'); // used for checkout out to an asset.
|
||||
$this->item['expiration_date'] = $this->findCsvMatch($row, 'expiration_date');
|
||||
$this->item['license_email'] = $this->findCsvMatch($row, "licensed_to_email");
|
||||
$this->item['license_name'] = $this->findCsvMatch($row, "licensed_to_name");
|
||||
$this->item['maintained'] = $this->findCsvMatch($row, 'maintained');
|
||||
$this->item['purchase_order'] = $this->findCsvMatch($row, 'purchase_order');
|
||||
$this->item['reassignable'] = $this->findCsvMatch($row, 'reassignable');
|
||||
$this->item['seats'] = $this->findCsvMatch($row, 'seats');
|
||||
$this->item['termination_date'] = $this->findCsvMatch($row, 'termination_date');
|
||||
|
||||
if ($editingLicense) {
|
||||
$license->update($this->sanitizeItemForUpdating($license));
|
||||
} else {
|
||||
$license->fill($this->sanitizeItemForStoring($license));
|
||||
}
|
||||
if (!$editingLicense) {
|
||||
$this->licenses->add($license);
|
||||
}
|
||||
|
||||
if (!$this->testRun) {
|
||||
if ($license->save()) {
|
||||
$license->logCreate('Imported using csv importer');
|
||||
|
@ -95,7 +89,7 @@ class LicenseImporter extends ItemImporter
|
|||
}
|
||||
return;
|
||||
}
|
||||
$this->jsonError($license, 'License "' . $this->item['name'].'"');
|
||||
$this->logError($license, 'License "' . $this->item['name'].'"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
73
app/Importer/UserImporter.php
Normal file
73
app/Importer/UserImporter.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\User;
|
||||
|
||||
class UserImporter extends ItemImporter
|
||||
{
|
||||
protected $users;
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
// $this->users = User::all();
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row);
|
||||
$this->createUserIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user if a duplicate does not exist.
|
||||
* @todo Investigate how this should interact with Importer::createOrFetchUser
|
||||
*
|
||||
* @author Daniel Melzter
|
||||
* @since 4.0
|
||||
*/
|
||||
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, 'user_email');
|
||||
$this->item['phone'] = $this->findCsvMatch($row, 'phone_number');
|
||||
$this->item['jobtitle'] = $this->findCsvMatch($row, 'jobtitle');
|
||||
$this->item['employee_num'] = $this->findCsvMatch($row, 'employee_num');
|
||||
$this->item['password'] = $this->tempPassword;
|
||||
$user = User::where('username', $this->item['username'])->first();
|
||||
if ($user) {
|
||||
if (!$this->updating) {
|
||||
$this->log('A matching User ' . $this->item["name"] . ' already exists. ');
|
||||
return;
|
||||
}
|
||||
$this->log('Updating User');
|
||||
// $user = $this->users[$userId];
|
||||
$user->update($this->sanitizeItemForUpdating($user));
|
||||
if (!$this->testRun) {
|
||||
$user->save();
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->log("No matching user, creating one");
|
||||
$user = new User();
|
||||
$user->fill($this->sanitizeItemForStoring($user));
|
||||
|
||||
if ($this->testRun) {
|
||||
$this->log('TEST RUN - User ' . $this->item['name'] . ' not created');
|
||||
return;
|
||||
}
|
||||
if ($user->save()) {
|
||||
// $user->logCreate('Imported using CSV Importer');
|
||||
$this->log("User " . $this->item["name"] . ' was created');
|
||||
$user = null;
|
||||
$this->item = null;
|
||||
return;
|
||||
}
|
||||
$this->logError($user, 'User');
|
||||
return;
|
||||
}
|
||||
}
|
33
app/Importer/import_mappings.md
Normal file
33
app/Importer/import_mappings.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
| CSV | Item | Applicable Types |
|
||||
|---------------------|------------------|-------------------------------------------|
|
||||
| asset tag | asset_tag | Asset |
|
||||
| category | category_id | All |
|
||||
| company | company_id | All |
|
||||
| item name | name | All |
|
||||
| image | image | asset |
|
||||
| expiration date | expiration_date | License |
|
||||
| location | location_id | All |
|
||||
| notes | notes | All |
|
||||
| licensed to email | license_email | License |
|
||||
| licensed to name | license_name | License |
|
||||
| maintained | maintained | License |
|
||||
| manufacturer | manufacturer_id | All |
|
||||
| model name | model_id | Asset |
|
||||
| model number | model_id | Asset |
|
||||
| order number | order_number | All ? |
|
||||
| purchase cost | purchase_cost | All ? |
|
||||
| purchase date | purchase_date | All ? |
|
||||
| purchase order | purchase_order | License |
|
||||
| quantity | qty | Accessory, Consumable, Component, License |
|
||||
| reassignable | reassignable | License |
|
||||
| requestable | requestable | Asset, Accessory? |
|
||||
| seats | seats | License |
|
||||
| serial number | serial | asset, license |
|
||||
| status | status_id | asset ? All |
|
||||
| supplier | supplier_id | Asset ? All |
|
||||
| termination date | termination_date | License |
|
||||
| warranty months | warranty_months | asset |
|
||||
| User Related Fields | assigned_to | Asset |
|
||||
| name | | |
|
||||
| email | | |
|
||||
| username | | |
|
|
@ -6,4 +6,9 @@ use Illuminate\Database\Eloquent\Model;
|
|||
|
||||
class Import extends Model
|
||||
{
|
||||
protected $casts = [
|
||||
'header_row' => 'array',
|
||||
'first_row' => 'array',
|
||||
'field_map' => 'json'
|
||||
];
|
||||
}
|
||||
|
|
|
@ -24,7 +24,19 @@ class User extends SnipeModel implements AuthenticatableContract, CanResetPasswo
|
|||
protected $hidden = ['password'];
|
||||
protected $table = 'users';
|
||||
protected $injectUniqueIdentifier = true;
|
||||
protected $fillable = ['first_name', 'last_name', 'email','password','username','department_id'];
|
||||
protected $fillable = [
|
||||
'email',
|
||||
'last_name',
|
||||
'company_id',
|
||||
'department_id',
|
||||
'employee_num',
|
||||
'jobtitle',
|
||||
'location_id',
|
||||
'password',
|
||||
'phone_number',
|
||||
'username',
|
||||
'first_name',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'activated' => 'boolean',
|
||||
|
|
|
@ -22,10 +22,8 @@ class AccessoryObserver
|
|||
$logAction->item_type = Accessory::class;
|
||||
$logAction->item_id = $accessory->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('update');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,13 +39,12 @@ class AccessoryObserver
|
|||
{
|
||||
$settings = Setting::first();
|
||||
$settings->increment('next_auto_tag_base');
|
||||
\Log::debug('Setting new next_auto_tag_base value');
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Accessory::class;
|
||||
$logAction->item_id = $accessory->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('create');
|
||||
|
||||
}
|
||||
|
@ -64,7 +61,7 @@ class AccessoryObserver
|
|||
$logAction->item_type = Accessory::class;
|
||||
$logAction->item_id = $accessory->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,12 @@ class AssetObserver
|
|||
*/
|
||||
public function updated(Asset $asset)
|
||||
{
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('update');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,13 +38,12 @@ class AssetObserver
|
|||
{
|
||||
$settings = Setting::first();
|
||||
$settings->increment('next_auto_tag_base');
|
||||
\Log::debug('Setting new next_auto_tag_base value');
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('create');
|
||||
|
||||
}
|
||||
|
@ -64,7 +60,7 @@ class AssetObserver
|
|||
$logAction->item_type = Asset::class;
|
||||
$logAction->item_id = $asset->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,8 @@ class ComponentObserver
|
|||
$logAction->item_type = Component::class;
|
||||
$logAction->item_id = $component->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('update');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,13 +39,12 @@ class ComponentObserver
|
|||
{
|
||||
$settings = Setting::first();
|
||||
$settings->increment('next_auto_tag_base');
|
||||
\Log::debug('Setting new next_auto_tag_base value');
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Component::class;
|
||||
$logAction->item_id = $component->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('create');
|
||||
|
||||
}
|
||||
|
@ -64,7 +61,7 @@ class ComponentObserver
|
|||
$logAction->item_type = Component::class;
|
||||
$logAction->item_id = $component->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,8 @@ class ConsumableObserver
|
|||
$logAction->item_type = Consumable::class;
|
||||
$logAction->item_id = $consumable->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('update');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,13 +39,12 @@ class ConsumableObserver
|
|||
{
|
||||
$settings = Setting::first();
|
||||
$settings->increment('next_auto_tag_base');
|
||||
\Log::debug('Setting new next_auto_tag_base value');
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = Consumable::class;
|
||||
$logAction->item_id = $consumable->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('create');
|
||||
|
||||
}
|
||||
|
@ -64,7 +61,7 @@ class ConsumableObserver
|
|||
$logAction->item_type = Consumable::class;
|
||||
$logAction->item_id = $consumable->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,8 @@ class LicenseObserver
|
|||
$logAction->item_type = License::class;
|
||||
$logAction->item_id = $license->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('update');
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,13 +39,12 @@ class LicenseObserver
|
|||
{
|
||||
$settings = Setting::first();
|
||||
$settings->increment('next_auto_tag_base');
|
||||
\Log::debug('Setting new next_auto_tag_base value');
|
||||
|
||||
$logAction = new Actionlog();
|
||||
$logAction->item_type = License::class;
|
||||
$logAction->item_id = $license->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('create');
|
||||
|
||||
}
|
||||
|
@ -64,7 +61,7 @@ class LicenseObserver
|
|||
$logAction->item_type = License::class;
|
||||
$logAction->item_id = $license->id;
|
||||
$logAction->created_at = date("Y-m-d H:i:s");
|
||||
$logAction->user_id = Auth::user()->id;
|
||||
$logAction->user_id = Auth::id();
|
||||
$logAction->logaction('delete');
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
18
build/mix.js
18
build/mix.js
|
@ -63,12 +63,19 @@
|
|||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 58);
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 61);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ 58:
|
||||
/***/ 6:
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
eval("// removed by extract-text-webpack-plugin//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNi5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc291cmNlcy9hc3NldHMvbGVzcy9vdmVycmlkZXMubGVzcz9mZjJhIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIHJlbW92ZWQgYnkgZXh0cmFjdC10ZXh0LXdlYnBhY2stcGx1Z2luXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9yZXNvdXJjZXMvYXNzZXRzL2xlc3Mvb3ZlcnJpZGVzLmxlc3Ncbi8vIG1vZHVsZSBpZCA9IDZcbi8vIG1vZHVsZSBjaHVua3MgPSAxIl0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZVJvb3QiOiIifQ==");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 61:
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
__webpack_require__(9);
|
||||
|
@ -77,13 +84,6 @@ __webpack_require__(8);
|
|||
module.exports = __webpack_require__(6);
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 6:
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
eval("// removed by extract-text-webpack-plugin//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiNi5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc291cmNlcy9hc3NldHMvbGVzcy9vdmVycmlkZXMubGVzcz9mZjJhIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIHJlbW92ZWQgYnkgZXh0cmFjdC10ZXh0LXdlYnBhY2stcGx1Z2luXG5cblxuLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyBXRUJQQUNLIEZPT1RFUlxuLy8gLi9yZXNvdXJjZXMvYXNzZXRzL2xlc3Mvb3ZlcnJpZGVzLmxlc3Ncbi8vIG1vZHVsZSBpZCA9IDZcbi8vIG1vZHVsZSBjaHVua3MgPSAxIl0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZVJvb3QiOiIifQ==");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 7:
|
||||
|
|
135
build/vue.js
135
build/vue.js
File diff suppressed because one or more lines are too long
|
@ -7,7 +7,7 @@
|
|||
"require": {
|
||||
"php": ">=5.6.4",
|
||||
"aws/aws-sdk-php-laravel": "^3.1",
|
||||
"barryvdh/laravel-debugbar": "^2.3",
|
||||
"barryvdh/laravel-debugbar": "^2.4",
|
||||
"doctrine/cache": "^1.6",
|
||||
"doctrine/common": "^2.7",
|
||||
"doctrine/dbal": "v2.4.2",
|
||||
|
|
|
@ -26,13 +26,11 @@ class AddNextAutoincrementToSettings extends Migration
|
|||
});
|
||||
|
||||
\Log::debug('Setting '.$next.' as default auto-increment');
|
||||
|
||||
|
||||
if ($settings = App\Models\Setting::first()) {
|
||||
$settings->next_auto_tag_base = $next;
|
||||
$settings->save();
|
||||
$settings->save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddHeaderAndFirstRowToImporterTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('imports', function (Blueprint $table) {
|
||||
// Add a json string representing the header row of the csv, and the first row of the csv.
|
||||
$table->text('header_row')->nullable()->default(null);
|
||||
$table->text('first_row')->nullable()->default(null);
|
||||
$table->text('field_map')->nullable()->default(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('imports', function (Blueprint $table) {
|
||||
//
|
||||
$table->dropColumn('header_row');
|
||||
$table->dropColumn('first_row');
|
||||
$table->dropColumn('field_map');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@
|
|||
"devDependencies": {
|
||||
"axios": "^0.15.3",
|
||||
"babel-preset-latest": "^6.24.1",
|
||||
"bootstrap-sass": "^3.3.7",
|
||||
"cross-env": "^3.2.4",
|
||||
"jquery": "^3.1.1",
|
||||
"laravel-mix": "0.12.1",
|
||||
|
@ -22,6 +21,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"blueimp-file-upload": "^9.18.0",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap-colorpicker": "^2.5.1",
|
||||
"bootstrap-datepicker": "^1.6.4",
|
||||
"bootstrap-less": "^3.3.8",
|
||||
|
@ -29,9 +29,9 @@
|
|||
"fastclick": "^1.0.6",
|
||||
"font-awesome": "^4.7.0",
|
||||
"jquery-ui": "^1.12.1",
|
||||
"papaparse": "^4.3.3",
|
||||
"select2": "^4.0.3",
|
||||
"tether": "^1.4.0",
|
||||
"vue-resource": "^1.3.3",
|
||||
"vue-strap": "github:wffranco/vue-strap"
|
||||
"vue-resource": "^1.3.3"
|
||||
}
|
||||
}
|
||||
|
|
BIN
public/css/dist/all.css
vendored
BIN
public/css/dist/all.css
vendored
Binary file not shown.
BIN
public/js/dist/all.js
vendored
BIN
public/js/dist/all.js
vendored
Binary file not shown.
1
resources/assets/js/bootstrap.js
vendored
1
resources/assets/js/bootstrap.js
vendored
|
@ -17,6 +17,7 @@ require('bootstrap-less');
|
|||
*/
|
||||
|
||||
window.Vue = require('vue');
|
||||
window.eventHub = new Vue();
|
||||
require('vue-resource');
|
||||
|
||||
/**
|
||||
|
|
211
resources/assets/js/components/importer/importer-file.vue
Normal file
211
resources/assets/js/components/importer/importer-file.vue
Normal file
|
@ -0,0 +1,211 @@
|
|||
<style>
|
||||
tr {
|
||||
padding-left:30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<tr v-show="processDetail">
|
||||
<td colspan="3">
|
||||
<h4 class="modal-title">Import File:</h4>
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<label for="import-type">Import Type:</label>
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<select2 :options="options.importTypes" v-model="options.importType">
|
||||
<option disabled value="0"></option>
|
||||
</select2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<label for="import-update">Update Existing Values?:</label>
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<input type="checkbox" name="import-update" v-model="options.update">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12" style="padding-top: 30px;">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Header Field</th>
|
||||
<th>Import Field</th>
|
||||
<th>Sample Value</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(header, index) in file.header_row">
|
||||
<tr>
|
||||
<td>
|
||||
<label :for="header" class="controllabel">{{ header }}</label>
|
||||
</td>
|
||||
<td>
|
||||
<div required>
|
||||
<select2 :options="columns" v-model="columnMappings[header]">
|
||||
<option value="0">Do Not Import</option>
|
||||
</select2>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>{{ activeFile.first_row[index] }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-default" @click="processDetail = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" @click="postSave">Import</button>
|
||||
<div class="alert alert-success col-md-5 col-md-offset-1" style="text-align:left" v-if="statusText">{{ this.statusText }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['file'],
|
||||
data() {
|
||||
return {
|
||||
activeFile: this.file,
|
||||
processDetail: false,
|
||||
statusText: null,
|
||||
options: {
|
||||
importType: this.file.import_type,
|
||||
update: false,
|
||||
importTypes: [
|
||||
{ id: 'asset', text: 'Assets' },
|
||||
{ id: 'accessory', text: 'Accessories' },
|
||||
{ id: 'consumable', text: 'Consumables' },
|
||||
{ id: 'component', text: 'Components' },
|
||||
{ id: 'license', text: 'Licenses' },
|
||||
{ id: 'user', text: 'Users' }
|
||||
],
|
||||
statusText: null,
|
||||
},
|
||||
columnOptions: {
|
||||
general: [
|
||||
{id: 'category', text: 'Category' },
|
||||
{id: 'company', text: 'Company' },
|
||||
{id: 'checkout_to', text: 'Checked out to' },
|
||||
{id: 'email', text: 'Email' },
|
||||
{id: 'first_name', text: 'First Name' },
|
||||
{id: 'item_name', text: 'Item Name' },
|
||||
{id: 'last_name', text: 'Last Name' },
|
||||
{id: 'location', text: 'Location' },
|
||||
{id: 'maintained', text: 'Maintained' },
|
||||
{id: 'manufacturer', text: 'Manufacturer' },
|
||||
{id: 'notes', text: 'Notes' },
|
||||
{id: 'order_number', text: 'Order Number' },
|
||||
{id: 'purchase_cost', text: 'Purchase Cost' },
|
||||
{id: 'purchase_date', text: 'Purchase Date' },
|
||||
{id: 'quantity', text: 'Quantity' },
|
||||
{id: 'requestable', text: 'Requestable' },
|
||||
{id: 'serial', text: 'Serial Number' },
|
||||
{id: 'supplier', text: 'Supplier' },
|
||||
{id: 'username', text: 'Username' },
|
||||
],
|
||||
assets: [
|
||||
{id: 'asset_tag', text: 'Asset Tag' },
|
||||
{id: 'asset_model', text: 'Model Name' },
|
||||
{id: 'image', text: 'Image Filename' },
|
||||
{id: 'model_number', text: 'Model Number' },
|
||||
{id: 'name', text: 'Full Name' },
|
||||
{id: 'status', text: 'Status' },
|
||||
{id: 'warranty_months', text: 'Warranty Months' },
|
||||
],
|
||||
licenses: [
|
||||
{id: 'expiration_date', text: 'Expiration Date' },
|
||||
{id: 'license_email', text: 'Licensed To Email' },
|
||||
{id: 'license_name', text: 'Licensed To Name' },
|
||||
{id: 'purchase_order', text: 'Purchase Order' },
|
||||
{id: 'reassignable', text: 'Reassignable' },
|
||||
{id: 'seats', text: 'Seats' },
|
||||
],
|
||||
users: [
|
||||
{id: 'employee_num', text: 'Employee Number' },
|
||||
{id: 'jobtitle', text: 'Job Title' },
|
||||
{id: 'phone_number', text: 'Phone Number' },
|
||||
],
|
||||
},
|
||||
columnMappings: this.file.field_map || {},
|
||||
activeColumn: null,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.eventHub.$on('showDetails', this.toggleExtendedDisplay)
|
||||
this.populateSelect2ActiveItems();
|
||||
},
|
||||
computed: {
|
||||
columns() {
|
||||
switch(this.options.importType) {
|
||||
case 'asset':
|
||||
return this.columnOptions.general.concat(this.columnOptions.assets);
|
||||
case 'license':
|
||||
return this.columnOptions.general.concat(this.columnOptions.licenses);
|
||||
case 'user':
|
||||
return this.columnOptions.general.concat(this.columnOptions.users);
|
||||
}
|
||||
return this.columnOptions.general;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
postSave() {
|
||||
this.statusText = "Processing...";
|
||||
this.$http.post('/api/v1/imports/process/'+this.file.id, {
|
||||
'import-update': this.options.update,
|
||||
'import-type': this.options.importType,
|
||||
'column-mappings': this.columnMappings
|
||||
}).then( (response) => {
|
||||
// Success
|
||||
this.statusText = "Success... Redirecting.";
|
||||
window.location.href = response.body.messages.redirect_url;
|
||||
}, (response) => {
|
||||
// Failure
|
||||
if(response.body.status == 'import-errors') {
|
||||
window.eventHub.$emit('importErrors', response.body.messages);
|
||||
this.statusText = "Error";
|
||||
} else {
|
||||
this.$emit('alert', {
|
||||
message: response.body.messages,
|
||||
type: "danger",
|
||||
visible: true,
|
||||
})
|
||||
}
|
||||
this.displayImportModal=false;
|
||||
});
|
||||
},
|
||||
populateSelect2ActiveItems() {
|
||||
if(this.file.field_map == null) {
|
||||
// Begin by populating the active selection in dropdowns with blank values.
|
||||
for (var i=0; i < this.file.header_row.length; i++) {
|
||||
this.$set(this.columnMappings, this.file.header_row[i], null);
|
||||
}
|
||||
// Then, for any values that have a likely match, we make that active.
|
||||
for(var j=0; j < this.columns.length; j++) {
|
||||
let column = this.columns[j];
|
||||
let index = this.file.header_row.indexOf(column.text)
|
||||
if(index != -1) {
|
||||
this.$set(this.columnMappings, this.file.header_row[index], column.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
toggleExtendedDisplay(fileId) {
|
||||
if(fileId == this.file.id) {
|
||||
this.processDetail = !this.processDetail
|
||||
}
|
||||
},
|
||||
updateModel(header, value) {
|
||||
console.log(header, value);
|
||||
this.columnMappings[header] = value;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
select2: require('../select2.vue')
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -7,95 +7,9 @@ th {
|
|||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="row">
|
||||
<alert v-show="alert.visible" :alertType="alert.type" v-on:hide="alert.visible = false">{{ alert.message }}</alert>
|
||||
<errors :errors="importErrors"></errors>
|
||||
<modal v-model="displayImportModal" effect="fade">
|
||||
<div slot="modal-header" class="modal-header">
|
||||
<h4 class="modal-title">Import File:</h4>
|
||||
</div>
|
||||
<div slot="modal-body" class="modal-body">
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<label for="import-type">Import Type:</label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xs-12">
|
||||
<select2 :options="modal.importTypes" v-model="modal.importType">
|
||||
<option disabled value="0"></option>
|
||||
</select2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dynamic-form-row">
|
||||
<div class="col-md-4 col-xs-12">
|
||||
<label for="import-update">Update Existing Values?:</label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xs-12">
|
||||
<input type="checkbox" name="import-update" v-model="modal.update">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer" slot="modal-footer">
|
||||
<div class="alert alert-success col-md-5 col-md-offset-1" style="text-align:left" v-if="modal.statusText">{{ this.modal.statusText }}</div>
|
||||
<button type="button" class="btn btn-default" @click="displayImportModal = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" @click="postSave">Process</button>
|
||||
</div>
|
||||
</modal>
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<!-- The fileinput-button span is used to style the file input field as button -->
|
||||
<span class="btn btn-info fileinput-button">
|
||||
<span>Select Import File...</span>
|
||||
<!-- The file input field used as target for the file upload widget -->
|
||||
<input id="fileupload" type="file" name="files[]" data-url="/api/v1/imports" accept="text/csv">
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-9" v-show="progress.visible" style="padding-bottom:20px">
|
||||
<div class="col-md-11">
|
||||
<div class="progress progress-striped-active" style="margin-top: 8px">
|
||||
<div class="progress-bar" :class="progress.currentClass" role="progressbar" :style="progressWidth">
|
||||
<span>{{ progress.statusText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="padding-top: 30px;">
|
||||
<table class="table table-striped" id="upload-table">
|
||||
<thead>
|
||||
<th>File</th>
|
||||
<th>Created</th>
|
||||
<th>Size</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="file in files">
|
||||
<td>{{ file.file_path }}</td>
|
||||
<td>{{ file.created_at }} </td>
|
||||
<td>{{ file.filesize }}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-info" @click="showModal(file)">Process</button>
|
||||
<button class="btn btn-danger" @click="deleteFile(file)"><i class="fa fa-trash icon-white"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
require('blueimp-file-upload');
|
||||
var modal = require('vue-strap').modal
|
||||
export default {
|
||||
/*
|
||||
* The component's data.
|
||||
|
@ -110,18 +24,6 @@ th {
|
|||
message: null,
|
||||
visible: false,
|
||||
},
|
||||
modal: {
|
||||
importType: 'asset',
|
||||
update: false,
|
||||
importTypes: [
|
||||
{ id: 'asset', text: 'Assets' },
|
||||
{ id: 'accessory', text: 'Accessories' },
|
||||
{ id: 'consumable', text: 'Consumable' },
|
||||
{ id: 'component', text: 'Components' },
|
||||
{ id: 'license', text: 'Licenses' }
|
||||
],
|
||||
statusText: null,
|
||||
},
|
||||
importErrors: null,
|
||||
progress: {
|
||||
currentClass: "progress-bar-warning",
|
||||
|
@ -136,6 +38,7 @@ th {
|
|||
* Prepare the component (Vue 2.x).
|
||||
*/
|
||||
mounted() {
|
||||
window.eventHub.$on('importErrors', this.updateImportErrors);
|
||||
this.fetchFiles();
|
||||
let vm = this;
|
||||
$('#fileupload').fileupload({
|
||||
|
@ -144,6 +47,7 @@ th {
|
|||
vm.progress.currentClass="progress-bar-success";
|
||||
vm.progress.statusText = "Success!";
|
||||
vm.files = data.result.files.concat(vm.files);
|
||||
console.log(data.result.header_row);
|
||||
},
|
||||
add(e, data) {
|
||||
data.headers = {
|
||||
|
@ -179,7 +83,7 @@ th {
|
|||
},
|
||||
deleteFile(file, key) {
|
||||
this.$http.delete("/api/v1/imports/"+file.id)
|
||||
.then((response) => this.files.splice(key, 1), // Success
|
||||
.then((response) => this.files.splice(key, 1), // Success, remove file from array.
|
||||
(response) => {// Fail
|
||||
this.alert.type="danger";
|
||||
this.alert.visible=true;
|
||||
|
@ -187,33 +91,15 @@ th {
|
|||
}
|
||||
);
|
||||
},
|
||||
showModal(file) {
|
||||
this.activeFile = file;
|
||||
this.displayImportModal = true;
|
||||
toggleEvent(fileId) {
|
||||
window.eventHub.$emit('showDetails', fileId)
|
||||
},
|
||||
|
||||
postSave() {
|
||||
this.modal.statusText = "Processing...";
|
||||
this.$http.post('/api/v1/imports/process/'+this.activeFile.id, {
|
||||
'import-update': this.modal.update,
|
||||
'import-type': this.modal.importType
|
||||
}).then( (response) => {
|
||||
// Success
|
||||
this.modal.statusText = "Success... Redirecting.";
|
||||
window.location.href = response.body.messages.redirect_url;
|
||||
}, (response) => {
|
||||
// Failure
|
||||
if(response.body.status == 'import-errors') {
|
||||
this.importErrors = response.body.messages;
|
||||
} else {
|
||||
this.alert.message= response.body.messages;
|
||||
this.alert.type="danger";
|
||||
this.alert.visible=true;
|
||||
}
|
||||
this.displayImportModal=false;
|
||||
});
|
||||
updateAlert(alert) {
|
||||
this.alert = alert;
|
||||
},
|
||||
updateImportErrors(errors) {
|
||||
this.importErrors = errors;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -223,10 +109,9 @@ th {
|
|||
},
|
||||
|
||||
components: {
|
||||
modal,
|
||||
errors: require('./importer-errors.vue'),
|
||||
alert: require('../alert.vue'),
|
||||
select2: require('../select2.vue')
|
||||
errors: require('./importer-errors.vue'),
|
||||
importFile: require('./importer-file.vue'),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
|
||||
<style scoped>
|
||||
.select2-dropdown {
|
||||
z-index:9999;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
|
@ -22,14 +25,18 @@
|
|||
.select2({
|
||||
data: this.options
|
||||
})
|
||||
.on('change', function() { vm.$emit('input', this.value) } );
|
||||
.on('change', function() { vm.$emit('input', this.value) } )
|
||||
.val(this.value).trigger('change');
|
||||
},
|
||||
watch: {
|
||||
value: function (value) {
|
||||
$(this.$el).val(value)
|
||||
},
|
||||
options: function (options) {
|
||||
$(this.$el).select2({data: options})
|
||||
var vm = this;
|
||||
$(this.$el).select2('destroy').empty().select2({data: options})
|
||||
.on('change', function() { vm.$emit('input', this.value) } )
|
||||
.val(this.value).trigger('change');
|
||||
},
|
||||
destroyed: function() {
|
||||
$(this.$el).off().select2('destroy')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
@icon-font-path: '../fonts';
|
||||
|
||||
@import '../../../node_modules/bootstrap/less/bootstrap';
|
||||
@import '../../../node_modules/bootstrap-less/bootstrap/bootstrap';
|
||||
@import '../../../node_modules/ekko-lightbox/ekko-lightbox';
|
||||
@import '../../../node_modules/bootstrap-colorpicker/src/less/colorpicker';
|
||||
|
||||
|
|
|
@ -518,22 +518,3 @@ Form::macro('customfield_elements', function ($name = "customfield_elements", $s
|
|||
return $select;
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
Form::macro('header_list', function ($headers = null, $name = "header_list", $selected = null, $class = null) {
|
||||
|
||||
|
||||
$select = '<select name="'.$name.'" class="'.$class.'" style="width: 100%">';
|
||||
$select .= '<option value="">Do Not Import</option>';
|
||||
|
||||
foreach ($headers as $header => $label) {
|
||||
$select .= '<option value="'.str_slug($label).'"'.($selected == str_slug($label) ? ' selected="selected"' : '').'>'.e($label).'</option> '."\n";
|
||||
}
|
||||
|
||||
$select .= '</select>';
|
||||
|
||||
return $select;
|
||||
|
||||
});
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
@extends('layouts/default')
|
||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/lib/jquery.fileupload.css') }}">
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
{{ trans('general.import') }}
|
||||
@parent
|
||||
@stop
|
||||
|
||||
{{-- Page content --}}
|
||||
@section('content')
|
||||
<div id="app">
|
||||
<importer>
|
||||
</div>
|
||||
@stop
|
||||
|
||||
@section('moar_scripts')
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
@endsection
|
|
@ -1,233 +0,0 @@
|
|||
@extends('layouts.default')
|
||||
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
Map Import Fields
|
||||
@parent
|
||||
@stop
|
||||
|
||||
@section('header_right')
|
||||
<a href="{{ URL::previous() }}" class="btn btn-primary pull-right">
|
||||
{{ trans('general.back') }}</a>
|
||||
@stop
|
||||
|
||||
|
||||
|
||||
{{-- Page content --}}
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
|
||||
</h3>
|
||||
@if (isset($helpText))
|
||||
<div class="box-tools pull-right">
|
||||
<button class="slideout-menu-toggle btn btn-box-tool btn-box-tool-lg" data-toggle="tooltip" title="Help"><i class="fa fa-question"></i></button>
|
||||
</div>
|
||||
@endif
|
||||
</div><!-- /.box-header -->
|
||||
|
||||
<div class="box-body">
|
||||
<form id="create-form" class="form-horizontal" method="post" action="{{ (isset($formAction)) ? $formAction : \Request::url() }}" autocomplete="off" role="form" enctype="multipart/form-data">
|
||||
|
||||
<!-- CSRF Token -->
|
||||
{{ csrf_field() }}
|
||||
|
||||
<pre>
|
||||
@php
|
||||
print_r($first_row);
|
||||
@endphp
|
||||
</pre>
|
||||
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<h3>Standard Fields</h3>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Check out to User (First Last)
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'user_name_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Username
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'username_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Email
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'email_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Item Name
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'item_name_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Asset Tag
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'asset_tag_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Serial Number
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'serial_number_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Model name
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'model_name_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Model Number
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'model_number_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Category
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'category_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Manufacturer
|
||||
</label>
|
||||
<div class="col-md-4 required">
|
||||
{!! Form::header_list($header_rows, 'manufacturer_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Company
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'company_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Location
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'location_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Purchase Date
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'purchase_date_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Purchase Cost
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'purchase_cost_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Status
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'status_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Notes
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'notes_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">Image Filename
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'image_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($custom_fields->count() > 0)
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<h3>Custom Fields</h3>
|
||||
<hr>
|
||||
</div>
|
||||
@foreach ($custom_fields as $custom_field)
|
||||
<div class="form-group">
|
||||
<label for="url" class="col-md-3 control-label">{{ $custom_field->name }}
|
||||
</label>
|
||||
<div class="col-md-4">
|
||||
{!! Form::header_list($header_rows, 'image_header', Input::old('header_row'), 'select2') !!}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
<div class="box-footer text-right">
|
||||
<a class="btn btn-link" href="{{ URL::previous() }}">{{ trans('button.cancel') }}</a>
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-check icon-white"></i> {{ trans('general.process') }}</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ((isset($helpText)) && (isset($helpTitle)))
|
||||
<div class="slideout-menu">
|
||||
<a href="#" class="slideout-menu-toggle pull-right">×</a>
|
||||
<h3>
|
||||
{{ $helpTitle}}
|
||||
</h3>
|
||||
<p>{{ $helpText }} </p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@stop
|
||||
|
86
resources/views/importer/import.blade.php
Normal file
86
resources/views/importer/import.blade.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
@extends('layouts/default')
|
||||
<link rel="stylesheet" type="text/css" href="{{ asset('css/lib/jquery.fileupload.css') }}">
|
||||
|
||||
{{-- Hide importer until vue has rendered it, if we continue using vue for other things we should move this higher in the style --}}
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display:none;
|
||||
}
|
||||
</style>
|
||||
{{-- Page title --}}
|
||||
@section('title')
|
||||
{{ trans('general.import') }}
|
||||
@parent
|
||||
@stop
|
||||
|
||||
{{-- Page content --}}
|
||||
@section('content')
|
||||
<div id="app">
|
||||
<importer inline-template v-cloak>
|
||||
<div class="row">
|
||||
<alert v-show="alert.visible" :alert-type="alert.type" v-on:hide="alert.visible = false">@{{ alert.message }}</alert>
|
||||
<errors :errors="importErrors"></errors>
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<!-- The fileinput-button span is used to style the file input field as button -->
|
||||
<span class="btn btn-info fileinput-button">
|
||||
<span>Select Import File...</span>
|
||||
<!-- The file input field used as target for the file upload widget -->
|
||||
<input id="fileupload" type="file" name="files[]" data-url="/api/v1/imports" accept="text/csv">
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-9" v-show="progress.visible" style="padding-bottom:20px">
|
||||
<div class="col-md-11">
|
||||
<div class="progress progress-striped-active" style="margin-top: 8px">
|
||||
<div class="progress-bar" :class="progress.currentClass" role="progressbar" :style="progressWidth">
|
||||
<span>@{{ progress.statusText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="padding-top: 30px;">
|
||||
<table class="table table-striped" id="upload-table">
|
||||
<thead>
|
||||
<th>File</th>
|
||||
<th>Created</th>
|
||||
<th>Size</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="currentFile in files">
|
||||
<tr>
|
||||
<td>@{{ currentFile.file_path }}</td>
|
||||
<td>@{{ currentFile.created_at }} </td>
|
||||
<td>@{{ currentFile.filesize }}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-info" @click="toggleEvent(currentFile.id)">Process</button>
|
||||
<button class="btn btn-danger" @click="deleteFile(currentFile)"><i class="fa fa-trash icon-white"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<import-file :key="currentFile.id" :file="currentFile" @alert="updateAlert(alert)">
|
||||
</import-file>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</importer>
|
||||
</div>
|
||||
@stop
|
||||
|
||||
@section('moar_scripts')
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app'
|
||||
});
|
||||
</script>
|
||||
@endsection
|
|
@ -473,8 +473,8 @@
|
|||
</li>
|
||||
@endcan
|
||||
@can('create', \App\Models\Asset::class)
|
||||
<li{!! (Request::is('hardware/import*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ url('hardware/import') }}">
|
||||
<li{!! (Request::is('import/*') ? ' class="active"' : '') !!}>
|
||||
<a href="{{ route('imports.index') }}">
|
||||
<i class="fa fa-cloud-download"></i>
|
||||
<span>@lang('general.import')</span>
|
||||
</a>
|
||||
|
|
|
@ -171,7 +171,20 @@ Route::resource('groups', 'GroupsController', [
|
|||
'parameters' => ['group' => 'group_id']
|
||||
]);
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Importer Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
|
|
||||
|
|
||||
*/
|
||||
Route::group([ 'prefix' => 'import', 'middleware' => ['auth']], function () {
|
||||
Route::get('/', [
|
||||
'as' => 'imports.index',
|
||||
'uses' => 'ImportsController@index'
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -79,23 +79,6 @@ Route::group(
|
|||
'uses' => 'AssetsController@displayFile'
|
||||
]);
|
||||
|
||||
Route::post( 'import/process/', [ 'as' => 'assets/import/process-file',
|
||||
'uses' => 'AssetsController@postProcessImportFile'
|
||||
]);
|
||||
|
||||
Route::get( 'import/delete/{filename}', [ 'as' => 'assets/import/delete-file',
|
||||
'uses' => 'AssetsController@getDeleteImportFile'
|
||||
]);
|
||||
|
||||
Route::get('import/map',[
|
||||
'as' => 'import.map',
|
||||
'uses' => 'AssetsController@getImportMap'
|
||||
]);
|
||||
|
||||
Route::get('import',[
|
||||
'as' => 'assets/import',
|
||||
'uses' => 'AssetsController@getImportUpload'
|
||||
]);
|
||||
|
||||
Route::post(
|
||||
'bulkedit',
|
||||
|
|
1001
sample_csvs/MOCK_USERS.csv
Normal file
1001
sample_csvs/MOCK_USERS.csv
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue