mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-30 20:22:58 -04:00 
			
		
		
		
	
						commit
						6c968f1390
					
				| @ -1,6 +1,6 @@ | |||||||
| APP_NAME="Invoice Ninja" | APP_NAME="Invoice Ninja" | ||||||
| APP_ENV=production | APP_ENV=production | ||||||
| APP_KEY= | APP_KEY=base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno= | ||||||
| APP_DEBUG=false | APP_DEBUG=false | ||||||
| 
 | 
 | ||||||
| APP_URL=http://localhost | 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\CreateClientRequest; | ||||||
| use App\Http\Requests\Client\DestroyClientRequest; | use App\Http\Requests\Client\DestroyClientRequest; | ||||||
| use App\Http\Requests\Client\EditClientRequest; | use App\Http\Requests\Client\EditClientRequest; | ||||||
|  | use App\Http\Requests\Client\PurgeClientRequest; | ||||||
| use App\Http\Requests\Client\ShowClientRequest; | use App\Http\Requests\Client\ShowClientRequest; | ||||||
| use App\Http\Requests\Client\StoreClientRequest; | use App\Http\Requests\Client\StoreClientRequest; | ||||||
| use App\Http\Requests\Client\UpdateClientRequest; | use App\Http\Requests\Client\UpdateClientRequest; | ||||||
| @ -36,7 +37,7 @@ use App\Utils\Traits\SavesDocuments; | |||||||
| use App\Utils\Traits\Uploadable; | use App\Utils\Traits\Uploadable; | ||||||
| use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||||
| use Illuminate\Http\Response; | use Illuminate\Http\Response; | ||||||
| 
 | use Illuminate\Support\Facades\Storage; | ||||||
| /** | /** | ||||||
|  * Class ClientController. |  * Class ClientController. | ||||||
|  * @covers App\Http\Controllers\ClientController |  * @covers App\Http\Controllers\ClientController | ||||||
| @ -510,7 +511,7 @@ class ClientController extends BaseController | |||||||
|         $ids = request()->input('ids'); |         $ids = request()->input('ids'); | ||||||
|         $clients = Client::withTrashed()->whereIn('id', $this->transformKeys($ids))->cursor(); |         $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); |             return response()->json(['message' => 'That action is not available.'], 400); | ||||||
| 
 | 
 | ||||||
|         $clients->each(function ($client, $key) use ($action) { |         $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) |     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')) { |         if ($request->has('locale')) { | ||||||
|             $this->setLocale($request->query('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); |         return array_merge($merge_rules, $rules); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| 
 | 
 | ||||||
| namespace App\Http\ValidationRules\Company; | namespace App\Http\ValidationRules\Company; | ||||||
| 
 | 
 | ||||||
|  | use App\Utils\Ninja; | ||||||
| use Illuminate\Contracts\Validation\Rule; | use Illuminate\Contracts\Validation\Rule; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -25,7 +26,12 @@ class ValidCompanyQuantity implements Rule | |||||||
|      */ |      */ | ||||||
|     public function passes($attribute, $value) |     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); |         $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() |     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() |     private function check() | ||||||
|     { |     { | ||||||
|         Company::cursor()->each(function ($company) { |  | ||||||
| 
 | 
 | ||||||
|             if ($company->invoices()->count() > 500 || $company->products()->count() > 500 || $company->clients()->count() > 500) { |         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"); |             nlog("Marking company {$company->id} as large"); | ||||||
| 
 | 
 | ||||||
|                 $company->is_large = true; |             $company->account->companies()->update(['is_large' => true]); | ||||||
|                 $company->save(); | 
 | ||||||
|             } |             } | ||||||
|             |             | ||||||
|  |             | ||||||
|          }); |          }); | ||||||
|  | 
 | ||||||
|  |         // 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
 |         // Generate Standard Invoice
 | ||||||
