1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-05 18:52:44 +01:00

Merge pull request #4502 from turbo124/v5-develop

Client CSV Imports
This commit is contained in:
David Bomba 2020-12-16 21:13:32 +11:00 committed by GitHub
commit e6419085b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
172 changed files with 773 additions and 37 deletions

View File

@ -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

View File

@ -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)
{

View File

@ -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'
];
}

View 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',
];
}
}

View 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;
}
}

View 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,
];
}
}

View File

@ -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);
}
}

View File

@ -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
View File

@ -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
View File

0
public/assets/AssetManifest.json Normal file → Executable file
View File

0
public/assets/FontManifest.json Normal file → Executable file
View File

0
public/assets/LICENSE Normal file → Executable file
View File

0
public/assets/NOTICES Normal file → Executable file
View File

0
public/assets/assets/images/google-icon.png Normal file → Executable file
View 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
View 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
View 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
View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View 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
View 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
View 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
View 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
View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

0
public/assets/fonts/MaterialIcons-Regular.ttf Normal file → Executable file
View File

0
public/assets/fonts/Roboto-Regular.ttf Normal file → Executable file
View File

View File

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 881 B

After

Width:  |  Height:  |  Size: 881 B

View File

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 704 B

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 162 B

After

Width:  |  Height:  |  Size: 162 B

View File

0
public/assets/web/assets/fonts/Roboto-Regular.ttf Normal file → Executable file
View File

0
public/css/app.css vendored Normal file → Executable file
View File

0
public/css/card-js.min.css vendored Normal file → Executable file
View File

0
public/css/ninja.css vendored Normal file → Executable file
View File

0
public/css/ninja.min.css vendored Normal file → Executable file
View File

0
public/css/tailwind-1.2.0.css vendored Normal file → Executable file
View File

0
public/css/tailwindcss@1.4.6.css vendored Normal file → Executable file
View File

0
public/favicon.ico Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

0
public/favicon.png Normal file → Executable file
View 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
View File

0
public/icons/Icon-192.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

0
public/icons/Icon-512.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

0
public/images/american-express.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

0
public/images/created-by-invoiceninja.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

0
public/images/diners-club.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

0
public/images/discover.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

0
public/images/invoiceninja-white-logo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

0
public/images/logo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

0
public/images/mastercard.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

0
public/images/paypal.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

0
public/images/svg/activity.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

0
public/images/svg/align-left.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 388 B

After

Width:  |  Height:  |  Size: 388 B

0
public/images/svg/clock.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 299 B

After

Width:  |  Height:  |  Size: 299 B

0
public/images/svg/credit-card.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

0
public/images/svg/download.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 365 B

0
public/images/svg/file-text.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 468 B

After

Width:  |  Height:  |  Size: 468 B

0
public/images/svg/file.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 332 B

0
public/images/svg/shield.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 274 B

After

Width:  |  Height:  |  Size: 274 B

0
public/images/svg/user.svg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 308 B

0
public/images/visa.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

0
public/index.php Normal file → Executable file
View File

0
public/js/app.js vendored Normal file → Executable file
View File

0
public/js/client_create.js vendored Normal file → Executable file
View File

0
public/js/client_create.min.js vendored Normal file → Executable file
View File

0
public/js/client_edit.js vendored Normal file → Executable file
View File

0
public/js/client_edit.min.js vendored Normal file → Executable file
View File

0
public/js/client_list.js vendored Normal file → Executable file
View File

0
public/js/client_list.min.js vendored Normal file → Executable file
View File

0
public/js/client_settings.js vendored Normal file → Executable file
View File

0
public/js/client_settings.min.js vendored Normal file → Executable file
View File

0
public/js/client_show.js vendored Normal file → Executable file
View File

0
public/js/client_show.min.js vendored Normal file → Executable file
View File

0
public/js/clients/invoices/action-selectors.js vendored Normal file → Executable file
View File

View File

0
public/js/clients/invoices/payment.js vendored Normal file → Executable file
View File

0
public/js/clients/invoices/payment.js.LICENSE.txt Normal file → Executable file
View File

0
public/js/clients/payment_methods/authorize-ach.js vendored Normal file → Executable file
View File

View File

0
public/js/clients/payment_methods/authorize-authorize-card.js vendored Normal file → Executable file
View File

0
public/js/clients/payment_methods/authorize-stripe-card.js vendored Normal file → Executable file
View File

View File

0
public/js/clients/payment_methods/stripe-ach.js vendored Normal file → Executable file
View File

0
public/js/clients/payments/alipay.js vendored Normal file → Executable file
View File

0
public/js/clients/payments/alipay.js.LICENSE.txt Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More