mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 16:37:31 -04:00 
			
		
		
		
	
						commit
						a0f0607f71
					
				
							
								
								
									
										3
									
								
								.env.ci
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								.env.ci
									
									
									
									
									
								
							| @ -23,4 +23,5 @@ API_SECRET=superdoopersecrethere | ||||
| PHANTOMJS_PDF_GENERATION=false | ||||
| CACHE_DRIVER=redis | ||||
| QUEUE_CONNECTION=redis | ||||
| SESSION_DRIVER=redis | ||||
| SESSION_DRIVER=redis | ||||
| PDF_GENERATOR=hosted_ninja | ||||
| @ -1 +1 @@ | ||||
| 5.9.6 | ||||
| 5.9.7 | ||||
| @ -176,6 +176,7 @@ class BackupUpdate extends Command | ||||
|                     try { | ||||
|                         $doc_bin = $document->getFile(); | ||||
|                     } catch(\Exception $e) { | ||||
|                         nlog("Exception:: BackupUpdate::" . $e->getMessage()); | ||||
|                         nlog($e->getMessage()); | ||||
|                     } | ||||
| 
 | ||||
| @ -184,8 +185,6 @@ class BackupUpdate extends Command | ||||
| 
 | ||||
|                         $document->disk = $this->option('disk'); | ||||
|                         $document->saveQuietly(); | ||||
| 
 | ||||
|                         nlog("Documents - Moving {$document->url} to {$this->option('disk')}"); | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
| @ -199,8 +198,6 @@ class BackupUpdate extends Command | ||||
| 
 | ||||