|         $invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client); |         $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; |             $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->auto_bill_enabled = false; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         $invoice->date = now()->format('Y-m-d'); |         $invoice->date = now()->format('Y-m-d'); | ||||||
|         $invoice->due_date = $this->recurring_invoice->calculateDueDate(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(); |         $this->authorize->init(); | ||||||
|         // Create the Bill To info for new payment type
 |         // 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(); |         $refId = 'ref'.time(); | ||||||
| 
 | 
 | ||||||
|         // Create a new CustomerProfileType and add the payment profile object
 |         // Create a new CustomerProfileType and add the payment profile object
 | ||||||
|  | |||||||
| @ -62,7 +62,7 @@ trait Utilities | |||||||
| 
 | 
 | ||||||
|         $data = [ |         $data = [ | ||||||
|             'payment_method' => $_payment->source['id'], |             'payment_method' => $_payment->source['id'], | ||||||
|             'payment_type' => PaymentType::parseCardType(strtolower($_payment->source['scheme'])), |             'payment_type' => 12, | ||||||
|             'amount' => $this->getParent()->payment_hash->data->raw_value, |             'amount' => $this->getParent()->payment_hash->data->raw_value, | ||||||
|             'transaction_reference' => $_payment->id, |             'transaction_reference' => $_payment->id, | ||||||
|             'gateway_type_id' => GatewayType::CREDIT_CARD, |             'gateway_type_id' => GatewayType::CREDIT_CARD, | ||||||
|  | |||||||
| @ -119,7 +119,8 @@ class PaymentIntentWebhook implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|             $payment_hash = PaymentHash::where('hash', $hash)->first(); |             $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'])) |             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->token_id = $token_id; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $activity->ip = $event_vars['ip']; |         $activity->ip = $event_vars['ip'] ?: ' '; | ||||||
|         $activity->is_system = $event_vars['is_system']; |         $activity->is_system = $event_vars['is_system']; | ||||||
| 
 | 
 | ||||||
|         $activity->save(); |         $activity->save(); | ||||||
|  | |||||||
| @ -91,7 +91,7 @@ class SubscriptionService | |||||||
|                 'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id), |                 'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id), | ||||||
|                 'client' => $recurring_invoice->client->hashed_id, |                 'client' => $recurring_invoice->client->hashed_id, | ||||||
|                 'subscription' => $this->subscription->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, |                 'account_key' => $recurring_invoice->client->custom_value2, | ||||||
|             ]; |             ]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -119,9 +119,9 @@ class Ninja | |||||||
|             if(request()->hasHeader('Cf-Connecting-Ip')) |             if(request()->hasHeader('Cf-Connecting-Ip')) | ||||||
|                 $ip = request()->header('Cf-Connecting-Ip'); |                 $ip = request()->header('Cf-Connecting-Ip'); | ||||||
|             elseif(request()->hasHeader('X-Forwarded-For')) |             elseif(request()->hasHeader('X-Forwarded-For')) | ||||||
|                 $ip = request()->header('Cf-Connecting-Ip'); |                 $ip = request()->header('X-Forwarded-For'); | ||||||
|             else |             else | ||||||
|                 $ip = request()->ip(); |                 $ip = request()->ip() ?: ' '; | ||||||
| 
 | 
 | ||||||
|         return [ |         return [ | ||||||
|             'ip' => $ip, |             '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), |     'require_https' => env('REQUIRE_HTTPS', true), | ||||||
|     'app_url' => rtrim(env('APP_URL', ''), '/'), |     'app_url' => rtrim(env('APP_URL', ''), '/'), | ||||||
|     'app_domain' => env('APP_DOMAIN', 'invoicing.co'), |     'app_domain' => env('APP_DOMAIN', 'invoicing.co'), | ||||||
|     'app_version' => '5.3.54', |     'app_version' => '5.3.55', | ||||||
|     'app_tag' => '5.3.54', |     'app_tag' => '5.3.55', | ||||||
|     'minimum_client_version' => '5.0.16', |     'minimum_client_version' => '5.0.16', | ||||||
|     'terms_version' => '1.0.1', |     'terms_version' => '1.0.1', | ||||||
|     'api_secret' => env('API_SECRET', ''), |     '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::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}/adjust_ledger', 'ClientController@adjustLedger')->name('clients.adjust_ledger'); | ||||||
|     Route::put('clients/{client}/upload', 'ClientController@upload')->name('clients.upload'); |     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('clients/bulk', 'ClientController@bulk')->name('clients.bulk'); | ||||||
| 
 | 
 | ||||||
|     Route::post('filters/{entity}', 'FilterController@index')->name('filters'); |     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::resource('subscriptions', 'SubscriptionController'); | ||||||
|     Route::post('subscriptions/bulk', 'SubscriptionController@bulk')->name('subscriptions.bulk'); |     Route::post('subscriptions/bulk', 'SubscriptionController@bulk')->name('subscriptions.bulk'); | ||||||
|     Route::get('statics', 'StaticController'); |     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); |         $this->withoutMiddleware(PasswordProtection::class); | ||||||
| 
 | 
 | ||||||
|  |         $cc = Company::first(); | ||||||
|  |         $cc->delete(); | ||||||
|  | 
 | ||||||
|         $response = $this->withHeaders([ |         $response = $this->withHeaders([ | ||||||
|                 'X-API-SECRET' => config('ninja.api_secret'), |                 'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|                 'X-API-TOKEN' => $this->token, |                 '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,7 +177,11 @@ 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->num_users = 3; | ||||||
|         $this->account->save(); |         $this->account->save(); | ||||||
|          |          | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user