diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php
index 7da808209b..53c25ed5d7 100644
--- a/app/Http/Controllers/ImportController.php
+++ b/app/Http/Controllers/ImportController.php
@@ -22,6 +22,7 @@ class ImportController extends BaseController
{
$source = Input::get('source');
$files = [];
+ $skipped = [];
foreach (ImportService::$entityTypes as $entityType) {
if (Input::file("{$entityType}_file")) {
@@ -34,8 +35,16 @@ class ImportController extends BaseController
$data = $this->importService->mapCSV($files);
return View::make('accounts.import_map', ['data' => $data]);
} else {
- $result = $this->importService->import($source, $files);
- Session::flash('message', trans('texts.imported_file') . ' - ' . $result);
+ $skipped = $this->importService->import($source, $files);
+ if (count($skipped)) {
+ $message = trans('texts.failed_to_import');
+ foreach ($skipped as $skip) {
+ $message .= '
' . json_encode($skip);
+ }
+ Session::flash('warning', $message);
+ } else {
+ Session::flash('message', trans('texts.imported_file'));
+ }
}
} catch (Exception $exception) {
Session::flash('error', $exception->getMessage());
@@ -48,15 +57,23 @@ class ImportController extends BaseController
{
$map = Input::get('map');
$headers = Input::get('headers');
+ $skipped = [];
- //try {
- $count = $this->importService->importCSV($map, $headers);
- $message = Utils::pluralize('created_client', $count);
+ try {
+ $skipped = $this->importService->importCSV($map, $headers);
- Session::flash('message', $message);
- //} catch (Exception $exception) {
- // Session::flash('error', $exception->getMessage());
- //}
+ if (count($skipped)) {
+ $message = trans('texts.failed_to_import');
+ foreach ($skipped as $skip) {
+ $message .= '
' . json_encode($skip);
+ }
+ Session::flash('warning', $message);
+ } else {
+ Session::flash('message', trans('texts.imported_file'));
+ }
+ } catch (Exception $exception) {
+ Session::flash('error', $exception->getMessage());
+ }
return Redirect::to('/settings/' . ACCOUNT_IMPORT_EXPORT);
}
diff --git a/app/Models/Client.php b/app/Models/Client.php
index ee0e80fc89..9399d2d91c 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -71,19 +71,18 @@ class Client extends EntityModel
public static function getImportMap()
{
return [
- 'first' => Contact::$fieldFirstName,
- 'last' => Contact::$fieldLastName,
- 'email' => Contact::$fieldEmail,
- 'mobile' => Contact::$fieldPhone,
- 'phone' => Client::$fieldPhone,
- 'name|organization' => Client::$fieldName,
- 'street|address|address1' => Client::$fieldAddress1,
- 'street2|address2' => Client::$fieldAddress2,
- 'city' => Client::$fieldCity,
- 'state|province' => Client::$fieldState,
- 'zip|postal|code' => Client::$fieldPostalCode,
- 'country' => Client::$fieldCountry,
- 'note' => Client::$fieldNotes,
+ 'first' => 'first_name',
+ 'last' => 'last_name',
+ 'email' => 'email',
+ 'mobile|phone' => 'phone',
+ 'name|organization' => 'name',
+ 'street2|address2' => 'address2',
+ 'street|address|address1' => 'address1',
+ 'city' => 'city',
+ 'state|province' => 'state',
+ 'zip|postal|code' => 'postal_code',
+ 'country' => 'country',
+ 'note' => 'notes',
];
}
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index dda8eaa2cc..f2c67b81c4 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -65,11 +65,11 @@ class Invoice extends EntityModel implements BalanceAffecting
public static function getImportMap()
{
return [
- 'number' => Invoice::$fieldInvoiceNumber,
- 'amount' => Invoice::$fieldAmount,
+ 'number^po' => 'invoice_number',
+ 'amount' => 'amount',
'organization' => 'name',
- 'paid' => 'paid',
- 'invoice_date' => Invoice::$fieldInvoiceDate,
+ 'paid^date' => 'paid',
+ 'invoice_date|create_date' => 'invoice_date',
'terms' => 'terms',
'notes' => 'notes',
];
diff --git a/app/Services/ImportService.php b/app/Services/ImportService.php
index cf105ae0bb..796c9815de 100644
--- a/app/Services/ImportService.php
+++ b/app/Services/ImportService.php
@@ -59,46 +59,24 @@ class ImportService
}
}
- private function checkClientCount($count)
- {
- $totalClients = $count + Client::scope()->withTrashed()->count();
- if ($totalClients > Auth::user()->getMaxNumClients()) {
- throw new Exception(trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]));
- }
- }
-
private function execute($source, $entityType, $file)
{
- Excel::load($file, function ($reader) use ($source, $entityType) {
+ $skipped = [];
+ Excel::load($file, function ($reader) use ($source, $entityType, $skipped) {
$this->checkData($entityType, count($reader->all()));
$maps = $this->createMaps();
$reader->each(function ($row) use ($source, $entityType, $maps) {
- $this->saveData($source, $entityType, $row, $maps);
+ $result = $this->saveData($source, $entityType, $row, $maps);
+
+ if ( ! $result) {
+ $skipped[] = $row;
+ }
});
});
- }
- private function executeCSV($entityType, $map, $hasHeaders)
- {
- $source = IMPORT_CSV;
-
- $data = Session::get("{$entityType}-data");
- $this->checkData($entityType, count($data));
- $maps = $this->createMaps();
-
- foreach ($data as $row) {
- if ($hasHeaders) {
- $hasHeaders = false;
- continue;
- }
-
- $row = $this->convertToObject($entityType, $row, $map);
- $this->saveData($source, $entityType, $row, $maps);
- }
-
- Session::forget("{$entityType}-data");
+ return $skipped;
}
private function saveData($source, $entityType, $row, $maps)
@@ -112,6 +90,7 @@ class ImportService
$data = $this->fractal->createData($resource)->toArray();
+ // if the invoice number is blank we'll assign it
if ($entityType == ENTITY_INVOICE && !$data['invoice_number']) {
$account = Auth::user()->account;
$invoice = Invoice::createNew();
@@ -123,7 +102,7 @@ class ImportService
}
$entity = $this->{"{$entityType}Repo"}->save($data);
-
+
// if the invoice is paid we'll also create a payment record
if ($entityType === ENTITY_INVOICE && isset($row->paid) && $row->paid) {
$this->createPayment($source, $row, $maps, $data['client_id'], $entity->public_id);
@@ -137,6 +116,14 @@ class ImportService
}
}
+ private function checkClientCount($count)
+ {
+ $totalClients = $count + Client::scope()->withTrashed()->count();
+ if ($totalClients > Auth::user()->getMaxNumClients()) {
+ throw new Exception(trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]));
+ }
+ }
+
public static function getTransformerClassName($source, $entityType)
{
return 'App\\Ninja\\Import\\'.$source.'\\'.ucwords($entityType).'Transformer';
@@ -162,15 +149,14 @@ class ImportService
}
}
- // looking for a better solution...
- // http://stackoverflow.com/questions/33781567/how-can-i-re-use-the-validation-code-in-my-laravel-formrequest-classes
private function validate($data, $entityType)
{
if ($entityType === ENTITY_CLIENT) {
$rules = [
'contacts' => 'valid_contacts',
];
- } if ($entityType === ENTITY_INVOICE) {
+ }
+ if ($entityType === ENTITY_INVOICE) {
$rules = [
'client.contacts' => 'valid_contacts',
'invoice_items' => 'valid_invoice_items',
@@ -279,13 +265,9 @@ class ImportService
if ($hasHeaders) {
foreach ($map as $search => $column) {
- foreach (explode("|", $search) as $string) {
- if (strpos($title, 'sec') === 0) {
- continue;
- } elseif (strpos($title, $string) !== false) {
- $mapped[$i] = $column;
- break(2);
- }
+ if ($this->checkForMatch($title, $search)) {
+ $mapped[$i] = $column;
+ break;
}
}
}
@@ -304,11 +286,77 @@ class ImportService
return $data;
}
+ private function checkForMatch($column, $pattern)
+ {
+ if (strpos($column, 'sec') === 0) {
+ return false;
+ }
+
+ if (strpos($pattern, '^')) {
+ list($include, $exclude) = explode('^', $pattern);
+ $includes = explode('|', $include);
+ $excludes = explode('|', $exclude);
+ } else {
+ $includes = explode('|', $pattern);
+ $excludes = [];
+ }
+
+ foreach ($includes as $string) {
+ if (strpos($column, $string) !== false) {
+ $excluded = false;
+ foreach ($excludes as $exclude) {
+ if (strpos($column, $exclude) !== false) {
+ $excluded = true;
+ break;
+ }
+ }
+ if (!$excluded) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
public function importCSV($maps, $headers)
{
+ $skipped = [];
+
foreach ($maps as $entityType => $map) {
- $this->executeCSV($entityType, $map, $headers[$entityType]);
+ $result = $this->executeCSV($entityType, $map, $headers[$entityType]);
+ $skipped = array_merge($skipped, $result);
}
+
+ return $skipped;
+ }
+
+ private function executeCSV($entityType, $map, $hasHeaders)
+ {
+ $skipped = [];
+ $source = IMPORT_CSV;
+
+ $data = Session::get("{$entityType}-data");
+ $this->checkData($entityType, count($data));
+ $maps = $this->createMaps();
+
+ foreach ($data as $row) {
+ if ($hasHeaders) {
+ $hasHeaders = false;
+ continue;
+ }
+
+ $row = $this->convertToObject($entityType, $row, $map);
+ $result = $this->saveData($source, $entityType, $row, $maps);
+
+ if ( ! $result) {
+ $skipped[] = $row;
+ }
+ }
+
+ Session::forget("{$entityType}-data");
+
+ return $skipped;
}
private function convertToObject($entityType, $data, $map)
@@ -330,6 +378,10 @@ class ImportService
continue;
}
+ if (isset($obj->$field) && $obj->$field) {
+ continue;
+ }
+
$obj->$field = $data[$index];
}
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index a2f7c8ba77..687bab36a1 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -948,5 +948,6 @@ return array(
'notes' => 'Notes',
'invoice_will_create' => 'client will be created',
'invoices_will_create' => 'invoices will be created',
+ 'failed_to_import' => 'The following records failed to import',
);