2
.github/workflows/phpunit.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['7.3', '7.4', '8.0']
|
||||
php-versions: ['7.3', '7.4']
|
||||
env:
|
||||
DB_DATABASE1: ninja
|
||||
DB_USERNAME1: root
|
||||
|
@ -79,14 +79,16 @@ class ImportController extends Controller
|
||||
$hash = Str::random(32);
|
||||
|
||||
//store the csv in cache with an expiry of 10 minutes
|
||||
Cache::put($hash, base64_encode(file_get_contents($request->file('file')->getPathname())), 60);
|
||||
Cache::put($hash, base64_encode(file_get_contents($request->file('file')->getPathname())), 3600);
|
||||
|
||||
//parse CSV
|
||||
$csv_array = $this->getCsvData(file_get_contents($request->file('file')->getPathname()));
|
||||
|
||||
$class_map = $this->getEntityMap($request->input('entity_type'));
|
||||
|
||||
$data = [
|
||||
'hash' => $hash,
|
||||
'available' => InvoiceMap::importable(),
|
||||
'available' => $class_map::importable(),
|
||||
'headers' => array_slice($csv_array, 0, 2)
|
||||
];
|
||||
|
||||
@ -95,11 +97,17 @@ class ImportController extends Controller
|
||||
|
||||
public function import(ImportRequest $request)
|
||||
{
|
||||
CSVImport::dispatch($request, auth()->user()->company());
|
||||
|
||||
CSVImport::dispatch($request->all(), auth()->user()->company());
|
||||
|
||||
return response()->json(['message' => 'Importing data, email will be sent on completion'], 200);
|
||||
}
|
||||
|
||||
private function getEntityMap($entity_type)
|
||||
{
|
||||
return sprintf('App\\Import\\Definitions\%sMap', ucfirst($entity_type));
|
||||
}
|
||||
|
||||
private function getCsvData($csvfile)
|
||||
{
|
||||
|
||||
|
@ -29,8 +29,10 @@ class ImportRequest extends Request
|
||||
{
|
||||
|
||||
return [
|
||||
'hash' => 'required',
|
||||
'entity_type' => 'required',
|
||||
'hash' => 'required|string',
|
||||
'entity_type' => 'required|string',
|
||||
'column_map' => 'required|array',
|
||||
'skip_header' => 'required|boolean'
|
||||
];
|
||||
|
||||
}
|
||||
|
101
app/Import/Definitions/ClientMap.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* client Ninja (https://clientninja.com).
|
||||
*
|
||||
* @link https://github.com/clientninja/clientninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. client Ninja LLC (https://clientninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Import\Definitions;
|
||||
|
||||
class ClientMap
|
||||
{
|
||||
public static function importable()
|
||||
{
|
||||
return [
|
||||
0 => 'client.name',
|
||||
1 => 'client.user_id',
|
||||
2 => 'client.balance',
|
||||
3 => 'client.paid_to_date',
|
||||
4 => 'client.currency_id',
|
||||
5 => 'client.website',
|
||||
6 => 'client.private_notes',
|
||||
7 => 'client.industry_id',
|
||||
8 => 'client.size_id',
|
||||
9 => 'client.address1',
|
||||
10 => 'client.address2',
|
||||
11 => 'client.city',
|
||||
12 => 'client.state',
|
||||
13 => 'client.postal_code',
|
||||
14 => 'client.country_id',
|
||||
15 => 'client.custom_value1',
|
||||
16 => 'client.custom_value2',
|
||||
17 => 'client.custom_value3',
|
||||
18 => 'client.custom_value4',
|
||||
19 => 'client.shipping_address1',
|
||||
20 => 'client.shipping_address2',
|
||||
21 => 'client.shipping_city',
|
||||
22 => 'client.shipping_state',
|
||||
23 => 'client.shipping_postal_code',
|
||||
24 => 'client.shipping_country_id',
|
||||
25 => 'client.payment_terms',
|
||||
26 => 'client.vat_number',
|
||||
27 => 'client.id_number',
|
||||
28 => 'client.public_notes',
|
||||
29 => 'contact.first_name',
|
||||
30 => 'contact.last_name',
|
||||
31 => 'contact.email',
|
||||
32 => 'contact.phone',
|
||||
33 => 'contact.custom_value1',
|
||||
34 => 'contact.custom_value2',
|
||||
35 => 'contact.custom_value3',
|
||||
36 => 'contact.custom_value4',
|
||||
];
|
||||
}
|
||||
|
||||
public static function import_keys()
|
||||
{
|
||||
return [
|
||||
0 => 'texts.client_name',
|
||||
1 => 'texts.user',
|
||||
2 => 'texts.balance',
|
||||
3 => 'texts.paid_to_date',
|
||||
4 => 'texts.currency',
|
||||
5 => 'texts.website',
|
||||
6 => 'texts.private_notes',
|
||||
7 => 'texts.industry',
|
||||
8 => 'texts.size',
|
||||
9 => 'texts.address1',
|
||||
10 => 'texts.address2',
|
||||
11 => 'texts.city',
|
||||
12 => 'texts.state',
|
||||
13 => 'texts.postal_code',
|
||||
14 => 'texts.country',
|
||||
15 => 'texts.custom_value',
|
||||
16 => 'texts.custom_value',
|
||||
17 => 'texts.custom_value',
|
||||
18 => 'texts.custom_value',
|
||||
19 => 'texts.address1',
|
||||
20 => 'texts.address2',
|
||||
21 => 'texts.shipping_city',
|
||||
22 => 'texts.shipping_state',
|
||||
23 => 'texts.shipping_postal_code',
|
||||
24 => 'texts.shipping_country',
|
||||
25 => 'texts.payment_terms',
|
||||
26 => 'texts.vat_number',
|
||||
27 => 'texts.id_number',
|
||||
28 => 'texts.public_notes',
|
||||
29 => 'texts.first_name',
|
||||
30 => 'texts.last_name',
|
||||
31 => 'texts.email',
|
||||
32 => 'texts.phone',
|
||||
33 => 'texts.custom_value',
|
||||
34 => 'texts.custom_value',
|
||||
35 => 'texts.custom_value',
|
||||
36 => 'texts.custom_value',
|
||||
];
|
||||
}
|
||||
}
|
350
app/Import/Transformers/BaseTransformer.php
Normal file
@ -0,0 +1,350 @@
|
||||
<?php
|
||||
|
||||
namespace App\Import\Transformers;
|
||||
|
||||
use Carbon;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class BaseTransformer.
|
||||
*/
|
||||
class BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $maps;
|
||||
|
||||
/**
|
||||
* BaseTransformer constructor.
|
||||
*
|
||||
* @param $maps
|
||||
*/
|
||||
public function __construct($maps)
|
||||
{
|
||||
$this->maps = $maps;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getString($data, $field)
|
||||
{
|
||||
return (isset($data[$field]) && $data[$field]) ? $data[$field] : '';
|
||||
}
|
||||
|
||||
public function getCurrencyByCode($data)
|
||||
{
|
||||
$code = array_key_exists('client.currency_id', $data) ? $data['client.currency_id'] : false;
|
||||
|
||||
if($code)
|
||||
return $this->maps['currencies']->where('code', $code)->first()->id;
|
||||
|
||||
return $this->maps['company']->settings->currency_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasClient($name)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVendor($name)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
|
||||
return isset($this->maps[ENTITY_VENDOR][$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasProduct($key)
|
||||
{
|
||||
$key = trim(strtolower($key));
|
||||
|
||||
return isset($this->maps[ENTITY_PRODUCT][$key]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $field
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumber($data, $field)
|
||||
{
|
||||
return (isset($data->$field) && $data->$field) ? $data->$field : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $field
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getFloat($data, $field)
|
||||
{
|
||||
return (isset($data->$field) && $data->$field) ? Utils::parseFloat($data->$field) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getClientId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]) ? $this->maps[ENTITY_CLIENT][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getProduct($data, $key, $field, $default = false)
|
||||
{
|
||||
$productKey = trim(strtolower($data->$key));
|
||||
|
||||
if (! isset($this->maps['product'][$productKey])) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$product = $this->maps['product'][$productKey];
|
||||
|
||||
return $product->$field ?: $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getContact($email)
|
||||
{
|
||||
$email = trim(strtolower($email));
|
||||
|
||||
if (! isset($this->maps['contact'][$email])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->maps['contact'][$email];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getCustomer($key)
|
||||
{
|
||||
$key = trim($key);
|
||||
|
||||
if (! isset($this->maps['customer'][$key])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->maps['customer'][$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getCountryId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps['countries'][$name]) ? $this->maps['countries'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getCountryIdBy2($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps['countries2'][$name]) ? $this->maps['countries2'][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getTaxRate($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps['tax_rates'][$name]) ? $this->maps['tax_rates'][$name] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getTaxName($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps['tax_names'][$name]) ? $this->maps['tax_names'][$name] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFirstName($name)
|
||||
{
|
||||
$name = Utils::splitName($name);
|
||||
|
||||
return $name[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
* @param string $format
|
||||
* @param mixed $data
|
||||
* @param mixed $field
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getDate($data, $field)
|
||||
{
|
||||
if ($date = data_get($data, $field)) {
|
||||
try {
|
||||
$date = new Carbon($date);
|
||||
} catch (Exception $e) {
|
||||
// if we fail to parse return blank
|
||||
$date = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $date ? $date->format('Y-m-d') : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLastName($name)
|
||||
{
|
||||
$name = Utils::splitName($name);
|
||||
|
||||
return $name[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $number
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInvoiceNumber($number)
|
||||
{
|
||||
return $number ? str_pad(trim($number), 4, '0', STR_PAD_LEFT) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoiceId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
return isset($this->maps[ENTITY_INVOICE][$invoiceNumber]) ? $this->maps[ENTITY_INVOICE][$invoiceNumber] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoicePublicId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
return isset($this->maps['invoices'][$invoiceNumber]) ? $this->maps['invoices'][$invoiceNumber]->public_id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInvoice($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
|
||||
return isset($this->maps[ENTITY_INVOICE][$invoiceNumber]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoiceNumber
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoiceClientId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
|
||||
return isset($this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber]) ? $this->maps[ENTITY_INVOICE.'_'.ENTITY_CLIENT][$invoiceNumber] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getVendorId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps[ENTITY_VENDOR][$name]) ? $this->maps[ENTITY_VENDOR][$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function getExpenseCategoryId($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
return isset($this->maps[ENTITY_EXPENSE_CATEGORY][$name]) ? $this->maps[ENTITY_EXPENSE_CATEGORY][$name] : null;
|
||||
}
|
||||
}
|
70
app/Import/Transformers/ClientTransformer.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Import\Transformers;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class ClientTransformer.
|
||||
*/
|
||||
class ClientTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return bool|Item
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
if (isset($data->name) && $this->hasClient($data->name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$settings = new \stdClass;
|
||||
$settings->currency_id = (string)$this->getCurrencyByCode($data);
|
||||
|
||||
return [
|
||||
'company_id' => $this->maps['company']->id,
|
||||
'name' => $this->getString($data, 'client.name'),
|
||||
'work_phone' => $this->getString($data, 'client.phone'),
|
||||
'address1' => $this->getString($data, 'client.address1'),
|
||||
'address2' => $this->getString($data, 'client.address2'),
|
||||
'city' => $this->getString($data, 'client.city'),
|
||||
'state' => $this->getString($data, 'client.state'),
|
||||
'shipping_address1' => $this->getString($data, 'client.shipping_address1'),
|
||||
'shipping_address2' => $this->getString($data, 'client.shipping_address2'),
|
||||
'shipping_city' => $this->getString($data, 'client.shipping_city'),
|
||||
'shipping_state' => $this->getString($data, 'client.shipping_state'),
|
||||
'shipping_postal_code' => $this->getString($data, 'client.shipping_postal_code'),
|
||||
'public_notes' => $this->getString($data, 'client.public_notes'),
|
||||
'private_notes' => $this->getString($data, 'client.private_notes'),
|
||||
'website' => $this->getString($data, 'client.website'),
|
||||
'vat_number' => $this->getString($data, 'client.vat_number'),
|
||||
'id_number' => $this->getString($data, 'client.id_number'),
|
||||
'custom_value1' => $this->getString($data, 'client.custom1'),
|
||||
'custom_value2' => $this->getString($data, 'client.custom2'),
|
||||
'custom_value3' => $this->getString($data, 'client.custom3'),
|
||||
'custom_value4' => $this->getString($data, 'client.custom4'),
|
||||
'balance' => $this->getString($data, 'client.balance'),
|
||||
'paid_to_date' => $this->getString($data, 'client.paid_to_date'),
|
||||
'credit_balance' => 0,
|
||||
'settings' => $settings,
|
||||
'client_hash' => Str::random(40),
|
||||
'contacts' => [
|
||||
[
|
||||
'first_name' => $this->getString($data, 'contact.first_name'),
|
||||
'last_name' => $this->getString($data, 'contact.last_name'),
|
||||
'email' => $this->getString($data, 'contact.email'),
|
||||
'phone' => $this->getString($data, 'contact.phone'),
|
||||
'custom_value1' => $this->getString($data, 'contact.custom1'),
|
||||
'custom_value2' => $this->getString($data, 'contact.custom2'),
|
||||
'custom_value3' => $this->getString($data, 'contact.custom3'),
|
||||
'custom_value4' => $this->getString($data, 'contact.custom4'),
|
||||
],
|
||||
],
|
||||
'country_id' => isset($data->country_id) ? $this->getCountryId($data->country_id) : null,
|
||||
'shipping_country_id' => isset($data->shipping_country_id) ? $this->getCountryId($data->shipping_country_id) : null,
|
||||
];
|
||||
|
||||
}
|
||||
}
|
@ -11,7 +11,16 @@
|
||||
|
||||
namespace App\Jobs\Import;
|
||||
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Http\Requests\Client\StoreClientRequest;
|
||||
use App\Import\Transformers\ClientTransformer;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Currency;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\ClientRepository;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -20,6 +29,11 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class CSVImport implements ShouldQueue
|
||||
{
|
||||
@ -33,19 +47,48 @@ class CSVImport implements ShouldQueue
|
||||
|
||||
public $entity_type;
|
||||
|
||||
public $skip_headers;
|
||||
public $skip_header;
|
||||
|
||||
public function __construct(Request $request, Company $company)
|
||||
public $column_map;
|
||||
|
||||
public $import_array;
|
||||
|
||||
public $error_array;
|
||||
|
||||
public $maps;
|
||||
|
||||
/*
|
||||
[hash] => 2lTm7HVR3i9Zv3y86eQYZIO16yVJ7J6l
|
||||
[entity_type] => client
|
||||
[skip_header] => 1
|
||||
[column_map] => Array
|
||||
(
|
||||
[0] => client.name
|
||||
[1] => client.user_id
|
||||
[2] => client.balance
|
||||
[3] => client.paid_to_date
|
||||
[4] => client.address1
|
||||
[5] => client.address2
|
||||
[6] => client.city
|
||||
[7] => client.state
|
||||
[8] => client.postal_code
|
||||
[9] => client.country_id
|
||||
[20] => client.currency_id
|
||||
[21] => client.public_notes
|
||||
[22] => client.private_notes
|
||||
)
|
||||
*/
|
||||
public function __construct(array $request, Company $company)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
$this->company = $company;
|
||||
|
||||
$this->hash = $request->input('hash');
|
||||
$this->hash = $request['hash'];
|
||||
|
||||
$this->entity_type = $request->input('entity_type');
|
||||
$this->entity_type = $request['entity_type'];
|
||||
|
||||
$this->skip_headers = $request->input('skip_headers');
|
||||
$this->skip_header = $request['skip_header'];
|
||||
|
||||
$this->column_map = $request['column_map'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,6 +101,54 @@ class CSVImport implements ShouldQueue
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->company->owner()->setCompany($this->company);
|
||||
Auth::login($this->company->owner(), true);
|
||||
|
||||
$this->buildMaps();
|
||||
|
||||
//sort the array by key
|
||||
ksort($this->column_map);
|
||||
|
||||
//clients
|
||||
$records = $this->getCsvData();
|
||||
|
||||
$contact_repository = new ClientContactRepository();
|
||||
$client_repository = new ClientRepository($contact_repository);
|
||||
$client_transformer = new ClientTransformer($this->maps);
|
||||
|
||||
if($this->skip_header)
|
||||
array_shift($records);
|
||||
|
||||
foreach($records as $record) {
|
||||
|
||||
$keys = $this->column_map;
|
||||
$values = array_intersect_key($record, $this->column_map);
|
||||
|
||||
$client_data = array_combine($keys, $values);
|
||||
|
||||
$client = $client_transformer->transform($client_data);
|
||||
|
||||
$validator = Validator::make($client, (new StoreClientRequest())->rules());
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->error_array[] = ['client' => $client, 'error' => json_encode($validator->errors())];
|
||||
}
|
||||
else{
|
||||
$client = $client_repository->save($client, ClientFactory::create($this->company->id, $this->setUser($record)));
|
||||
|
||||
if(array_key_exists('client.balance', $client_data))
|
||||
$client->balance = preg_replace('/[^0-9,.]+/', '', $client_data['client.balance']);
|
||||
|
||||
if(array_key_exists('client.paid_to_date', $client_data))
|
||||
$client->paid_to_date = preg_replace('/[^0-9,.]+/', '', $client_data['client.paid_to_date']);
|
||||
|
||||
$client->save();
|
||||
|
||||
$this->import_array['clients'][] = $client->id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function failed($exception)
|
||||
@ -65,10 +156,67 @@ class CSVImport implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
private function getCsv()
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private function buildMaps()
|
||||
{
|
||||
$this->maps['currencies'] = Currency::all();
|
||||
$this->maps['users'] = $this->company->users;
|
||||
$this->maps['company'] = $this->company;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
private function setUser($record)
|
||||
{
|
||||
$user_key_exists = array_search('client.user_id', $this->column_map);
|
||||
|
||||
if($user_key_exists)
|
||||
return $this->findUser($record[$user_key_exists]);
|
||||
else
|
||||
return $this->company->owner()->id;
|
||||
|
||||
}
|
||||
|
||||
private function findUser($user_hash)
|
||||
{
|
||||
$user = User::where('company_id', $this->company->id)
|
||||
->where(\DB::raw('CONCAT_WS(" ", first_name, last_name)'), 'like', '%' . $user_hash . '%')
|
||||
->first();
|
||||
|
||||
if($user)
|
||||
return $user->id;
|
||||
else
|
||||
return $this->company->owner()->id;
|
||||
|
||||
}
|
||||
|
||||
private function getCsvData()
|
||||
{
|
||||
$base64_encoded_csv = Cache::get($this->hash);
|
||||
$csv = base64_decode($base64_encoded_csv);
|
||||
$csv = Reader::createFromString($csv);
|
||||
|
||||
$stmt = new Statement();
|
||||
$data = iterator_to_array($stmt->process($csv));
|
||||
|
||||
if (count($data) > 0) {
|
||||
$headers = $data[0];
|
||||
|
||||
// Remove Invoice Ninja headers
|
||||
if (count($headers) && count($data) > 4) {
|
||||
$firstCell = $headers[0];
|
||||
if (strstr($firstCell, config('ninja.app_name'))) {
|
||||
array_shift($data); // Invoice Ninja...
|
||||
array_shift($data); // <blank line>
|
||||
array_shift($data); // Enitty Type Header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
|
||||
|
||||
return base64_decode($base64_encoded_csv);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@
|
||||
"CRM",
|
||||
"Credit card billing",
|
||||
"projects",
|
||||
"tasks"
|
||||
"tasks",
|
||||
"freelancer"
|
||||
],
|
||||
"license": "Attribution Assurance License",
|
||||
"authors": [
|
||||
@ -52,7 +53,7 @@
|
||||
"league/flysystem-aws-s3-v3": "~1.0",
|
||||
"league/flysystem-cached-adapter": "^1.1",
|
||||
"league/fractal": "^0.17.0",
|
||||
"league/omnipay": "^3.0",
|
||||
"league/omnipay": "^3",
|
||||
"livewire/livewire": "^2.0",
|
||||
"maennchen/zipstream-php": "^1.2",
|
||||
"nwidart/laravel-modules": "^8.0",
|
||||
|
38
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "75cb7287f45830678381d8e27486cf4a",
|
||||
"content-hash": "aeacc9e2738ddc41353c497c14feadb0",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asgrim/ofxparser",
|
||||
@ -116,16 +116,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.169.0",
|
||||
"version": "3.170.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "d15a231355e4435fc33bab83df075ec31edd0a9b"
|
||||
"reference": "7f457a08219173eba4b856cb7aef3a903cd790cc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d15a231355e4435fc33bab83df075ec31edd0a9b",
|
||||
"reference": "d15a231355e4435fc33bab83df075ec31edd0a9b",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7f457a08219173eba4b856cb7aef3a903cd790cc",
|
||||
"reference": "7f457a08219173eba4b856cb7aef3a903cd790cc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -200,9 +200,9 @@
|
||||
"support": {
|
||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.169.0"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.170.0"
|
||||
},
|
||||
"time": "2020-12-14T19:12:33+00:00"
|
||||
"time": "2020-12-15T19:13:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "beganovich/chromium-pdf",
|
||||
@ -2715,16 +2715,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v8.18.1",
|
||||
"version": "v8.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "31747193c26ba0a9cb7929a912895d3cdefd10cf"
|
||||
"reference": "f5f331cee60f1bbe672503b7eb9ba5b22b2ceacb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/31747193c26ba0a9cb7929a912895d3cdefd10cf",
|
||||
"reference": "31747193c26ba0a9cb7929a912895d3cdefd10cf",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/f5f331cee60f1bbe672503b7eb9ba5b22b2ceacb",
|
||||
"reference": "f5f331cee60f1bbe672503b7eb9ba5b22b2ceacb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2878,7 +2878,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2020-12-08T22:05:12+00:00"
|
||||
"time": "2020-12-15T16:16:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/slack-notification-channel",
|
||||
@ -9558,22 +9558,22 @@
|
||||
},
|
||||
{
|
||||
"name": "turbo124/beacon",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/turbo124/beacon.git",
|
||||
"reference": "687484955bdc8bfca957ccd060e6c1fa41ada056"
|
||||
"reference": "ae328ad12364186dd07d893d35f6991880b97f18"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/turbo124/beacon/zipball/687484955bdc8bfca957ccd060e6c1fa41ada056",
|
||||
"reference": "687484955bdc8bfca957ccd060e6c1fa41ada056",
|
||||
"url": "https://api.github.com/repos/turbo124/beacon/zipball/ae328ad12364186dd07d893d35f6991880b97f18",
|
||||
"reference": "ae328ad12364186dd07d893d35f6991880b97f18",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7",
|
||||
"illuminate/support": "^6.0|^7.0|^8.0",
|
||||
"php": "^7.3"
|
||||
"php": "^7.3|^7.4|^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^4.0",
|
||||
@ -9615,9 +9615,9 @@
|
||||
"turbo124"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/turbo124/beacon/tree/1.0.3"
|
||||
"source": "https://github.com/turbo124/beacon/tree/1.0.4"
|
||||
},
|
||||
"time": "2020-10-01T05:21:36+00:00"
|
||||
"time": "2020-12-15T20:56:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "turbo124/laravel-gmail",
|
||||
|
0
public/.htaccess
Normal file → Executable file
0
public/assets/AssetManifest.json
Normal file → Executable file
0
public/assets/FontManifest.json
Normal file → Executable file
0
public/assets/LICENSE
Normal file → Executable file
0
public/assets/NOTICES
Normal file → Executable file
0
public/assets/assets/images/google-icon.png
Normal file → Executable file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
public/assets/assets/images/logo.png
Normal file → Executable file
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
0
public/assets/assets/images/payment_types/ach.png
Normal file → Executable file
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
public/assets/assets/images/payment_types/amex.png
Normal file → Executable file
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
0
public/assets/assets/images/payment_types/carteblanche.png
Normal file → Executable file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
public/assets/assets/images/payment_types/dinerscard.png
Normal file → Executable file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
0
public/assets/assets/images/payment_types/discover.png
Normal file → Executable file
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
0
public/assets/assets/images/payment_types/jcb.png
Normal file → Executable file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
0
public/assets/assets/images/payment_types/laser.png
Normal file → Executable file
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
0
public/assets/assets/images/payment_types/maestro.png
Normal file → Executable file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
0
public/assets/assets/images/payment_types/mastercard.png
Normal file → Executable file
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
0
public/assets/assets/images/payment_types/other.png
Normal file → Executable file
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
0
public/assets/assets/images/payment_types/paypal.png
Normal file → Executable file
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
0
public/assets/assets/images/payment_types/solo.png
Normal file → Executable file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
0
public/assets/assets/images/payment_types/switch.png
Normal file → Executable file
Before Width: | Height: | Size: 938 B After Width: | Height: | Size: 938 B |
0
public/assets/assets/images/payment_types/unionpay.png
Normal file → Executable file
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
public/assets/assets/images/payment_types/visa.png
Normal file → Executable file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
public/assets/fonts/MaterialIcons-Regular.otf
Normal file → Executable file
0
public/assets/fonts/MaterialIcons-Regular.ttf
Normal file → Executable file
0
public/assets/fonts/Roboto-Regular.ttf
Normal file → Executable file
0
public/assets/packages/flutter_auth_buttons/fonts/SF-Pro-Medium.ttf
Normal file → Executable file
0
public/assets/packages/flutter_auth_buttons/graphics/apple_logo_black.png
Normal file → Executable file
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
0
public/assets/packages/flutter_auth_buttons/graphics/apple_logo_white.png
Normal file → Executable file
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 881 B |
0
public/assets/packages/flutter_auth_buttons/graphics/flogo-HexRBG-Wht-100.png
Normal file → Executable file
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 704 B |
0
public/assets/packages/flutter_auth_buttons/graphics/google-logo.png
Normal file → Executable file
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
0
public/assets/packages/flutter_auth_buttons/graphics/ms-symbollockup_mssymbol_19.png
Normal file → Executable file
Before Width: | Height: | Size: 162 B After Width: | Height: | Size: 162 B |
0
public/assets/packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf
Normal file → Executable file
0
public/assets/packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf
Normal file → Executable file
0
public/assets/packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf
Normal file → Executable file
0
public/assets/web/assets/fonts/Roboto-Regular.ttf
Normal file → Executable file
0
public/css/app.css
vendored
Normal file → Executable file
0
public/css/card-js.min.css
vendored
Normal file → Executable file
0
public/css/ninja.css
vendored
Normal file → Executable file
0
public/css/ninja.min.css
vendored
Normal file → Executable file
0
public/css/tailwind-1.2.0.css
vendored
Normal file → Executable file
0
public/css/tailwindcss@1.4.6.css
vendored
Normal file → Executable file
0
public/favicon.ico
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
public/favicon.png
Normal file → Executable file
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
0
public/flutter_service_worker.js
vendored
Normal file → Executable file
0
public/icons/Icon-192.png
Normal file → Executable file
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
0
public/icons/Icon-512.png
Normal file → Executable file
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
0
public/images/american-express.png
Normal file → Executable file
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
0
public/images/created-by-invoiceninja-new.png
Normal file → Executable file
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
0
public/images/created-by-invoiceninja.jpg
Normal file → Executable file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
public/images/diners-club.png
Normal file → Executable file
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
0
public/images/discover.png
Normal file → Executable file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
public/images/invoiceninja-black-logo-2.png
Normal file → Executable file
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
0
public/images/invoiceninja-white-logo.png
Normal file → Executable file
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
0
public/images/logo.png
Normal file → Executable file
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
0
public/images/mastercard.png
Normal file → Executable file
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
0
public/images/paypal.png
Normal file → Executable file
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
0
public/images/svg/activity.svg
Normal file → Executable file
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 277 B |
0
public/images/svg/align-left.svg
Normal file → Executable file
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 388 B |
0
public/images/svg/clock.svg
Normal file → Executable file
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 299 B |
0
public/images/svg/credit-card.svg
Normal file → Executable file
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
0
public/images/svg/download.svg
Normal file → Executable file
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 365 B |
0
public/images/svg/file-text.svg
Normal file → Executable file
Before Width: | Height: | Size: 468 B After Width: | Height: | Size: 468 B |
0
public/images/svg/file.svg
Normal file → Executable file
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
0
public/images/svg/shield.svg
Normal file → Executable file
Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 274 B |
0
public/images/svg/user.svg
Normal file → Executable file
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
0
public/images/visa.png
Normal file → Executable file
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |