mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 12:12:48 +01:00
Updated readme
This commit is contained in:
parent
b4b027de5c
commit
ae57fdf9a3
@ -22,6 +22,7 @@ MAIL_PASSWORD
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
LOG=single
|
||||
REQUIRE_HTTPS=false
|
||||
|
||||
GOOGLE_CLIENT_ID
|
||||
GOOGLE_CLIENT_SECRET
|
||||
|
@ -21,45 +21,43 @@ class ImportController extends BaseController
|
||||
public function doImport()
|
||||
{
|
||||
$source = Input::get('source');
|
||||
$files = [];
|
||||
|
||||
if ($source === IMPORT_CSV) {
|
||||
$filename = Input::file('client_file')->getRealPath();
|
||||
$data = $this->importService->mapFile($filename);
|
||||
|
||||
return View::make('accounts.import_map', $data);
|
||||
} else {
|
||||
$files = [];
|
||||
foreach (ImportService::$entityTypes as $entityType) {
|
||||
if (Input::file("{$entityType}_file")) {
|
||||
$files[$entityType] = Input::file("{$entityType}_file")->getRealPath();
|
||||
}
|
||||
foreach (ImportService::$entityTypes as $entityType) {
|
||||
if (Input::file("{$entityType}_file")) {
|
||||
$files[$entityType] = Input::file("{$entityType}_file")->getRealPath();
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->importService->import($source, $files);
|
||||
Session::flash('message', trans('texts.imported_file') . ' - ' . $result);
|
||||
} catch (Exception $exception) {
|
||||
Session::flash('error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return Redirect::to('/settings/' . ACCOUNT_IMPORT_EXPORT);
|
||||
}
|
||||
}
|
||||
|
||||
public function doImportCSV()
|
||||
{
|
||||
$map = Input::get('map');
|
||||
$hasHeaders = Input::get('header_checkbox');
|
||||
|
||||
try {
|
||||
$count = $this->importService->importCSV($map, $hasHeaders);
|
||||
$message = Utils::pluralize('created_client', $count);
|
||||
|
||||
Session::flash('message', $message);
|
||||
if ($source === IMPORT_CSV) {
|
||||
$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);
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
Session::flash('error', $exception->getMessage());
|
||||
}
|
||||
|
||||
return Redirect::to('/settings/' . ACCOUNT_IMPORT_EXPORT);
|
||||
}
|
||||
|
||||
public function doImportCSV()
|
||||
{
|
||||
$map = Input::get('map');
|
||||
$headers = Input::get('headers');
|
||||
|
||||
//try {
|
||||
$count = $this->importService->importCSV($map, $headers);
|
||||
$message = Utils::pluralize('created_client', $count);
|
||||
|
||||
Session::flash('message', $message);
|
||||
//} catch (Exception $exception) {
|
||||
// Session::flash('error', $exception->getMessage());
|
||||
//}
|
||||
|
||||
return Redirect::to('/settings/' . ACCOUNT_IMPORT_EXPORT);
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class StartupCheck
|
||||
}
|
||||
|
||||
// Ensure all request are over HTTPS in production
|
||||
if (Utils::isNinjaProd() && !Request::secure()) {
|
||||
if (Utils::requireHTTPS() && !Request::secure()) {
|
||||
return Redirect::secure(Request::getRequestUri());
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,11 @@ class Utils
|
||||
return isset($_ENV['NINJA_DEV']) && $_ENV['NINJA_DEV'] == 'true';
|
||||
}
|
||||
|
||||
public static function requireHTTPS()
|
||||
{
|
||||
return Utils::isNinjaProd() || (isset($_ENV['REQUIRE_HTTPS']) && $_ENV['REQUIRE_HTTPS'] == 'true');
|
||||
}
|
||||
|
||||
public static function isOAuthEnabled()
|
||||
{
|
||||
$providers = [
|
||||
|
@ -39,15 +39,53 @@ class Client extends EntityModel
|
||||
'website',
|
||||
];
|
||||
|
||||
public static $fieldName = 'Client - Name';
|
||||
public static $fieldPhone = 'Client - Phone';
|
||||
public static $fieldAddress1 = 'Client - Street';
|
||||
public static $fieldAddress2 = 'Client - Apt/Floor';
|
||||
public static $fieldCity = 'Client - City';
|
||||
public static $fieldState = 'Client - State';
|
||||
public static $fieldPostalCode = 'Client - Postal Code';
|
||||
public static $fieldNotes = 'Client - Notes';
|
||||
public static $fieldCountry = 'Client - Country';
|
||||
public static $fieldName = 'name';
|
||||
public static $fieldPhone = 'work_phone';
|
||||
public static $fieldAddress1 = 'address1';
|
||||
public static $fieldAddress2 = 'address2';
|
||||
public static $fieldCity = 'city';
|
||||
public static $fieldState = 'state';
|
||||
public static $fieldPostalCode = 'postal_code';
|
||||
public static $fieldNotes = 'notes';
|
||||
public static $fieldCountry = 'country';
|
||||
|
||||
public static function getImportColumns()
|
||||
{
|
||||
return [
|
||||
Client::$fieldName,
|
||||
Client::$fieldPhone,
|
||||
Client::$fieldAddress1,
|
||||
Client::$fieldAddress2,
|
||||
Client::$fieldCity,
|
||||
Client::$fieldState,
|
||||
Client::$fieldPostalCode,
|
||||
Client::$fieldCountry,
|
||||
Client::$fieldNotes,
|
||||
Contact::$fieldFirstName,
|
||||
Contact::$fieldLastName,
|
||||
Contact::$fieldPhone,
|
||||
Contact::$fieldEmail,
|
||||
];
|
||||
}
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
|
||||
public function account()
|
||||
{
|
||||
|
@ -17,10 +17,10 @@ class Contact extends EntityModel
|
||||
'send_invoice',
|
||||
];
|
||||
|
||||
public static $fieldFirstName = 'Contact - First Name';
|
||||
public static $fieldLastName = 'Contact - Last Name';
|
||||
public static $fieldEmail = 'Contact - Email';
|
||||
public static $fieldPhone = 'Contact - Phone';
|
||||
public static $fieldFirstName = 'first_name';
|
||||
public static $fieldLastName = 'last_name';
|
||||
public static $fieldEmail = 'email';
|
||||
public static $fieldPhone = 'phone';
|
||||
|
||||
public function account()
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ use DateTime;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Models\BalanceAffecting;
|
||||
use App\Models\Client;
|
||||
use App\Events\QuoteWasCreated;
|
||||
use App\Events\QuoteWasUpdated;
|
||||
use App\Events\InvoiceWasCreated;
|
||||
@ -29,6 +30,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
'auto_bill' => 'boolean',
|
||||
];
|
||||
|
||||
// used for custom invoice numbers
|
||||
public static $patternFields = [
|
||||
'counter',
|
||||
'custom1',
|
||||
@ -38,6 +40,40 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
'date:',
|
||||
];
|
||||
|
||||
public static $fieldInvoiceNumber = 'invoice_number';
|
||||
public static $fieldInvoiceDate = 'invoice_date';
|
||||
public static $fieldDueDate = 'due_date';
|
||||
public static $fieldAmount = 'amount';
|
||||
public static $fieldPaid = 'paid';
|
||||
public static $fieldNotes = 'notes';
|
||||
public static $fieldTerms = 'terms';
|
||||
|
||||
public static function getImportColumns()
|
||||
{
|
||||
return [
|
||||
Client::$fieldName,
|
||||
Invoice::$fieldInvoiceNumber,
|
||||
Invoice::$fieldInvoiceDate,
|
||||
Invoice::$fieldDueDate,
|
||||
Invoice::$fieldAmount,
|
||||
Invoice::$fieldPaid,
|
||||
Invoice::$fieldNotes,
|
||||
Invoice::$fieldTerms,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getImportMap()
|
||||
{
|
||||
return [
|
||||
'number' => Invoice::$fieldInvoiceNumber,
|
||||
'amount' => Invoice::$fieldAmount,
|
||||
'organization' => 'name',
|
||||
'paid' => 'paid',
|
||||
'create_date' => Invoice::$fieldInvoiceDate,
|
||||
'terms' => 'terms',
|
||||
'notes' => 'notes',
|
||||
];
|
||||
}
|
||||
public function getRoute()
|
||||
{
|
||||
$entityType = $this->getEntityType();
|
||||
|
40
app/Ninja/Import/CSV/ClientTransformer.php
Normal file
40
app/Ninja/Import/CSV/ClientTransformer.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php namespace App\Ninja\Import\CSV;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use App\Models\Country;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
class ClientTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform($data, $maps)
|
||||
{
|
||||
if (isset($maps[ENTITY_CLIENT][$data->name])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($maps['countries'][$data->country])) {
|
||||
$data->country_id = $maps['countries'][$data->country];
|
||||
}
|
||||
|
||||
return new Item($data, function ($data) use ($maps) {
|
||||
return [
|
||||
'name' => isset($data->name) ? $data->name : null,
|
||||
'work_phone' => isset($data->work_phone) ? $data->work_phone : null,
|
||||
'address1' => isset($data->address1) ? $data->address1 : null,
|
||||
'city' => isset($data->city) ? $data->city : null,
|
||||
'state' => isset($data->state) ? $data->state : null,
|
||||
'postal_code' => isset($data->postal_code) ? $data->postal_code : null,
|
||||
'private_notes' => isset($data->notes) ? $data->notes : null,
|
||||
'contacts' => [
|
||||
[
|
||||
'first_name' => isset($data->first_name) ? $data->first_name : null,
|
||||
'last_name' => isset($data->last_name) ? $data->last_name : null,
|
||||
'email' => isset($data->email) ? $data->email : null,
|
||||
'phone' => isset($data->phone) ? $data->phone : null,
|
||||
],
|
||||
],
|
||||
'country_id' => isset($data->country_id) ? $data->country_id : null,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
40
app/Ninja/Import/CSV/InvoiceTransformer.php
Normal file
40
app/Ninja/Import/CSV/InvoiceTransformer.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php namespace App\Ninja\Import\CSV;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use League\Fractal\Resource\Item;
|
||||
use App\Models\Client;
|
||||
|
||||
class InvoiceTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform($data, $maps)
|
||||
{
|
||||
if (isset($maps[ENTITY_INVOICE][$data->invoice_number])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($maps[ENTITY_CLIENT][$data->name])) {
|
||||
$data->client_id = $maps[ENTITY_CLIENT][$data->name];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Item($data, function ($data) use ($maps) {
|
||||
return [
|
||||
'invoice_number' => isset($data->invoice_number) ? $data->invoice_number : null,
|
||||
'paid' => isset($data->paid) ? (float) $data->paid : null,
|
||||
'client_id' => (int) $data->client_id,
|
||||
'po_number' => isset($data->po_number) ? $data->po_number : null,
|
||||
'terms' => isset($data->terms) ? $data->terms : null,
|
||||
'public_notes' => isset($data->notes) ? $data->notes : null,
|
||||
'invoice_date_sql' => isset($data->create_date) ? $data->create_date : null,
|
||||
'invoice_items' => [
|
||||
[
|
||||
'notes' => isset($data->notes) ? $data->notes : null,
|
||||
'cost' => isset($data->amount) ? (float) $data->amount : null,
|
||||
'qty' => 1,
|
||||
]
|
||||
],
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
19
app/Ninja/Import/CSV/PaymentTransformer.php
Normal file
19
app/Ninja/Import/CSV/PaymentTransformer.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php namespace App\Ninja\Import\CSV;
|
||||
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
class PaymentTransformer extends TransformerAbstract
|
||||
{
|
||||
public function transform($data, $maps)
|
||||
{
|
||||
return new Item($data, function ($data) use ($maps) {
|
||||
return [
|
||||
'amount' => $data->paid,
|
||||
'payment_date_sql' => $data->create_date,
|
||||
'client_id' => $data->client_id,
|
||||
'invoice_id' => $data->invoice_id,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
<?php namespace App\Services;
|
||||
<?php namespace app\Services;
|
||||
|
||||
use stdClass;
|
||||
use Excel;
|
||||
use Cache;
|
||||
use Exception;
|
||||
use Auth;
|
||||
use Utils;
|
||||
use parsecsv;
|
||||
use Session;
|
||||
use Validator;
|
||||
@ -14,7 +14,7 @@ use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
use App\Ninja\Serializers\ArraySerializer;
|
||||
use App\Models\Client;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Invoice;
|
||||
|
||||
class ImportService
|
||||
{
|
||||
@ -53,54 +53,109 @@ class ImportService
|
||||
public function import($source, $files)
|
||||
{
|
||||
$imported_files = null;
|
||||
|
||||
|
||||
foreach ($files as $entityType => $file) {
|
||||
$this->execute($source, $entityType, $file);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$transformerClassName = $this->getTransformerClassName($source, $entityType);
|
||||
$transformer = new $transformerClassName;
|
||||
|
||||
Excel::load($file, function($reader) use ($source, $entityType, $transformer) {
|
||||
|
||||
if ($entityType === ENTITY_CLIENT) {
|
||||
$totalClients = count($reader->all()) + Client::scope()->withTrashed()->count();
|
||||
if ($totalClients > Auth::user()->getMaxNumClients()) {
|
||||
throw new Exception(trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]));
|
||||
}
|
||||
}
|
||||
Excel::load($file, function ($reader) use ($source, $entityType, $transformer) {
|
||||
|
||||
$this->checkData($entityType, count($reader->all()));
|
||||
$maps = $this->createMaps();
|
||||
|
||||
$reader->each(function($row) use ($source, $entityType, $transformer, $maps) {
|
||||
if ($resource = $transformer->transform($row, $maps)) {
|
||||
$data = $this->fractal->createData($resource)->toArray();
|
||||
|
||||
if ($this->validate($data, $entityType) !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity = $this->{"{$entityType}Repo"}->save($data);
|
||||
|
||||
// if the invoice is paid we'll also create a payment record
|
||||
if ($entityType === ENTITY_INVOICE && isset($data['paid']) && $data['paid']) {
|
||||
$class = self::getTransformerClassName($source, ENTITY_PAYMENT);
|
||||
$paymentTransformer = new $class;
|
||||
$row->client_id = $data['client_id'];
|
||||
$row->invoice_id = $entity->public_id;
|
||||
if ($resource = $paymentTransformer->transform($row, $maps)) {
|
||||
$data = $this->fractal->createData($resource)->toArray();
|
||||
$this->paymentRepo->save($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
$reader->each(function ($row) use ($source, $entityType, $transformer, $maps) {
|
||||
$this->saveData($source, $entityType, $row, $maps);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
private function saveData($source, $entityType, $row, $maps)
|
||||
{
|
||||
$transformer = $this->getTransformer($source, $entityType);
|
||||
$resource = $transformer->transform($row, $maps);
|
||||
|
||||
if (!$resource) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->fractal->createData($resource)->toArray();
|
||||
|
||||
if ($this->validate($data, $entityType) !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity = $this->{"{$entityType}Repo"}->save($data);
|
||||
|
||||
// if the invoice is paid we'll also create a payment record
|
||||
if ($entityType === ENTITY_INVOICE && isset($data['paid']) && $data['paid']) {
|
||||
$this->createPayment($source, $row, $maps, $data['client_id'], $entity->public_id);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkData($entityType, $count)
|
||||
{
|
||||
if ($entityType === ENTITY_CLIENT) {
|
||||
$this->checkClientCount($count);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getTransformerClassName($source, $entityType)
|
||||
{
|
||||
return 'App\\Ninja\\Import\\'.$source.'\\'.ucwords($entityType).'Transformer';
|
||||
}
|
||||
|
||||
public static function getTransformer($source, $entityType)
|
||||
{
|
||||
$className = self::getTransformerClassName($source, $entityType);
|
||||
|
||||
return new $className();
|
||||
}
|
||||
|
||||
private function createPayment($source, $data, $maps, $clientId, $invoiceId)
|
||||
{
|
||||
$paymentTransformer = $this->getTransformer($source, ENTITY_PAYMENT);
|
||||
|
||||
$row->client_id = $clientId;
|
||||
$row->invoice_id = $invoiceId;
|
||||
|
||||
if ($resource = $paymentTransformer->transform($data, $maps)) {
|
||||
$data = $this->fractal->createData($resource)->toArray();
|
||||
$this->paymentRepo->save($data);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -109,7 +164,8 @@ class ImportService
|
||||
$rules = [
|
||||
'contacts' => 'valid_contacts',
|
||||
];
|
||||
} if ($entityType === ENTITY_INVOICE) {
|
||||
}
|
||||
if ($entityType === ENTITY_INVOICE) {
|
||||
$rules = [
|
||||
'client.contacts' => 'valid_contacts',
|
||||
'invoice_items' => 'valid_invoice_items',
|
||||
@ -122,6 +178,7 @@ class ImportService
|
||||
|
||||
if ($validator->fails()) {
|
||||
$messages = $validator->messages();
|
||||
|
||||
return $messages->first();
|
||||
} else {
|
||||
return true;
|
||||
@ -155,42 +212,50 @@ class ImportService
|
||||
];
|
||||
}
|
||||
|
||||
public static function getTransformerClassName($source, $entityType)
|
||||
public function mapCSV($files)
|
||||
{
|
||||
return 'App\\Ninja\\Import\\' . $source . '\\' . ucwords($entityType) . 'Transformer';
|
||||
$data = [];
|
||||
|
||||
foreach ($files as $entityType => $filename) {
|
||||
if ($entityType === ENTITY_CLIENT) {
|
||||
$columns = Client::getImportColumns();
|
||||
$map = Client::getImportMap();
|
||||
} else {
|
||||
$columns = Invoice::getImportColumns();
|
||||
$map = Invoice::getImportMap();
|
||||
}
|
||||
|
||||
// Lookup field translations
|
||||
foreach ($columns as $key => $value) {
|
||||
unset($columns[$key]);
|
||||
$columns[$value] = trans("texts.{$value}");
|
||||
}
|
||||
array_unshift($columns, ' ');
|
||||
|
||||
$data[$entityType] = $this->mapFile($entityType, $filename, $columns, $map);
|
||||
|
||||
if ($entityType === ENTITY_CLIENT) {
|
||||
if (count($data[$entityType]['data']) + Client::scope()->count() > Auth::user()->getMaxNumClients()) {
|
||||
throw new Exception(trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function mapFile($filename)
|
||||
public function mapFile($entityType, $filename, $columns, $map)
|
||||
{
|
||||
require_once app_path().'/Includes/parsecsv.lib.php';
|
||||
$csv = new parseCSV();
|
||||
$csv->heading = false;
|
||||
$csv->auto($filename);
|
||||
|
||||
if (count($csv->data) + Client::scope()->count() > Auth::user()->getMaxNumClients()) {
|
||||
throw new Exception(trans('texts.limit_clients', ['count' => Auth::user()->getMaxNumClients()]));
|
||||
}
|
||||
|
||||
Session::put('data', $csv->data);
|
||||
Session::put("{$entityType}-data", $csv->data);
|
||||
|
||||
$headers = false;
|
||||
$hasHeaders = false;
|
||||
$mapped = array();
|
||||
$columns = array('',
|
||||
Client::$fieldName,
|
||||
Client::$fieldPhone,
|
||||
Client::$fieldAddress1,
|
||||
Client::$fieldAddress2,
|
||||
Client::$fieldCity,
|
||||
Client::$fieldState,
|
||||
Client::$fieldPostalCode,
|
||||
Client::$fieldCountry,
|
||||
Client::$fieldNotes,
|
||||
Contact::$fieldFirstName,
|
||||
Contact::$fieldLastName,
|
||||
Contact::$fieldPhone,
|
||||
Contact::$fieldEmail,
|
||||
);
|
||||
|
||||
if (count($csv->data) > 0) {
|
||||
$headers = $csv->data[0];
|
||||
@ -206,29 +271,11 @@ class ImportService
|
||||
$mapped[$i] = '';
|
||||
|
||||
if ($hasHeaders) {
|
||||
$map = array(
|
||||
'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,
|
||||
);
|
||||
|
||||
foreach ($map as $search => $column) {
|
||||
foreach (explode("|", $search) as $string) {
|
||||
if (strpos($title, 'sec') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strpos($title, $string) !== false) {
|
||||
} elseif (strpos($title, $string) !== false) {
|
||||
$mapped[$i] = $column;
|
||||
break(2);
|
||||
}
|
||||
@ -239,6 +286,7 @@ class ImportService
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'entityType' => $entityType,
|
||||
'data' => $csv->data,
|
||||
'headers' => $headers,
|
||||
'hasHeaders' => $hasHeaders,
|
||||
@ -249,78 +297,35 @@ class ImportService
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function importCSV($map, $hasHeaders)
|
||||
public function importCSV($maps, $headers)
|
||||
{
|
||||
$count = 0;
|
||||
$data = Session::get('data');
|
||||
$countries = Cache::get('countries');
|
||||
$countryMap = [];
|
||||
|
||||
foreach ($countries as $country) {
|
||||
$countryMap[strtolower($country->name)] = $country->id;
|
||||
foreach ($maps as $entityType => $map) {
|
||||
$this->executeCSV($entityType, $map, $headers[$entityType]);
|
||||
}
|
||||
|
||||
foreach ($data as $row) {
|
||||
if ($hasHeaders) {
|
||||
$hasHeaders = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'contacts' => [[]]
|
||||
];
|
||||
|
||||
foreach ($row as $index => $value) {
|
||||
$field = $map[$index];
|
||||
if ( ! $value = trim($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($field == Client::$fieldName) {
|
||||
$data['name'] = $value;
|
||||
} elseif ($field == Client::$fieldPhone) {
|
||||
$data['work_phone'] = $value;
|
||||
} elseif ($field == Client::$fieldAddress1) {
|
||||
$data['address1'] = $value;
|
||||
} elseif ($field == Client::$fieldAddress2) {
|
||||
$data['address2'] = $value;
|
||||
} elseif ($field == Client::$fieldCity) {
|
||||
$data['city'] = $value;
|
||||
} elseif ($field == Client::$fieldState) {
|
||||
$data['state'] = $value;
|
||||
} elseif ($field == Client::$fieldPostalCode) {
|
||||
$data['postal_code'] = $value;
|
||||
} elseif ($field == Client::$fieldCountry) {
|
||||
$value = strtolower($value);
|
||||
$data['country_id'] = isset($countryMap[$value]) ? $countryMap[$value] : null;
|
||||
} elseif ($field == Client::$fieldNotes) {
|
||||
$data['private_notes'] = $value;
|
||||
} elseif ($field == Contact::$fieldFirstName) {
|
||||
$data['contacts'][0]['first_name'] = $value;
|
||||
} elseif ($field == Contact::$fieldLastName) {
|
||||
$data['contacts'][0]['last_name'] = $value;
|
||||
} elseif ($field == Contact::$fieldPhone) {
|
||||
$data['contacts'][0]['phone'] = $value;
|
||||
} elseif ($field == Contact::$fieldEmail) {
|
||||
$data['contacts'][0]['email'] = strtolower($value);
|
||||
}
|
||||
}
|
||||
|
||||
$rules = [
|
||||
'contacts' => 'valid_contacts',
|
||||
];
|
||||
$validator = Validator::make($data, $rules);
|
||||
if ($validator->fails()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->clientRepo->save($data);
|
||||
$count++;
|
||||
}
|
||||
|
||||
Session::forget('data');
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function convertToObject($entityType, $data, $map)
|
||||
{
|
||||
$obj = new stdClass();
|
||||
|
||||
if ($entityType === ENTITY_CLIENT) {
|
||||
$columns = Client::getImportColumns();
|
||||
} else {
|
||||
$columns = Invoice::getImportColumns();
|
||||
}
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$obj->$column = false;
|
||||
}
|
||||
|
||||
foreach ($map as $index => $field) {
|
||||
if (! $field) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$obj->$field = $data[$index];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,6 @@ We offer two options:
|
||||
* 10% of revenue
|
||||
* $1,000 for a site limited to 1,000 accounts
|
||||
|
||||
If you'd like to use our code to sell your own invoicing app email us for details about our affiliate program.
|
||||
|
||||
### Installation Options
|
||||
* [Self-Host Zip](https://www.invoiceninja.com/knowledgebase/self-host/) - Free
|
||||
* [Docker File](https://github.com/invoiceninja/dockerfiles) - Free
|
||||
|
@ -87,7 +87,7 @@ return array(
|
||||
'guest' => 'Guest',
|
||||
'company_details' => 'Company Details',
|
||||
'online_payments' => 'Online Payments',
|
||||
'notifications' => 'Notifications',
|
||||
'notifications' => 'Email Notifications',
|
||||
'import_export' => 'Import/Export',
|
||||
'done' => 'Done',
|
||||
'save' => 'Save',
|
||||
@ -334,7 +334,7 @@ return array(
|
||||
// product management
|
||||
'product_library' => 'Product Library',
|
||||
'product' => 'Product',
|
||||
'products' => 'Products',
|
||||
'products' => 'Product Library',
|
||||
'fill_products' => 'Auto-fill products',
|
||||
'fill_products_help' => 'Selecting a product will automatically <b>fill in the description and cost</b>',
|
||||
'update_products' => 'Auto-update products',
|
||||
@ -945,4 +945,8 @@ return array(
|
||||
'admin' => 'Admin',
|
||||
'disabled' => 'Disabled',
|
||||
'show_archived_users' => 'Show archived users',
|
||||
'notes' => 'Notes',
|
||||
'invoice_will_create' => 'client will be created',
|
||||
'invoices_will_create' => 'invoices will be created',
|
||||
|
||||
);
|
||||
|
@ -4,7 +4,6 @@
|
||||
@parent
|
||||
|
||||
<style type="text/css">
|
||||
.invoice-file,
|
||||
.task-file {
|
||||
display: none;
|
||||
}
|
||||
@ -131,15 +130,11 @@
|
||||
@endforeach
|
||||
@foreach (\App\Services\ImportService::$sources as $source)
|
||||
if (val === '{{ $source }}') {
|
||||
@if ($source == IMPORT_CSV)
|
||||
$('.client-file').show();
|
||||
@else
|
||||
@foreach (\App\Services\ImportService::$entityTypes as $entityType)
|
||||
@if (class_exists(\App\Services\ImportService::getTransformerClassName($source, $entityType)))
|
||||
$('.{{ $entityType }}-file').show();
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@foreach (\App\Services\ImportService::$entityTypes as $entityType)
|
||||
@if (class_exists(\App\Services\ImportService::getTransformerClassName($source, $entityType)))
|
||||
$('.{{ $entityType }}-file').show();
|
||||
@endif
|
||||
@endforeach
|
||||
}
|
||||
@endforeach
|
||||
}
|
||||
|
@ -7,83 +7,18 @@
|
||||
|
||||
{!! Former::open('/import_csv')->addClass('warn-on-exit') !!}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.import_clients') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
@if (isset($data[ENTITY_CLIENT]))
|
||||
@include('accounts.partials.map', $data[ENTITY_CLIENT])
|
||||
@endif
|
||||
|
||||
@if ($headers)
|
||||
@if (isset($data[ENTITY_INVOICE]))
|
||||
@include('accounts.partials.map', $data[ENTITY_INVOICE])
|
||||
@endif
|
||||
|
||||
<label for="header_checkbox">
|
||||
<input type="checkbox" name="header_checkbox" id="header_checkbox" {{ $hasHeaders ? 'CHECKED' : '' }}> {{ trans('texts.first_row_headers') }}
|
||||
</label>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<table class="table invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ trans('texts.column') }}</th>
|
||||
<th class="col_sample">{{ trans('texts.sample') }}</th>
|
||||
<th>{{ trans('texts.import_to') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@for ($i=0; $i<count($headers); $i++)
|
||||
<tr>
|
||||
<td>{{ $headers[$i] }}</td>
|
||||
<td class="col_sample">{{ $data[1][$i] }}</td>
|
||||
<td>{!! Former::select('map[' . $i . ']')->options($columns, $mapped[$i], true)->raw() !!}</td>
|
||||
</tr>
|
||||
@endfor
|
||||
</table>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<span id="numClients"></span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{!! Former::actions(
|
||||
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/import_export'))->appendIcon(Icon::create('remove-circle')),
|
||||
Button::success(trans('texts.import'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
|
||||
var numClients = {{ count($data) }};
|
||||
function setSampleShown() {
|
||||
if ($('#header_checkbox').is(':checked')) {
|
||||
$('.col_sample').show();
|
||||
setNumClients(numClients - 1);
|
||||
} else {
|
||||
$('.col_sample').hide();
|
||||
setNumClients(numClients);
|
||||
}
|
||||
}
|
||||
|
||||
function setNumClients(num)
|
||||
{
|
||||
if (num == 1)
|
||||
{
|
||||
$('#numClients').html("1 {{ trans('texts.client_will_create') }}");
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#numClients').html(num + " {{ trans('texts.clients_will_create') }}");
|
||||
}
|
||||
}
|
||||
|
||||
$('#header_checkbox').click(setSampleShown);
|
||||
setSampleShown();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
{!! Former::actions(
|
||||
Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/settings/import_export'))->appendIcon(Icon::create('remove-circle')),
|
||||
Button::success(trans('texts.import'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))) !!}
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
@stop
|
66
resources/views/accounts/partials/map.blade.php
Normal file
66
resources/views/accounts/partials/map.blade.php
Normal file
@ -0,0 +1,66 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans("texts.import_{$entityType}s") !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<label for="{{ $entityType }}_header_checkbox">
|
||||
<input type="checkbox" name="headers[{{ $entityType }}]" id="{{ $entityType }}_header_checkbox" {{ $hasHeaders ? 'CHECKED' : '' }}> {{ trans('texts.first_row_headers') }}
|
||||
</label>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<table class="table invoice-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ trans('texts.column') }}</th>
|
||||
<th class="col_sample">{{ trans('texts.sample') }}</th>
|
||||
<th>{{ trans('texts.import_to') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@for ($i=0; $i<count($headers); $i++)
|
||||
<tr>
|
||||
<td>{{ $headers[$i] }}</td>
|
||||
<td class="col_sample">{{ $data[1][$i] }}</td>
|
||||
<td>{!! Former::select('map['.$entityType.'][' . $i . ']')->options($columns, $mapped[$i])->raw() !!}</td>
|
||||
</tr>
|
||||
@endfor
|
||||
</table>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<span id="num{{ $entityType }}"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(function() {
|
||||
|
||||
var num{{ $entityType }} = {{ count($data) }};
|
||||
function set{{ $entityType }}SampleShown() {
|
||||
if ($('#{{ $entityType }}_header_checkbox').is(':checked')) {
|
||||
$('.col_sample').show();
|
||||
setNum{{ $entityType }}(num{{ $entityType }} - 1);
|
||||
} else {
|
||||
$('.col_sample').hide();
|
||||
setNum{{ $entityType }}(num{{ $entityType }});
|
||||
}
|
||||
}
|
||||
|
||||
function setNum{{ $entityType }}(num)
|
||||
{
|
||||
if (num == 1) {
|
||||
$('#num{{ $entityType }}').html("1 {{ trans("texts.{$entityType}_will_create") }}");
|
||||
} else {
|
||||
$('#num{{ $entityType }}').html(num + " {{ trans("texts.{$entityType}s_will_create") }}");
|
||||
}
|
||||
}
|
||||
|
||||
$('#{{ $entityType }}_header_checkbox').click(set{{ $entityType }}SampleShown);
|
||||
set{{ $entityType }}SampleShown();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user