|                     if ($backup_bin) { | ||||
|                         Storage::disk($this->option('disk'))->put($backup->filename, $backup_bin); | ||||
| 
 | ||||
|                         nlog("Backups - Moving {$backup->filename} to {$this->option('disk')}"); | ||||
|                     } | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
| @ -220,6 +220,7 @@ class BaseRule implements RuleInterface | ||||
|                 try { | ||||
|                     $this->invoice->saveQuietly(); | ||||
|                 } catch(\Exception $e) { | ||||
|                     nlog("Exception:: BaseRule::" . $e->getMessage()); | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|  | ||||
							
								
								
									
										44
									
								
								app/Exceptions/DuplicatePaymentException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								app/Exceptions/DuplicatePaymentException.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Exceptions; | ||||
| 
 | ||||
| use Exception; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Request; | ||||
| 
 | ||||
| class DuplicatePaymentException extends Exception | ||||
| { | ||||
|     /** | ||||
|      * Report the exception. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function report() | ||||
|     { | ||||
|         //
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Render the exception into an HTTP response. | ||||
|      * | ||||
|      * @param  Request  $request | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function render($request) | ||||
|     { | ||||
| 
 | ||||
|         return response()->json([ | ||||
|             'message' => 'Duplicate request', | ||||
|         ], 400); | ||||
|          | ||||
|     } | ||||
| } | ||||
| @ -1,111 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Helpers\Bank\Yodlee\DTO; | ||||
| 
 | ||||
| use Illuminate\Support\Collection; | ||||
| use Spatie\LaravelData\Attributes\MapInputName; | ||||
| use Spatie\LaravelData\Data; | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated | ||||
|  * [ | ||||
|     "account": [ | ||||
|       [ | ||||
|         "CONTAINER": "bank", | ||||
|         "providerAccountId": 1005, | ||||
|         "accountName": "Business Acct", | ||||
|         "accountStatus": "ACTIVE", | ||||
|         "accountNumber": "1011", | ||||
|         "aggregationSource": "USER", | ||||
|         "isAsset": true, | ||||
|         "balance": [ | ||||
|           "currency": "AUD", | ||||
|           "amount": 304.98, | ||||
|         ], | ||||
|         "id": 10139315, | ||||
|         "includeInNetWorth": true, | ||||
|         "providerId": "3857", | ||||
|         "providerName": "Bank", | ||||
|         "isManual": false, | ||||
|         "availableBalance": {#2966
 | ||||
|           "currency": "AUD", | ||||
|           "amount": 304.98, | ||||
|         ], | ||||
|         "currentBalance": [ | ||||
|           "currency": "AUD", | ||||
|           "amount": 3044.98, | ||||
|         ], | ||||
|         "accountType": "CHECKING", | ||||
|         "displayedName": "after David", | ||||
|         "createdDate": "2023-01-10T08:29:07Z", | ||||
|         "classification": "SMALL_BUSINESS", | ||||
|         "lastUpdated": "2023-08-01T23:50:13Z", | ||||
|         "nickname": "Business ", | ||||
|         "bankTransferCode": [ | ||||
|           [ | ||||
|             "id": "062", | ||||
|             "type": "BSB", | ||||
|           ], | ||||
|         ], | ||||
|         "dataset": [ | ||||
|           [ | ||||
|             "name": "BASIC_AGG_DATA", | ||||
|             "additionalStatus": "AVAILABLE_DATA_RETRIEVED", | ||||
|             "updateEligibility": "ALLOW_UPDATE", | ||||
|             "lastUpdated": "2023-08-01T23:49:53Z", | ||||
|             "lastUpdateAttempt": "2023-08-01T23:49:53Z", | ||||
|             "nextUpdateScheduled": "2023-08-03T14:45:14Z", | ||||
|           ], | ||||
|         ], | ||||
|       ], | ||||
|     ], | ||||
|   ]; | ||||
|  */ | ||||
| class AccountSummary extends Data | ||||
| { | ||||
|     public ?int $id; | ||||
| 
 | ||||
|     #[MapInputName('CONTAINER')]
 | ||||
|     public ?string $account_type = ''; | ||||
| 
 | ||||
|     #[MapInputName('accountName')]
 | ||||
|     public ?string $account_name = ''; | ||||
| 
 | ||||
|     #[MapInputName('accountStatus')]
 | ||||
|     public ?string $account_status = ''; | ||||
| 
 | ||||
|     #[MapInputName('accountNumber')]
 | ||||
|     public ?string $account_number = ''; | ||||
| 
 | ||||
|     #[MapInputName('providerAccountId')]
 | ||||
|     public int $provider_account_id; | ||||
| 
 | ||||
|     #[MapInputName('providerId')]
 | ||||
|     public ?string $provider_id = ''; | ||||
| 
 | ||||
|     #[MapInputName('providerName')]
 | ||||
|     public ?string $provider_name = ''; | ||||
| 
 | ||||
|     public ?string $nickname = ''; | ||||
| 
 | ||||
|     public ?float $current_balance = 0; | ||||
|     public ?string $account_currency = ''; | ||||
| 
 | ||||
|     public static function prepareForPipeline(Collection $properties): Collection | ||||
|     { | ||||
| 
 | ||||
|         $properties->put('current_balance', $properties['currentBalance']['amount'] ?? 0); | ||||
|         $properties->put('account_currency', $properties['currentBalance']['currency'] ?? 0); | ||||
| 
 | ||||
|         return $properties; | ||||
|     } | ||||
| } | ||||
| @ -11,7 +11,6 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Bank; | ||||
| 
 | ||||
| use App\Helpers\Bank\Yodlee\DTO\AccountSummary; | ||||
| use App\Helpers\Bank\Yodlee\Yodlee; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Yodlee\YodleeAdminRequest; | ||||
| @ -301,8 +300,6 @@ class YodleeController extends BaseController | ||||
| 
 | ||||
|         $summary = $yodlee->getAccountSummary($account_number); | ||||
| 
 | ||||
|         //@todo remove laravel-data
 | ||||
|         // $transformed_summary = AccountSummary::from($summary[0]);
 | ||||
|         $transformed_summary = $this->transformSummary($summary[0]); | ||||
| 
 | ||||
|         return response()->json($transformed_summary, 200); | ||||
|  | ||||
| @ -495,7 +495,7 @@ class PurchaseOrderController extends BaseController | ||||
| 
 | ||||
|         $purchase_orders = PurchaseOrder::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); | ||||
| 
 | ||||
|         if (! $purchase_orders) { | ||||
|         if ($purchase_orders->count() == 0) { | ||||
|             return response()->json(['message' => 'No Purchase Orders Found']); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -141,6 +141,7 @@ class StripeConnectController extends BaseController | ||||
|                 $company_gateway->save(); | ||||
|             } | ||||
|         } catch(\Exception $e) { | ||||
|             nlog("Exception:: StripeConnectController::" . $e->getMessage()); | ||||
|             nlog("could not harvest stripe company name"); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -93,6 +93,7 @@ class PasswordProtection | ||||
|                 try { | ||||
|                     $payload = json_decode(base64_decode(str_replace('_', '/', str_replace('-', '+', explode('.', request()->header('X-API-OAUTH-PASSWORD'))[1])))); | ||||
|                 } catch(\Exception $e) { | ||||
|                     nlog("Exception:: PasswordProtection::" . $e->getMessage()); | ||||
|                     nlog("could not decode microsoft response"); | ||||
|                     return response()->json(['message' => 'Could not decode the response from Microsoft'], 412); | ||||
|                 } | ||||
|  | ||||
| @ -11,6 +11,7 @@ | ||||
| 
 | ||||
| namespace App\Http\Requests\Payment; | ||||
| 
 | ||||
| use App\Exceptions\DuplicatePaymentException; | ||||
| use App\Http\Requests\Request; | ||||
| use App\Http\ValidationRules\Credit\CreditsSumRule; | ||||
| use App\Http\ValidationRules\Credit\ValidCreditsRules; | ||||
| @ -79,6 +80,11 @@ class StorePaymentRequest extends Request | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         if(\Illuminate\Support\Facades\Cache::has($this->ip()."|".$this->input('amount', 0)."|".$this->input('client_id', '')."|".$user->company()->company_key)) | ||||
|             throw new DuplicatePaymentException('Duplicate request.', 429); | ||||
| 
 | ||||
|         \Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->input('amount', 0)."|".$this->input('client_id', '')."|".$user->company()->company_key), true, 1); | ||||
|          | ||||
|         $input = $this->all(); | ||||
| 
 | ||||
