mirror of
https://github.com/snipe/snipe-it.git
synced 2025-02-21 03:15:45 -08:00
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
|
@ -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;
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/js/dist/all.js
vendored
BIN
public/js/dist/all.js
vendored
Binary file not shown.
|
@ -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…
Reference in a new issue