Merge remote-tracking branch 'origin/develop'
This commit is contained in:
@@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -234,6 +234,9 @@ class ImportController extends Controller
|
||||
case 'location':
|
||||
$redirectTo = 'locations.index';
|
||||
break;
|
||||
case 'supplier':
|
||||
$redirectTo = 'suppliers.index';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($errors) { //Failure
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Importer;
|
||||
|
||||
use App\Models\Supplier;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* When we are importing users via an Asset/etc import, we use createOrFetchUser() in
|
||||
* Importer\Importer.php. [ALG]
|
||||
*
|
||||
* Class SupplierImporter
|
||||
*/
|
||||
class SupplierImporter extends ItemImporter
|
||||
{
|
||||
protected $suppliers;
|
||||
|
||||
public function __construct($filename)
|
||||
{
|
||||
parent::__construct($filename);
|
||||
}
|
||||
|
||||
protected function handle($row)
|
||||
{
|
||||
parent::handle($row);
|
||||
$this->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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -54,6 +54,9 @@
|
||||
<th data-searchable="false" data-sortable="true" data-field="licenses_count">{{ trans('admin/suppliers/table.licenses') }}</th>
|
||||
<th data-searchable="false" data-sortable="true" data-field="components_count">{{ trans('general.components') }}</th>
|
||||
<th data-searchable="false" data-sortable="true" data-field="consumables_count">{{ trans('general.consumables') }}</th>
|
||||
<th data-searchable="true" data-sortable="true" data-field="notes">{{ trans('general.notes') }}</th>
|
||||
<th data-searchable="true" data-sortable="true" data-field="created_at" data-formatter="dateDisplayFormatter">{{ trans('general.created_at') }}</th>
|
||||
<th data-searchable="true" data-sortable="true" data-field="created_by" data-formatter="usersLinkObjFormatter">{{ trans('general.created_by') }}</th>
|
||||
<th data-switchable="false" data-formatter="suppliersActionsFormatter" data-searchable="false" data-sortable="false" data-field="actions">{{ trans('table.actions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Location;
|
||||
use App\Models\User;
|
||||
use App\Models\Import;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
use Tests\Support\Importing\LocationsImportFileBuilder as ImportFileBuilder;
|
||||
|
||||
class ImportLocationsTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'location';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->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())),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Importing\Api;
|
||||
|
||||
use App\Models\Supplier;
|
||||
use App\Models\User;
|
||||
use App\Models\Import;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\Concerns\TestsPermissionsRequirement;
|
||||
use Tests\Support\Importing\CleansUpImportFiles;
|
||||
use Tests\Support\Importing\SuppliersImportFileBuilder as ImportFileBuilder;
|
||||
|
||||
class ImportSuppliersTest extends ImportDataTestCase implements TestsPermissionsRequirement
|
||||
{
|
||||
use CleansUpImportFiles;
|
||||
use WithFaker;
|
||||
|
||||
protected function importFileResponse(array $parameters = []): TestResponse
|
||||
{
|
||||
if (!array_key_exists('import-type', $parameters)) {
|
||||
$parameters['import-type'] = 'supplier';
|
||||
}
|
||||
|
||||
return parent::importFileResponse($parameters);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function testRequiresPermission()
|
||||
{
|
||||
$this->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())),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build a users import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* companyName?: string,
|
||||
* email?: string,
|
||||
* employeeNumber?: int,
|
||||
* firstName?: string,
|
||||
* lastName?: string,
|
||||
* location?: string,
|
||||
* phoneNumber?: string,
|
||||
* position?: string,
|
||||
* username?: string,
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
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,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Support\Importing;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Build a users import file at runtime for testing.
|
||||
*
|
||||
* @template Row of array{
|
||||
* companyName?: string,
|
||||
* email?: string,
|
||||
* employeeNumber?: int,
|
||||
* firstName?: string,
|
||||
* lastName?: string,
|
||||
* location?: string,
|
||||
* phoneNumber?: string,
|
||||
* position?: string,
|
||||
* username?: string,
|
||||
* }
|
||||
*
|
||||
* @extends FileBuilder<Row>
|
||||
*/
|
||||
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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user