|         $invoices_total = 0; | ||||
|  | ||||
| @ -126,13 +126,12 @@ class MatchBankTransactions implements ShouldQueue | ||||
|     { | ||||
|         $collection = collect(); | ||||
| 
 | ||||
|         /** @array $invoices */ | ||||
|         $invoices = explode(",", $invoice_hashed_ids); | ||||
| 
 | ||||
|         if (count($invoices) >= 1) { | ||||
|             foreach ($invoices as $invoice) { | ||||
|                 if (is_string($invoice) && strlen($invoice) > 1) { | ||||
|                     $collection->push($this->decodePrimaryKey($invoice)); | ||||
|                 } | ||||
|         foreach ($invoices as $invoice) { | ||||
|             if (is_string($invoice) && strlen($invoice) > 1) { | ||||
|                 $collection->push($this->decodePrimaryKey($invoice)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -189,7 +188,7 @@ class MatchBankTransactions implements ShouldQueue | ||||
|     private function coalesceExpenses($expense): string | ||||
|     { | ||||
| 
 | ||||
|         if (!$this->bt->expense_id || strlen($this->bt->expense_id) < 1) { | ||||
|         if (!$this->bt->expense_id || strlen($this->bt->expense_id ?? '') < 2) { | ||||
|             return $expense; | ||||
|         } | ||||
| 
 | ||||
| @ -233,11 +232,12 @@ class MatchBankTransactions implements ShouldQueue | ||||
|         $_invoices = Invoice::query() | ||||
|             ->withTrashed() | ||||
|             ->where('company_id', $this->bt->company_id) | ||||
|             ->whereIn('id', $this->getInvoices($input['invoice_ids'])); | ||||
|             ->whereIn('id', $this->getInvoices($input['invoice_ids'])) | ||||
|             ->get(); | ||||
| 
 | ||||
|         $amount = $this->bt->amount; | ||||
| 
 | ||||
|         if ($_invoices && $this->checkPayable($_invoices)) { | ||||
|         if ($_invoices->count() >0 && $this->checkPayable($_invoices)) { | ||||
|             $this->createPayment($_invoices, $amount); | ||||
| 
 | ||||
|             $this->bts->push($this->bt->id); | ||||
| @ -323,6 +323,7 @@ class MatchBankTransactions implements ShouldQueue | ||||
|             }); | ||||
|         }, 2); | ||||
| 
 | ||||
|         // @phpstan-ignore-next-line
 | ||||
|         if (!$this->invoice) { | ||||
|             return; | ||||
|         } | ||||
| @ -355,7 +356,7 @@ class MatchBankTransactions implements ShouldQueue | ||||
|         $this->setExchangeRate($payment); | ||||
| 
 | ||||
|         /* Create a payment relationship to the invoice entity */ | ||||
|         foreach ($this->attachable_invoices as $attachable_invoice) { | ||||
|         foreach ($this->attachable_invoices as $attachable_invoice) { // @phpstan-ignore-line
 | ||||
|             $payment->invoices()->attach($attachable_invoice['id'], [ | ||||
|                 'amount' => $attachable_invoice['amount'], | ||||
|             ]); | ||||
|  | ||||
| @ -69,7 +69,8 @@ class UpdateTaxData implements ShouldQueue | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
|         } catch(\Exception $e) {             | ||||
|             nlog("Exception:: UpdateTaxData::" . $e->getMessage()); | ||||
|             nlog("problem getting tax data => ".$e->getMessage()); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -61,6 +61,7 @@ class CompanyTaxRate implements ShouldQueue | ||||
|                 try { | ||||
|                     $calculated_state = USStates::getState($this->company->settings->postal_code); | ||||
|                 } catch(\Exception $e) { | ||||
|                     nlog("Exception:: CompanyTaxRate::" . $e->getMessage()); | ||||
|                     nlog("could not calculate state from postal code => {$this->company->settings->postal_code} or from state {$this->company->settings->state}"); | ||||
|                 } | ||||
| 
 | ||||
|  | ||||
| @ -123,6 +123,7 @@ class CreateCompany | ||||
| 
 | ||||
|             } | ||||
|         } catch(\Exception $e) { | ||||
|             nlog("Exception:: CreateCompany::" . $e->getMessage()); | ||||
|             nlog("Could not resolve country => {$e->getMessage()}"); | ||||
|         } | ||||
| 
 | ||||
| @ -156,6 +157,7 @@ class CreateCompany | ||||
|             return $company; | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
|             nlog("Exception:: CreateCompany::" . $e->getMessage()); | ||||
|             nlog("SETUP: could not complete setup for Spanish Locale"); | ||||
|         } | ||||
| 
 | ||||
| @ -189,6 +191,7 @@ class CreateCompany | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
|             nlog($e->getMessage()); | ||||
|             nlog("Exception:: CreateCompany::" . $e->getMessage()); | ||||
|             nlog("SETUP: could not complete setup for South African Locale"); | ||||
|         } | ||||
| 
 | ||||
| @ -222,6 +225,7 @@ class CreateCompany | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
|             nlog($e->getMessage()); | ||||
|             nlog("Exception:: CreateCompany::" . $e->getMessage()); | ||||
|             nlog("SETUP: could not complete setup for Australian Locale"); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -111,14 +111,17 @@ class CreateRawPdf | ||||
| 
 | ||||
|         try { | ||||
|             $pdf = $ps->boot()->getPdf(); | ||||
|         } catch (\Exception) { | ||||
|         } catch (\Exception $e) { | ||||
|             echo "EXCEPTION::".PHP_EOL; | ||||
|             echo $e->getMessage().PHP_EOL; | ||||
|             throw new FilePermissionsFailure('Unable to generate the raw PDF'); | ||||
|         } | ||||
| 
 | ||||
|         if ($this->entity_string == "invoice" && $this->entity->client->getSetting("merge_e_invoice_to_pdf")) { | ||||
|             $pdf = (new MergeEDocument($this->entity, $pdf))->handle(); | ||||
|         }         | ||||
| 
 | ||||
|         $merge_docs = $this->entity->client ? $this->entity->client->getSetting('embed_documents') : $this->company->getSetting('embed_documents'); | ||||
|         $merge_docs = isset($this->entity->client) ? $this->entity->client->getSetting('embed_documents') : $this->company->getSetting('embed_documents'); | ||||
| 
 | ||||
|         if($merge_docs && ($this->entity->documents()->where('is_public', true)->count() > 0 || $this->company->documents()->where('is_public', true)->count() > 0)) { | ||||
|             $pdf = $this->entity->documentMerge($pdf); | ||||
|  | ||||
| @ -71,6 +71,7 @@ class AdjustEmailQuota implements ShouldQueue | ||||
|                 try { | ||||
|                     LightLogs::create(new EmailCount($email_count, $account->key))->send(); // this runs syncronously
 | ||||
|                 } catch(\Exception $e) { | ||||
|                     nlog("Exception:: AdjustEmailQuota::" . $e->getMessage()); | ||||
|                     nlog($e->getMessage()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -93,6 +93,7 @@ class BankTransactionSync implements ShouldQueue | ||||
|                         try { | ||||
|                             (new ProcessBankTransactionsNordigen($bank_integration))->handle(); | ||||
|                         } catch(\Exception $e) { | ||||
|                             nlog("Exception:: BankTransactioSync::" . $e->getMessage()); | ||||
|                             sleep(20); | ||||
|                         } | ||||
| 
 | ||||
|  | ||||
| @ -89,6 +89,7 @@ class TaskScheduler implements ShouldQueue | ||||
|                         /** @var \App\Models\Scheduler $scheduler */ | ||||
|                         $scheduler->service()->runTask(); | ||||
|                     } catch(\Exception $e) { | ||||
|                         nlog("Exception:: TaskScheduler::" . $e->getMessage()); | ||||
|                         nlog($e->getMessage()); | ||||
|                     } | ||||
| 
 | ||||
|  | ||||
| @ -179,6 +179,7 @@ class Document extends BaseModel | ||||
|         try { | ||||
|             return route('api.documents.show', ['document' => $this->hashed_id]).'/download'; | ||||
|         } catch(\Exception $e) { | ||||
|             nlog("Exception:: Document::" . $e->getMessage()); | ||||
|             return ''; | ||||
|         } | ||||
|     } | ||||
| @ -252,7 +253,7 @@ class Document extends BaseModel | ||||
|             return $img->getImageBlob(); | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
| 
 | ||||
|             nlog("Exception:: Document::" . $e->getMessage()); | ||||
|             nlog($e->getMessage()); | ||||
|             return $catch_image; | ||||
|         } | ||||
|  | ||||
| @ -66,7 +66,7 @@ class EmailQuotaNotification extends Notification | ||||
|     { | ||||
|         $content = "Email quota exceeded by Account {$this->account->key} \n"; | ||||
| 
 | ||||
|         $owner = $this->account->companies()->first()->owner(); | ||||
|         $owner = $this->account->companies()->first()->owner() ?? $this->account->users()->orderBy('id','asc')->first(); | ||||
|         $owner_name = $owner->present()->name() ?? 'No Owner Found'; | ||||
|         $owner_email = $owner->email ?? 'No Owner Email Found'; | ||||
| 
 | ||||
|  | ||||
| @ -92,8 +92,7 @@ class RouteServiceProvider extends ServiceProvider | ||||
|         RateLimiter::for('portal', function (Request $request) { | ||||
|             return Limit::perMinute(15)->by($request->ip()); | ||||
|         }); | ||||
| 
 | ||||
| 
 | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -21,10 +21,6 @@ class PaymentMethod | ||||
| { | ||||
|     use MakesHash; | ||||
| 
 | ||||
|     private $client; | ||||
| 
 | ||||
|     private $amount; | ||||
| 
 | ||||
|     /** @var \Illuminate\Support\Collection<CompanyGateway> $gateways **/ | ||||
|     private $gateways; | ||||
| 
 | ||||
| @ -32,10 +28,8 @@ class PaymentMethod | ||||
| 
 | ||||
|     private $payment_urls = []; | ||||
| 
 | ||||
|     public function __construct(Client $client, float $amount) | ||||
|     public function __construct(private Client $client, private float $amount) | ||||
|     { | ||||
|         $this->client = $client; | ||||
|         $this->amount = $amount; | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
| @ -105,7 +99,6 @@ class PaymentMethod | ||||
|                 $transformed_ids = []; | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             $this->gateways = $this->client | ||||
|                              ->company | ||||
|                              ->company_gateways | ||||
| @ -140,7 +133,7 @@ class PaymentMethod | ||||
| 
 | ||||
|             foreach ($gateway->driver($this->client)->gatewayTypes() as $type) { | ||||
|                 if (isset($gateway->fees_and_limits) && is_object($gateway->fees_and_limits) && property_exists($gateway->fees_and_limits, $type)) { | ||||
|                     if ($this->validGatewayForAmount($gateway->fees_and_limits->{$type}, $this->amount) && $gateway->fees_and_limits->{$type}->is_enabled) { | ||||
|                     if ($this->validGatewayForAmount($gateway->fees_and_limits->{$type}) && $gateway->fees_and_limits->{$type}->is_enabled) { | ||||
|                         $this->payment_methods[] = [$gateway->id => $type]; | ||||
|                     } | ||||
|                 } else { | ||||
| @ -159,8 +152,8 @@ class PaymentMethod | ||||
|         { | ||||
|             foreach ($type as $gateway_id => $gateway_type_id) | ||||
|             { | ||||
|             $gate = $this->gateways->where('id',$gateway_id)->first(); | ||||
|             $this->buildUrl($gate, $gateway_type_id); | ||||
|                 $gate = $this->gateways->where('id', $gateway_id)->first(); | ||||
|                 $this->buildUrl($gate, $gateway_type_id); | ||||
|             } | ||||
|         } | ||||
|          | ||||
| @ -211,6 +204,7 @@ class PaymentMethod | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     //@deprecated as buildUrl() supercedes
 | ||||
| @ -256,14 +250,14 @@ class PaymentMethod | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     private function validGatewayForAmount($fees_and_limits_for_payment_type, $amount): bool | ||||
|     private function validGatewayForAmount($fees_and_limits_for_payment_type): bool | ||||
|     { | ||||
|         if (isset($fees_and_limits_for_payment_type)) { | ||||
|             $fees_and_limits = $fees_and_limits_for_payment_type; | ||||
|         } else { | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|         if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $fees_and_limits->min_limit != -1 && ($this->amount < $fees_and_limits->min_limit && $this->amount != -1)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
| @ -68,6 +68,7 @@ class CompanyService | ||||
|             } | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
|             nlog("Exception:: CompanyService::" . $e->getMessage()); | ||||
|             nlog($e->getMessage()); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -36,7 +36,7 @@ class TriggeredActions extends AbstractService | ||||
|             try { | ||||
|                 $this->invoice->service()->autoBill(); | ||||
|             } catch(\Exception $e) { | ||||
| 
 | ||||
|                 nlog("Exception:: TriggeredActions::" . $e->getMessage()); | ||||
|             } //update notification sends automatically for this.
 | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -100,6 +100,7 @@ class TaxProvider | ||||
|             } | ||||
| 
 | ||||
|         } catch(\Exception $e) { | ||||
|             nlog("Exception:: TaxProvider::" . $e->getMessage()); | ||||
|             nlog("Could not updated company tax data: " . $e->getMessage()); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -91,7 +91,6 @@ | ||||
|         "setasign/fpdi": "^2.3", | ||||
|         "socialiteproviders/apple": "dev-master", | ||||
|         "socialiteproviders/microsoft": "^4.1", | ||||
|         "spatie/laravel-data": "^3.6", | ||||
|         "sprain/swiss-qr-bill": "^4.3", | ||||
|         "square/square": "30.0.0.*", | ||||
|         "stripe/stripe-php": "^12", | ||||
|  | ||||
							
								
								
									
										1472
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1472
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -17,8 +17,8 @@ return [ | ||||
|     'require_https' => env('REQUIRE_HTTPS', true), | ||||
|     'app_url' => rtrim(env('APP_URL', ''), '/'), | ||||
|     'app_domain' => env('APP_DOMAIN', 'invoicing.co'), | ||||
|     'app_version' => env('APP_VERSION', '5.9.6'), | ||||
|     'app_tag' => env('APP_TAG', '5.9.6'), | ||||
|     'app_version' => env('APP_VERSION', '5.9.7'), | ||||
|     'app_tag' => env('APP_TAG', '5.9.7'), | ||||
|     'minimum_client_version' => '5.0.16', | ||||
|     'terms_version' => '1.0.1', | ||||
|     'api_secret' => env('API_SECRET', false), | ||||
|  | ||||
| @ -77,6 +77,7 @@ | ||||
| @section('gateway_footer') | ||||
| @endsection | ||||
| 
 | ||||
| 
 | ||||
| @push('footer') | ||||
| <script type="application/json" fncls="fnparams-dede7cc5-15fd-4c75-a9f4-36c430ee3a99"> | ||||
|     { | ||||
|  | ||||
| @ -38,12 +38,15 @@ class ClientModelTest extends TestCase | ||||
|         if (! config('ninja.testvars.stripe')) { | ||||
|             $this->markTestSkipped('Skip test no company gateways installed'); | ||||
|         } | ||||
| 
 | ||||
|         if(CompanyGateway::count() == 0) | ||||
|             $this->markTestSkipped('Skip test no company gateways installed'); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function testPaymentMethodsWithCreditsEnforced() | ||||
|     { | ||||
|         $amount = 40; | ||||
| 
 | ||||
|          | ||||
|         $payment_methods = $this->client->service()->getPaymentMethods(40); | ||||
| 
 | ||||
|         $this->assertGreaterThan(0, CompanyGateway::count()); | ||||
|  | ||||
| @ -54,7 +54,9 @@ class CompanyGatewayResolutionTest extends TestCase | ||||
| 
 | ||||
|         $this->withoutExceptionHandling(); | ||||
| 
 | ||||
|         CompanyGateway::whereNotNull('id')->delete(); | ||||
|         CompanyGateway::query()->withTrashed()->cursor()->each(function ($cg){ | ||||
|             $cg->forceDelete(); | ||||
|         }); | ||||
| 
 | ||||
|         $data = []; | ||||
|         $data[1]['min_limit'] = -1; | ||||
| @ -123,11 +125,14 @@ class CompanyGatewayResolutionTest extends TestCase | ||||
|     { | ||||
|         $amount = 10; | ||||
| 
 | ||||
|         $this->client->country_id = 840; | ||||
|         $this->client->save(); | ||||
| 
 | ||||
|         $this->assertInstanceOf('\\stdClass', $this->cg->fees_and_limits); | ||||
|         // $this->assertObjectHasAttribute('min_limit', $this->cg->fees_and_limits->{1});
 | ||||
|         $this->assertNotNull($this->cg->fees_and_limits->{1}->min_limit); | ||||
|         $payment_methods = $this->client->service()->getPaymentMethods($amount); | ||||
| 
 | ||||
|          | ||||
|         $this->assertEquals(2, count($payment_methods)); | ||||
|     } | ||||
| 
 | ||||
| @ -135,7 +140,9 @@ class CompanyGatewayResolutionTest extends TestCase | ||||
|     { | ||||
|         $amount = 10; | ||||
| 
 | ||||
|         CompanyGateway::whereNotNull('id')->delete(); | ||||
|         CompanyGateway::query()->withTrashed()->cursor()->each(function ($cg) { | ||||
|             $cg->forceDelete(); | ||||
|         }); | ||||
| 
 | ||||
|         $data = []; | ||||
|         $data[1]['min_limit'] = -1; | ||||
| @ -181,9 +188,10 @@ class CompanyGatewayResolutionTest extends TestCase | ||||
|         $this->cg->fees_and_limits = $data; | ||||
|         $this->cg->save(); | ||||
| 
 | ||||
|         // nlog($this->client->service()->getPaymentMethods($amount));
 | ||||
|         $this->client->country_id = 840; | ||||
|         $this->client->save(); | ||||
| 
 | ||||
|         $this->assertEquals(2, count($this->client->service()->getPaymentMethods($amount))); | ||||
|         $this->assertEquals(1, count($this->client->service()->getPaymentMethods($amount))); | ||||
|     } | ||||
| 
 | ||||
|     public function testEnableFeeAdjustment() | ||||
|  | ||||
| @ -82,7 +82,7 @@ class InventoryManagementTest extends TestCase | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
|         ])->post('/api/v1/invoices/', $invoice_array) | ||||
|             ->assertStatus(200); | ||||
|         ->assertStatus(200); | ||||
| 
 | ||||
|         $product = $product->fresh(); | ||||
| 
 | ||||
|  | ||||
| @ -75,6 +75,7 @@ class PaymentTest extends TestCase | ||||
|                 ], | ||||
|             ], | ||||
|             'date' => '2020/12/11', | ||||
|             'idempotency_key' => 'xx', | ||||
|         ]; | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
| @ -83,7 +84,9 @@ class PaymentTest extends TestCase | ||||
|         ])->postJson('/api/v1/payments/', $data); | ||||
| 
 | ||||
|         $response->assertStatus(200); | ||||
|                  | ||||
|          | ||||
|         sleep(1); | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
| @ -1869,6 +1872,7 @@ class PaymentTest extends TestCase | ||||
|             'date' => '2020/12/12', | ||||
|             'number' => 'duplicate', | ||||
|         ]; | ||||
| sleep(1); | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
| @ -1876,7 +1880,7 @@ class PaymentTest extends TestCase | ||||
|         ])->postJson('/api/v1/payments', $data); | ||||
| 
 | ||||
|         $response->assertStatus(200); | ||||
| 
 | ||||
| sleep(1); | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
|  | ||||
| @ -11,19 +11,20 @@ | ||||
| 
 | ||||
| namespace Tests\Feature; | ||||
| 
 | ||||
| use Tests\TestCase; | ||||
| use App\Utils\Ninja; | ||||
| use App\Models\Activity; | ||||
| use Tests\MockAccountData; | ||||
| use Illuminate\Support\Str; | ||||
| use App\Models\PurchaseOrder; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Models\PurchaseOrderInvitation; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Support\Facades\Session; | ||||
| use App\Repositories\ActivityRepository; | ||||
| use App\Events\PurchaseOrder\PurchaseOrderWasCreated; | ||||
| use App\Events\PurchaseOrder\PurchaseOrderWasUpdated; | ||||
| use App\Models\Activity; | ||||
| use App\Models\PurchaseOrder; | ||||
| use App\Repositories\ActivityRepository; | ||||
| use App\Utils\Ninja; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Foundation\Testing\DatabaseTransactions; | ||||
| use Illuminate\Support\Facades\Session; | ||||
| use Illuminate\Support\Str; | ||||
| use Tests\MockAccountData; | ||||
| use Tests\TestCase; | ||||
| 
 | ||||
| class PurchaseOrderTest extends TestCase | ||||
| { | ||||
| @ -36,13 +37,9 @@ class PurchaseOrderTest extends TestCase | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         parent::setUp(); | ||||
| 
 | ||||
|         Session::start(); | ||||
| 
 | ||||
|          | ||||
|         $this->faker = \Faker\Factory::create(); | ||||
| 
 | ||||
|         Model::reguard(); | ||||
| 
 | ||||
|         $this->makeTestData(); | ||||
|     } | ||||
| 
 | ||||
| @ -97,10 +94,39 @@ class PurchaseOrderTest extends TestCase | ||||
| 
 | ||||
|     public function testPurchaseOrderBulkActions() | ||||
|     { | ||||
|         $i = $this->purchase_order->invitations->first(); | ||||
| 
 | ||||
|         $po = PurchaseOrder::factory()->create([ | ||||
|             'user_id' => $this->user->id, | ||||
|             'company_id' => $this->company->id, | ||||
|             'vendor_id' => $this->vendor->id, | ||||
|         ]); | ||||
| 
 | ||||
|         // PurchaseOrderInvitation::factory()->create([
 | ||||
|         //     'user_id' => $this->user->id,
 | ||||
|         //     'company_id' => $this->company->id,
 | ||||
|         //     'vendor_contact_id' => $this->vendor->contacts()->first()->id,
 | ||||
|         //     'purchase_order_id' => $po->id,
 | ||||
|         // ]);
 | ||||
| 
 | ||||
| 
 | ||||
|         $po->service()->createInvitations()->save(); | ||||
| 
 | ||||
|         $i = $po->invitations->first(); | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'ids' => [$po->hashed_id], | ||||
|             'action' => 'download', | ||||
|         ]; | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
|         ])->post("/api/v1/purchase_orders/bulk", $data) | ||||
|         ->assertStatus(200); | ||||
| 
 | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$po->hashed_id], | ||||
