diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php
index 995b7a7e4e..613bf10f7e 100644
--- a/app/Helpers/Helper.php
+++ b/app/Helpers/Helper.php
@@ -1313,25 +1313,20 @@ class Helper
switch ($item) {
case 'asset':
return 'fas fa-barcode';
- break;
case 'accessory':
return 'fas fa-keyboard';
- break;
case 'component':
return 'fas fa-hdd';
- break;
case 'consumable':
return 'fas fa-tint';
- break;
case 'license':
return 'far fa-save';
- break;
case 'location':
return 'fas fa-map-marker-alt';
- break;
case 'user':
return 'fas fa-user';
- break;
+ case 'supplier':
+ return 'fa-solid fa-store';
}
}
diff --git a/app/Http/Controllers/Api/ImportController.php b/app/Http/Controllers/Api/ImportController.php
index 41597cd2c5..e3b644ed78 100644
--- a/app/Http/Controllers/Api/ImportController.php
+++ b/app/Http/Controllers/Api/ImportController.php
@@ -234,6 +234,9 @@ class ImportController extends Controller
case 'location':
$redirectTo = 'locations.index';
break;
+ case 'supplier':
+ $redirectTo = 'suppliers.index';
+ break;
}
if ($errors) { //Failure
diff --git a/app/Importer/ItemImporter.php b/app/Importer/ItemImporter.php
index dfcd644c9d..0f3461e19f 100644
--- a/app/Importer/ItemImporter.php
+++ b/app/Importer/ItemImporter.php
@@ -110,7 +110,7 @@ class ItemImporter extends Importer
protected function determineCheckout($row)
{
// Locations don't get checked out to anyone/anything
- if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class)) {
+ if ((get_class($this) == LocationImporter::class) || (get_class($this) == AssetModelImporter::class) || (get_class($this) == SupplierImporter::class)) {
return;
}
diff --git a/app/Importer/SupplierImporter.php b/app/Importer/SupplierImporter.php
new file mode 100644
index 0000000000..7878aff835
--- /dev/null
+++ b/app/Importer/SupplierImporter.php
@@ -0,0 +1,105 @@
+createSupplierIfNotExists($row);
+ }
+
+ /**
+ * Create a supplier if a duplicate does not exist.
+ * @todo Investigate how this should interact with Importer::createSupplierIfNotExists
+ *
+ * @author A. Gianotto
+ * @since 6.1.0
+ * @param array $row
+ */
+ public function createSupplierIfNotExists(array $row)
+ {
+
+ $editingSupplier = false;
+
+ $supplier = Supplier::where('name', '=', $this->findCsvMatch($row, 'name'))->first();
+
+ if ($this->findCsvMatch($row, 'id')!='') {
+ // Override supplier if an ID was given
+ \Log::debug('Finding supplier by ID: '.$this->findCsvMatch($row, 'id'));
+ $supplier = Supplier::find($this->findCsvMatch($row, 'id'));
+ }
+
+
+ if ($supplier) {
+ if (! $this->updating) {
+ $this->log('A matching Supplier '.$this->item['name'].' already exists');
+ return;
+ }
+
+ $this->log('Updating Supplier');
+ $editingSupplier = true;
+ } else {
+ $this->log('No Matching Supplier, Create a new one');
+ $supplier = new Supplier;
+ $supplier->created_by = auth()->id();
+ }
+
+ // Pull the records from the CSV to determine their values
+ $this->item['name'] = trim($this->findCsvMatch($row, 'name'));
+ $this->item['address'] = trim($this->findCsvMatch($row, 'address'));
+ $this->item['address2'] = trim($this->findCsvMatch($row, 'address2'));
+ $this->item['city'] = trim($this->findCsvMatch($row, 'city'));
+ $this->item['state'] = trim($this->findCsvMatch($row, 'state'));
+ $this->item['country'] = trim($this->findCsvMatch($row, 'country'));
+ $this->item['zip'] = trim($this->findCsvMatch($row, 'zip'));
+ $this->item['phone'] = trim($this->findCsvMatch($row, 'phone'));
+ $this->item['fax'] = trim($this->findCsvMatch($row, 'fax'));
+ $this->item['email'] = trim($this->findCsvMatch($row, 'email'));
+ $this->item['contact'] = trim($this->findCsvMatch($row, 'contact'));
+ $this->item['url'] = trim($this->findCsvMatch($row, 'url'));
+ $this->item['notes'] = trim($this->findCsvMatch($row, 'notes'));
+
+
+ Log::debug('Item array is: ');
+ Log::debug(print_r($this->item, true));
+
+
+ if ($editingSupplier) {
+ Log::debug('Updating existing supplier');
+ $supplier->update($this->sanitizeItemForUpdating($supplier));
+ } else {
+ Log::debug('Creating supplier');
+ $supplier->fill($this->sanitizeItemForStoring($supplier));
+ }
+
+ if ($supplier->save()) {
+ $this->log('Supplier '.$supplier->name.' created or updated from CSV import');
+ return $supplier;
+
+ } else {
+ Log::debug($supplier->getErrors());
+ $this->logError($supplier, 'Supplier "'.$this->item['name'].'"');
+ return $supplier->errors;
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/Livewire/Importer.php b/app/Livewire/Importer.php
index 5f30dca7be..63c9dec3e7 100644
--- a/app/Livewire/Importer.php
+++ b/app/Livewire/Importer.php
@@ -35,6 +35,8 @@ class Importer extends Component
public $accessories_fields;
public $assets_fields;
public $users_fields;
+ public $assetmodels_fields;
+ public $suppliers_fields;
public $licenses_fields;
public $locations_fields;
public $consumables_fields;
@@ -85,9 +87,6 @@ class Importer extends Component
case 'component':
$results = $this->components_fields;
break;
- case 'consumable':
- $results = $this->consumables_fields;
- break;
case 'license':
$results = $this->licenses_fields;
break;
@@ -97,8 +96,8 @@ class Importer extends Component
case 'location':
$results = $this->locations_fields;
break;
- case 'user':
- $results = $this->users_fields;
+ case 'supplier':
+ $results = $this->suppliers_fields;
break;
default:
$results = [];
@@ -128,7 +127,7 @@ class Importer extends Component
//yes, this key *is* valid. Continue on to the next field.
continue;
} else {
- //no, this key is *INVALID* for this import type. Better set it to null
+ //no, this key is *INVALID* for this import type. Better set it to null,
// and we'll hope that the $aliases_fields or something else picks it up.
$this->field_map[$i] = null; // fingers crossed! But it's not likely, tbh.
} // TODO - strictly speaking, this isn't necessary here I don't think.
@@ -149,7 +148,7 @@ class Importer extends Component
// in "Accessories"!)
if (array_key_exists($key, $this->columnOptions[$type])) {
$this->field_map[$i] = $key;
- continue 3; // bust out of both of these loops; as well as the surrounding one - e.g. move on to the next header
+ continue 3; // bust out of both of these loops and the surrounding one - e.g. move on to the next header
}
}
}
@@ -171,6 +170,7 @@ class Importer extends Component
'license' => trans('general.licenses'),
'location' => trans('general.locations'),
'user' => trans('general.users'),
+ 'supplier' => trans('general.suppliers'),
];
/**
@@ -332,6 +332,7 @@ class Importer extends Component
$this->locations_fields = [
'id' => trans('general.id'),
+ 'name' => trans('general.name'),
'address' => trans('general.address'),
'address2' => trans('general.importer.address2'),
'city' => trans('general.city'),
@@ -340,13 +341,28 @@ class Importer extends Component
'ldap_ou' => trans('admin/locations/table.ldap_ou'),
'manager' => trans('general.importer.manager_full_name'),
'manager_username' => trans('general.importer.manager_username'),
- 'name' => trans('general.item_name_var', ['item' => trans('general.location')]),
'notes' => trans('general.notes'),
'parent_location' => trans('admin/locations/table.parent'),
'state' => trans('general.state'),
'zip' => trans('general.zip'),
];
+ $this->suppliers_fields = [
+ 'id' => trans('general.id'),
+ 'name' => trans('general.name'),
+ 'address' => trans('general.address'),
+ 'address2' => trans('general.importer.address2'),
+ 'city' => trans('general.city'),
+ 'notes' => trans('general.notes'),
+ 'state' => trans('general.state'),
+ 'zip' => trans('general.zip'),
+ 'phone' => trans('general.phone'),
+ 'fax' => trans('general.fax'),
+ 'url' => trans('general.url'),
+ 'contact' => trans('general.contact'),
+ 'email' => trans('general.email'),
+ ];
+
$this->assetmodels_fields = [
'category' => trans('general.category'),
'eol' => trans('general.eol'),
@@ -371,6 +387,8 @@ class Importer extends Component
'consumable name',
'component name',
'name',
+ 'supplier name',
+ 'location name',
],
'item_no' => [
'item number',
diff --git a/database/factories/ImportFactory.php b/database/factories/ImportFactory.php
index 11fdbe17a9..f0b0bc0da4 100644
--- a/database/factories/ImportFactory.php
+++ b/database/factories/ImportFactory.php
@@ -165,4 +165,40 @@ class ImportFactory extends Factory
});
}
+ /**
+ * Create a supplier import type.
+ *
+ * @return static
+ */
+ public function suppliers()
+ {
+ return $this->state(function (array $attributes) {
+ $fileBuilder = Importing\SuppliersImportFileBuilder::new();
+ $attributes['name'] = "Supplier {$attributes['name']}";
+ $attributes['import_type'] = 'supplier';
+ $attributes['header_row'] = $fileBuilder->toCsv()[0];
+ $attributes['first_row'] = $fileBuilder->firstRow();
+
+ return $attributes;
+ });
+ }
+
+ /**
+ * Create an supplier import type.
+ *
+ * @return static
+ */
+ public function locations()
+ {
+ return $this->state(function (array $attributes) {
+ $fileBuilder = Importing\SuppliersImportFileBuilder::new();
+ $attributes['name'] = "Location {$attributes['name']}";
+ $attributes['import_type'] = 'location';
+ $attributes['header_row'] = $fileBuilder->toCsv()[0];
+ $attributes['first_row'] = $fileBuilder->firstRow();
+
+ return $attributes;
+ });
+ }
+
}
diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php
index d2e16aafbb..599a15c139 100644
--- a/resources/lang/en-US/general.php
+++ b/resources/lang/en-US/general.php
@@ -534,6 +534,9 @@ return [
'action_source' => 'Action Source',
'or' => 'or',
'url' => 'URL',
+ 'phone' => 'Phone',
+ 'fax' => 'Fax',
+ 'contact' => 'Contact',
'edit_fieldset' => 'Edit fieldset fields and options',
'permission_denied_superuser_demo' => 'Permission denied. You cannot update user information for superadmins on the demo.',
'pwd_reset_not_sent' => 'User is not activated, is LDAP synced, or does not have an email address',
diff --git a/resources/views/suppliers/index.blade.php b/resources/views/suppliers/index.blade.php
index d0b911bf1a..f35ad983d8 100755
--- a/resources/views/suppliers/index.blade.php
+++ b/resources/views/suppliers/index.blade.php
@@ -54,6 +54,9 @@
{{ trans('admin/suppliers/table.licenses') }} |
{{ trans('general.components') }} |
{{ trans('general.consumables') }} |
+ {{ trans('general.notes') }} |
+ {{ trans('general.created_at') }} |
+ {{ trans('general.created_by') }} |
{{ trans('table.actions') }} |
diff --git a/sample_csvs/locations-sample.csv b/sample_csvs/locations-sample.csv
index 42285f2168..707e2830d5 100644
--- a/sample_csvs/locations-sample.csv
+++ b/sample_csvs/locations-sample.csv
@@ -1,20 +1,11 @@
-name,address,address2,city,state,country,zip,manager,manager username,currency
-Peace River,8 Brentwood Court,,Birendranagar,AB,CA,T8S,Danika Mostyn,dmostyn0,CAD
-Airdrie,14 Summer Ridge Court,306 Buhler Parkway,Poniatowa,AB,CA,T4B,Clementina Van Halen,cvan1,CAD
-Calgary,3 Fieldstone Drive,,Iwanai,AB,CA,,Harwilll Heffernan,hheffernan2,CAD
-High Prairie,1906 Weeping Birch Park,,Lopar,AB,CA,,Christian Pache,cpache3,CAD
-Sundre,20 Summer Ridge Court,,Burujul,AB,CA,,,,
-Athabasca,22 Browning Drive,424 Rieder Court,Itambacuri,AB,CA,,Alphonso Ashbridge,aashbridge5,CAD
-Drayton Valley,56064 Onsgard Center,,Bahía Honda,AB,CA,,,,
-Crossfield,0 Lighthouse Bay Place,,Bengras,AB,CA,,Vania Dufton,vdufton7,CAD
-Beaverlodge,6 Katie Terrace,,Zhajin,AB,CA,,Papageno Baldi,pbaldi8,CAD
-Grande Prairie,0 Ridgeview Parkway,,Yunxi,AB,CA,R3J,Selia Biggadike,sbiggadike9,CAD
-Sherwood Park,263 Aberg Alley,,El Paso,AB,CA,,,,
-Vegreville,9039 Shoshone Parkway,,Huazhou,AB,CA,,Georgy Eversfield,geversfieldb,CAD
-Rocky Mountain House,8617 Arapahoe Parkway,,Paraipaba,AB,CA,,Mara Gilfoyle,mgilfoylec,CAD
-Calmar,14 Green Ridge Circle,,Medveditskiy,AB,CA,S0G,Paulette Rylatt,prylattd,CAD
-Rocky Mountain House,517 Bowman Terrace,,Viana,AB,CA,,Neal Gabitis,ngabitise,CAD
-Pincher Creek,6054 Anzinger Hill,,Chlumec,AB,CA,,Bonnee Fowle,bfowlef,CAD
-Airdrie,8 Lien Drive,,Reims,AB,CA,,Kerry Aherne,kaherneg,CAD
-Camrose,4 Summit Parkway,,Xinqiao,AB,CA,T4V,Sherlock Stobbart,sstobbarth,CAD
-Lamont,12 Ilene Park,,Huangtang,AB,CA,N2E,Karlotta Pinckstone,kpinckstonei,CAD
+name,address,address2,city,state,country,zip,notes,phone,fax,currency
+Shizi,811 Algoma Center,1690,Shimen,,CN,,ipsum ac tellus semper interdum mauris ullamcorper purus sit amet nulla quisque arcu libero rutrum ac lobortis vel dapibus,280-997-3795,673-737-7438,CNY
+Takum,354 Sugar Way,88222,Old Shinyanga,,NG,,proin risus praesent lectus vestibulum quam sapien varius ut blandit non interdum in ante vestibulum ante,810-325-0609,746-868-1843,NGN
+Unaizah,56243 Lawn Drive,14,Irecê,,SA,,in blandit ultrices enim lorem ipsum dolor sit amet consectetuer adipiscing elit proin interdum mauris,841-568-9664,419-246-9966,SAR
+Pontarlier,80411 Lyons Alley,0,Colinas,Franche-Comté,FR,25304 CEDEX,enim in tempor turpis nec euismod scelerisque quam turpis adipiscing lorem vitae,927-638-2175,683-361-5521,EUR
+Inongo,377 Nancy Lane,236,Placencia,,CD,,nam dui proin leo odio porttitor id consequat in consequat ut nulla sed accumsan felis ut at dolor quis,278-277-8326,744-879-3383,CDF
+San Diego,294 Spohn Center,04,Yanmen,,CO,202038,quis orci eget orci vehicula condimentum curabitur in libero ut,255-785-7948,874-491-7898,COP
+Lewotola,22308 Oriole Way,505,Celso Ramos,,ID,,nam nulla integer pede justo lacinia eget tincidunt eget tempus vel pede morbi porttitor lorem id ligula suspendisse ornare,431-503-0615,960-849-7404,IDR
+Pieńsk,7 Maryland Junction,77656,Cela,,PL,59-930,ut blandit non interdum in ante vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae,723-739-7799,431-889-4342,PLN
+Mataguži,5 Mayfield Drive,0,Zhaogezhuang,,ME,,nulla neque libero convallis eget eleifend luctus ultricies eu nibh quisque id justo sit amet sapien dignissim vestibulum vestibulum,356-728-4662,689-866-7531,EUR
+Xixiang,2540 Rockefeller Place,2,Alegria,,CN,,vivamus metus arcu adipiscing molestie hendrerit at vulputate vitae nisl aenean lectus,388-760-0441,110-938-9032,CNY
diff --git a/sample_csvs/suppliers-sample.csv b/sample_csvs/suppliers-sample.csv
new file mode 100644
index 0000000000..4383a5f703
--- /dev/null
+++ b/sample_csvs/suppliers-sample.csv
@@ -0,0 +1,11 @@
+name,address,address2,city,state,country,zip,notes,contact,phone,fax
+Mybuzz,1 Hoffman Circle,55,New York City,New York,US,10045,et tempus semper est quam pharetra magna ac consequat metus sapien,Debee Gouldstone,347-266-3178,995-892-7579
+Jabbersphere,0 Coolidge Circle,20357,Mapalad,,PH,3132,neque vestibulum eget vulputate ut ultrices vel augue vestibulum ante ipsum primis in faucibus orci luctus et ultrices,Malia Weatherdon,197-981-5379,488-325-1715
+Topicblab,88612 Fair Oaks Way,7989,Cogon,,PH,1686,aliquam convallis nunc proin at turpis a pede posuere nonummy integer,Wiley Brigdale,958-422-9615,899-962-6225
+Trupe,4861 Sheridan Pass,8,San Pedro,,PH,4023,vulputate nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget eros elementum pellentesque quisque porta,Tobin Madill,387-755-4972,784-700-7390
+Yotz,40370 Stang Road,8,Kairouan,,TN,,in tempus sit amet sem fusce consequat nulla nisl nunc nisl,Obadiah Sample,656-536-9835,682-787-3779
+Fatz,5 Scoville Court,75043,Krasiczyn,,PL,37-741,metus sapien ut nunc vestibulum ante ipsum primis in faucibus orci luctus et ultrices,Caprice Cadagan,985-361-5597,543-332-3487
+Mycat,66 Sugar Parkway,4946,Andalucía,,CO,763008,velit nec nisi vulputate nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget eros,Jody Veasey,954-596-9805,404-452-3171
+Livefish,24 Artisan Street,0,Brest,Bretagne,FR,29213 CEDEX 1,suspendisse ornare consequat lectus in est risus auctor sed tristique in tempus sit amet sem,Eddie Decayette,102-873-3656,558-233-6215
+Roomm,292 Crest Line Drive,10,Gribanovskiy,,RU,397243,nonummy maecenas tincidunt lacus at velit vivamus vel nulla eget eros elementum pellentesque quisque porta volutpat,Cesare Fosten,247-353-7043,248-710-4887
+Yodel,29457 Eastlawn Crossing,019,Divnomorskoye,,RU,353490,turpis a pede posuere nonummy integer non velit donec diam neque,Delmer Milton,499-847-6224,868-912-6630
diff --git a/tests/Feature/Importing/Api/ImportLocationsTest.php b/tests/Feature/Importing/Api/ImportLocationsTest.php
new file mode 100644
index 0000000000..88eeb5fa18
--- /dev/null
+++ b/tests/Feature/Importing/Api/ImportLocationsTest.php
@@ -0,0 +1,136 @@
+actingAsForApi(User::factory()->create());
+
+ $this->importFileResponse(['import' => 44])->assertForbidden();
+ }
+
+ #[Test]
+ public function importLocation(): void
+ {
+ $importFileBuilder = ImportFileBuilder::new();
+ $row = $importFileBuilder->firstRow();
+ $import = Import::factory()->locations()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+ $this->importFileResponse(['import' => $import->id, 'send-welcome' => 0])
+ ->assertOk()
+ ->assertExactJson([
+ 'payload' => null,
+ 'status' => 'success',
+ 'messages' => ['redirect_url' => route('locations.index')]
+ ]);
+
+ $newLocation = Location::query()
+ ->where('name', $row['name'])
+ ->sole();
+
+ $this->assertEquals($row['name'], $newLocation->name);
+
+ }
+
+ #[Test]
+ public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
+ {
+ $row = ImportFileBuilder::new()->definition();
+ $row['unknownColumnInCsvFile'] = 'foo';
+
+ $importFileBuilder = new ImportFileBuilder([$row]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+
+ $import = Import::factory()->locations()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->importFileResponse(['import' => $import->id])->assertOk();
+ }
+
+
+ #[Test]
+ public function whenRequiredColumnsAreMissingInImportFile(): void
+ {
+ $importFileBuilder = ImportFileBuilder::new(['name' => '']);
+ $import = Import::factory()->locations()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+
+ $this->importFileResponse(['import' => $import->id])
+ ->assertInternalServerError()
+ ->assertExactJson([
+ 'status' => 'import-errors',
+ 'payload' => null,
+ 'messages' => [
+ '' => [
+ 'Location ""' => [
+ 'name' =>
+ ['The name field is required.'],
+ ],
+ ]
+
+ ]
+ ]);
+
+ $newLocation = Location::query()
+ ->where('name', $importFileBuilder->firstRow()['name'])
+ ->get();
+
+ $this->assertCount(0, $newLocation);
+ }
+
+
+ #[Test]
+ public function updateLocationFromImport(): void
+ {
+ $location = Location::factory()->create()->refresh();
+ $importFileBuilder = ImportFileBuilder::new(['name' => $location->name, 'phone' => $location->phone]);
+
+ $row = $importFileBuilder->firstRow();
+ $import = Import::factory()->locations()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+ $this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
+
+ $updatedLocation = Location::query()->find($location->id);
+ $updatedAttributes = [
+ 'name',
+ 'phone',
+ ];
+
+ $this->assertEquals($row['name'], $updatedLocation->name);
+
+ $this->assertEquals(
+ Arr::except($location->attributesToArray(), array_merge($updatedAttributes, $location->getDates())),
+ Arr::except($updatedLocation->attributesToArray(), array_merge($updatedAttributes, $location->getDates())),
+ );
+ }
+
+}
diff --git a/tests/Feature/Importing/Api/ImportSuppliersTest.php b/tests/Feature/Importing/Api/ImportSuppliersTest.php
new file mode 100644
index 0000000000..c9d0b9c72e
--- /dev/null
+++ b/tests/Feature/Importing/Api/ImportSuppliersTest.php
@@ -0,0 +1,141 @@
+actingAsForApi(User::factory()->create());
+
+ $this->importFileResponse(['import' => 44])->assertForbidden();
+ }
+
+ #[Test]
+ public function importSupplier(): void
+ {
+ $importFileBuilder = ImportFileBuilder::new();
+ $row = $importFileBuilder->firstRow();
+ $import = Import::factory()->suppliers()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+ $this->importFileResponse(['import' => $import->id, 'send-welcome' => 0])
+ ->assertOk()
+ ->assertExactJson([
+ 'payload' => null,
+ 'status' => 'success',
+ 'messages' => ['redirect_url' => route('suppliers.index')]
+ ]);
+
+ $newSupplier = Supplier::query()
+ ->where('name', $row['name'])
+ ->sole();
+
+ $this->assertEquals($row['name'], $newSupplier->name);
+
+ }
+
+ #[Test]
+ public function willIgnoreUnknownColumnsWhenFileContainsUnknownColumns(): void
+ {
+ $row = ImportFileBuilder::new()->definition();
+ $row['unknownColumnInCsvFile'] = 'foo';
+
+ $importFileBuilder = new ImportFileBuilder([$row]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+
+ $import = Import::factory()->suppliers()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->importFileResponse(['import' => $import->id])->assertOk();
+ }
+
+
+ #[Test]
+ public function whenRequiredColumnsAreMissingInImportFile(): void
+ {
+ $importFileBuilder = ImportFileBuilder::new(['name' => '']);
+ $import = Import::factory()->suppliers()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+
+ $this->importFileResponse(['import' => $import->id])
+ ->assertInternalServerError()
+ ->assertExactJson([
+ 'status' => 'import-errors',
+ 'payload' => null,
+ 'messages' => [
+ '' => [
+ 'Supplier ""' => [
+ 'name' =>
+ ['The name field is required.'],
+ ],
+ ]
+
+ ]
+ ]);
+
+ $newSupplier = Supplier::query()
+ ->where('name', $importFileBuilder->firstRow()['name'])
+ ->get();
+
+ $this->assertCount(0, $newSupplier);
+ }
+
+
+ #[Test]
+ public function updateSupplierFromImport(): void
+ {
+ $supplier = Supplier::factory()->create()->refresh();
+ $importFileBuilder = ImportFileBuilder::new(['name' => $supplier->name, 'url' => $supplier->url, 'phone' => $supplier->phone, 'fax' => $supplier->fax, 'contact' => $supplier->contact, 'email' => $supplier->email]);
+
+ $row = $importFileBuilder->firstRow();
+ $import = Import::factory()->suppliers()->create(['file_path' => $importFileBuilder->saveToImportsDirectory()]);
+
+ $this->actingAsForApi(User::factory()->superuser()->create());
+ $this->importFileResponse(['import' => $import->id, 'import-update' => true])->assertOk();
+
+ $updatedSupplier = Supplier::query()->find($supplier->id);
+ $updatedAttributes = [
+ 'name',
+ 'url',
+ 'phone',
+ 'fax',
+ 'contact',
+ 'email',
+ ];
+
+ $this->assertEquals($row['name'], $updatedSupplier->name);
+
+ $this->assertEquals(
+ Arr::except($supplier->attributesToArray(), array_merge($updatedAttributes, $supplier->getDates())),
+ Arr::except($updatedSupplier->attributesToArray(), array_merge($updatedAttributes, $supplier->getDates())),
+ );
+ }
+
+}
diff --git a/tests/Support/Importing/LocationsImportFileBuilder.php b/tests/Support/Importing/LocationsImportFileBuilder.php
new file mode 100644
index 0000000000..59b7dcc416
--- /dev/null
+++ b/tests/Support/Importing/LocationsImportFileBuilder.php
@@ -0,0 +1,58 @@
+
+ */
+class LocationsImportFileBuilder extends FileBuilder
+{
+ /**
+ * @inheritdoc
+ */
+ protected function getDictionary(): array
+ {
+ return [
+ 'name' => 'name',
+ 'phone' => 'Phone',
+ 'address' => 'address',
+ 'address2' => 'address2',
+ 'city' => 'city',
+ 'state' => 'state',
+ 'country' => 'country',
+ 'zip' => 'zip',
+ 'notes' => 'notes',
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function definition(): array
+ {
+ $faker = fake();
+
+ return [
+ 'name' => $faker->company,
+ 'phone' => $faker->phoneNumber,
+ ];
+ }
+}
diff --git a/tests/Support/Importing/SuppliersImportFileBuilder.php b/tests/Support/Importing/SuppliersImportFileBuilder.php
new file mode 100644
index 0000000000..7666174993
--- /dev/null
+++ b/tests/Support/Importing/SuppliersImportFileBuilder.php
@@ -0,0 +1,66 @@
+
+ */
+class SuppliersImportFileBuilder extends FileBuilder
+{
+ /**
+ * @inheritdoc
+ */
+ protected function getDictionary(): array
+ {
+ return [
+ 'name' => 'name',
+ 'email' => 'email',
+ 'contact' => 'contact',
+ 'phone' => 'Phone',
+ 'address' => 'address',
+ 'address2' => 'address2',
+ 'city' => 'city',
+ 'state' => 'state',
+ 'country' => 'country',
+ 'zip' => 'zip',
+ 'notes' => 'notes',
+ 'fax' => 'fax',
+ 'url' => 'url',
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function definition(): array
+ {
+ $faker = fake();
+
+ return [
+ 'name' => $faker->company,
+ 'email' => Str::random(32) . "@{$faker->freeEmailDomain}",
+ 'contact' => $faker->firstName,
+ 'phone' => $faker->phoneNumber,
+ 'fax' => $faker->phoneNumber,
+ 'url' => $faker->url(),
+ ];
+ }
+}