Importer2 checkout (#5771)
* Importer: checkout to location, backend changes+tests. * Import location checkout. Frontend changes. * Allow importing of item number/model number for consumables.
This commit is contained in:
parent
311f9fcefb
commit
880faa83a6
15 changed files with 183 additions and 91 deletions
|
@ -81,8 +81,8 @@ class AssetImporter extends ItemImporter
|
|||
|
||||
// We need to save the user if it exists so that we can checkout to user later.
|
||||
// Sanitizing the item will remove it.
|
||||
if(array_key_exists('user', $this->item)) {
|
||||
$user = $this->item['user'];
|
||||
if(array_key_exists('checkout_target', $this->item)) {
|
||||
$target = $this->item['checkout_target'];
|
||||
}
|
||||
$item = $this->sanitizeItemForStoring($asset, $editingAsset);
|
||||
// The location id fetched by the csv reader is actually the rtd_location_id.
|
||||
|
@ -112,9 +112,9 @@ class AssetImporter extends ItemImporter
|
|||
$asset->logCreate('Imported using csv importer');
|
||||
$this->log('Asset ' . $this->item["name"] . ' with serial number ' . $this->item['serial'] . ' was created');
|
||||
|
||||
// If we have a user to checkout to, lets do so.
|
||||
if(isset($user)) {
|
||||
$asset->fresh()->checkOut($user);
|
||||
// If we have a target to checkout to, lets do so.
|
||||
if(isset($target)) {
|
||||
$asset->fresh()->checkOut($target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -14,17 +14,18 @@ class ConsumableImporter extends ItemImporter
|
|||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row); // TODO: Change the autogenerated stub
|
||||
$this->createConsumableIfNotExists();
|
||||
parent::handle($row);
|
||||
$this->createConsumableIfNotExists($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a consumable if a duplicate does not exist
|
||||
*
|
||||
* @author Daniel Melzter
|
||||
* @param array $row CSV Row Being parsed.
|
||||
* @since 3.0
|
||||
*/
|
||||
public function createConsumableIfNotExists()
|
||||
public function createConsumableIfNotExists($row)
|
||||
{
|
||||
$consumable = Consumable::where('name', $this->item['name'])->first();
|
||||
if ($consumable) {
|
||||
|
@ -39,6 +40,8 @@ class ConsumableImporter extends ItemImporter
|
|||
}
|
||||
$this->log("No matching consumable, creating one");
|
||||
$consumable = new Consumable();
|
||||
$this->item['model_number'] = $this->findCsvMatch($row, "model_number");;
|
||||
$this->item['item_no'] = $this->findCsvMatch($row, "item_number");
|
||||
$consumable->fill($this->sanitizeItemForStoring($consumable));
|
||||
//FIXME: this disables model validation. Need to find a way to avoid double-logs without breaking everything.
|
||||
$consumable->unsetEventDispatcher();
|
||||
|
|
|
@ -30,8 +30,11 @@ abstract class Importer
|
|||
private $defaultFieldMap = [
|
||||
'asset_tag' => 'asset tag',
|
||||
'category' => 'category',
|
||||
'checkout_class' => 'checkout type', // Supports Location or User for assets. Using checkout_class instead of checkout_type because type exists on asset already.
|
||||
'checkout_location' => 'checkout location',
|
||||
'company' => 'company',
|
||||
'item_name' => 'item name',
|
||||
'item_number' => "item number",
|
||||
'image' => 'image',
|
||||
'expiration_date' => 'expiration date',
|
||||
'location' => 'location',
|
||||
|
@ -89,11 +92,12 @@ abstract class Importer
|
|||
public function __construct($file)
|
||||
{
|
||||
$this->fieldMap = $this->defaultFieldMap;
|
||||
// By default the importer passes a url to the file.
|
||||
// However, for testing we also support passing a string directly
|
||||
if (! ini_get("auto_detect_line_endings")) {
|
||||
ini_set("auto_detect_line_endings", '1');
|
||||
}
|
||||
|
||||
// By default the importer passes a url to the file.
|
||||
// However, for testing we also support passing a string directly
|
||||
if (is_file($file)) {
|
||||
$this->csv = Reader::createFromPath($file);
|
||||
} else {
|
||||
|
@ -179,9 +183,8 @@ abstract class Importer
|
|||
*/
|
||||
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]);
|
||||
// $this->log("Found a match in our custom map: {$key} is " . $this->fieldMap[$key]);
|
||||
return $this->fieldMap[$key];
|
||||
}
|
||||
// Otherwise no custom key, return original.
|
||||
|
@ -189,6 +192,8 @@ abstract class Importer
|
|||
}
|
||||
|
||||
/**
|
||||
* Used to lowercase header values to ensure we're comparing values properly.
|
||||
*
|
||||
* @param $results
|
||||
* @return array
|
||||
*/
|
||||
|
@ -249,11 +254,11 @@ abstract class Importer
|
|||
$last_name = '';
|
||||
if(empty($user_name) && empty($user_email) && empty($user_username)) {
|
||||
$this->log('No user data provided - skipping user creation, just adding asset');
|
||||
//$user_username = '';
|
||||
return false;
|
||||
}
|
||||
// A username was given.
|
||||
|
||||
if( !empty($user_username)) {
|
||||
// A username was given.
|
||||
$user = User::where('username', $user_username)->first();
|
||||
if($user) {
|
||||
return $user;
|
||||
|
|
|
@ -69,11 +69,36 @@ class ItemImporter extends Importer
|
|||
$this->item['serial'] = $this->findCsvMatch($row, "serial");
|
||||
// NO need to call this method if we're running the user import.
|
||||
// TODO: Merge these methods.
|
||||
$this->item['checkout_class'] = $this->findCsvMatch($row, "checkout_class");
|
||||
if(get_class($this) !== UserImporter::class) {
|
||||
$this->item["user"] = $this->createOrFetchUser($row);
|
||||
// $this->item["user"] = $this->createOrFetchUser($row);
|
||||
$this->item["checkout_target"] = $this->determineCheckout($row);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse row to determine what (if anything) we should checkout to.
|
||||
* @param array $row CSV Row being parsed
|
||||
* @return SnipeModel Model to be checked out to
|
||||
*/
|
||||
protected function determineCheckout($row)
|
||||
{
|
||||
// We only supporty checkout-to-location for asset, so short circuit otherw.
|
||||
if(get_class($this) != AssetImporter::class) {
|
||||
return $this->createOrFetchUser($row);
|
||||
}
|
||||
|
||||
if ($this->item['checkout_class'] === 'location') {
|
||||
// dd($this->findCsvMatch($row, 'checkout_location'));
|
||||
return Location::findOrFail($this->createOrFetchLocation($this->findCsvMatch($row, 'checkout_location')));
|
||||
// dd('here');
|
||||
}
|
||||
|
||||
return $this->createOrFetchUser($row);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the $item array before storing.
|
||||
* We need to remove any values that are not part of the fillable fields.
|
||||
|
|
|
@ -71,11 +71,11 @@ class LicenseImporter extends ItemImporter
|
|||
|
||||
// Lets try to checkout seats if the fields exist and we have seats.
|
||||
if ($license->seats > 0) {
|
||||
$user = $this->item['user'];
|
||||
$checkout_target = $this->item['checkout_target'];
|
||||
$asset = Asset::where('asset_tag', $asset_tag)->first();
|
||||
$targetLicense = $license->licenseSeats()->first();
|
||||
if ($user) {
|
||||
$targetLicense->assigned_to = $user->id;
|
||||
if ($checkout_target) {
|
||||
$targetLicense->assigned_to = $checkout_target->id;
|
||||
if ($asset) {
|
||||
$targetLicense->asset_id = $asset->id;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
52
public/js/dist/all.js
vendored
52
public/js/dist/all.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"/js/build/vue.js": "/js/build/vue.js?id=992bcb968a7f2da998b5",
|
||||
"/js/build/vue.js": "/js/build/vue.js?id=886005d54e9097be1aa2",
|
||||
"/css/AdminLTE.css": "/css/AdminLTE.css?id=5e72463a66acbcc740d5",
|
||||
"/css/app.css": "/css/app.css?id=407edb63cc6b6dc62405",
|
||||
"/css/overrides.css": "/css/overrides.css?id=2d81c3704393bac77011",
|
||||
"/js/build/vue.js.map": "/js/build/vue.js.map?id=e0eb0edc0b761965973f",
|
||||
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map?id=99f5a5a03c4155cf69f6",
|
||||
"/css/app.css.map": "/css/app.css.map?id=bdbe05e6ecd70ccfac72",
|
||||
"/css/overrides.css.map": "/css/overrides.css.map?id=898c91d4a425b01b589b",
|
||||
"/js/build/vue.js.map": "/js/build/vue.js.map?id=43e461db5a50dbd76462",
|
||||
"/css/AdminLTE.css.map": "/css/AdminLTE.css.map?id=0be7790b84909dca6a0a",
|
||||
"/css/app.css.map": "/css/app.css.map?id=96b5c985e860716e6a16",
|
||||
"/css/overrides.css.map": "/css/overrides.css.map?id=f7ce9ca49027594ac402",
|
||||
"/css/dist/all.css": "/css/dist/all.css?id=98db4e9b7650453c8b00",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=39b95992f478d68c44a8",
|
||||
"/js/dist/all.js": "/js/dist/all.js?id=2d0469b07a6bc188bedd",
|
||||
"/css/build/all.css": "/css/build/all.css?id=98db4e9b7650453c8b00",
|
||||
"/js/build/all.js": "/js/build/all.js?id=39b95992f478d68c44a8"
|
||||
"/js/build/all.js": "/js/build/all.js?id=2d0469b07a6bc188bedd"
|
||||
}
|
|
@ -117,12 +117,18 @@ tr {
|
|||
assets: [
|
||||
{id: 'asset_tag', text: 'Asset Tag' },
|
||||
{id: 'asset_model', text: 'Model Name' },
|
||||
{id: 'checkout_class', text: 'Checkout Type' },
|
||||
{id: 'checkout_location', text: 'Checkout Location' },
|
||||
{id: 'image', text: 'Image Filename' },
|
||||
{id: 'model_number', text: 'Model Number' },
|
||||
{id: 'full_name', text: 'Full Name' },
|
||||
{id: 'status', text: 'Status' },
|
||||
{id: 'warranty_months', text: 'Warranty Months' },
|
||||
],
|
||||
consumables: [
|
||||
{id: 'item_no', text: "Item Number"},
|
||||
{id: 'model_number', text: "Model Number"},
|
||||
],
|
||||
licenses: [
|
||||
{id: 'expiration_date', text: 'Expiration Date' },
|
||||
{id: 'license_email', text: 'Licensed To Email' },
|
||||
|
@ -165,6 +171,11 @@ tr {
|
|||
.concat(this.columnOptions.assets)
|
||||
.concat(this.columnOptions.customFields)
|
||||
.sort(sorter);
|
||||
|
||||
case 'consumable':
|
||||
return this.columnOptions.general
|
||||
.concat(this.columnOptions.consumables)
|
||||
.sort(sorter);
|
||||
case 'license':
|
||||
return this.columnOptions.general.concat(this.columnOptions.licenses).sort(sorter);
|
||||
case 'user':
|
||||
|
@ -184,7 +195,6 @@ tr {
|
|||
},
|
||||
watch: {
|
||||
columns() {
|
||||
console.log("CHANGED");
|
||||
this.populateSelect2ActiveItems();
|
||||
}
|
||||
},
|
||||
|
@ -234,7 +244,6 @@ tr {
|
|||
for(var j=0; j < this.columns.length; j++) {
|
||||
let column = this.columns[j];
|
||||
let lower = this.file.header_row.map((value) => value.toLowerCase());
|
||||
console.dir(lower);
|
||||
let index = lower.indexOf(column.text.toLowerCase())
|
||||
if(index != -1) {
|
||||
this.$set(this.columnMappings, this.file.header_row[index], column.id)
|
||||
|
@ -248,7 +257,6 @@ tr {
|
|||
}
|
||||
},
|
||||
updateModel(header, value) {
|
||||
console.log(header, value);
|
||||
this.columnMappings[header] = value;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@ use App\Models\Asset;
|
|||
use App\Models\AssetModel;
|
||||
use App\Models\Category;
|
||||
use App\Models\CustomField;
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
@ -72,12 +73,61 @@ EOT;
|
|||
'asset_tag' => '970882174-8',
|
||||
'notes' => "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",
|
||||
'purchase_date' => '2016-04-05 00:00:01',
|
||||
'purchase_cost' => 133289.59,
|
||||
'warranty_months' => 14,
|
||||
'purchase_cost' => 133289.59
|
||||
, 'warranty_months' => 14,
|
||||
'_snipeit_weight_2' => 35
|
||||
]);
|
||||
}
|
||||
|
||||
public function testImportCheckoutToLocation()
|
||||
{
|
||||
$this->signIn();
|
||||
|
||||
// Testing in order:
|
||||
// * Asset to user, no checkout type defined (default to user).
|
||||
// * Asset to user, explicit user checkout type (Checkout to user)
|
||||
// * Asset to location, location does not exist to begin with
|
||||
// * Asset to preexisting location.
|
||||
$csv = <<<'EOT'
|
||||
Full Name,Email,Username,Checkout Location,Checkout Type,item Name,Category,Model name,Manufacturer,Model Number,Serial,Asset Tag,Location,Notes,Purchase Date,Purchase Cost,Company,Status,Warranty,Supplier,Weight
|
||||
Bonnie Nelson,bnelson0@cdbaby.com,bnelson0,,,eget nunc donec quis,quam,massa id,Linkbridge,6377018600094472,27aa8378-b0f4-4289-84a4-405da95c6147,970882174-8,Daping,"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.",2016-04-05,133289.59,Alpha,Undeployable,14,Blogspan,35
|
||||
Mildred Gibson,mgibson2@wiley.com,mgibson2,,user,morbi quis tortor id,nunc nisl duis,convallis tortor risus,Lajo,374622546776765,2837ab20-8f0d-4935-8a52-226392f2b1b0,710141467-2,Shekou,In congue. Etiam justo. Etiam pretium iaculis justo.,2015-08-09,233.57,Konklab,Lost,,,
|
||||
,,,Planet Earth,location,dictumst maecenas ut,sem praesent,accumsan felis,Layo,30052522651756,4751495c-cee0-4961-b788-94a545b5643e,998233705-X,Dante Delgado,,2016-04-16,261.79,,Archived,15,Ntag,
|
||||
,,,Daping,location,viverra diam vitae,semper sapien,dapibus dolor vel,Flashset,3559785746335392,e287bb64-ff4f-434c-88ab-210ad433c77b,927820758-6,Achiaman,,2016-03-05,675.3,,Archived,22,Meevee,
|
||||
EOT;
|
||||
|
||||
$this->import(new AssetImporter($csv));
|
||||
$user = User::where('username', 'bnelson0')->firstOrFail();
|
||||
|
||||
$this->tester->seeRecord('assets', [
|
||||
'asset_tag' => '970882174-8',
|
||||
'assigned_type' => User::class,
|
||||
'assigned_to' => $user->id
|
||||
]);
|
||||
|
||||
$user = User::where('username', 'mgibson2')->firstOrFail();
|
||||
$this->tester->seeRecord('assets', [
|
||||
'asset_tag' => '710141467-2',
|
||||
'assigned_type' => User::class,
|
||||
'assigned_to' => $user->id
|
||||
]);
|
||||
|
||||
$location = Location::where('name', 'Planet Earth')->firstOrFail();
|
||||
$this->tester->seeRecord('assets', [
|
||||
'asset_tag' => '998233705-X',
|
||||
'assigned_type' => Location::class,
|
||||
'assigned_to' => $location->id
|
||||
]);
|
||||
|
||||
$location = Location::where('name', 'Daping')->firstOrFail();
|
||||
$this->tester->seeRecord('assets', [
|
||||
'asset_tag' => '927820758-6',
|
||||
'assigned_type' => Location::class,
|
||||
'assigned_to' => $location->id
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function testUpdateAssetIncludingCustomFields()
|
||||
{
|
||||
$this->signIn();
|
||||
|
@ -385,18 +435,19 @@ EOT;
|
|||
public function testDefaultConsumableImport()
|
||||
{
|
||||
$csv = <<<'EOT'
|
||||
Item Name,Purchase Date,Purchase Cost,Location,Company,Order Number,Category,Requestable,Quantity
|
||||
eget,01/03/2011,$85.91,mauris blandit mattis.,Lycos,T295T06V,Triamterene/Hydrochlorothiazide,No,322
|
||||
Item Name,Purchase Date,Purchase Cost,Location,Company,Order Number,Category,Requestable,Quantity,Item Number,Model Number
|
||||
eget,01/03/2011,$85.91,mauris blandit mattis.,Lycos,T295T06V,Triamterene/Hydrochlorothiazide,No,322,3305,30123
|
||||
EOT;
|
||||
$this->import(new ConsumableImporter($csv));
|
||||
|
||||
$this->tester->seeRecord('consumables', [
|
||||
'name' => 'eget',
|
||||
'purchase_date' => '2011-01-03 00:00:01',
|
||||
'purchase_cost' => 85.91,
|
||||
'order_number' => 'T295T06V',
|
||||
'requestable' => 0,
|
||||
'qty' => 322
|
||||
'qty' => 322,
|
||||
'item_no' => 3305,
|
||||
'model_number' => 30123
|
||||
]);
|
||||
|
||||
$this->tester->seeRecord('locations', [
|
||||
|
|
Loading…
Add table
Reference in a new issue