|             'action' => 'archive', | ||||
|         ]; | ||||
| 
 | ||||
| @ -111,7 +137,7 @@ class PurchaseOrderTest extends TestCase | ||||
|         ->assertStatus(200); | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'ids' =>[$po->hashed_id], | ||||
|             'action' => 'restore', | ||||
|         ]; | ||||
| 
 | ||||
| @ -122,7 +148,7 @@ class PurchaseOrderTest extends TestCase | ||||
|         ->assertStatus(200); | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'ids' =>[$po->hashed_id], | ||||
|             'action' => 'delete', | ||||
|         ]; | ||||
| 
 | ||||
| @ -134,7 +160,7 @@ class PurchaseOrderTest extends TestCase | ||||
| 
 | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'ids' =>[$po->hashed_id], | ||||
|             'action' => 'restore', | ||||
|         ]; | ||||
| 
 | ||||
| @ -144,16 +170,6 @@ class PurchaseOrderTest extends TestCase | ||||
|         ])->post("/api/v1/purchase_orders/bulk", $data) | ||||
|         ->assertStatus(200); | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'action' => 'download', | ||||
|         ]; | ||||
| 
 | ||||
|         $response = $this->withHeaders([ | ||||
|             'X-API-SECRET' => config('ninja.api_secret'), | ||||
|             'X-API-TOKEN' => $this->token, | ||||
|         ])->post("/api/v1/purchase_orders/bulk", $data) | ||||
|         ->assertStatus(200); | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[], | ||||
| @ -167,7 +183,7 @@ class PurchaseOrderTest extends TestCase | ||||
|         ->assertStatus(302); | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'ids' =>[$po->hashed_id], | ||||
|             'action' => '', | ||||
|         ]; | ||||
| 
 | ||||
