mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 13:12:50 +01:00
commit
6c968f1390
@ -1,6 +1,6 @@
|
||||
APP_NAME="Invoice Ninja"
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_KEY=base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno=
|
||||
APP_DEBUG=false
|
||||
|
||||
APP_URL=http://localhost
|
||||
|
@ -1 +1 @@
|
||||
5.3.54
|
||||
5.3.55
|
@ -19,6 +19,7 @@ use App\Http\Requests\Client\AdjustClientLedgerRequest;
|
||||
use App\Http\Requests\Client\CreateClientRequest;
|
||||
use App\Http\Requests\Client\DestroyClientRequest;
|
||||
use App\Http\Requests\Client\EditClientRequest;
|
||||
use App\Http\Requests\Client\PurgeClientRequest;
|
||||
use App\Http\Requests\Client\ShowClientRequest;
|
||||
use App\Http\Requests\Client\StoreClientRequest;
|
||||
use App\Http\Requests\Client\UpdateClientRequest;
|
||||
@ -36,7 +37,7 @@ use App\Utils\Traits\SavesDocuments;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
/**
|
||||
* Class ClientController.
|
||||
* @covers App\Http\Controllers\ClientController
|
||||
@ -510,7 +511,7 @@ class ClientController extends BaseController
|
||||
$ids = request()->input('ids');
|
||||
$clients = Client::withTrashed()->whereIn('id', $this->transformKeys($ids))->cursor();
|
||||
|
||||
if(!in_array($action, ['restore','archive','delete','purge']))
|
||||
if(!in_array($action, ['restore','archive','delete']))
|
||||
return response()->json(['message' => 'That action is not available.'], 400);
|
||||
|
||||
$clients->each(function ($client, $key) use ($action) {
|
||||
@ -586,5 +587,71 @@ class ClientController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UploadClientRequest $request
|
||||
* @param Client $client
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/clients/{id}/purge",
|
||||
* operationId="uploadClient",
|
||||
* tags={"clients"},
|
||||
* summary="Purges a client from the system",
|
||||
* description="Handles purging a client",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Client Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit")
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function purge(PurgeClientRequest $request, Client $client)
|
||||
{
|
||||
//delete all documents
|
||||
$client->documents->each(function ($document){
|
||||
|
||||
Storage::disk(config('filesystems.default'))->delete($document->url);
|
||||
|
||||
});
|
||||
|
||||
//force delete the client
|
||||
$this->client_repo->purge($client);
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
//todo add an event here using the client name as reference for purge event
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,6 +24,12 @@ class SubscriptionPurchaseController extends Controller
|
||||
{
|
||||
public function index(Subscription $subscription, Request $request)
|
||||
{
|
||||
/* Make sure the contact is logged into the correct company for this subscription */
|
||||
if(auth()->guard('contact')->user() && auth()->guard('contact')->user()->company_id != $subscription->company_id){
|
||||
auth()->guard('contact')->logout();
|
||||
$request->session()->invalidate();
|
||||
}
|
||||
|
||||
if ($request->has('locale')) {
|
||||
$this->setLocale($request->query('locale'));
|
||||
}
|
||||
|
27
app/Http/Requests/Client/PurgeClientRequest.php
Normal file
27
app/Http/Requests/Client/PurgeClientRequest.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Client;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class PurgeClientRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
}
|
@ -39,6 +39,10 @@ class Request extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
//01-02-2022 needed for CSV Imports
|
||||
if(!$merge_rules)
|
||||
return $rules;
|
||||
|
||||
return array_merge($merge_rules, $rules);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Http\ValidationRules\Company;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
/**
|
||||
@ -25,7 +26,12 @@ class ValidCompanyQuantity implements Rule
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
return auth()->user()->company()->account->companies->count() <= 10;
|
||||
if(Ninja::isSelfHost())
|
||||
return auth()->user()->company()->account->companies->count() < 10;
|
||||
|
||||
|
||||
return auth()->user()->company()->account->companies->count() < auth()->user()->company()->account->hosted_company_count;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,7 @@ class CanStoreClientsRule implements Rule
|
||||
{
|
||||
$company = Company::find($this->company_id);
|
||||
|
||||
return $company->clients->count() < config('ninja.quotas.free.clients');
|
||||
return $company->clients->count() < $company->account->hosted_client_count;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,6 +43,6 @@ class CanStoreClientsRule implements Rule
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return ctrans('texts.limit_clients', ['count' => config('ninja.quotas.free.clients')]);
|
||||
return ctrans('texts.limit_clients', ['count' => $company->account->hosted_client_count]);
|
||||
}
|
||||
}
|
||||
|
306
app/Import/Providers/BaseImport.php
Normal file
306
app/Import/Providers/BaseImport.php
Normal file
@ -0,0 +1,306 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
use App\Import\ImportException;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
class BaseImport {
|
||||
|
||||
use CleanLineItems;
|
||||
|
||||
public Company $company;
|
||||
|
||||
public array $request;
|
||||
|
||||
public array $error_array = [];
|
||||
|
||||
public $request_name;
|
||||
|
||||
public $repository_name;
|
||||
|
||||
public $factory_name;
|
||||
|
||||
public $repository;
|
||||
|
||||
public $transformer;
|
||||
|
||||
|
||||
public function __construct( array $request, Company $company ) {
|
||||
$this->company = $company;
|
||||
$this->request = $request;
|
||||
$this->hash = $request['hash'];
|
||||
$this->import_type = $request['import_type'];
|
||||
$this->skip_header = $request['skip_header'] ?? null;
|
||||
$this->column_map =
|
||||
! empty( $request['column_map'] ) ?
|
||||
array_combine( array_keys( $request['column_map'] ), array_column( $request['column_map'], 'mapping' ) ) : null;
|
||||
|
||||
auth()->login( $this->company->owner(), true );
|
||||
|
||||
auth()->user()->setCompany($this->company);
|
||||
}
|
||||
|
||||
|
||||
protected function getCsvData( $entity_type ) {
|
||||
|
||||
$base64_encoded_csv = Cache::pull( $this->hash . '-' . $entity_type );
|
||||
if ( empty( $base64_encoded_csv ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$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 && $this->import_type === 'csv' ) {
|
||||
$first_cell = $headers[0];
|
||||
if ( strstr( $first_cell, config( 'ninja.app_name' ) ) ) {
|
||||
array_shift( $data ); // Invoice Ninja...
|
||||
array_shift( $data ); // <blank line>
|
||||
array_shift( $data ); // Enitty Type Header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function mapCSVHeaderToKeys( $csvData ) {
|
||||
$keys = array_shift( $csvData );
|
||||
|
||||
return array_map( function ( $values ) use ( $keys ) {
|
||||
return array_combine( $keys, $values );
|
||||
}, $csvData );
|
||||
}
|
||||
|
||||
private function groupInvoices( $csvData, $key ) {
|
||||
// Group by invoice.
|
||||
$grouped = [];
|
||||
|
||||
foreach ( $csvData as $line_item ) {
|
||||
if ( empty( $line_item[ $key ] ) ) {
|
||||
$this->error_array['invoice'][] = [ 'invoice' => $line_item, 'error' => 'No invoice number' ];
|
||||
} else {
|
||||
$grouped[ $line_item[ $key ] ][] = $line_item;
|
||||
}
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->error_array;
|
||||
}
|
||||
|
||||
public function ingest($data, $entity_type)
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
foreach ( $data as $record ) {
|
||||
try {
|
||||
$entity = $this->transformer->transform( $record );
|
||||
|
||||
/** @var \App\Http\Requests\Request $request */
|
||||
$request = new $this->request_name();
|
||||
|
||||
// Pass entity data to request so it can be validated
|
||||
$request->query = $request->request = new ParameterBag( $entity );
|
||||
$validator = Validator::make( $entity, $request->rules() );
|
||||
|
||||
if ( $validator->fails() ) {
|
||||
$this->error_array[ $entity_type ][] =
|
||||
[ $entity_type => $record, 'error' => $validator->errors()->all() ];
|
||||
} else {
|
||||
$entity =
|
||||
$this->repository->save(
|
||||
array_diff_key( $entity, [ 'user_id' => false ] ),
|
||||
$this->factory_name::create( $this->company->id, $this->getUserIDForRecord( $entity ) ) );
|
||||
|
||||
$entity->saveQuietly();
|
||||
$count++;
|
||||
|
||||
}
|
||||
} catch ( \Exception $ex ) {
|
||||
if ( $ex instanceof ImportException ) {
|
||||
$message = $ex->getMessage();
|
||||
} else {
|
||||
report( $ex );
|
||||
$message = 'Unknown error';
|
||||
}
|
||||
|
||||
$this->error_array[ $entity_type ][] = [ $entity_type => $record, 'error' => $message ];
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
|
||||
public function ingestInvoices( $invoices ) {
|
||||
$invoice_transformer = $this->transformer;
|
||||
|
||||
/** @var PaymentRepository $payment_repository */
|
||||
$payment_repository = app()->make( PaymentRepository::class );
|
||||
$payment_repository->import_mode = true;
|
||||
|
||||
/** @var ClientRepository $client_repository */
|
||||
$client_repository = app()->make( ClientRepository::class );
|
||||
$client_repository->import_mode = true;
|
||||
|
||||
$invoice_repository = new InvoiceRepository();
|
||||
$invoice_repository->import_mode = true;
|
||||
|
||||
foreach ( $invoices as $raw_invoice ) {
|
||||
try {
|
||||
$invoice_data = $invoice_transformer->transform( $raw_invoice );
|
||||
|
||||
$invoice_data['line_items'] = $this->cleanItems( $invoice_data['line_items'] ?? [] );
|
||||
|
||||
|
||||
// If we don't have a client ID, but we do have client data, go ahead and create the client.
|
||||
if ( empty( $invoice_data['client_id'] ) && ! empty( $invoice_data['client'] ) ) {
|
||||
$client_data = $invoice_data['client'];
|
||||
$client_data['user_id'] = $this->getUserIDForRecord( $invoice_data );
|
||||
|
||||
$client_repository->save(
|
||||
$client_data,
|
||||
$client = ClientFactory::create( $this->company->id, $client_data['user_id'] )
|
||||
);
|
||||
$invoice_data['client_id'] = $client->id;
|
||||
unset( $invoice_data['client'] );
|
||||
}
|
||||
|
||||
$validator = Validator::make( $invoice_data, ( new StoreInvoiceRequest() )->rules() );
|
||||
if ( $validator->fails() ) {
|
||||
$this->error_array['invoice'][] =
|
||||
[ 'invoice' => $invoice_data, 'error' => $validator->errors()->all() ];
|
||||
} else {
|
||||
$invoice = InvoiceFactory::create( $this->company->id, $this->getUserIDForRecord( $invoice_data ) );
|
||||
if ( ! empty( $invoice_data['status_id'] ) ) {
|
||||
$invoice->status_id = $invoice_data['status_id'];
|
||||
}
|
||||
$invoice_repository->save( $invoice_data, $invoice );
|
||||
$this->addInvoiceToMaps( $invoice );
|
||||
|
||||
// If we're doing a generic CSV import, only import payment data if we're not importing a payment CSV.
|
||||
// If we're doing a platform-specific import, trust the platform to only return payment info if there's not a separate payment CSV.
|
||||
if ( $this->import_type !== 'csv' || empty( $this->column_map['payment'] ) ) {
|
||||
// Check for payment columns
|
||||
if ( ! empty( $invoice_data['payments'] ) ) {
|
||||
foreach ( $invoice_data['payments'] as $payment_data ) {
|
||||
$payment_data['user_id'] = $invoice->user_id;
|
||||
$payment_data['client_id'] = $invoice->client_id;
|
||||
$payment_data['invoices'] = [
|
||||
[
|
||||
'invoice_id' => $invoice->id,
|
||||
'amount' => $payment_data['amount'] ?? null,
|
||||
],
|
||||
];
|
||||
|
||||
/* Make sure we don't apply any payments to invoices with a Zero Amount*/
|
||||
if($invoice->amount > 0)
|
||||
{
|
||||
$payment_repository->save(
|
||||
$payment_data,
|
||||
PaymentFactory::create( $this->company->id, $invoice->user_id, $invoice->client_id )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->actionInvoiceStatus( $invoice, $invoice_data, $invoice_repository );
|
||||
}
|
||||
} catch ( \Exception $ex ) {
|
||||
if ( $ex instanceof ImportException ) {
|
||||
$message = $ex->getMessage();
|
||||
} else {
|
||||
report( $ex );
|
||||
$message = 'Unknown error';
|
||||
}
|
||||
|
||||
$this->error_array['invoice'][] = [ 'invoice' => $raw_invoice, 'error' => $message ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected function getUserIDForRecord( $record ) {
|
||||
if ( ! empty( $record['user_id'] ) ) {
|
||||
return $this->findUser( $record['user_id'] );
|
||||
} else {
|
||||
return $this->company->owner()->id;
|
||||
}
|
||||
}
|
||||
|
||||
protected function findUser( $user_hash ) {
|
||||
$user = User::where( 'account_id', $this->company->account->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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
166
app/Import/Providers/Csv.php
Normal file
166
app/Import/Providers/Csv.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\ProductFactory;
|
||||
use App\Http\Requests\Client\StoreClientRequest;
|
||||
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
||||
use App\Http\Requests\Product\StoreProductRequest;
|
||||
use App\Import\ImportException;
|
||||
use App\Import\Providers\BaseImport;
|
||||
use App\Import\Providers\ImportInterface;
|
||||
use App\Import\Transformer\Csv\ClientTransformer;
|
||||
use App\Import\Transformer\Csv\ProductTransformer;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Repositories\ProductRepository;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
class Csv extends BaseImport implements ImportInterface
|
||||
{
|
||||
|
||||
public array $entity_count = [];
|
||||
|
||||
public function import(string $entity)
|
||||
{
|
||||
|
||||
if(in_array($entity, [ 'client', 'product', 'invoice', 'payment', 'vendor', 'expense' ]))
|
||||
$this->{$entity}();
|
||||
|
||||
//collate any errors
|
||||
}
|
||||
|
||||
private function client()
|
||||
{
|
||||
|
||||
$entity_type = 'client';
|
||||
|
||||
$data = $this->getCsvData($entity_type);
|
||||
|
||||
$data = $this->preTransform($data, $entity_type);
|
||||
|
||||
if(empty($data)){
|
||||
|
||||
$this->entity_count['clients'] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->request_name = StoreClientRequest::class;
|
||||
$this->repository_name = ClientRepository::class;
|
||||
$this->factory_name = ClientFactory::class;
|
||||
|
||||
$this->repository = app()->make( $this->repository_name );
|
||||
$this->repository->import_mode = true;
|
||||
|
||||
$this->transformer = new ClientTransformer($this->company);
|
||||
|
||||
$client_count = $this->ingest($data, $entity_type);
|
||||
|
||||
$this->entity_count['clients'] = $client_count;
|
||||
|
||||
}
|
||||
|
||||
private function product()
|
||||
{
|
||||
|
||||
$entity_type = 'product';
|
||||
|
||||
$data = $this->getCsvData($entity_type);
|
||||
|
||||
$data = $this->preTransform($data, $entity_type);
|
||||
|
||||
if(empty($data)){
|
||||
|
||||
$this->entity_count['products'] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->request_name = StoreProductRequest::class;
|
||||
$this->repository_name = ProductRepository::class;
|
||||
$this->factory_name = ProductFactory::class;
|
||||
|
||||
$this->repository = app()->make( $this->repository_name );
|
||||
$this->repository->import_mode = true;
|
||||
|
||||
$this->transformer = new ProductTransformer($this->company);
|
||||
|
||||
$product_count = $this->ingest($data, $entity_type);
|
||||
|
||||
$this->entity_count['products'] = $product_count;
|
||||
|
||||
}
|
||||
|
||||
private function invoice()
|
||||
{
|
||||
|
||||
$entity_type = 'invoice';
|
||||
|
||||
$data = $this->getCsvData($entity_type);
|
||||
|
||||
$data = $this->preTransform($data, $entity_type);
|
||||
|
||||
if(empty($data)){
|
||||
|
||||
$this->entity_count['invoices'] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->request_name = StoreInvoiceRequest::class;
|
||||
$this->repository_name = InvoiceRepository::class;
|
||||
$this->factory_name = InvoiceFactory::class;
|
||||
|
||||
$this->repository = app()->make( $this->repository_name );
|
||||
$this->repository->import_mode = true;
|
||||
|
||||
$this->transformer = new ProductTransformer($this->company);
|
||||
|
||||
$invoice_count = $this->ingest($data, $entity_type);
|
||||
|
||||
$this->entity_count['invoices'] = $invoice_count;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function preTransform(array $data, $entity_type)
|
||||
{
|
||||
|
||||
|
||||
if ( empty( $this->column_map[ $entity_type ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->skip_header ) {
|
||||
array_shift( $data );
|
||||
}
|
||||
|
||||
//sort the array by key
|
||||
$keys = $this->column_map[ $entity_type ];
|
||||
ksort( $keys );
|
||||
|
||||
$data = array_map( function ( $row ) use ( $keys ) {
|
||||
return array_combine( $keys, array_intersect_key( $row, $keys ) );
|
||||
}, $data );
|
||||
|
||||
|
||||
return $data;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function transform(array $data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
16
app/Import/Providers/Freshbooks.php
Normal file
16
app/Import/Providers/Freshbooks.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
class Freshbooks extends BaseImport
|
||||
{
|
||||
|
||||
}
|
21
app/Import/Providers/ImportInterface.php
Normal file
21
app/Import/Providers/ImportInterface.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
interface ImportInterface
|
||||
{
|
||||
|
||||
public function import(string $entity);
|
||||
|
||||
public function preTransform(array $data, string $entity_type);
|
||||
|
||||
public function transform(array $data);
|
||||
}
|
16
app/Import/Providers/Invoice2Go.php
Normal file
16
app/Import/Providers/Invoice2Go.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
class Invoice2Go extends BaseImport
|
||||
{
|
||||
|
||||
}
|
16
app/Import/Providers/Invoicely.php
Normal file
16
app/Import/Providers/Invoicely.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
class Invoicely extends BaseImport
|
||||
{
|
||||
|
||||
}
|
16
app/Import/Providers/Wave.php
Normal file
16
app/Import/Providers/Wave.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
class Wave extends BaseImport
|
||||
{
|
||||
|
||||
}
|
16
app/Import/Providers/Zoho.php
Normal file
16
app/Import/Providers/Zoho.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
namespace App\Import\Providers;
|
||||
|
||||
class Zoho extends BaseImport
|
||||
{
|
||||
|
||||
}
|
354
app/Import/Transformer/BaseTransformer.php
Normal file
354
app/Import/Transformer/BaseTransformer.php
Normal file
@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Import\Transformer;
|
||||
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Country;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\User;
|
||||
use App\Utils\Number;
|
||||
use Exception;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Class BaseTransformer.
|
||||
*/
|
||||
class BaseTransformer
|
||||
{
|
||||
|
||||
protected $company;
|
||||
|
||||
public function __construct($company)
|
||||
{
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
public function getString($data, $field)
|
||||
{
|
||||
return (isset($data[$field]) && $data[$field]) ? $data[$field] : '';
|
||||
}
|
||||
|
||||
public function getCurrencyByCode( $data, $key = 'client.currency_id' )
|
||||
{
|
||||
|
||||
$code = array_key_exists( $key, $data ) ? $data[ $key ] : false;
|
||||
|
||||
$currencies = Cache::get('currencies');
|
||||
|
||||
$currency = $currencies->filter(function ($item) use($code) {
|
||||
return $item->code == $code;
|
||||
})->first();
|
||||
|
||||
return $currency ? $currency->id : $this->company->settings->currency_id;
|
||||
|
||||
}
|
||||
|
||||
public function getClient($client_name, $client_email) {
|
||||
|
||||
$client_id_search = $this->company->clients()->where( 'id_number', $client_name );
|
||||
|
||||
if ( $client_id_search->count() >= 1 ) {
|
||||
return $client_id_search->first()->id;
|
||||
nlog("found via id number");
|
||||
}
|
||||
|
||||
$client_name_search = $this->company->clients()->where( 'name', $client_name );
|
||||
|
||||
if ( $client_name_search->count() >= 1 ) {
|
||||
return $client_name_search->first()->id;
|
||||
nlog("found via name");
|
||||
}
|
||||
|
||||
if ( ! empty( $client_email ) ) {
|
||||
$contacts = ClientContact::where( 'company_id', $this->company->id )
|
||||
->where( 'email', $client_email );
|
||||
|
||||
if ( $contacts->count() >= 1 ) {
|
||||
return $contacts->first()->client_id;
|
||||
nlog("found via contact");
|
||||
}
|
||||
}
|
||||
nlog("did not find client");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasClient($name)
|
||||
{
|
||||
return $this->company->clients()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVendor($name)
|
||||
{
|
||||
return $this->company->vendors()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasProduct($key)
|
||||
{
|
||||
return $this->company->products()->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $key))])->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $field
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getFloat($data, $field)
|
||||
{
|
||||
if (array_key_exists($field, $data)) {
|
||||
$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
||||
} else {
|
||||
$number = 0;
|
||||
}
|
||||
|
||||
return Number::parseFloat($number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getClientId($name)
|
||||
{
|
||||
$client = $this->company->clients()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $client ? $client->id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProduct($data, $key, $field, $default = false)
|
||||
{
|
||||
|
||||
$product = $this->company->products()->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $data->{$key}))])->first();
|
||||
|
||||
if($product)
|
||||
return $product->{$field} ?: $default;
|
||||
|
||||
return $default;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
*
|
||||
* @return ?Contact
|
||||
*/
|
||||
public function getContact($email)
|
||||
{
|
||||
|
||||
$contact = $this->company->client_contacts()->whereRaw("LOWER(REPLACE(`email`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $email))])->first();
|
||||
|
||||
if(!$contact)
|
||||
return null;
|
||||
|
||||
return $contact;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getCountryId($name)
|
||||
{
|
||||
if(strlen($name) == 2)
|
||||
return $this->getCountryIdBy2($name);
|
||||
|
||||
$country = Country::whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $country ? $country->id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getCountryIdBy2($name)
|
||||
{
|
||||
return Country::where('iso_3166_2', $name)->exists() ? Country::where('iso_3166_2', $name)->first()->id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTaxRate($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
$tax_rate = $this->company->tax_rates()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $tax_rate ? $tax_rate->rate : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTaxName($name)
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
$tax_rate = $this->company->tax_rates()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $tax_rate ? $tax_rate->name : '';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 $number
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getInvoiceNumber($number)
|
||||
{
|
||||
return $number ? ltrim( trim( $number ), '0' ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice_number
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getInvoiceId($invoice_number)
|
||||
{
|
||||
$invoice = $this->company->invoices()->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $invoice_number))])->first();
|
||||
|
||||
return $invoice ? $invoice->id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice_number
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasInvoice($invoice_number)
|
||||
{
|
||||
|
||||
return $this->company->invoices()->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $invoice_number))])->exists();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice_number
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getInvoiceClientId($invoice_number)
|
||||
{
|
||||
$invoice = $this->company->invoices()->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $invoice_number))])->first();
|
||||
|
||||
return $invoice ? $invoice->client_id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getVendorId($name)
|
||||
{
|
||||
$vendor = $this->company->vendors()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $vendor ? $vendor->id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getExpenseCategoryId( $name ) {
|
||||
|
||||
$ec = $this->company->expense_categories()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $ec ? $ec->id : null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getProjectId( $name ) {
|
||||
|
||||
$project = $this->company->projects()->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $project ? $project->id : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPaymentTypeId( $name ) {
|
||||
|
||||
$pt = PaymentType::whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [strtolower(str_replace(' ', '', $name))])->first();
|
||||
|
||||
return $pt ? $pt->id : null;
|
||||
}
|
||||
|
||||
|
||||
}
|
82
app/Import/Transformer/Csv/ClientTransformer.php
Normal file
82
app/Import/Transformer/Csv/ClientTransformer.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* client Ninja (https://clientninja.com).
|
||||
*
|
||||
* @link https://github.com/clientninja/clientninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. client Ninja LLC (https://clientninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Import\Transformer\Csv;
|
||||
use App\Import\ImportException;
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class ClientTransformer.
|
||||
*/
|
||||
class ClientTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
if (isset($data->name) && $this->hasClient($data->name)) {
|
||||
throw new ImportException('Client already exists');
|
||||
}
|
||||
|
||||
$settings = new \stdClass;
|
||||
$settings->currency_id = (string)$this->getCurrencyByCode($data);
|
||||
|
||||
return [
|
||||
'company_id' => $this->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' ),
|
||||
'postal_code' => $this->getString( $data, 'client.postal_code'),
|
||||
'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.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $data, 'client.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $data, 'client.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $data, 'client.custom_value4' ),
|
||||
'balance' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.balance' ) ),
|
||||
'paid_to_date' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $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.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $data, 'contact.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $data, 'contact.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $data, 'contact.custom_value4' ),
|
||||
],
|
||||
],
|
||||
'country_id' => isset( $data['client.country'] ) ? $this->getCountryId( $data['client.country']) : null,
|
||||
'shipping_country_id' => isset($data['client.shipping_country'] ) ? $this->getCountryId( $data['client.shipping_country'] ) : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
129
app/Import/Transformer/Csv/InvoiceTransformer.php
Normal file
129
app/Import/Transformer/Csv/InvoiceTransformer.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* client Ninja (https://clientninja.com).
|
||||
*
|
||||
* @link https://github.com/clientninja/clientninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. client Ninja LLC (https://clientninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Import\Transformer\Csv;
|
||||
|
||||
use App\Import\ImportException;
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
use App\Models\Invoice;
|
||||
|
||||
/**
|
||||
* Class InvoiceTransformer.
|
||||
*/
|
||||
class InvoiceTransformer extends BaseTransformer {
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
public function transform( $line_items_data ) {
|
||||
$invoice_data = reset( $line_items_data );
|
||||
|
||||
if ( $this->hasInvoice( $invoice_data['invoice.number'] ) ) {
|
||||
throw new ImportException( 'Invoice number already exists' );
|
||||
}
|
||||
|
||||
$invoiceStatusMap = [
|
||||
'sent' => Invoice::STATUS_SENT,
|
||||
'draft' => Invoice::STATUS_DRAFT,
|
||||
];
|
||||
|
||||
$transformed = [
|
||||
'company_id' => $this->company->id,
|
||||
'number' => $this->getString( $invoice_data, 'invoice.number' ),
|
||||
'user_id' => $this->getString( $invoice_data, 'invoice.user_id' ),
|
||||
'amount' => $amount = $this->getFloat( $invoice_data, 'invoice.amount' ),
|
||||
'balance' => isset( $invoice_data['invoice.balance'] ) ? $this->getFloat( $invoice_data, 'invoice.balance' ) : $amount,
|
||||
'client_id' => $this->getClient( $this->getString( $invoice_data, 'client.name' ), $this->getString( $invoice_data, 'client.email' ) ),
|
||||
'discount' => $this->getFloat( $invoice_data, 'invoice.discount' ),
|
||||
'po_number' => $this->getString( $invoice_data, 'invoice.po_number' ),
|
||||
'date' => isset( $invoice_data['invoice.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['invoice.date'] ) ) : now()->format('Y-m-d'),
|
||||
'due_date' => isset( $invoice_data['invoice.due_date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['invoice.due_date'] ) ) : null,
|
||||
'terms' => $this->getString( $invoice_data, 'invoice.terms' ),
|
||||
'public_notes' => $this->getString( $invoice_data, 'invoice.public_notes' ),
|
||||
'private_notes' => $this->getString( $invoice_data, 'invoice.private_notes' ),
|
||||
'tax_name1' => $this->getString( $invoice_data, 'invoice.tax_name1' ),
|
||||
'tax_rate1' => $this->getFloat( $invoice_data, 'invoice.tax_rate1' ),
|
||||
'tax_name2' => $this->getString( $invoice_data, 'invoice.tax_name2' ),
|
||||
'tax_rate2' => $this->getFloat( $invoice_data, 'invoice.tax_rate2' ),
|
||||
'tax_name3' => $this->getString( $invoice_data, 'invoice.tax_name3' ),
|
||||
'tax_rate3' => $this->getFloat( $invoice_data, 'invoice.tax_rate3' ),
|
||||
'custom_value1' => $this->getString( $invoice_data, 'invoice.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $invoice_data, 'invoice.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $invoice_data, 'invoice.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $invoice_data, 'invoice.custom_value4' ),
|
||||
'footer' => $this->getString( $invoice_data, 'invoice.footer' ),
|
||||
'partial' => $this->getFloat( $invoice_data, 'invoice.partial' ),
|
||||
'partial_due_date' => $this->getString( $invoice_data, 'invoice.partial_due_date' ),
|
||||
'custom_surcharge1' => $this->getString( $invoice_data, 'invoice.custom_surcharge1' ),
|
||||
'custom_surcharge2' => $this->getString( $invoice_data, 'invoice.custom_surcharge2' ),
|
||||
'custom_surcharge3' => $this->getString( $invoice_data, 'invoice.custom_surcharge3' ),
|
||||
'custom_surcharge4' => $this->getString( $invoice_data, 'invoice.custom_surcharge4' ),
|
||||
'exchange_rate' => $this->getString( $invoice_data, 'invoice.exchange_rate' ),
|
||||
'status_id' => $invoiceStatusMap[ $status =
|
||||
strtolower( $this->getString( $invoice_data, 'invoice.status' ) ) ] ??
|
||||
Invoice::STATUS_SENT,
|
||||
'archived' => $status === 'archived',
|
||||
];
|
||||
|
||||
if ( isset( $invoice_data['payment.amount'] ) ) {
|
||||
$transformed['payments'] = [
|
||||
[
|
||||
'date' => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ),
|
||||
'transaction_reference' => $this->getString( $invoice_data, 'payment.transaction_reference' ),
|
||||
'amount' => $this->getFloat( $invoice_data, 'payment.amount' ),
|
||||
],
|
||||
];
|
||||
} elseif ( $status === 'paid' ) {
|
||||
$transformed['payments'] = [
|
||||
[
|
||||
'date' => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ),
|
||||
'transaction_reference' => $this->getString( $invoice_data, 'payment.transaction_reference' ),
|
||||
'amount' => $this->getFloat( $invoice_data, 'invoice.amount' ),
|
||||
],
|
||||
];
|
||||
} elseif ( isset( $transformed['amount'] ) && isset( $transformed['balance'] ) && ($transformed['amount'] != $transformed['balance'])) {
|
||||
$transformed['payments'] = [
|
||||
[
|
||||
'date' => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ),
|
||||
'transaction_reference' => $this->getString( $invoice_data, 'payment.transaction_reference' ),
|
||||
'amount' => $transformed['amount'] - $transformed['balance'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$line_items = [];
|
||||
foreach ( $line_items_data as $record ) {
|
||||
$line_items[] = [
|
||||
'quantity' => $this->getFloat( $record, 'item.quantity' ),
|
||||
'cost' => $this->getFloat( $record, 'item.cost' ),
|
||||
'product_key' => $this->getString( $record, 'item.product_key' ),
|
||||
'notes' => $this->getString( $record, 'item.notes' ),
|
||||
'discount' => $this->getFloat( $record, 'item.discount' ),
|
||||
'is_amount_discount' => filter_var( $this->getString( $record, 'item.is_amount_discount' ), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ),
|
||||
'tax_name1' => $this->getString( $record, 'item.tax_name1' ),
|
||||
'tax_rate1' => $this->getFloat( $record, 'item.tax_rate1' ),
|
||||
'tax_name2' => $this->getString( $record, 'item.tax_name2' ),
|
||||
'tax_rate2' => $this->getFloat( $record, 'item.tax_rate2' ),
|
||||
'tax_name3' => $this->getString( $record, 'item.tax_name3' ),
|
||||
'tax_rate3' => $this->getFloat( $record, 'item.tax_rate3' ),
|
||||
'custom_value1' => $this->getString( $record, 'item.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $record, 'item.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $record, 'item.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $record, 'item.custom_value4' ),
|
||||
'type_id' => $this->getInvoiceTypeId( $record, 'item.type_id' ),
|
||||
];
|
||||
}
|
||||
$transformed['line_items'] = $line_items;
|
||||
|
||||
return $transformed;
|
||||
}
|
||||
}
|
46
app/Import/Transformer/Csv/ProductTransformer.php
Normal file
46
app/Import/Transformer/Csv/ProductTransformer.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Import\Transformer\Csv;
|
||||
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
/**
|
||||
* Class ProductTransformer.
|
||||
*/
|
||||
class ProductTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
return [
|
||||
'company_id' => $this->company->id,
|
||||
'product_key' => $this->getString($data, 'product.product_key'),
|
||||
'notes' => $this->getString($data, 'product.notes'),
|
||||
'cost' => $this->getFloat($data, 'product.cost'),
|
||||
'price' => $this->getFloat($data, 'product.price'),
|
||||
'quantity' => $this->getFloat($data, 'product.quantity'),
|
||||
'tax_name1' => $this->getString($data, 'product.tax_name1'),
|
||||
'tax_rate1' => $this->getFloat($data, 'product.tax_rate1'),
|
||||
'tax_name2' => $this->getString($data, 'product.tax_name2'),
|
||||
'tax_rate2' => $this->getFloat($data, 'product.tax_rate2'),
|
||||
'tax_name3' => $this->getString($data, 'product.tax_name3'),
|
||||
'tax_rate3' => $this->getFloat($data, 'product.tax_rate3'),
|
||||
'custom_value1' => $this->getString($data, 'product.custom_value1'),
|
||||
'custom_value2' => $this->getString($data, 'product.custom_value2'),
|
||||
'custom_value3' => $this->getString($data, 'product.custom_value3'),
|
||||
'custom_value4' => $this->getString($data, 'product.custom_value4'),
|
||||
];
|
||||
}
|
||||
}
|
99
app/Jobs/Import/CSVIngest.php
Normal file
99
app/Jobs/Import/CSVIngest.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Import;
|
||||
|
||||
use App\Import\Providers\Csv;
|
||||
use App\Import\Providers\Freshbooks;
|
||||
use App\Import\Providers\Invoice2Go;
|
||||
use App\Import\Providers\Invoicely;
|
||||
use App\Import\Providers\Wave;
|
||||
use App\Import\Providers\Zoho;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CSVIngest implements ShouldQueue {
|
||||
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public Company $company;
|
||||
|
||||
public string $hash;
|
||||
|
||||
public string $import_type;
|
||||
|
||||
public ?string $skip_header;
|
||||
|
||||
public array $column_map;
|
||||
|
||||
public function __construct( array $request, Company $company ) {
|
||||
$this->company = $company;
|
||||
$this->request = $request;
|
||||
$this->hash = $request['hash'];
|
||||
$this->import_type = $request['import_type'];
|
||||
$this->skip_header = $request['skip_header'] ?? null;
|
||||
$this->column_map =
|
||||
! empty( $request['column_map'] ) ?
|
||||
array_combine( array_keys( $request['column_map'] ), array_column( $request['column_map'], 'mapping' ) ) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle() {
|
||||
|
||||
MultiDB::setDb( $this->company->db );
|
||||
|
||||
$engine = $this->bootEngine($this->import_type);
|
||||
|
||||
foreach ( [ 'client', 'product', 'invoice', 'payment', 'vendor', 'expense' ] as $entity ) {
|
||||
|
||||
$engine->import($entity);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function bootEngine(string $import_type)
|
||||
{
|
||||
switch ($import_type) {
|
||||
case 'csv':
|
||||
return new Csv( $this->request, $this->company);
|
||||
break;
|
||||
case 'waveaccounting':
|
||||
return new Wave( $this->request, $this->company);
|
||||
break;
|
||||
case 'invoicely':
|
||||
return new Invoicely( $this->request, $this->company);
|
||||
break;
|
||||
case 'invoice2go':
|
||||
return new Invoice2Go( $this->request, $this->company);
|
||||
break;
|
||||
case 'zoho':
|
||||
return new Zoho( $this->request, $this->company);
|
||||
break;
|
||||
case 'freshbooks':
|
||||
return new Freshbooks( $this->request, $this->company);
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -54,16 +54,31 @@ class CompanySizeCheck implements ShouldQueue
|
||||
|
||||
private function check()
|
||||
{
|
||||
Company::cursor()->each(function ($company) {
|
||||
|
||||
if ($company->invoices()->count() > 500 || $company->products()->count() > 500 || $company->clients()->count() > 500) {
|
||||
|
||||
nlog("Marking company {$company->id} as large");
|
||||
|
||||
$company->is_large = true;
|
||||
$company->save();
|
||||
}
|
||||
Company::where('is_large', false)->withCount(['invoices','clients','products'])->cursor()->each(function ($company){
|
||||
|
||||
});
|
||||
if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500)
|
||||
{
|
||||
|
||||
nlog("Marking company {$company->id} as large");
|
||||
|
||||
$company->account->companies()->update(['is_large' => true]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Company::where('is_large', false)->cursor()->each(function ($company) {
|
||||
|
||||
// if ($company->invoices()->count() > 500 || $company->products()->count() > 500 || $company->clients()->count() > 500) {
|
||||
|
||||
// nlog("Marking company {$company->id} as large");
|
||||
|
||||
// $company->account->companies->update(['is_large' => true])
|
||||
|
||||
// }
|
||||
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
@ -65,10 +65,15 @@ class SendRecurring implements ShouldQueue
|
||||
// Generate Standard Invoice
|
||||
$invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client);
|
||||
|
||||
if($this->recurring_invoice->auto_bill == "always")
|
||||
if($this->recurring_invoice->auto_bill === "always"){
|
||||
$invoice->auto_bill_enabled = true;
|
||||
elseif($this->recurring_invoice->auto_bill == "off")
|
||||
}
|
||||
elseif($this->recurring_invoice->auto_bill === "optout" || $this->recurring_invoice->auto_bill === "optin"){
|
||||
|
||||
}
|
||||
elseif($this->recurring_invoice->auto_bill === "off"){
|
||||
$invoice->auto_bill_enabled = false;
|
||||
}
|
||||
|
||||
$invoice->date = now()->format('Y-m-d');
|
||||
$invoice->due_date = $this->recurring_invoice->calculateDueDate(now()->format('Y-m-d'));
|
||||
|
@ -44,7 +44,7 @@ class AuthorizeCreateCustomer
|
||||
$this->authorize->init();
|
||||
// Create the Bill To info for new payment type
|
||||
|
||||
$contact = $this->client->primary_contact()->first();
|
||||
$contact = $this->client->primary_contact()->first() ?: $this->client->contacts()->first();
|
||||
$refId = 'ref'.time();
|
||||
|
||||
// Create a new CustomerProfileType and add the payment profile object
|
||||
|
@ -62,7 +62,7 @@ trait Utilities
|
||||
|
||||
$data = [
|
||||
'payment_method' => $_payment->source['id'],
|
||||
'payment_type' => PaymentType::parseCardType(strtolower($_payment->source['scheme'])),
|
||||
'payment_type' => 12,
|
||||
'amount' => $this->getParent()->payment_hash->data->raw_value,
|
||||
'transaction_reference' => $_payment->id,
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
|
@ -119,7 +119,8 @@ class PaymentIntentWebhook implements ShouldQueue
|
||||
|
||||
$payment_hash = PaymentHash::where('hash', $hash)->first();
|
||||
|
||||
nlog("no payment found");
|
||||
if(!$payment_hash)
|
||||
return;
|
||||
|
||||
if(optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('card', $this->stripe_request['object']['allowed_source_types']))
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ class ActivityRepository extends BaseRepository
|
||||
$activity->token_id = $token_id;
|
||||
}
|
||||
|
||||
$activity->ip = $event_vars['ip'];
|
||||
$activity->ip = $event_vars['ip'] ?: ' ';
|
||||
$activity->is_system = $event_vars['is_system'];
|
||||
|
||||
$activity->save();
|
||||
|
@ -91,7 +91,7 @@ class SubscriptionService
|
||||
'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id),
|
||||
'client' => $recurring_invoice->client->hashed_id,
|
||||
'subscription' => $this->subscription->hashed_id,
|
||||
'contact' => auth('contact')->user()->hashed_id,
|
||||
'contact' => auth('contact')->user() ? auth('contact')->user()->hashed_id : $recurring_invoice->client->contacts()->first()->hashed_id,
|
||||
'account_key' => $recurring_invoice->client->custom_value2,
|
||||
];
|
||||
|
||||
|
@ -119,9 +119,9 @@ class Ninja
|
||||
if(request()->hasHeader('Cf-Connecting-Ip'))
|
||||
$ip = request()->header('Cf-Connecting-Ip');
|
||||
elseif(request()->hasHeader('X-Forwarded-For'))
|
||||
$ip = request()->header('Cf-Connecting-Ip');
|
||||
$ip = request()->header('X-Forwarded-For');
|
||||
else
|
||||
$ip = request()->ip();
|
||||
$ip = request()->ip() ?: ' ';
|
||||
|
||||
return [
|
||||
'ip' => $ip,
|
||||
|
@ -54,7 +54,8 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'asset_url' => null,
|
||||
//'asset_url' => null,
|
||||
'asset_url' => env('ASSET_URL', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.54',
|
||||
'app_tag' => '5.3.54',
|
||||
'app_version' => '5.3.55',
|
||||
'app_tag' => '5.3.55',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -40,6 +40,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::put('clients/{client}/adjust_ledger', 'ClientController@adjustLedger')->name('clients.adjust_ledger');
|
||||
Route::put('clients/{client}/upload', 'ClientController@upload')->name('clients.upload');
|
||||
Route::post('clients/{client}/purge', 'ClientController@purge')->name('clients.purge')->middleware('password_protected');
|
||||
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
|
||||
|
||||
Route::post('filters/{entity}', 'FilterController@index')->name('filters');
|
||||
@ -212,7 +213,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
|
||||
Route::resource('subscriptions', 'SubscriptionController');
|
||||
Route::post('subscriptions/bulk', 'SubscriptionController@bulk')->name('subscriptions.bulk');
|
||||
Route::get('statics', 'StaticController');
|
||||
Route::post('apple_pay/upload_file','ApplyPayController@upload');
|
||||
// Route::post('apple_pay/upload_file','ApplyPayController@upload');
|
||||
|
||||
});
|
||||
|
||||
|
@ -50,6 +50,9 @@ class CompanyTest extends TestCase
|
||||
{
|
||||
$this->withoutMiddleware(PasswordProtection::class);
|
||||
|
||||
$cc = Company::first();
|
||||
$cc->delete();
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
|
345
tests/Feature/Import/CSV/BaseTransformerTest.php
Normal file
345
tests/Feature/Import/CSV/BaseTransformerTest.php
Normal file
@ -0,0 +1,345 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Feature\Import\CSV;
|
||||
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
use App\Jobs\Import\CSVImport;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\Vendor;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Import\Transformer\BaseTransformer
|
||||
*/
|
||||
class BaseTransformerTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
config(['database.default' => config('ninja.db.default')]);
|
||||
|
||||
// $this->faker = \Faker\Factory::create();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
$this->withoutExceptionHandling();
|
||||
}
|
||||
|
||||
public function testGetString()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$data = [
|
||||
'key' => 'value'
|
||||
];
|
||||
|
||||
$field = 'key';
|
||||
|
||||
$this->assertEquals('value', $base_transformer->getString($data, $field));
|
||||
|
||||
}
|
||||
|
||||
public function testGetCurrencyCode()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$code = ['client.currency_id' => "USD"];
|
||||
|
||||
$currency_id = $base_transformer->getCurrencyByCode($code);
|
||||
|
||||
$this->assertEquals(1, $currency_id);
|
||||
}
|
||||
|
||||
public function testGetClient()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'id_number' => 'hit',
|
||||
'name' => 'magic ',
|
||||
]);
|
||||
|
||||
$contact = ClientContact::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $this->company->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
'email' => 'test@gmail.com'
|
||||
]);
|
||||
|
||||
|
||||
$this->assertEquals($client->id, $base_transformer->getClient('hit', 'null'));
|
||||
$this->assertEquals($client->id, $base_transformer->getClient('magic', 'null'));
|
||||
$this->assertEquals($client->id, $base_transformer->getClient('nomagic', 'test@gmail.com'));
|
||||
$this->assertEquals($client->id, $base_transformer->getClient(null, 'test@gmail.com'));
|
||||
$this->assertNull($base_transformer->getClient('null', 'notest@gmail.com'));
|
||||
|
||||
$this->assertEquals($client->id, $base_transformer->getClientId(' magic'));
|
||||
$this->assertEquals($client->id, $base_transformer->getClientId('Magic '));
|
||||
|
||||
}
|
||||
|
||||
public function testGetContact()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'id_number' => 'hit',
|
||||
'name' => 'magic ',
|
||||
]);
|
||||
|
||||
$contact = ClientContact::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $this->company->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
'email' => 'test@gmail.com'
|
||||
]);
|
||||
|
||||
$this->assertEquals($contact->id, $base_transformer->getContact('TeSt@gmail.com')->id);
|
||||
$this->assertEquals($contact->id, $base_transformer->getContact('TeSt@gmail.com ')->id);
|
||||
$this->assertEquals($contact->id, $base_transformer->getContact('TeSt@gmaiL.com')->id);
|
||||
|
||||
}
|
||||
|
||||
public function testHasClient()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'id_number' => 'hit',
|
||||
'name' => 'maGic ',
|
||||
]);
|
||||
|
||||
|
||||
$this->assertTrue($base_transformer->hasClient("magic"));
|
||||
$this->assertTrue($base_transformer->hasClient("Magic"));
|
||||
$this->assertTrue($base_transformer->hasClient("Ma gi c "));
|
||||
|
||||
}
|
||||
|
||||
public function testHasVendor()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Vendor::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'id_number' => 'hit',
|
||||
'name' => 'maGic ',
|
||||
]);
|
||||
|
||||
|
||||
$this->assertTrue($base_transformer->hasVendor("magic"));
|
||||
$this->assertTrue($base_transformer->hasVendor("Magic"));
|
||||
$this->assertTrue($base_transformer->hasVendor("Ma gi c "));
|
||||
|
||||
}
|
||||
|
||||
public function testHasProduct()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Product::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'product_key' => 'HiT ',
|
||||
]);
|
||||
|
||||
|
||||
$this->assertTrue($base_transformer->hasProduct("hit"));
|
||||
$this->assertTrue($base_transformer->hasProduct(" hIt"));
|
||||
$this->assertTrue($base_transformer->hasProduct(" h i T "));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testGetCountryId()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$this->assertEquals(840, $base_transformer->getCountryId("us"));
|
||||
$this->assertEquals(840, $base_transformer->getCountryId("US"));
|
||||
$this->assertEquals(840, $base_transformer->getCountryId("United States"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testGetTaxRate()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = TaxRate::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'rate' => '10',
|
||||
'name' => 'GST'
|
||||
]);
|
||||
|
||||
$this->assertEquals(10, $base_transformer->getTaxRate("gst"));
|
||||
$this->assertEquals(10, $base_transformer->getTaxRate(" GST"));
|
||||
$this->assertEquals(10, $base_transformer->getTaxRate(" gS t "));
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testGetTaxName()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = TaxRate::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'rate' => '17.5',
|
||||
'name' => 'VAT'
|
||||
]);
|
||||
|
||||
$this->assertEquals("VAT", $base_transformer->getTaxName("vat"));
|
||||
$this->assertEquals("VAT", $base_transformer->getTaxName(" VaT"));
|
||||
$this->assertEquals("VAT", $base_transformer->getTaxName(" va T "));
|
||||
|
||||
}
|
||||
|
||||
public function testGetInvoiceId()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$invoice = Invoice::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'client_id' => $this->client->id,
|
||||
'number' => 'trick_number_123',
|
||||
]);
|
||||
|
||||
$this->assertEquals($invoice->id, $base_transformer->getInvoiceId("TRICK_number_123"));
|
||||
$this->assertEquals($invoice->id, $base_transformer->getInvoiceId(" TRICK_number_123"));
|
||||
$this->assertEquals($invoice->id, $base_transformer->getInvoiceId(" TRICK_number_123 "));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testHasInvoiceWithNumber()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Invoice::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'client_id' => $this->client->id,
|
||||
'number' => 'tricky_number_123',
|
||||
]);
|
||||
|
||||
$this->assertTrue($base_transformer->hasInvoice("TRICKY_number_123"));
|
||||
$this->assertTrue($base_transformer->hasInvoice(" TRICKY_number_123"));
|
||||
$this->assertTrue($base_transformer->hasInvoice(" TRICKY_number_123 "));
|
||||
|
||||
}
|
||||
public function testInvoiceClientId()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$client = Invoice::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'client_id' => $this->client->id,
|
||||
'number' => 'tricky_number_123',
|
||||
]);
|
||||
|
||||
$this->assertEquals($this->client->id, $base_transformer->getInvoiceClientId("TRICKY_number_123"));
|
||||
$this->assertEquals($this->client->id, $base_transformer->getInvoiceClientId(" TRICKY_number_123"));
|
||||
$this->assertEquals($this->client->id, $base_transformer->getInvoiceClientId(" TRICKY_number_123 "));
|
||||
|
||||
}
|
||||
|
||||
public function testGetVendorId()
|
||||
{
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$vendor = Vendor::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'id_number' => 'hit',
|
||||
'name' => 'maGic ',
|
||||
]);
|
||||
|
||||
|
||||
$this->assertEquals($vendor->id, $base_transformer->getVendorId("magic"));
|
||||
$this->assertEquals($vendor->id, $base_transformer->getVendorId("Magic"));
|
||||
$this->assertEquals($vendor->id, $base_transformer->getVendorId("Ma gi c "));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// public function testClientCsvImport()
|
||||
// {
|
||||
// $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv');
|
||||
// $hash = Str::random(32);
|
||||
// $column_map = [
|
||||
// 1 => 'client.balance',
|
||||
// 2 => 'client.paid_to_date',
|
||||
// 0 => 'client.name',
|
||||
// 19 => 'client.currency_id',
|
||||
// 20 => 'client.public_notes',
|
||||
// 21 => 'client.private_notes',
|
||||
// 22 => 'contact.first_name',
|
||||
// 23 => 'contact.last_name',
|
||||
// ];
|
||||
|
||||
// $data = [
|
||||
// 'hash' => $hash,
|
||||
// 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ],
|
||||
// 'skip_header' => true,
|
||||
// 'import_type' => 'csv',
|
||||
// ];
|
||||
|
||||
// $pre_import = Client::count();
|
||||
|
||||
// Cache::put( $hash . '-client', base64_encode( $csv ), 360 );
|
||||
|
||||
// CSVImport::dispatchNow( $data, $this->company );
|
||||
|
||||
// $this->assertGreaterThan( $pre_import, Client::count() );
|
||||
// }
|
||||
|
||||
}
|
136
tests/Feature/Import/CSV/CsvImportTest.php
Normal file
136
tests/Feature/Import/CSV/CsvImportTest.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Feature\Import\CSV;
|
||||
|
||||
use App\Import\Providers\Csv;
|
||||
use App\Import\Transformer\BaseTransformer;
|
||||
use App\Jobs\Import\CSVImport;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\Vendor;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Import\Providers\Csv
|
||||
*/
|
||||
class CsvImportTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
|
||||
config(['database.default' => config('ninja.db.default')]);
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
$this->withoutExceptionHandling();
|
||||
}
|
||||
|
||||
public function testCsvFeature()
|
||||
{
|
||||
$csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv');
|
||||
$hash = Str::random(32);
|
||||
$column_map = [
|
||||
1 => 'client.balance',
|
||||
2 => 'client.paid_to_date',
|
||||
0 => 'client.name',
|
||||
19 => 'client.currency_id',
|
||||
20 => 'client.public_notes',
|
||||
21 => 'client.private_notes',
|
||||
22 => 'contact.first_name',
|
||||
23 => 'contact.last_name',
|
||||
24 => 'contact.email',
|
||||
];
|
||||
|
||||
$data = [
|
||||
'hash' => $hash,
|
||||
'column_map' => [ 'client' => [ 'mapping' => $column_map ] ],
|
||||
'skip_header' => true,
|
||||
'import_type' => 'csv',
|
||||
];
|
||||
|
||||
Cache::put( $hash . '-client', base64_encode( $csv ), 360 );
|
||||
|
||||
$csv_importer = new Csv($data, $this->company);
|
||||
|
||||
$this->assertInstanceOf(Csv::class, $csv_importer);
|
||||
|
||||
$csv_importer->import('client');
|
||||
|
||||
$base_transformer = new BaseTransformer($this->company);
|
||||
|
||||
$this->assertTrue($base_transformer->hasClient("Ludwig Krajcik DVM"));
|
||||
|
||||
$client_id = $base_transformer->getClient("Ludwig Krajcik DVM", null);
|
||||
|
||||
$c = Client::find($client_id);
|
||||
|
||||
$this->assertEquals($client_id, $c->id);
|
||||
|
||||
$client_id = $base_transformer->getClient("a non existent clent", "brook59@example.org");
|
||||
|
||||
$this->assertEquals($client_id, $c->id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// public function testClientCsvImport()
|
||||
// {
|
||||
// $csv = file_get_contents(base_path().'/tests/Feature/Import/clients.csv');
|
||||
// $hash = Str::random(32);
|
||||
// $column_map = [
|
||||
// 1 => 'client.balance',
|
||||
// 2 => 'client.paid_to_date',
|
||||
// 0 => 'client.name',
|
||||
// 19 => 'client.currency_id',
|
||||
// 20 => 'client.public_notes',
|
||||
// 21 => 'client.private_notes',
|
||||
// 22 => 'contact.first_name',
|
||||
// 23 => 'contact.last_name',
|
||||
// ];
|
||||
|
||||
// $data = [
|
||||
// 'hash' => $hash,
|
||||
// 'column_map' => [ 'client' => [ 'mapping' => $column_map ] ],
|
||||
// 'skip_header' => true,
|
||||
// 'import_type' => 'csv',
|
||||
// ];
|
||||
|
||||
// $pre_import = Client::count();
|
||||
|
||||
// Cache::put( $hash . '-client', base64_encode( $csv ), 360 );
|
||||
|
||||
// CSVImport::dispatchNow( $data, $this->company );
|
||||
|
||||
// $this->assertGreaterThan( $pre_import, Client::count() );
|
||||
// }
|
@ -177,13 +177,17 @@ trait MockAccountData
|
||||
}
|
||||
}
|
||||
|
||||
$this->account = Account::factory()->create();
|
||||
$this->account = Account::factory()->create([
|
||||
'hosted_client_count' => 1000,
|
||||
'hosted_company_count' => 1000
|
||||
]);
|
||||
|
||||
$this->account->num_users = 3;
|
||||
$this->account->save();
|
||||
|
||||
$this->company = Company::factory()->create([
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
'account_id' => $this->account->id,
|
||||
]);
|
||||
|
||||
$this->company->client_registration_fields = ClientRegistrationFields::generate();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user