diff --git a/app/Console/Commands/ObjectImportCommand.php b/app/Console/Commands/ObjectImportCommand.php index 05198c418df4..d8cea51f9b2e 100644 --- a/app/Console/Commands/ObjectImportCommand.php +++ b/app/Console/Commands/ObjectImportCommand.php @@ -1,25 +1,26 @@ companies = Company::All(['name', 'id']); $this->status_labels = Statuslabel::All(['name', 'id']); $this->suppliers = Supplier::All(['name', 'id']); - $this->assets = Asset::all(['asset_tag']); - $this->accessories = Accessory::All(['name']); - $this->consumables = Consumable::All(['name']); + switch (strtolower($this->option('item-type'))) { + case "asset": + $this->assets = Asset::all(); + break; + case "accessory": + $this->accessories = Accessory::All(); + break; + case "consumable": + $this->consumables = Consumable::All(); + break; + } $this->customfields = CustomField::All(['name']); $bar = null; @@ -732,6 +741,24 @@ public function createOrFetchUser($row, $password = null) */ public function createAssetIfNotExists(array $row, array $item) { + $asset = null; + $editingAsset = false; + foreach ($this->assets as $tempasset) { + if (strcasecmp($tempasset->asset_tag, $item['asset_tag']) == 0) { + $this->log('A matching Asset ' . $item['asset_tag'] . ' already exists'); + if (!$this->option('update')) { + $this->log("Skipping item."); + return; + } + $this->log('Updating matching asset with new values'); + $editingAsset = true; + $asset = $tempasset; + } + } + if (is_null($asset)) { + $this->log("No Matching Asset, Creating a new one"); + $asset = new Asset; + } $asset_serial = $this->array_smart_fetch($row, "serial number"); $asset_image = $this->array_smart_fetch($row, "image"); $asset_warranty_months = intval($this->array_smart_fetch($row, "warranty months")); @@ -747,12 +774,7 @@ public function createAssetIfNotExists(array $row, array $item) $this->log('Notes: '.$item["notes"]); $this->log('Warranty Months: ' . $asset_warranty_months); - foreach ($this->assets as $tempasset) { - if (strcasecmp($tempasset->asset_tag, $item['asset_tag']) == 0) { - $this->log('A matching Asset ' . $item['asset_tag'] . ' already exists'); - return; - } - } + if ($item["status_label"]) { $status_id = $item["status_label"]->id; @@ -763,12 +785,14 @@ public function createAssetIfNotExists(array $row, array $item) $status_id = 1; } - $asset = new Asset(); - $asset->name = $item["item_name"]; - if ($item["purchase_date"] != '') { + if (!$editingAsset) { + $asset->asset_tag = $item['asset_tag']; // This doesn't need to be guarded for empty because it's the key we use to identify the asset. + } + if (!empty($item['item_name'])) { + $asset->name = $item["item_name"]; + } + if (!empty($item["purchase_date"])) { $asset->purchase_date = $item["purchase_date"]; - } else { - $asset->purchase_date = null; } if (array_key_exists('custom_fields', $item)) { @@ -780,37 +804,53 @@ public function createAssetIfNotExists(array $row, array $item) if (!empty($item["purchase_cost"])) { //TODO How to generalize this for not USD? $purchase_cost = substr($item["purchase_cost"], 0, 1) === '$' ? substr($item["purchase_cost"], 1) : $item["purchase_cost"]; - $asset->purchase_cost = number_format($purchase_cost, 2, '.', ''); + // $asset->purchase_cost = number_format($purchase_cost, 2, '.', ''); + $asset->purchase_cost = Helper::ParseFloat($purchase_cost); $this->log("Asset cost parsed: " . $asset->purchase_cost); } else { $asset->purchase_cost = 0.00; } - $asset->serial = $asset_serial; - $asset->asset_tag = $item['asset_tag']; - $asset->warranty_months = $asset_warranty_months; + if (!empty($asset_serial)) { + $asset->serial = $asset_serial; + } + if (!empty($asset_warranty_months)) { + $asset->warranty_months = $asset_warranty_months; + } if ($asset_model) { $asset->model_id = $asset_model->id; } + if ($item["user"]) { $asset->assigned_to = $item["user"]->id; } + if ($item["location"]) { $asset->rtd_location_id = $item["location"]->id; } + $asset->user_id = $this->option('user_id'); - $this->log("status_id: " . $status_id); - $asset->status_id = $status_id; + if (!empty($status_id)) { + $asset->status_id = $status_id; + } if ($item["company"]) { $asset->company_id = $item["company"]->id; } - $asset->order_number = $item["order_number"]; + if ($item["order_number"]) { + $asset->order_number = $item["order_number"]; + } if ($supplier) { $asset->supplier_id = $supplier->id; } - $asset->notes = $item["notes"]; - $asset->image = $asset_image; - $this->assets->add($asset); + if ($item["notes"]) { + $asset->notes = $item["notes"]; + } + if (!empty($asset_image)) { + $asset->image = $asset_image; + } + if (!$editingAsset) { + $this->assets->add($asset); + } if (!$this->option('testrun')) { if ($asset->save()) { @@ -835,17 +875,29 @@ public function createAssetIfNotExists(array $row, array $item) */ public function createAccessoryIfNotExists(array $item) { + $accessory = null; + $editingAccessory = false; $this->log("Creating Accessory"); foreach ($this->accessories as $tempaccessory) { if (strcasecmp($tempaccessory->name, $item["item_name"]) == 0) { $this->log('A matching Accessory ' . $item["item_name"] . ' already exists. '); - // FUTURE: Adjust quantity on import maybe? - return; + if (!$this->option('update')) { + $this->log("Skipping accessory."); + return; + } + $this->log('Updating matching accessory with new values'); + $editingAccessory = true; + $accessory = $tempaccessory; } } + if (is_null($accessory)) { + $this->log("No Matching Accessory, Creating a new one"); + $accessory = new Accessory(); + } - $accessory = new Accessory(); - $accessory->name = $item["item_name"]; + if (!$editingAccessory) { + $accessory->name = $item["item_name"]; + } if (!empty($item["purchase_date"])) { $accessory->purchase_date = $item["purchase_date"]; @@ -853,10 +905,9 @@ public function createAccessoryIfNotExists(array $item) $accessory->purchase_date = null; } if (!empty($item["purchase_cost"])) { - $accessory->purchase_cost = number_format(e($item["purchase_cost"]), 2); - } else { - $accessory->purchase_cost = 0.00; + $accessory->purchase_cost = Helper::ParseFloat($item["purchase_cost"]); } + if ($item["location"]) { $accessory->location_id = $item["location"]->id; } @@ -864,20 +915,26 @@ public function createAccessoryIfNotExists(array $item) if ($item["company"]) { $accessory->company_id = $item["company"]->id; } - $accessory->order_number = $item["order_number"]; + if (!empty($item["order_number"])) { + $accessory->order_number = $item["order_number"]; + } if ($item["category"]) { $accessory->category_id = $item["category"]->id; } //TODO: Implement // $accessory->notes = e($item_notes); - $accessory->requestable = filter_var($item["requestable"], FILTER_VALIDATE_BOOLEAN); + if (!empty($item["requestable"])) { + $accessory->requestable = filter_var($item["requestable"], FILTER_VALIDATE_BOOLEAN); + } //Must have at least zero of the item if we import it. - if ($item["quantity"] > -1) { - $accessory->qty = $item["quantity"]; - } else { - $accessory->qty = 1; + if (!empty($item["quantity"])) { + if ($item["quantity"] > -1) { + $accessory->qty = $item["quantity"]; + } else { + $accessory->qty = 1; + } } if (!$this->option('testrun')) { @@ -904,18 +961,29 @@ public function createAccessoryIfNotExists(array $item) */ public function createConsumableIfNotExists(array $item) { + $consumable = null; + $editingConsumable = false; $this->log("Creating Consumable"); foreach ($this->consumables as $tempconsumable) { if (strcasecmp($tempconsumable->name, $item["item_name"]) == 0) { $this->log("A matching consumable " . $item["item_name"] . " already exists"); - //TODO: Adjust quantity if different maybe? - return; + if (!$this->option('update')) { + $this->log("Skipping consumable."); + return; + } + $this->log('Updating matching consumable with new values'); + $editingConsumable = true; + $consumable = $tempconsumable; } } - $consumable = new Consumable(); - $consumable->name = $item["item_name"]; - + if (is_null($consumable)) { + $this->log("No matching consumable, creating one"); + $consumable = new Consumable(); + } + if (!$editingConsumable) { + $consumable->name = $item["item_name"]; + } if (!empty($item["purchase_date"])) { $consumable->purchase_date = $item["purchase_date"]; } else { @@ -923,26 +991,37 @@ public function createConsumableIfNotExists(array $item) } if (!empty($item["purchase_cost"])) { - $consumable->purchase_cost = number_format(e($item["purchase_cost"]), 2); - } else { - $consumable->purchase_cost = 0.00; + $consumable->purchase_cost = Helper::ParseFloat($item["purchase_cost"]); + } + if ($item["location"]) { + $consumable->location_id = $item["location"]->id; } - $consumable->location_id = $item["location"]->id; $consumable->user_id = $this->option('user_id'); - $consumable->company_id = $item["company"]->id; - $consumable->order_number = $item["order_number"]; - $consumable->category_id = $item["category"]->id; + if ($item["company"]) { + $consumable->company_id = $item["company"]->id; + } + if (!empty($item["order_number"])) { + $consumable->order_number = $item["order_number"]; + } + if ($item["category"]) { + $consumable->category_id = $item["category"]->id; + } // TODO:Implement //$consumable->notes= e($item_notes); - $consumable->requestable = filter_var($item["requestable"], FILTER_VALIDATE_BOOLEAN); + if (!empty($item["requestable"])) { + $consumable->requestable = filter_var($item["requestable"], FILTER_VALIDATE_BOOLEAN); + } - if ($item["quantity"] > -1) { - $consumable->qty = $item["quantity"]; - } else { - $consumable->qty = 1; + if (!empty($item["quantity"])) { + if ($item["quantity"] > -1) { + $consumable->qty = $item["quantity"]; + } else { + $consumable->qty = 1; + } } if (!$this->option("testrun")) { + // dd($consumable); if ($consumable->save()) { $this->log("Consumable " . $item["item_name"] . ' was created'); // $this->comment("Consumable " . $item["item_name"] . ' was created'); @@ -985,8 +1064,9 @@ protected function getOptions() 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('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('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'), ); } diff --git a/app/Http/Controllers/AssetsController.php b/app/Http/Controllers/AssetsController.php index ddd81d1c6cf7..51d81ec64c30 100755 --- a/app/Http/Controllers/AssetsController.php +++ b/app/Http/Controllers/AssetsController.php @@ -927,6 +927,18 @@ public function postAPIImportUpload(AssetFileRequest $request) } + public function getDeleteImportFile($filename) + { + if (!Company::isCurrentUserAuthorized()) { + return redirect()->to('hardware')->with('error', trans('general.insufficient_permissions')); + } + + if (unlink(config('app.private_uploads').'/imports/assets/'.$filename)) { + return redirect()->back()->with('success', trans('admin/hardware/message.import.file_delete_success')); + } + return redirect()->back()->with('error', trans('admin/hardware/message.import.file_delete_error')); + } + /** * Process the uploaded file @@ -936,28 +948,47 @@ public function postAPIImportUpload(AssetFileRequest $request) * @since [v2.0] * @return Redirect */ - public function getProcessImportFile($filename) + public function postProcessImportFile() { // php artisan asset-import:csv path/to/your/file.csv --domain=yourdomain.com --email_format=firstname.lastname + $filename = Input::get('filename'); + $itemType = Input::get('import-type'); + $updateItems = Input::get('import-update'); if (!Company::isCurrentUserAuthorized()) { return redirect()->to('hardware')->with('error', trans('general.insufficient_permissions')); } - - $return = Artisan::call( - 'snipeit:import', - ['filename'=> config('app.private_uploads').'/imports/assets/'.$filename, + $importOptions = ['filename'=> config('app.private_uploads').'/imports/assets/'.$filename, '--email_format'=>'firstname.lastname', '--username_format'=>'firstname.lastname', '--web-importer' => true, - '--user_id' => Auth::user()->id - ] - ); + '--user_id' => Auth::user()->id, + '--item-type' => $itemType, + ]; + if ($updateItems) { + $importOptions['--update'] = true; + } + + $return = Artisan::call('snipeit:import', $importOptions); $display_output = Artisan::output(); $file = config('app.private_uploads').'/imports/assets/'.str_replace('.csv', '', $filename).'-output-'.date("Y-m-d-his").'.txt'; file_put_contents($file, $display_output); + // We use hardware instead of asset in the url + $redirectTo = "hardware"; + switch($itemType) { + case "asset": + $redirectTo = "hardware"; + break; + case "accessory": + $redirectTo = "accessories"; + break; + case "consumable": + $redirectTo = "consumables"; + break; + } + if ($return === 0) { //Success - return redirect()->to('hardware')->with('success', trans('admin/hardware/message.import.success')); + return redirect()->to(route($redirectTo))->with('success', trans('admin/hardware/message.import.success')); } elseif ($return === 1) { // Failure return redirect()->back()->with('import_errors', json_decode($display_output))->with('error', trans('admin/hardware/message.import.error')); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 25feecf9cfde..f23c09872323 100755 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -293,9 +293,13 @@ function () { 'uses' => 'AssetsController@getDeleteImportFile' ]); - Route::get( 'import/process/{filename}', [ 'as' => 'assets/import/process-file', + Route::post( 'import/process/', [ 'as' => 'assets/import/process-file', 'middleware' => 'authorize:assets.create', - 'uses' => 'AssetsController@getProcessImportFile' + 'uses' => 'AssetsController@postProcessImportFile' + ]); + Route::get( 'import/delete/{filename}', [ 'as' => 'assets/import/delete-file', + 'middleware' => 'authorize:assets.create', // TODO What permissions should this require? + 'uses' => 'AssetsController@getDeleteImportFile' ]); Route::get('import',[ diff --git a/resources/lang/en/admin/hardware/message.php b/resources/lang/en/admin/hardware/message.php index 26a5eec28f5c..8a170db8bff5 100644 --- a/resources/lang/en/admin/hardware/message.php +++ b/resources/lang/en/admin/hardware/message.php @@ -37,9 +37,11 @@ ), 'import' => array( - 'error' => 'Some items did not import correctly.', - 'errorDetail' => 'The following Items were not imported because of errors.', - 'success' => "Your file has been imported", + 'error' => 'Some items did not import correctly.', + 'errorDetail' => 'The following Items were not imported because of errors.', + 'success' => "Your file has been imported", + 'file_delete_success' => "Your file has been been successfully deleted", + 'file_delete_error' => "The file was unable to be deleted", ), diff --git a/resources/views/hardware/import.blade.php b/resources/views/hardware/import.blade.php index cb8ff0f71403..c243eb86ecb3 100644 --- a/resources/views/hardware/import.blade.php +++ b/resources/views/hardware/import.blade.php @@ -9,6 +9,49 @@ {{-- Page content --}} @section('content') + +{{-- Modal import dialog --}} + + + +
@@ -40,8 +83,6 @@
- - @@ -56,8 +97,8 @@ @endforeach @@ -66,8 +107,8 @@ - + @if (session()->has('import_errors'))
@@ -149,8 +190,7 @@ $('.progress-bar-text').html('Finished!'); $('.progress-checkmark').fadeIn('fast').html(''); $.each(data.result.files, function (index, file) { - $('
').prependTo("#upload-table > tbody"); - //$('').prependTo("#upload-table > tbody"); }); } $('#progress').removeClass('active'); @@ -159,6 +199,14 @@ } }); }); + + // Modal Import options handling + $('#importModal').on("show.bs.modal", function(event) { + var link = $(event.relatedTarget); + var filename = link.data('filename'); + $(this).find('.modal-title').text("Import File: " + filename ); + $("#modal-filename").val(filename); + }); @stop
File{{ date("M d, Y g:i A", $file['modified']) }} {{ $file['filesize'] }} - - Process + Process +
' + file.name + 'Just now' + file.filesize + ' Process
').text(file.name).appendTo(document.body); + $('
' + file.name + 'Just now' + file.filesize + ' Process