| @ -179,7 +195,7 @@ class PurchaseOrderTest extends TestCase | ||||
| 
 | ||||
| 
 | ||||
|         $data = [ | ||||
|             'ids' =>[$this->purchase_order->hashed_id], | ||||
|             'ids' =>[$po->hashed_id], | ||||
|             'action' => 'molly', | ||||
|         ]; | ||||
| 
 | ||||
|  | ||||
| @ -128,18 +128,6 @@ class AccountSummaryTest extends TestCase | ||||
|         parent::setUp(); | ||||
|     } | ||||
| 
 | ||||
|     public function testWithBadDataTransformations() | ||||
|     { | ||||
|         $dtox = \App\Helpers\Bank\Yodlee\DTO\AccountSummary::from($this->bad_data[0]); | ||||
|         $this->assertEquals(19315, $dtox->id); | ||||
|         $this->assertEquals('', $dtox->account_status); | ||||
|     } | ||||
| 
 | ||||
|     public function testTransform() | ||||
|     { | ||||
|         $dto = \App\Helpers\Bank\Yodlee\DTO\AccountSummary::from($this->data[0]); | ||||
|         $this->assertEquals($dto->id, 19315); | ||||
|     } | ||||
| 
 | ||||
|     public function testTransformRefactor() | ||||
|     { | ||||
|  | ||||
| @ -203,32 +203,32 @@ trait MockAccountData | ||||
|     { | ||||
|         config(['database.default' => config('ninja.db.default')]); | ||||
| 
 | ||||
|         /* Warm up the cache !*/ | ||||
|         $cached_tables = config('ninja.cached_tables'); | ||||
|         // /* Warm up the cache !*/
 | ||||
|         // $cached_tables = config('ninja.cached_tables');
 | ||||
| 
 | ||||
|         Artisan::call('db:seed', [ | ||||
|         '--force' => true | ||||
|         ]); | ||||
|         // Artisan::call('db:seed', [
 | ||||
|         // '--force' => true
 | ||||
|         // ]);
 | ||||
| 
 | ||||
|         foreach ($cached_tables as $name => $class) { | ||||
|             // check that the table exists in case the migration is pending
 | ||||
|             if (! Schema::hasTable((new $class())->getTable())) { | ||||
|                 continue; | ||||
|             } | ||||
|             if ($name == 'payment_terms') { | ||||
|                 $orderBy = 'num_days'; | ||||
|             } elseif ($name == 'fonts') { | ||||
|                 $orderBy = 'sort_order'; | ||||
|             } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) { | ||||
|                 $orderBy = 'name'; | ||||
|             } else { | ||||
|                 $orderBy = 'id'; | ||||
|             } | ||||
|             $tableData = $class::orderBy($orderBy)->get(); | ||||
|             if ($tableData->count()) { | ||||
|                 Cache::forever($name, $tableData); | ||||
|             } | ||||
|         } | ||||
|         // foreach ($cached_tables as $name => $class) {
 | ||||
|         //     // check that the table exists in case the migration is pending
 | ||||
|         //     if (! Schema::hasTable((new $class())->getTable())) {
 | ||||
|         //         continue;
 | ||||
|         //     }
 | ||||
|         //     if ($name == 'payment_terms') {
 | ||||
|         //         $orderBy = 'num_days';
 | ||||
|         //     } elseif ($name == 'fonts') {
 | ||||
|         //         $orderBy = 'sort_order';
 | ||||
|         //     } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
 | ||||
|         //         $orderBy = 'name';
 | ||||
|         //     } else {
 | ||||
|         //         $orderBy = 'id';
 | ||||
|         //     }
 | ||||
|         //     $tableData = $class::orderBy($orderBy)->get();
 | ||||
|         //     if ($tableData->count()) {
 | ||||
|         //         Cache::forever($name, $tableData);
 | ||||
|         //     }
 | ||||
|         // }
 | ||||
| 
 | ||||
|         $this->faker = \Faker\Factory::create(); | ||||
|         $fake_email = $this->faker->email(); | ||||
| @ -600,14 +600,9 @@ trait MockAccountData | ||||
|             'purchase_order_id' => $this->purchase_order->id, | ||||
|         ]); | ||||
| 
 | ||||
|         $purchase_order_invitations = PurchaseOrderInvitation::whereCompanyId($this->purchase_order->company_id) | ||||
|             ->wherePurchaseOrderId($this->purchase_order->id); | ||||
| 
 | ||||
|         $this->purchase_order->setRelation('invitations', $purchase_order_invitations); | ||||
| 
 | ||||
|         $this->purchase_order->service()->markSent(); | ||||
| 
 | ||||
|         $this->purchase_order->setRelation('client', $this->client); | ||||
|         $this->purchase_order->setRelation('vendor', $this->vendor); | ||||
|         $this->purchase_order->setRelation('company', $this->company); | ||||
| 
 | ||||
|         $this->purchase_order->save(); | ||||
| @ -813,7 +808,7 @@ trait MockAccountData | ||||
| 
 | ||||
|         if (config('ninja.testvars.stripe')) { | ||||
|             $data = []; | ||||
|             $data[1]['min_limit'] = 234; | ||||
|             $data[1]['min_limit'] = 22; | ||||
|             $data[1]['max_limit'] = 65317; | ||||
|             $data[1]['fee_amount'] = 0.00; | ||||
|             $data[1]['fee_percent'] = 0.000; | ||||
|  | ||||
| @ -24,6 +24,11 @@ class PdfGenerationTest extends TestCase | ||||
|     protected function setUp(): void | ||||
|     { | ||||
|         parent::setUp(); | ||||
| 
 | ||||
| if (config('ninja.testvars.travis') !== false) { | ||||
|     $this->markTestSkipped('Skip test for Travis'); | ||||
| } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function testPdfGeneration() | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user