mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 09:37:33 -04:00 
			
		
		
		
	Merge branch 'v5-develop' into v5-stable
This commit is contained in:
		
						commit
						ef2a8b832f
					
				| @ -1 +1 @@ | |||||||
| 5.4.4 | 5.4.7 | ||||||
| @ -25,6 +25,9 @@ class CompanySettings extends BaseSettings | |||||||
|     /*Invoice*/ |     /*Invoice*/ | ||||||
|     public $auto_archive_invoice = false; // @implemented
 |     public $auto_archive_invoice = false; // @implemented
 | ||||||
| 
 | 
 | ||||||
|  |     public $qr_iban = ''; //@implemented
 | ||||||
|  |     public $besr_id = ''; //@implemented
 | ||||||
|  | 
 | ||||||
|     public $lock_invoices = 'off'; //off,when_sent,when_paid //@implemented
 |     public $lock_invoices = 'off'; //off,when_sent,when_paid //@implemented
 | ||||||
| 
 | 
 | ||||||
|     public $enable_client_portal_tasks = false; //@ben to implement
 |     public $enable_client_portal_tasks = false; //@ben to implement
 | ||||||
| @ -289,6 +292,8 @@ class CompanySettings extends BaseSettings | |||||||
|     public $auto_archive_invoice_cancelled = false; |     public $auto_archive_invoice_cancelled = false; | ||||||
| 
 | 
 | ||||||
|     public static $casts = [ |     public static $casts = [ | ||||||
|  |         'besr_id'                            => 'string', | ||||||
|  |         'qr_iban'                            => 'string', | ||||||
|         'email_subject_purchase_order'       => 'string', |         'email_subject_purchase_order'       => 'string', | ||||||
|         'email_template_purchase_order'      => 'string', |         'email_template_purchase_order'      => 'string', | ||||||
|         'require_purchase_order_signature'   => 'bool', |         'require_purchase_order_signature'   => 'bool', | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| 
 | 
 | ||||||
| namespace App\Factory; | namespace App\Factory; | ||||||
| 
 | 
 | ||||||
|  | use App\Models\CompanyUser; | ||||||
| use App\Models\User; | use App\Models\User; | ||||||
| 
 | 
 | ||||||
| class UserFactory | class UserFactory | ||||||
|  | |||||||
| @ -138,6 +138,14 @@ class InvoiceFilters extends QueryFilters | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function without_deleted_clients() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         return $this->builder->whereHas('client', function ($query) { | ||||||
|  |                         $query->where('is_deleted',0); | ||||||
|  |                        }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function upcoming() |     public function upcoming() | ||||||
|     { |     { | ||||||
|         return $this->builder |         return $this->builder | ||||||
|  | |||||||
							
								
								
									
										151
									
								
								app/Helpers/SwissQr/SwissQrGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								app/Helpers/SwissQr/SwissQrGenerator.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Invoice Ninja (https://invoiceninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Helpers\SwissQr; | ||||||
|  | 
 | ||||||
|  | use App\Models\Client; | ||||||
|  | use App\Models\Company; | ||||||
|  | use App\Models\Invoice; | ||||||
|  | use Sprain\SwissQrBill as QrBill; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * SwissQrGenerator. | ||||||
|  |  */ | ||||||
|  | class SwissQrGenerator  | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     protected Company $company; | ||||||
|  | 
 | ||||||
|  |     protected $invoice; | ||||||
|  | 
 | ||||||
|  |     protected Client $client; | ||||||
|  | 
 | ||||||
|  |     public function __construct($invoice, Company $company) | ||||||
|  |     { | ||||||
|  |         $this->company = $company; | ||||||
|  | 
 | ||||||
|  |         $this->invoice = $invoice; | ||||||
|  |      | ||||||
|  |         $this->client = $invoice->client; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private function calcDueAmount() | ||||||
|  |     { | ||||||
|  |         if($this->invoice->partial > 0) | ||||||
|  |             return $this->invoice->partial; | ||||||
|  | 
 | ||||||
|  |         if($this->invoice->status_id == Invoice::STATUS_DRAFT) | ||||||
|  |             return $this->invoice->amount; | ||||||
|  | 
 | ||||||
|  |         return $this->invoice->balance; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function run() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |     // This is an example how to create a typical qr bill:
 | ||||||
|  |     // - with reference number
 | ||||||
|  |     // - with known debtor
 | ||||||
|  |     // - with specified amount
 | ||||||
|  |     // - with human-readable additional information
 | ||||||
|  |     // - using your QR-IBAN
 | ||||||
|  |     //
 | ||||||
|  |     // Likely the most common use-case in the business world.
 | ||||||
|  | 
 | ||||||
|  |     // Create a new instance of QrBill, containing default headers with fixed values
 | ||||||
|  |     $qrBill = QrBill\QrBill::create(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // Add creditor information
 | ||||||
|  |     // Who will receive the payment and to which bank account?
 | ||||||
|  |     $qrBill->setCreditor( | ||||||
|  |         QrBill\DataGroup\Element\CombinedAddress::create( | ||||||
|  |             $this->company->present()->name(), | ||||||
|  |             $this->company->present()->address1(), | ||||||
|  |             $this->company->present()->getCompanyCityState(), | ||||||
|  |             'CH' | ||||||
|  |         )); | ||||||
|  | 
 | ||||||
|  |     $qrBill->setCreditorInformation( | ||||||
|  |         QrBill\DataGroup\Element\CreditorInformation::create( | ||||||
|  |             $this->company->present()->qr_iban() ?: '' // This is a special QR-IBAN. Classic IBANs will not be valid here.
 | ||||||
|  |         )); | ||||||
|  | 
 | ||||||
|  |     // Add debtor information
 | ||||||
|  |     // Who has to pay the invoice? This part is optional.
 | ||||||
|  |     //
 | ||||||
|  |     // Notice how you can use two different styles of addresses: CombinedAddress or StructuredAddress
 | ||||||
|  |     // They are interchangeable for creditor as well as debtor.
 | ||||||
|  |     $qrBill->setUltimateDebtor( | ||||||
|  |         QrBill\DataGroup\Element\StructuredAddress::createWithStreet( | ||||||
|  |             substr($this->client->present()->name(), 0 , 70), | ||||||
|  |             $this->client->address1 ? substr($this->client->address1, 0 , 70) : '', | ||||||
|  |             $this->client->address2 ? substr($this->client->address2, 0 , 16) : '', | ||||||
|  |             $this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '', | ||||||
|  |             $this->client->city ? substr($this->client->postal_code, 0, 35) : '', | ||||||
|  |             'CH' | ||||||
|  |         )); | ||||||
|  | 
 | ||||||
|  |     // Add payment amount information
 | ||||||
|  |     // What amount is to be paid?
 | ||||||
|  |     $qrBill->setPaymentAmountInformation( | ||||||
|  |         QrBill\DataGroup\Element\PaymentAmountInformation::create( | ||||||
|  |             'CHF', | ||||||
|  |             $this->calcDueAmount() | ||||||
|  |         )); | ||||||
|  | 
 | ||||||
|  |     // Add payment reference
 | ||||||
|  |     // This is what you will need to identify incoming payments.
 | ||||||
|  |     $referenceNumber = QrBill\Reference\QrPaymentReferenceGenerator::generate( | ||||||
|  |         $this->company->present()->besr_id() ?: '',  // You receive this number from your bank (BESR-ID). Unless your bank is PostFinance, in that case use NULL.
 | ||||||
|  |         $this->invoice->number// A number to match the payment with your internal data, e.g. an invoice number
 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     $qrBill->setPaymentReference( | ||||||
|  |         QrBill\DataGroup\Element\PaymentReference::create( | ||||||
|  |             QrBill\DataGroup\Element\PaymentReference::TYPE_QR, | ||||||
|  |             $referenceNumber | ||||||
|  |         )); | ||||||
|  | 
 | ||||||
|  |     // Optionally, add some human-readable information about what the bill is for.
 | ||||||
|  |     $qrBill->setAdditionalInformation( | ||||||
|  |         QrBill\DataGroup\Element\AdditionalInformation::create( | ||||||
|  |             $this->invoice->public_notes ?: '' | ||||||
|  |         ) | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // Now get the QR code image and save it as a file.
 | ||||||
|  |         try { | ||||||
|  | 
 | ||||||
|  |             $output = new QrBill\PaymentPart\Output\HtmlOutput\HtmlOutput($qrBill, 'en'); | ||||||
|  | 
 | ||||||
|  |             $html = $output | ||||||
|  |                 ->setPrintable(false) | ||||||
|  |                 ->getPaymentPart(); | ||||||
|  | 
 | ||||||
|  |                 return $html; | ||||||
|  | 
 | ||||||
|  |         } catch (\Exception $e) { | ||||||
|  | 
 | ||||||
|  |             foreach($qrBill->getViolations() as $key => $violation) { | ||||||
|  |                 nlog("qr"); | ||||||
|  |                 nlog($violation); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return ''; | ||||||
|  |             // return $e->getMessage();
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -332,11 +332,6 @@ class LoginController extends BaseController | |||||||
|         if (request()->input('provider') == 'google') { |         if (request()->input('provider') == 'google') { | ||||||
|             return $this->handleGoogleOauth(); |             return $this->handleGoogleOauth(); | ||||||
|         } elseif (request()->input('provider') == 'microsoft') { |         } elseif (request()->input('provider') == 'microsoft') { | ||||||
|             // if (request()->has('token')) {
 |  | ||||||
|             //     return $this->handleSocialiteLogin('microsoft', request()->get('token'));
 |  | ||||||
|             // } else {
 |  | ||||||
|             //     $message = 'Bearer token missing for the microsoft login';
 |  | ||||||
|             // }
 |  | ||||||
|             return $this->handleMicrosoftOauth(); |             return $this->handleMicrosoftOauth(); | ||||||
|         } elseif (request()->input('provider') == 'apple') { |         } elseif (request()->input('provider') == 'apple') { | ||||||
|             // if (request()->has('token')) {
 |             // if (request()->has('token')) {
 | ||||||
| @ -498,8 +493,10 @@ class LoginController extends BaseController | |||||||
|     { |     { | ||||||
|         if(request()->has('accessToken')) |         if(request()->has('accessToken')) | ||||||
|             $accessToken = request()->input('accessToken'); |             $accessToken = request()->input('accessToken'); | ||||||
|  |         elseif(request()->has('access_token')) | ||||||
|  |             $accessToken = request()->input('access_token'); | ||||||
|         else |         else | ||||||
|             return response()->json(['message' => 'Invalid response from oauth server'], 400); |             return response()->json(['message' => 'Invalid response from oauth server, no access token in response.'], 400); | ||||||
| 
 | 
 | ||||||
|         $graph = new \Microsoft\Graph\Graph(); |         $graph = new \Microsoft\Graph\Graph(); | ||||||
|         $graph->setAccessToken($accessToken); |         $graph->setAccessToken($accessToken); | ||||||
| @ -510,7 +507,6 @@ class LoginController extends BaseController | |||||||
| 
 | 
 | ||||||
|         if($user){ |         if($user){ | ||||||
| 
 | 
 | ||||||
|             $account = request()->input('account'); |  | ||||||
|             $email = $user->getMail() ?: $user->getUserPrincipalName(); |             $email = $user->getMail() ?: $user->getUserPrincipalName(); | ||||||
| 
 | 
 | ||||||
|             $query = [ |             $query = [ | ||||||
| @ -551,6 +547,8 @@ class LoginController extends BaseController | |||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         return response()->json(['message' => 'Unable to authenticate this user'], 400); | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function existingOauthUser($existing_user) |     private function existingOauthUser($existing_user) | ||||||
| @ -698,7 +696,7 @@ class LoginController extends BaseController | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if($provider == 'microsoft'){ |         if($provider == 'microsoft'){ | ||||||
|             $scopes = ['email', 'Mail.ReadWrite', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid']; |             $scopes = ['email', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid']; | ||||||
|             $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url')."/auth/microsoft"]; |             $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url')."/auth/microsoft"]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -770,6 +768,8 @@ class LoginController extends BaseController | |||||||
| 
 | 
 | ||||||
|         $oauth_user_token = $socialite_user->accessTokenResponseBody['access_token']; |         $oauth_user_token = $socialite_user->accessTokenResponseBody['access_token']; | ||||||
| 
 | 
 | ||||||
|  |         $oauth_expiry = now()->addSeconds($socialite_user->accessTokenResponseBody['expires_in']) ?: now()->addSeconds(300); | ||||||
|  | 
 | ||||||
|         if($user = OAuth::handleAuth($socialite_user, $provider)) |         if($user = OAuth::handleAuth($socialite_user, $provider)) | ||||||
|         { |         { | ||||||
| 
 | 
 | ||||||
| @ -783,7 +783,8 @@ class LoginController extends BaseController | |||||||
|                 'oauth_user_id' => $socialite_user->getId(), |                 'oauth_user_id' => $socialite_user->getId(), | ||||||
|                 'oauth_provider_id' => $provider, |                 'oauth_provider_id' => $provider, | ||||||
|                 'oauth_user_token' => $oauth_user_token, |                 'oauth_user_token' => $oauth_user_token, | ||||||
|                 'oauth_user_refresh_token' => $socialite_user->accessTokenResponseBody['refresh_token'] |                 'oauth_user_refresh_token' => $socialite_user->accessTokenResponseBody['refresh_token'], | ||||||
|  |                 'oauth_user_token_expiry' => $oauth_expiry, | ||||||
|             ]; |             ]; | ||||||
| 
 | 
 | ||||||
|             $user->update($update_user); |             $user->update($update_user); | ||||||
|  | |||||||
| @ -79,6 +79,7 @@ class BaseController extends Controller | |||||||
|           'company.groups.documents', |           'company.groups.documents', | ||||||
|           'company.invoices.invitations.contact', |           'company.invoices.invitations.contact', | ||||||
|           'company.invoices.invitations.company', |           'company.invoices.invitations.company', | ||||||
|  |           'company.purchase_orders.invitations', | ||||||
|           'company.invoices.documents', |           'company.invoices.documents', | ||||||
|           'company.products', |           'company.products', | ||||||
|           'company.products.documents', |           'company.products.documents', | ||||||
| @ -778,8 +779,13 @@ class BaseController extends Controller | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             /* Clean up URLs and remove query parameters from the URL*/ |             /* Clean up URLs and remove query parameters from the URL*/ | ||||||
|             if(request()->has('login') && request()->input('login') == 'true') |             if (request()->has('login') && request()->input('login') == 'true') { | ||||||
|                 return redirect('/')->with(['login' => "true"]); |                 return redirect('/')->with(['login' => 'true']); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (request()->has('signup') && request()->input('signup') == 'true') { | ||||||
|  |                 return redirect('/')->with(['signup' => 'true']); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             $data = []; |             $data = []; | ||||||
| 
 | 
 | ||||||
| @ -789,10 +795,16 @@ class BaseController extends Controller | |||||||
|             //pass referral code to front end
 |             //pass referral code to front end
 | ||||||
|             $data['rc'] = request()->has('rc') ? request()->input('rc') : ''; |             $data['rc'] = request()->has('rc') ? request()->input('rc') : ''; | ||||||
|             $data['build'] = request()->has('build') ? request()->input('build') : ''; |             $data['build'] = request()->has('build') ? request()->input('build') : ''; | ||||||
|             $data['login'] = request()->has('login') ? request()->input('login') : "false"; |             $data['login'] = request()->has('login') ? request()->input('login') : 'false'; | ||||||
|  |             $data['signup'] = request()->has('signup') ? request()->input('signup') : 'false'; | ||||||
| 
 | 
 | ||||||
|             if(request()->session()->has('login')) |             if (request()->session()->has('login')) { | ||||||
|                 $data['login'] = "true"; |                 $data['login'] = 'true'; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(request()->session()->has('signup')){ | ||||||
|  |                 $data['signup'] = 'true'; | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             $data['user_agent'] = request()->server('HTTP_USER_AGENT'); |             $data['user_agent'] = request()->server('HTTP_USER_AGENT'); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -235,6 +235,9 @@ class InvitationController extends Controller | |||||||
|                                     ->with('contact.client') |                                     ->with('contact.client') | ||||||
|                                     ->firstOrFail(); |                                     ->firstOrFail(); | ||||||
| 
 | 
 | ||||||
|  |         if($invitation->contact->trashed()) | ||||||
|  |             $invitation->contact->restore(); | ||||||
|  |          | ||||||
|         auth()->guard('contact')->loginUsingId($invitation->contact->id, true); |         auth()->guard('contact')->loginUsingId($invitation->contact->id, true); | ||||||
| 
 | 
 | ||||||
|         $invoice = $invitation->invoice; |         $invoice = $invitation->invoice; | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ use App\Models\GatewayType; | |||||||
| use App\Models\Invoice; | use App\Models\Invoice; | ||||||
| use App\Models\RecurringInvoice; | use App\Models\RecurringInvoice; | ||||||
| use App\Models\Subscription; | use App\Models\Subscription; | ||||||
|  | use App\Notifications\Ninja\NewAccountNotification; | ||||||
| use App\Repositories\SubscriptionRepository; | use App\Repositories\SubscriptionRepository; | ||||||
| use App\Utils\Ninja; | use App\Utils\Ninja; | ||||||
| use App\Utils\Traits\MakesHash; | use App\Utils\Traits\MakesHash; | ||||||
| @ -165,6 +166,9 @@ class NinjaPlanController extends Controller | |||||||
|                  ->increment() |                  ->increment() | ||||||
|                  ->queue(); |                  ->queue(); | ||||||
| 
 | 
 | ||||||
|  |         $ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id')); | ||||||
|  |         $ninja_company->notification(new NewAccountNotification($account, $client))->ninja(); | ||||||
|  | 
 | ||||||
|         return $this->render('plan.trial_confirmed', $data); |         return $this->render('plan.trial_confirmed', $data); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -190,7 +190,7 @@ class QuoteController extends Controller | |||||||
|         if ($process) { |         if ($process) { | ||||||
|             foreach ($quotes as $quote) { |             foreach ($quotes as $quote) { | ||||||
|                 $quote->service()->approve(auth()->user())->save(); |                 $quote->service()->approve(auth()->user())->save(); | ||||||
|                 event(new QuoteWasApproved(auth()->guard('contact')->user(), $quote, $quote->company, Ninja::eventVars())); |                 // event(new QuoteWasApproved(auth()->guard('contact')->user(), $quote, $quote->company, Ninja::eventVars()));
 | ||||||
| 
 | 
 | ||||||
|                 if (request()->has('signature') && !is_null(request()->signature) && !empty(request()->signature)) { |                 if (request()->has('signature') && !is_null(request()->signature) && !empty(request()->signature)) { | ||||||
|                     InjectSignature::dispatch($quote, request()->signature); |                     InjectSignature::dispatch($quote, request()->signature); | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ use Google_Client; | |||||||
| use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||||
| use Illuminate\Support\Facades\Cache; | use Illuminate\Support\Facades\Cache; | ||||||
| use Illuminate\Support\Str; | use Illuminate\Support\Str; | ||||||
|  | use Microsoft\Graph\Model; | ||||||
| 
 | 
 | ||||||
| class ConnectedAccountController extends BaseController | class ConnectedAccountController extends BaseController | ||||||
| { | { | ||||||
| @ -81,12 +82,61 @@ class ConnectedAccountController extends BaseController | |||||||
|             return $this->handleGoogleOauth(); |             return $this->handleGoogleOauth(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if ($request->input('provider') == 'microsoft') { | ||||||
|  |             return $this->handleMicrosoftOauth($request); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         return response() |         return response() | ||||||
|         ->json(['message' => 'Provider not supported'], 400) |         ->json(['message' => 'Provider not supported'], 400) | ||||||
|         ->header('X-App-Version', config('ninja.app_version')) |         ->header('X-App-Version', config('ninja.app_version')) | ||||||
|         ->header('X-Api-Version', config('ninja.minimum_client_version')); |         ->header('X-Api-Version', config('ninja.minimum_client_version')); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private function handleMicrosoftOauth($request) | ||||||
|  |     { | ||||||
|  |         nlog($request->all()); | ||||||
|  | 
 | ||||||
|  |         if(!$request->has('access_token')) | ||||||
|  |             return response()->json(['message' => 'No access_token parameter found!'], 400); | ||||||
|  | 
 | ||||||
|  |         $graph = new \Microsoft\Graph\Graph(); | ||||||
|  |         $graph->setAccessToken($request->input('access_token')); | ||||||
|  | 
 | ||||||
|  |         $user = $graph->createRequest("GET", "/me") | ||||||
|  |                       ->setReturnType(Model\User::class) | ||||||
|  |                       ->execute(); | ||||||
|  | 
 | ||||||
|  |         if($user){ | ||||||
|  | 
 | ||||||
|  |             $email = $user->getMail() ?: $user->getUserPrincipalName(); | ||||||
|  | 
 | ||||||
|  |             if(auth()->user()->email != $email && MultiDB::checkUserEmailExists($email)) | ||||||
|  |                 return response()->json(['message' => ctrans('texts.email_already_register')], 400); | ||||||
|  | 
 | ||||||
|  |             $connected_account = [ | ||||||
|  |                 'email' => $email, | ||||||
|  |                 'oauth_user_id' => $user->getId(), | ||||||
|  |                 'oauth_provider_id' => 'microsoft', | ||||||
|  |                 'email_verified_at' =>now() | ||||||
|  |             ]; | ||||||
|  | 
 | ||||||
|  |             auth()->user()->update($connected_account); | ||||||
|  |             auth()->user()->email_verified_at = now(); | ||||||
|  |             auth()->user()->save(); | ||||||
|  |              | ||||||
|  |             $this->setLoginCache(auth()->user()); | ||||||
|  |              | ||||||
|  |             return $this->itemResponse(auth()->user()); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return response() | ||||||
|  |         ->json(['message' => ctrans('texts.invalid_credentials')], 401) | ||||||
|  |         ->header('X-App-Version', config('ninja.app_version')) | ||||||
|  |         ->header('X-Api-Version', config('ninja.minimum_client_version')); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private function handleGoogleOauth() |     private function handleGoogleOauth() | ||||||
|     { |     { | ||||||
|         $user = false; |         $user = false; | ||||||
|  | |||||||
| @ -284,12 +284,11 @@ class PreviewController extends BaseController | |||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
|         catch(\Exception $e){ |         catch(\Exception $e){ | ||||||
| 
 |             nlog($e->getMessage()); | ||||||
|             DB::connection(config('database.default'))->rollBack(); |             DB::connection(config('database.default'))->rollBack(); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|             //if phantom js...... inject here..
 |             //if phantom js...... inject here..
 | ||||||
|             if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { |             if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { | ||||||
|                 return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); |                 return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); | ||||||
|  | |||||||
							
								
								
									
										470
									
								
								app/Http/Controllers/PreviewPurchaseOrderController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										470
									
								
								app/Http/Controllers/PreviewPurchaseOrderController.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,470 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Invoice Ninja (https://invoiceninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Http\Controllers; | ||||||
|  | 
 | ||||||
|  | use App\DataMapper\Analytics\LivePreview; | ||||||
|  | use App\Factory\CreditFactory; | ||||||
|  | use App\Factory\InvoiceFactory; | ||||||
|  | use App\Factory\PurchaseOrderFactory; | ||||||
|  | use App\Factory\QuoteFactory; | ||||||
|  | use App\Factory\RecurringInvoiceFactory; | ||||||
|  | use App\Http\Requests\Invoice\StoreInvoiceRequest; | ||||||
|  | use App\Http\Requests\Preview\PreviewInvoiceRequest; | ||||||
|  | use App\Http\Requests\Preview\PreviewPurchaseOrderRequest; | ||||||
|  | use App\Jobs\Util\PreviewPdf; | ||||||
|  | use App\Libraries\MultiDB; | ||||||
|  | use App\Models\Client; | ||||||
|  | use App\Models\ClientContact; | ||||||
|  | use App\Models\Credit; | ||||||
|  | use App\Models\Invoice; | ||||||
|  | use App\Models\InvoiceInvitation; | ||||||
|  | use App\Models\PurchaseOrder; | ||||||
|  | use App\Models\PurchaseOrderInvitation; | ||||||
|  | use App\Models\Quote; | ||||||
|  | use App\Models\RecurringInvoice; | ||||||
|  | use App\Models\Vendor; | ||||||
|  | use App\Models\VendorContact; | ||||||
|  | use App\Repositories\CreditRepository; | ||||||
|  | use App\Repositories\InvoiceRepository; | ||||||
|  | use App\Repositories\PurchaseOrderRepository; | ||||||
|  | use App\Repositories\QuoteRepository; | ||||||
|  | use App\Repositories\RecurringInvoiceRepository; | ||||||
|  | use App\Services\PdfMaker\Design as PdfDesignModel; | ||||||
|  | use App\Services\PdfMaker\Design as PdfMakerDesign; | ||||||
|  | use App\Services\PdfMaker\Design; | ||||||
|  | use App\Services\PdfMaker\PdfMaker; | ||||||
|  | use App\Utils\HostedPDF\NinjaPdf; | ||||||
|  | use App\Utils\HtmlEngine; | ||||||
|  | use App\Utils\Ninja; | ||||||
|  | use App\Utils\PhantomJS\Phantom; | ||||||
|  | use App\Utils\Traits\MakesHash; | ||||||
|  | use App\Utils\Traits\MakesInvoiceHtml; | ||||||
|  | use App\Utils\Traits\Pdf\PageNumbering; | ||||||
|  | use App\Utils\VendorHtmlEngine; | ||||||
|  | use Illuminate\Support\Facades\App; | ||||||
|  | use Illuminate\Support\Facades\DB; | ||||||
|  | use Illuminate\Support\Facades\Lang; | ||||||
|  | use Illuminate\Support\Facades\Response; | ||||||
|  | use Turbo124\Beacon\Facades\LightLogs; | ||||||
|  | 
 | ||||||
|  | class PreviewPurchaseOrderController extends BaseController | ||||||
|  | { | ||||||
|  |     use MakesHash; | ||||||
|  |     use MakesInvoiceHtml; | ||||||
|  |     use PageNumbering; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  |         parent::__construct(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns a template filled with entity variables. | ||||||
|  |      * | ||||||
|  |      * @return \Illuminate\Http\Response | ||||||
|  |      * | ||||||
|  |      * @OA\Post( | ||||||
|  |      *      path="/api/v1/preview/purchase_order", | ||||||
|  |      *      operationId="getPreviewPurchaseOrder", | ||||||
|  |      *      tags={"preview"}, | ||||||
|  |      *      summary="Returns a pdf preview for purchase order", | ||||||
|  |      *      description="Returns a pdf preview for purchase order.", | ||||||
|  |      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||||
|  |      *      @OA\Parameter(ref="#/components/parameters/X-Requested-With"), | ||||||
|  |      *      @OA\Response( | ||||||
|  |      *          response=200, | ||||||
|  |      *          description="The pdf response", | ||||||
|  |      *          @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 show() | ||||||
|  |     { | ||||||
|  |         if (request()->has('entity') && | ||||||
|  |             request()->has('entity_id') && | ||||||
|  |             ! empty(request()->input('entity')) && | ||||||
|  |             ! empty(request()->input('entity_id')) && | ||||||
|  |             request()->has('body')) { | ||||||
|  |              | ||||||
|  |             $design_object = json_decode(json_encode(request()->input('design'))); | ||||||
|  | 
 | ||||||
|  |             if (! is_object($design_object)) { | ||||||
|  |                 return response()->json(['message' => ctrans('texts.invalid_design_object')], 400); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $entity_obj = PurchaseOrder::whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first(); | ||||||
|  | 
 | ||||||
|  |             if (! $entity_obj) { | ||||||
|  |                 return $this->blankEntity(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             App::forgetInstance('translator'); | ||||||
|  |             $t = app('translator'); | ||||||
|  |             App::setLocale($entity_obj->company->locale()); | ||||||
|  |             $t->replace(Ninja::transformTranslations($entity_obj->company->settings)); | ||||||
|  | 
 | ||||||
|  |             $html = new VendorHtmlEngine($entity_obj->invitations()->first()); | ||||||
|  | 
 | ||||||
|  |             $design_namespace = 'App\Services\PdfMaker\Designs\\'.request()->design['name']; | ||||||
|  | 
 | ||||||
|  |             $design_class = new $design_namespace(); | ||||||
|  | 
 | ||||||
|  |             $state = [ | ||||||
|  |                 'template' => $design_class->elements([ | ||||||
|  |                     'client' => null, | ||||||
|  |                     'vendor' => $entity_obj->vendor, | ||||||
|  |                     'entity' => $entity_obj, | ||||||
|  |                     'pdf_variables' => (array) $entity_obj->company->settings->pdf_variables, | ||||||
|  |                     'variables' => $html->generateLabelsAndValues(), | ||||||
|  |                 ]), | ||||||
|  |                 'variables' => $html->generateLabelsAndValues(), | ||||||
|  |                 'process_markdown' => $entity_obj->company->markdown_enabled, | ||||||
|  |             ]; | ||||||
|  | 
 | ||||||
|  |             $design = new Design(request()->design['name']); | ||||||
|  |             $maker = new PdfMaker($state); | ||||||
|  | 
 | ||||||
|  |             $maker | ||||||
|  |                 ->design($design) | ||||||
|  |                 ->build(); | ||||||
|  | 
 | ||||||
|  |             if (request()->query('html') == 'true') { | ||||||
|  |                 return $maker->getCompiledHTML(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             //if phantom js...... inject here..
 | ||||||
|  |             if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { | ||||||
|  |                 return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){ | ||||||
|  |                 $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true)); | ||||||
|  | 
 | ||||||
|  |                 $numbered_pdf = $this->pageNumbering($pdf, auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |                 if($numbered_pdf) | ||||||
|  |                     $pdf = $numbered_pdf; | ||||||
|  | 
 | ||||||
|  |                 return $pdf; | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             //else
 | ||||||
|  |             $file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |             return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $this->blankEntity(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function live(PreviewPurchaseOrderRequest $request) | ||||||
|  |     { | ||||||
|  |         $company = auth()->user()->company(); | ||||||
|  | 
 | ||||||
|  |         MultiDB::setDb($company->db); | ||||||
|  | 
 | ||||||
|  |         $repo = new PurchaseOrderRepository(); | ||||||
|  |         $entity_obj = PurchaseOrderFactory::create($company->id, auth()->user()->id); | ||||||
|  |         $class = PurchaseOrder::class; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  | 
 | ||||||
|  |             DB::connection(config('database.default'))->beginTransaction(); | ||||||
|  | 
 | ||||||
|  |             if($request->has('entity_id')){ | ||||||
|  | 
 | ||||||
|  |                 $entity_obj = $class::on(config('database.default')) | ||||||
|  |                                     ->with('vendor.company') | ||||||
|  |                                     ->where('id', $this->decodePrimaryKey($request->input('entity_id'))) | ||||||
|  |                                     ->where('company_id', $company->id) | ||||||
|  |                                     ->withTrashed() | ||||||
|  |                                     ->first(); | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $entity_obj = $repo->save($request->all(), $entity_obj); | ||||||
|  | 
 | ||||||
|  |             if(!$request->has('entity_id')) | ||||||
|  |                 $entity_obj->service()->fillDefaults()->save(); | ||||||
|  |                  | ||||||
|  |             App::forgetInstance('translator'); | ||||||
|  |             $t = app('translator'); | ||||||
|  |             App::setLocale($entity_obj->company->locale()); | ||||||
|  |             $t->replace(Ninja::transformTranslations($entity_obj->company->settings)); | ||||||
|  | 
 | ||||||
|  |             $html = new VendorHtmlEngine($entity_obj->invitations()->first()); | ||||||
|  | 
 | ||||||
|  |             $design = \App\Models\Design::find($entity_obj->design_id); | ||||||
|  | 
 | ||||||
|  |             /* Catch all in case migration doesn't pass back a valid design */ | ||||||
|  |             if(!$design) | ||||||
|  |                 $design = \App\Models\Design::find(2); | ||||||
|  | 
 | ||||||
|  |             if ($design->is_custom) { | ||||||
|  |                 $options = [ | ||||||
|  |                 'custom_partials' => json_decode(json_encode($design->design), true) | ||||||
|  |               ]; | ||||||
|  |                 $template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options); | ||||||
|  |             } else { | ||||||
|  |                 $template = new PdfMakerDesign(strtolower($design->name)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $variables = $html->generateLabelsAndValues(); | ||||||
|  | 
 | ||||||
|  |             $state = [ | ||||||
|  |                 'template' => $template->elements([ | ||||||
|  |                     'client' => null, | ||||||
|  |                     'vendor' => $entity_obj->vendor, | ||||||
|  |                     'entity' => $entity_obj, | ||||||
|  |                     'pdf_variables' => (array) $entity_obj->company->settings->pdf_variables, | ||||||
|  |                     'variables' => $html->generateLabelsAndValues(), | ||||||
|  |                     '$product' => $design->design->product, | ||||||
|  |                 ]), | ||||||
|  |                 'variables' => $html->generateLabelsAndValues(), | ||||||
|  |                 'process_markdown' => $entity_obj->company->markdown_enabled, | ||||||
|  |             ]; | ||||||
|  | 
 | ||||||
|  |             $maker = new PdfMaker($state); | ||||||
|  | 
 | ||||||
|  |             $maker | ||||||
|  |                 ->design($template) | ||||||
|  |                 ->build(); | ||||||
|  | 
 | ||||||
|  |             DB::connection(config('database.default'))->rollBack(); | ||||||
|  | 
 | ||||||
|  |             if (request()->query('html') == 'true') { | ||||||
|  |                 return $maker->getCompiledHTML(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |         catch(\Exception $e){ | ||||||
|  | 
 | ||||||
|  |             DB::connection(config('database.default'))->rollBack(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             //if phantom js...... inject here..
 | ||||||
|  |             if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { | ||||||
|  |                 return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){ | ||||||
|  |                 $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true)); | ||||||
|  | 
 | ||||||
|  |                 $numbered_pdf = $this->pageNumbering($pdf, auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |                 if($numbered_pdf) | ||||||
|  |                     $pdf = $numbered_pdf; | ||||||
|  | 
 | ||||||
|  |                 return $pdf; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             $file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), $company); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             if(Ninja::isHosted()) | ||||||
|  |             { | ||||||
|  |                 LightLogs::create(new LivePreview()) | ||||||
|  |                          ->increment() | ||||||
|  |                          ->queue(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         $response = Response::make($file_path, 200); | ||||||
|  |         $response->header('Content-Type', 'application/pdf'); | ||||||
|  | 
 | ||||||
|  |         return $response; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private function blankEntity() | ||||||
|  |     { | ||||||
|  |         App::forgetInstance('translator'); | ||||||
|  |         $t = app('translator'); | ||||||
|  |         $t->replace(Ninja::transformTranslations(auth()->user()->company()->settings)); | ||||||
|  | 
 | ||||||
|  |         $invitation = PurchaseOrderInvitation::where('company_id', auth()->user()->company()->id)->orderBy('id', 'desc')->first(); | ||||||
|  | 
 | ||||||
|  |         /* If we don't have a valid invitation in the system - create a mock using transactions */ | ||||||
|  |         if(!$invitation) | ||||||
|  |             return $this->mockEntity(); | ||||||
|  | 
 | ||||||
|  |         $design_object = json_decode(json_encode(request()->input('design'))); | ||||||
|  | 
 | ||||||
|  |         if (! is_object($design_object)) { | ||||||
|  |             return response()->json(['message' => 'Invalid custom design object'], 400); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $html = new VendorHtmlEngine($invitation); | ||||||
|  | 
 | ||||||
|  |         $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]); | ||||||
|  | 
 | ||||||
|  |         $state = [ | ||||||
|  |             'template' => $design->elements([ | ||||||
|  |                 'client' => null, | ||||||
|  |                 'vendor' => $invitation->purchase_order->vendor, | ||||||
|  |                 'entity' => $invitation->purchase_order, | ||||||
|  |                 'pdf_variables' => (array) $invitation->company->settings->pdf_variables, | ||||||
|  |                 'products' => request()->design['design']['product'], | ||||||
|  |             ]), | ||||||
|  |             'variables' => $html->generateLabelsAndValues(), | ||||||
|  |             'process_markdown' => $invitation->company->markdown_enabled, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         $maker = new PdfMaker($state); | ||||||
|  | 
 | ||||||
|  |         $maker | ||||||
|  |             ->design($design) | ||||||
|  |             ->build(); | ||||||
|  | 
 | ||||||
|  |         if (request()->query('html') == 'true') { | ||||||
|  |             return $maker->getCompiledHTML(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { | ||||||
|  |             return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){ | ||||||
|  |             $pdf =  (new NinjaPdf())->build($maker->getCompiledHTML(true)); | ||||||
|  | 
 | ||||||
|  |             $numbered_pdf = $this->pageNumbering($pdf, auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |                 if($numbered_pdf) | ||||||
|  |                     $pdf = $numbered_pdf; | ||||||
|  | 
 | ||||||
|  |                 return $pdf; | ||||||
|  |         } | ||||||
|  |              | ||||||
|  |         $file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |         $response = Response::make($file_path, 200); | ||||||
|  |         $response->header('Content-Type', 'application/pdf'); | ||||||
|  | 
 | ||||||
|  |         return $response; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private function mockEntity() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         DB::connection(auth()->user()->company()->db)->beginTransaction(); | ||||||
|  | 
 | ||||||
|  |         $vendor = Vendor::factory()->create([ | ||||||
|  |                 'user_id' => auth()->user()->id, | ||||||
|  |                 'company_id' => auth()->user()->company()->id, | ||||||
|  |             ]); | ||||||
|  | 
 | ||||||
|  |         $contact = VendorContact::factory()->create([ | ||||||
|  |                 'user_id' => auth()->user()->id, | ||||||
|  |                 'company_id' => auth()->user()->company()->id, | ||||||
|  |                 'vendor_id' => $vendor->id, | ||||||
|  |                 'is_primary' => 1, | ||||||
|  |                 'send_email' => true, | ||||||
|  |             ]); | ||||||
|  | 
 | ||||||
|  |         $purchase_order = PurchaseOrder::factory()->create([ | ||||||
|  |                     'user_id' => auth()->user()->id, | ||||||
|  |                     'company_id' => auth()->user()->company()->id, | ||||||
|  |                     'vendor_id' => $vendor->id, | ||||||
|  |                     'terms' => 'Sample Terms', | ||||||
|  |                     'footer' => 'Sample Footer', | ||||||
|  |                     'public_notes' => 'Sample Public Notes', | ||||||
|  |                 ]); | ||||||
|  | 
 | ||||||
|  |         $invitation = PurchaseOrderInvitation::factory()->create([ | ||||||
|  |                     'user_id' => auth()->user()->id, | ||||||
|  |                     'company_id' => auth()->user()->company()->id, | ||||||
|  |                     'purchase_order_id' => $purchase_order->id, | ||||||
|  |                     'vendor_contact_id' => $contact->id, | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|  |         $purchase_order->setRelation('invitations', $invitation); | ||||||
|  |         $purchase_order->setRelation('vendor', $vendor); | ||||||
|  |         $purchase_order->setRelation('company', auth()->user()->company()); | ||||||
|  |         $purchase_order->load('vendor.company'); | ||||||
|  | 
 | ||||||
|  |         $design_object = json_decode(json_encode(request()->input('design'))); | ||||||
|  | 
 | ||||||
|  |         if (! is_object($design_object)) { | ||||||
|  |             return response()->json(['message' => 'Invalid custom design object'], 400); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $html = new VendorHtmlEngine($purchase_order->invitations()->first()); | ||||||
|  | 
 | ||||||
|  |         $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]); | ||||||
|  | 
 | ||||||
|  |         $state = [ | ||||||
|  |             'template' => $design->elements([ | ||||||
|  |                 'client' => null, | ||||||
|  |                 'vendor' => $purchase_order->vendor, | ||||||
|  |                 'entity' => $purchase_order, | ||||||
|  |                 'pdf_variables' => (array) $purchase_order->company->settings->pdf_variables, | ||||||
|  |                 'products' => request()->design['design']['product'], | ||||||
|  |             ]), | ||||||
|  |             'variables' => $html->generateLabelsAndValues(), | ||||||
|  |             'process_markdown' => $purchase_order->company->markdown_enabled, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $maker = new PdfMaker($state); | ||||||
|  | 
 | ||||||
|  |         $maker | ||||||
|  |             ->design($design) | ||||||
|  |             ->build(); | ||||||
|  | 
 | ||||||
|  |         DB::connection(auth()->user()->company()->db)->rollBack(); | ||||||
|  | 
 | ||||||
|  |         if (request()->query('html') == 'true') { | ||||||
|  |             return $maker->getCompiledHTML(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { | ||||||
|  |             return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){ | ||||||
|  |             $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true)); | ||||||
|  | 
 | ||||||
|  |             $numbered_pdf = $this->pageNumbering($pdf, auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |                 if($numbered_pdf) | ||||||
|  |                     $pdf = $numbered_pdf; | ||||||
|  | 
 | ||||||
|  |                 return $pdf; | ||||||
|  |         } | ||||||
|  |              | ||||||
|  |         $file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company()); | ||||||
|  | 
 | ||||||
|  |         $response = Response::make($file_path, 200); | ||||||
|  |         $response->header('Content-Type', 'application/pdf'); | ||||||
|  | 
 | ||||||
|  |         return $response; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -633,11 +633,23 @@ class PurchaseOrderController extends BaseController | |||||||
|                 //check query parameter for email_type and set the template else use calculateTemplate
 |                 //check query parameter for email_type and set the template else use calculateTemplate
 | ||||||
|                 PurchaseOrderEmail::dispatch($purchase_order, $purchase_order->company); |                 PurchaseOrderEmail::dispatch($purchase_order, $purchase_order->company); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|                 if (! $bulk) { |                 if (! $bulk) { | ||||||
|                     return response()->json(['message' => 'email sent'], 200); |                     return response()->json(['message' => 'email sent'], 200); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |             case 'cancel': | ||||||
|  | 
 | ||||||
|  |                 if($purchase_order->status_id <= PurchaseOrder::STATUS_SENT) | ||||||
|  |                 { | ||||||
|  |                     $purchase_order->status_id = PurchaseOrder::STATUS_CANCELLED; | ||||||
|  |                     $purchase_order->save(); | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 if (! $bulk) { | ||||||
|  |                     return $this->listResponse($purchase_order); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|             default: |             default: | ||||||
|                 return response()->json(['message' => ctrans('texts.action_unavailable', ['action' => $action])], 400); |                 return response()->json(['message' => ctrans('texts.action_unavailable', ['action' => $action])], 400); | ||||||
|                 break; |                 break; | ||||||
|  | |||||||
| @ -158,7 +158,7 @@ class UserController extends BaseController | |||||||
|      */ |      */ | ||||||
|     public function create(CreateUserRequest $request) |     public function create(CreateUserRequest $request) | ||||||
|     { |     { | ||||||
|         $user = UserFactory::create(auth()->user()->account->id); |         $user = UserFactory::create(auth()->user()->account_id); | ||||||
| 
 | 
 | ||||||
|         return $this->itemResponse($user); |         return $this->itemResponse($user); | ||||||
|     } |     } | ||||||
| @ -396,7 +396,7 @@ class UserController extends BaseController | |||||||
|             UserEmailChanged::dispatch($new_user, json_decode($old_user), auth()->user()->company()); |             UserEmailChanged::dispatch($new_user, json_decode($old_user), auth()->user()->company()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $user->company_users()->update(["permissions_updated_at" => now()]);         |        // $user->company_users()->update(["permissions_updated_at" => now()]);        
 | ||||||
| 
 | 
 | ||||||
|         event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); |         event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,11 +17,13 @@ use App\Events\Misc\InvitationWasViewed; | |||||||
| use App\Events\Quote\QuoteWasViewed; | use App\Events\Quote\QuoteWasViewed; | ||||||
| use App\Http\Controllers\Controller; | use App\Http\Controllers\Controller; | ||||||
| use App\Jobs\Entity\CreateRawPdf; | use App\Jobs\Entity\CreateRawPdf; | ||||||
|  | use App\Jobs\Vendor\CreatePurchaseOrderPdf; | ||||||
| use App\Models\Client; | use App\Models\Client; | ||||||
| use App\Models\ClientContact; | use App\Models\ClientContact; | ||||||
| use App\Models\CreditInvitation; | use App\Models\CreditInvitation; | ||||||
| use App\Models\InvoiceInvitation; | use App\Models\InvoiceInvitation; | ||||||
| use App\Models\Payment; | use App\Models\Payment; | ||||||
|  | use App\Models\PurchaseOrder; | ||||||
| use App\Models\PurchaseOrderInvitation; | use App\Models\PurchaseOrderInvitation; | ||||||
| use App\Models\QuoteInvitation; | use App\Models\QuoteInvitation; | ||||||
| use App\Services\ClientPortal\InstantPayment; | use App\Services\ClientPortal\InstantPayment; | ||||||
| @ -95,50 +97,32 @@ class InvitationController extends Controller | |||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function download(string $invitation_key) | ||||||
|  |     { | ||||||
|  |         $invitation = PurchaseOrderInvitation::withTrashed() | ||||||
|  |                             ->where('key', $invitation_key) | ||||||
|  |                             ->with('contact.vendor') | ||||||
|  |                             ->firstOrFail(); | ||||||
| 
 | 
 | ||||||
|  |         if(!$invitation) | ||||||
|  |             return response()->json(["message" => "no record found"], 400); | ||||||
| 
 | 
 | ||||||
|     // public function routerForDownload(string $entity, string $invitation_key)
 |         $file_name = $invitation->purchase_order->numberFormatter().'.pdf'; | ||||||
|     // {
 |  | ||||||
| 
 | 
 | ||||||
|     //     set_time_limit(45);
 |         // $file = CreateRawPdf::dispatchNow($invitation, $invitation->company->db);
 | ||||||
| 
 | 
 | ||||||
|     //     if(Ninja::isHosted())
 |         $file = (new CreatePurchaseOrderPdf($invitation))->rawPdf(); | ||||||
|     //         return $this->returnRawPdf($entity, $invitation_key);
 |  | ||||||
| 
 | 
 | ||||||
|     //     return redirect('client/'.$entity.'/'.$invitation_key.'/download_pdf');
 |         $headers = ['Content-Type' => 'application/pdf']; | ||||||
|     // }
 |  | ||||||
| 
 | 
 | ||||||
|     // private function returnRawPdf(string $entity, string $invitation_key)
 |         if(request()->input('inline') == 'true') | ||||||
|     // {
 |             $headers = array_merge($headers, ['Content-Disposition' => 'inline']); | ||||||
| 
 | 
 | ||||||
|     //     if(!in_array($entity, ['invoice', 'credit', 'quote', 'recurring_invoice']))
 |         return response()->streamDownload(function () use($file) { | ||||||
|     //         return response()->json(['message' => 'Invalid resource request']);
 |                 echo $file; | ||||||
|  |         },  $file_name, $headers); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     //     $key = $entity.'_id';
 |  | ||||||
| 
 |  | ||||||
|     //     $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
 |  | ||||||
| 
 |  | ||||||
|     //     $invitation = $entity_obj::where('key', $invitation_key)
 |  | ||||||
|     //                                 ->with('contact.client')
 |  | ||||||
|     //                                 ->firstOrFail();
 |  | ||||||
| 
 |  | ||||||
|     //     if(!$invitation)
 |  | ||||||
|     //         return response()->json(["message" => "no record found"], 400);
 |  | ||||||
| 
 |  | ||||||
|     //     $file_name = $invitation->purchase_order->numberFormatter().'.pdf';
 |  | ||||||
| 
 |  | ||||||
|     //     $file = CreateRawPdf::dispatchNow($invitation, $invitation->company->db);
 |  | ||||||
| 
 |  | ||||||
|     //     $headers = ['Content-Type' => 'application/pdf'];
 |  | ||||||
| 
 |  | ||||||
|     //     if(request()->input('inline') == 'true')
 |  | ||||||
|     //         $headers = array_merge($headers, ['Content-Disposition' => 'inline']);
 |  | ||||||
| 
 |  | ||||||
|     //     return response()->streamDownload(function () use($file) {
 |  | ||||||
|     //             echo $file;
 |  | ||||||
|     //     },  $file_name, $headers);
 |  | ||||||
| 
 |  | ||||||
|     // }
 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -63,35 +63,57 @@ class PasswordProtection | |||||||
| 
 | 
 | ||||||
|             //user is attempting to reauth with OAuth - check the token value
 |             //user is attempting to reauth with OAuth - check the token value
 | ||||||
|             //todo expand this to include all OAuth providers
 |             //todo expand this to include all OAuth providers
 | ||||||
|             $user = false; |             if(auth()->user()->oauth_provider_id == 'google') | ||||||
|             $google = new Google(); |             { | ||||||
|             $user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD')); |                 $user = false; | ||||||
|  |                 $google = new Google(); | ||||||
|  |                 $user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD')); | ||||||
| 
 | 
 | ||||||
|             if (is_array($user)) { |                 if (is_array($user)) { | ||||||
|                      |                      | ||||||
|                 $query = [ |                     $query = [ | ||||||
|                     'oauth_user_id' => $google->harvestSubField($user), |                         'oauth_user_id' => $google->harvestSubField($user), | ||||||
|                     'oauth_provider_id'=> 'google' |                         'oauth_provider_id'=> 'google' | ||||||
|                 ]; |                     ]; | ||||||
| 
 | 
 | ||||||
|                 //If OAuth and user also has a password set  - check both
 |                     //If OAuth and user also has a password set  - check both
 | ||||||
|                 if ($existing_user = MultiDB::hasUser($query) && auth()->user()->company()->oauth_password_required && auth()->user()->has_password && Hash::check(auth()->user()->password, $x_api_password)) { |                     if ($existing_user = MultiDB::hasUser($query) && auth()->user()->company()->oauth_password_required && auth()->user()->has_password && Hash::check(auth()->user()->password, $x_api_password)) { | ||||||
| 
 | 
 | ||||||
|                     nlog("existing user with password"); |                         nlog("existing user with password"); | ||||||
| 
 | 
 | ||||||
|                     Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); |                         Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); | ||||||
| 
 | 
 | ||||||
|                     return $next($request); |                         return $next($request); | ||||||
|  |                     } | ||||||
|  |                     elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->company()->oauth_password_required){ | ||||||
|  | 
 | ||||||
|  |                         nlog("existing user without password"); | ||||||
|  | 
 | ||||||
|  |                         Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); | ||||||
|  |                         return $next($request);                     | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|                 elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->company()->oauth_password_required){ |  | ||||||
| 
 | 
 | ||||||
|                     nlog("existing user without password"); |             } | ||||||
|  |             elseif(auth()->user()->oauth_provider_id == 'microsoft') | ||||||
|  |             { | ||||||
|  |                 try{ | ||||||
|  |                     $payload = json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', request()->header('X-API-OAUTH-PASSWORD'))[1])))); | ||||||
|  |                 } | ||||||
|  |                 catch(\Exception $e){ | ||||||
|  |                     nlog("could not decode microsoft response"); | ||||||
|  |                     return response()->json(['message' => 'Could not decode the response from Microsoft'], 412); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if($payload->preferred_username == auth()->user()->email){ | ||||||
| 
 | 
 | ||||||
|                     Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); |                     Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); | ||||||
|                     return $next($request); |                     return $next($request); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|             return response()->json($error, 412); |             return response()->json($error, 412); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ class ShowRecurringInvoiceRequest extends Request | |||||||
| { | { | ||||||
|     public function authorize() : bool |     public function authorize() : bool | ||||||
|     { |     { | ||||||
|         return auth()->guard('contact')->user()->client->id === $this->recurring_invoice->client_id |         return auth()->guard('contact')->user()->client->id == $this->recurring_invoice->client_id | ||||||
|             && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_RECURRING_INVOICES; |             && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_RECURRING_INVOICES; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -73,7 +73,9 @@ class UpdatePaymentRequest extends Request | |||||||
| 
 | 
 | ||||||
|         if (isset($input['invoices']) && is_array($input['invoices']) !== false) { |         if (isset($input['invoices']) && is_array($input['invoices']) !== false) { | ||||||
|             foreach ($input['invoices'] as $key => $value) { |             foreach ($input['invoices'] as $key => $value) { | ||||||
|                 $input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']); | 
 | ||||||
|  |                 if(array_key_exists('invoice_id', $input['invoices'][$key])) | ||||||
|  |                     $input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         $this->replace($input); |         $this->replace($input); | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								app/Http/Requests/Preview/PreviewPurchaseOrderRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								app/Http/Requests/Preview/PreviewPurchaseOrderRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Invoice Ninja (https://invoiceninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Http\Requests\Preview; | ||||||
|  | 
 | ||||||
|  | use App\Http\Requests\Request; | ||||||
|  | use App\Http\ValidationRules\Project\ValidProjectForClient; | ||||||
|  | use App\Models\Credit; | ||||||
|  | use App\Models\Invoice; | ||||||
|  | use App\Models\PurchaseOrder; | ||||||
|  | use App\Models\Quote; | ||||||
|  | use App\Models\RecurringInvoice; | ||||||
|  | use App\Utils\Traits\CleanLineItems; | ||||||
|  | use App\Utils\Traits\MakesHash; | ||||||
|  | use Illuminate\Validation\Rule; | ||||||
|  | 
 | ||||||
|  | class PreviewPurchaseOrderRequest extends Request | ||||||
|  | { | ||||||
|  |     use MakesHash; | ||||||
|  |     use CleanLineItems; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Determine if the user is authorized to make this request. | ||||||
|  |      * | ||||||
|  |      * @return bool | ||||||
|  |      */ | ||||||
|  |     public function authorize() : bool | ||||||
|  |     { | ||||||
|  |         return auth()->user()->can('create', PurchaseOrder::class); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function rules() | ||||||
|  |     { | ||||||
|  |         $rules = []; | ||||||
|  | 
 | ||||||
|  |         $rules['number'] = ['nullable']; | ||||||
|  | 
 | ||||||
|  |         return $rules; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected function prepareForValidation() | ||||||
|  |     { | ||||||
|  |         $input = $this->all(); | ||||||
|  | 
 | ||||||
|  |         $input = $this->decodePrimaryKeys($input); | ||||||
|  | 
 | ||||||
|  |         $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; | ||||||
|  |         $input['amount'] = 0; | ||||||
|  |         $input['balance'] = 0; | ||||||
|  |         $input['number'] = ctrans('texts.live_preview') . " #". rand(0,1000); | ||||||
|  |          | ||||||
|  |         $this->replace($input); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -58,6 +58,6 @@ class StoreProjectRequest extends Request | |||||||
| 
 | 
 | ||||||
|     public function getClient($client_id) |     public function getClient($client_id) | ||||||
|     { |     { | ||||||
|         return Client::find($client_id); |         return Client::withTrashed()->find($client_id); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,12 +14,15 @@ namespace App\Http\Requests\PurchaseOrder; | |||||||
| 
 | 
 | ||||||
| use App\Http\Requests\Request; | use App\Http\Requests\Request; | ||||||
| use App\Models\PurchaseOrder; | use App\Models\PurchaseOrder; | ||||||
|  | use App\Utils\Traits\CleanLineItems; | ||||||
| use App\Utils\Traits\MakesHash; | use App\Utils\Traits\MakesHash; | ||||||
| use Illuminate\Validation\Rule; | use Illuminate\Validation\Rule; | ||||||
| 
 | 
 | ||||||
| class StorePurchaseOrderRequest extends Request | class StorePurchaseOrderRequest extends Request | ||||||
| { | { | ||||||
|     use MakesHash; |     use MakesHash; | ||||||
|  |     use CleanLineItems; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Determine if the user is authorized to make this request. |      * Determine if the user is authorized to make this request. | ||||||
|      * |      * | ||||||
| @ -43,8 +46,6 @@ class StorePurchaseOrderRequest extends Request | |||||||
|         $rules['number'] = ['nullable', Rule::unique('purchase_orders')->where('company_id', auth()->user()->company()->id)]; |         $rules['number'] = ['nullable', Rule::unique('purchase_orders')->where('company_id', auth()->user()->company()->id)]; | ||||||
|         $rules['discount']  = 'sometimes|numeric'; |         $rules['discount']  = 'sometimes|numeric'; | ||||||
|         $rules['is_amount_discount'] = ['boolean']; |         $rules['is_amount_discount'] = ['boolean']; | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         $rules['line_items'] = 'array'; |         $rules['line_items'] = 'array'; | ||||||
| 
 | 
 | ||||||
|         return $rules; |         return $rules; | ||||||
| @ -56,6 +57,12 @@ class StorePurchaseOrderRequest extends Request | |||||||
| 
 | 
 | ||||||
|         $input = $this->decodePrimaryKeys($input); |         $input = $this->decodePrimaryKeys($input); | ||||||
| 
 | 
 | ||||||
|  |         if (isset($input['line_items']) && is_array($input['line_items']))  | ||||||
|  |             $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; | ||||||
|  | 
 | ||||||
|  |         $input['amount'] = 0; | ||||||
|  |         $input['balance'] = 0; | ||||||
|  | 
 | ||||||
|         $this->replace($input); |         $this->replace($input); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ namespace App\Http\Requests\PurchaseOrder; | |||||||
| 
 | 
 | ||||||
| use App\Http\Requests\Request; | use App\Http\Requests\Request; | ||||||
| use App\Utils\Traits\ChecksEntityStatus; | use App\Utils\Traits\ChecksEntityStatus; | ||||||
|  | use App\Utils\Traits\CleanLineItems; | ||||||
| use App\Utils\Traits\MakesHash; | use App\Utils\Traits\MakesHash; | ||||||
| use Illuminate\Validation\Rule; | use Illuminate\Validation\Rule; | ||||||
| 
 | 
 | ||||||
| @ -21,6 +22,7 @@ class UpdatePurchaseOrderRequest extends Request | |||||||
| { | { | ||||||
|     use ChecksEntityStatus; |     use ChecksEntityStatus; | ||||||
|     use MakesHash; |     use MakesHash; | ||||||
|  |     use CleanLineItems; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Determine if the user is authorized to make this request. |      * Determine if the user is authorized to make this request. | ||||||
| @ -57,6 +59,10 @@ class UpdatePurchaseOrderRequest extends Request | |||||||
| 
 | 
 | ||||||
|         $input['id'] = $this->purchase_order->id; |         $input['id'] = $this->purchase_order->id; | ||||||
| 
 | 
 | ||||||
|  |         if (isset($input['line_items']) && is_array($input['line_items'])) { | ||||||
|  |             $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         $this->replace($input); |         $this->replace($input); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1423,21 +1423,21 @@ class CompanyImport implements ShouldQueue | |||||||
|                 $new_obj->company_id = $this->company->id; |                 $new_obj->company_id = $this->company->id; | ||||||
|                 $new_obj->fill($obj_array); |                 $new_obj->fill($obj_array); | ||||||
|                 $new_obj->save(['timestamps' => false]); |                 $new_obj->save(['timestamps' => false]); | ||||||
|                 $new_obj->number = $this->getNextInvoiceNumber($client = Client::find($obj_array['client_id']),$new_obj); |                 $new_obj->number = $this->getNextInvoiceNumber($client = Client::withTrashed()->find($obj_array['client_id']),$new_obj); | ||||||
|             } |             } | ||||||
|             elseif($class == 'App\Models\Payment' && is_null($obj->{$match_key})){ |             elseif($class == 'App\Models\Payment' && is_null($obj->{$match_key})){ | ||||||
|                 $new_obj = new Payment(); |                 $new_obj = new Payment(); | ||||||
|                 $new_obj->company_id = $this->company->id; |                 $new_obj->company_id = $this->company->id; | ||||||
|                 $new_obj->fill($obj_array); |                 $new_obj->fill($obj_array); | ||||||
|                 $new_obj->save(['timestamps' => false]); |                 $new_obj->save(['timestamps' => false]); | ||||||
|                 $new_obj->number = $this->getNextPaymentNumber($client = Client::find($obj_array['client_id']), $new_obj); |                 $new_obj->number = $this->getNextPaymentNumber($client = Client::withTrashed()->find($obj_array['client_id']), $new_obj); | ||||||
|             } |             } | ||||||
|             elseif($class == 'App\Models\Quote' && is_null($obj->{$match_key})){ |             elseif($class == 'App\Models\Quote' && is_null($obj->{$match_key})){ | ||||||
|                 $new_obj = new Quote(); |                 $new_obj = new Quote(); | ||||||
|                 $new_obj->company_id = $this->company->id; |                 $new_obj->company_id = $this->company->id; | ||||||
|                 $new_obj->fill($obj_array); |                 $new_obj->fill($obj_array); | ||||||
|                 $new_obj->save(['timestamps' => false]); |                 $new_obj->save(['timestamps' => false]); | ||||||
|                 $new_obj->number = $this->getNextQuoteNumber($client = Client::find($obj_array['client_id']), $new_obj); |                 $new_obj->number = $this->getNextQuoteNumber($client = Client::withTrashed()->find($obj_array['client_id']), $new_obj); | ||||||
|             } |             } | ||||||
|             elseif($class == 'App\Models\ClientContact'){ |             elseif($class == 'App\Models\ClientContact'){ | ||||||
|                 $new_obj = new ClientContact(); |                 $new_obj = new ClientContact(); | ||||||
|  | |||||||
| @ -200,6 +200,14 @@ class NinjaMailerJob implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|         $user = User::find($this->decodePrimaryKey($sending_user)); |         $user = User::find($this->decodePrimaryKey($sending_user)); | ||||||
|          |          | ||||||
|  |         /* Always ensure the user is set on the correct account */ | ||||||
|  |         if($user->account_id != $this->company->account_id){ | ||||||
|  | 
 | ||||||
|  |             $this->nmo->settings->email_sending_method = 'default'; | ||||||
|  |             return $this->setMailDriver(); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         nlog("Sending via {$user->name()}"); |         nlog("Sending via {$user->name()}"); | ||||||
| 
 | 
 | ||||||
|         $token = $this->refreshOfficeToken($user); |         $token = $this->refreshOfficeToken($user); | ||||||
| @ -236,6 +244,14 @@ class NinjaMailerJob implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|         $user = User::find($this->decodePrimaryKey($sending_user)); |         $user = User::find($this->decodePrimaryKey($sending_user)); | ||||||
| 
 | 
 | ||||||
|  |         /* Always ensure the user is set on the correct account */ | ||||||
|  |         if($user->account_id != $this->company->account_id){ | ||||||
|  | 
 | ||||||
|  |             $this->nmo->settings->email_sending_method = 'default'; | ||||||
|  |             return $this->setMailDriver(); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |          | ||||||
|         nlog("Sending via {$user->name()}"); |         nlog("Sending via {$user->name()}"); | ||||||
| 
 | 
 | ||||||
|         $google = (new Google())->init(); |         $google = (new Google())->init(); | ||||||
| @ -292,8 +308,9 @@ class NinjaMailerJob implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|     private function preFlightChecksFail() |     private function preFlightChecksFail() | ||||||
|     { |     { | ||||||
|  | 
 | ||||||
|         /* If we are migrating data we don't want to fire any emails */ |         /* If we are migrating data we don't want to fire any emails */ | ||||||
|         if ($this->nmo->company->is_disabled && !$this->override)  |         if($this->company->is_disabled && !$this->override)  | ||||||
|             return true; |             return true; | ||||||
| 
 | 
 | ||||||
|         /* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */ |         /* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */ | ||||||
| @ -301,17 +318,25 @@ class NinjaMailerJob implements ShouldQueue | |||||||
|             return true; |             return true; | ||||||
| 
 | 
 | ||||||
|         /* GMail users are uncapped */ |         /* GMail users are uncapped */ | ||||||
|         if(Ninja::isHosted() && $this->nmo->settings->email_sending_method == 'gmail') |         if(Ninja::isHosted() && ($this->nmo->settings->email_sending_method == 'gmail' || $this->nmo->settings->email_sending_method == 'office365'))  | ||||||
|             return false; |             return false; | ||||||
| 
 | 
 | ||||||
|         /* On the hosted platform, if the user is over the email quotas, we do not send the email. */ |         /* On the hosted platform, if the user is over the email quotas, we do not send the email. */ | ||||||
|         if(Ninja::isHosted() && $this->company->account && $this->company->account->emailQuotaExceeded()) |         if(Ninja::isHosted() && $this->company->account && $this->company->account->emailQuotaExceeded()) | ||||||
|             return true; |             return true; | ||||||
| 
 | 
 | ||||||
|  |         /* To handle spam users we drop all emails from flagged accounts */ | ||||||
|  |         if(Ninja::isHosted() && $this->company->account && $this->company->account->is_flagged)  | ||||||
|  |             return true; | ||||||
|  | 
 | ||||||
|         /* Ensure the user has a valid email address */ |         /* Ensure the user has a valid email address */ | ||||||
|         if(!str_contains($this->nmo->to_user->email, "@")) |         if(!str_contains($this->nmo->to_user->email, "@")) | ||||||
|             return true; |             return true; | ||||||
|       |       | ||||||
|  |         /* On the hosted platform we actively scan all outbound emails to ensure outbound email quality remains high */ | ||||||
|  |         if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class)) | ||||||
|  |             return (new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run(); | ||||||
|  | 
 | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -342,23 +367,48 @@ class NinjaMailerJob implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|     private function refreshOfficeToken($user) |     private function refreshOfficeToken($user) | ||||||
|     { |     { | ||||||
|         $guzzle = new \GuzzleHttp\Client();  |         $expiry = $user->oauth_user_token_expiry ?: now()->subDay(); | ||||||
|         $url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';  |  | ||||||
| 
 | 
 | ||||||
|         $token = json_decode($guzzle->post($url, [ |         if($expiry->lt(now())) | ||||||
|             'form_params' => [ |         { | ||||||
|                 'client_id' => config('ninja.o365.client_id') , |             $guzzle = new \GuzzleHttp\Client();  | ||||||
|                 'client_secret' => config('ninja.o365.client_secret') , |             $url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';  | ||||||
|                 'scope' => 'email Mail.ReadWrite Mail.Send offline_access profile User.Read openid', |  | ||||||
|                 'grant_type' => 'refresh_token', |  | ||||||
|                 'refresh_token' => $user->oauth_user_refresh_token |  | ||||||
|             ], |  | ||||||
|         ])->getBody()->getContents()); |  | ||||||
| 
 | 
 | ||||||
|         if($token) |             $token = json_decode($guzzle->post($url, [ | ||||||
|             return $token->access_token; |                 'form_params' => [ | ||||||
|  |                     'client_id' => config('ninja.o365.client_id') , | ||||||
|  |                     'client_secret' => config('ninja.o365.client_secret') , | ||||||
|  |                     'scope' => 'email Mail.Send offline_access profile User.Read openid', | ||||||
|  |                     'grant_type' => 'refresh_token', | ||||||
|  |                     'refresh_token' => $user->oauth_user_refresh_token | ||||||
|  |                 ], | ||||||
|  |             ])->getBody()->getContents()); | ||||||
|  | 
 | ||||||
|  |             nlog($token); | ||||||
|  |              | ||||||
|  |             if($token){ | ||||||
|  |                  | ||||||
|  |                 $user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token; | ||||||
|  |                 $user->oauth_user_token = $token->access_token; | ||||||
|  |                 $user->oauth_user_token_expiry = now()->addSeconds($token->expires_in); | ||||||
|  |                 $user->save(); | ||||||
|  | 
 | ||||||
|  |                 return $token->access_token; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $user->oauth_user_refresh_token; | ||||||
|          |          | ||||||
|         return false; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Is this the cleanest way to requeue a job? | ||||||
|  |      *  | ||||||
|  |      * $this->delete(); | ||||||
|  |      * | ||||||
|  |      * $job = NinjaMailerJob::dispatch($this->nmo, $this->override)->delay(3600); | ||||||
|  |     */ | ||||||
|  | 
 | ||||||
| } | } | ||||||
| @ -923,6 +923,9 @@ class Import implements ShouldQueue | |||||||
|             $modified['company_id'] = $this->company->id; |             $modified['company_id'] = $this->company->id; | ||||||
|             $modified['line_items'] = $this->cleanItems($modified['line_items']); |             $modified['line_items'] = $this->cleanItems($modified['line_items']); | ||||||
| 
 | 
 | ||||||
|  |             if(array_key_exists('next_send_date', $resource)) | ||||||
|  |                 $modified['next_send_date_client'] = $resource['next_send_date']; | ||||||
|  | 
 | ||||||
|             if(array_key_exists('created_at', $modified)) |             if(array_key_exists('created_at', $modified)) | ||||||
|                 $modified['created_at'] = Carbon::parse($modified['created_at']); |                 $modified['created_at'] = Carbon::parse($modified['created_at']); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										53
									
								
								app/Jobs/Vendor/CreatePurchaseOrderPdf.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										53
									
								
								app/Jobs/Vendor/CreatePurchaseOrderPdf.php
									
									
									
									
										vendored
									
									
								
							| @ -65,6 +65,10 @@ class CreatePurchaseOrderPdf implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|     public $vendor; |     public $vendor; | ||||||
| 
 | 
 | ||||||
|  |     private string $path = ''; | ||||||
|  | 
 | ||||||
|  |     private string $file_path = ''; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Create a new job instance. |      * Create a new job instance. | ||||||
|      * |      * | ||||||
| @ -88,6 +92,32 @@ class CreatePurchaseOrderPdf implements ShouldQueue | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function handle() |     public function handle() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         $pdf = $this->rawPdf(); | ||||||
|  | 
 | ||||||
|  |         if ($pdf) { | ||||||
|  | 
 | ||||||
|  |             try{ | ||||||
|  |                  | ||||||
|  |                 if(!Storage::disk($this->disk)->exists($this->path))  | ||||||
|  |                     Storage::disk($this->disk)->makeDirectory($this->path, 0775); | ||||||
|  | 
 | ||||||
|  |                 Storage::disk($this->disk)->put($this->file_path, $pdf, 'public'); | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |             catch(\Exception $e) | ||||||
|  |             { | ||||||
|  | 
 | ||||||
|  |                 throw new FilePermissionsFailure($e->getMessage()); | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return $this->file_path; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function rawPdf() | ||||||
|     { |     { | ||||||
| 
 | 
 | ||||||
|         MultiDB::setDb($this->company->db); |         MultiDB::setDb($this->company->db); | ||||||
| @ -109,10 +139,10 @@ class CreatePurchaseOrderPdf implements ShouldQueue | |||||||
| 
 | 
 | ||||||
|         $entity_design_id = ''; |         $entity_design_id = ''; | ||||||
|          |          | ||||||
|         $path = $this->vendor->purchase_order_filepath($this->invitation); |         $this->path = $this->vendor->purchase_order_filepath($this->invitation); | ||||||
|         $entity_design_id = 'purchase_order_design_id'; |         $entity_design_id = 'purchase_order_design_id'; | ||||||
| 
 | 
 | ||||||
|         $file_path = $path.$this->entity->numberFormatter().'.pdf'; |         $this->file_path = $this->path.$this->entity->numberFormatter().'.pdf'; | ||||||
| 
 | 
 | ||||||
|         $entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey('Wpmbk5ezJn'); |         $entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey('Wpmbk5ezJn'); | ||||||
| 
 | 
 | ||||||
| @ -191,25 +221,8 @@ class CreatePurchaseOrderPdf implements ShouldQueue | |||||||
|             info($maker->getCompiledHTML()); |             info($maker->getCompiledHTML()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ($pdf) { |         return $pdf; | ||||||
| 
 | 
 | ||||||
|             try{ |  | ||||||
|                  |  | ||||||
|                 if(!Storage::disk($this->disk)->exists($path))  |  | ||||||
|                     Storage::disk($this->disk)->makeDirectory($path, 0775); |  | ||||||
| 
 |  | ||||||
|                 Storage::disk($this->disk)->put($file_path, $pdf, 'public'); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
|             catch(\Exception $e) |  | ||||||
|             { |  | ||||||
| 
 |  | ||||||
|                 throw new FilePermissionsFailure($e->getMessage()); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         return $file_path; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function failed($e) |     public function failed($e) | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ class Account extends BaseModel | |||||||
|     use PresentableTrait; |     use PresentableTrait; | ||||||
|     use MakesHash; |     use MakesHash; | ||||||
| 
 | 
 | ||||||
|     private $free_plan_email_quota = 250; |     private $free_plan_email_quota = 100; | ||||||
| 
 | 
 | ||||||
|     private $paid_plan_email_quota = 500; |     private $paid_plan_email_quota = 500; | ||||||
|     /** |     /** | ||||||
| @ -373,10 +373,15 @@ class Account extends BaseModel | |||||||
| 
 | 
 | ||||||
|     public function getDailyEmailLimit() |     public function getDailyEmailLimit() | ||||||
|     { |     { | ||||||
|  |         if($this->is_flagged) | ||||||
|  |             return 0; | ||||||
| 
 | 
 | ||||||
|         if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() == 0) |         if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() == 0) | ||||||
|             return 20; |             return 20; | ||||||
| 
 | 
 | ||||||
|  |         if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() <= 2 && !$this->payment_id) | ||||||
|  |             return 20; | ||||||
|  | 
 | ||||||
|         if($this->isPaid()){ |         if($this->isPaid()){ | ||||||
|             $limit = $this->paid_plan_email_quota; |             $limit = $this->paid_plan_email_quota; | ||||||
|             $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100; |             $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100; | ||||||
|  | |||||||
| @ -63,6 +63,8 @@ class Gateway extends StaticModel | |||||||
|             $link = 'https://applications.sagepay.com/apply/2C02C252-0F8A-1B84-E10D-CF933EFCAA99'; |             $link = 'https://applications.sagepay.com/apply/2C02C252-0F8A-1B84-E10D-CF933EFCAA99'; | ||||||
|         } elseif ($this->id == 20 || $this->id == 56) { |         } elseif ($this->id == 20 || $this->id == 56) { | ||||||
|             $link = 'https://dashboard.stripe.com/account/apikeys'; |             $link = 'https://dashboard.stripe.com/account/apikeys'; | ||||||
|  |         } elseif ($this->id == 59) { | ||||||
|  |             $link = 'https://www.forte.net/'; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return $link; |         return $link; | ||||||
| @ -170,6 +172,12 @@ class Gateway extends StaticModel | |||||||
|                     GatewayType::HOSTED_PAGE => ['refund' => false, 'token_billing' => false, 'webhooks' => [' ']] // Razorpay
 |                     GatewayType::HOSTED_PAGE => ['refund' => false, 'token_billing' => false, 'webhooks' => [' ']] // Razorpay
 | ||||||
|                 ]; |                 ]; | ||||||
|                 break; |                 break; | ||||||
|  |             case 59: | ||||||
|  |                 return [ | ||||||
|  |                     GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], // Forte
 | ||||||
|  |                     GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']], | ||||||
|  |                 ]; | ||||||
|  |                 break; | ||||||
|             default: |             default: | ||||||
|                 return []; |                 return []; | ||||||
|                 break; |                 break; | ||||||
|  | |||||||
| @ -126,6 +126,26 @@ class CompanyPresenter extends EntityPresenter | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function address1() | ||||||
|  |     { | ||||||
|  |         return $this->entity->settings->address1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function address2() | ||||||
|  |     { | ||||||
|  |         return $this->entity->settings->address2; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function qr_iban() | ||||||
|  |     { | ||||||
|  |         return $this->entity->getSetting('qr_iban'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function besr_id() | ||||||
|  |     { | ||||||
|  |         return $this->entity->getSetting('besr_id'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function getSpcQrCode($client_currency, $invoice_number, $balance_due_raw, $user_iban) |     public function getSpcQrCode($client_currency, $invoice_number, $balance_due_raw, $user_iban) | ||||||
|     { |     { | ||||||
|         $settings = $this->entity->settings; |         $settings = $this->entity->settings; | ||||||
|  | |||||||
| @ -34,7 +34,6 @@ class PurchaseOrder extends BaseModel | |||||||
|         'discount', |         'discount', | ||||||
|         'company_id', |         'company_id', | ||||||
|         'status_id', |         'status_id', | ||||||
|         'user_id', |  | ||||||
|         'last_sent_date', |         'last_sent_date', | ||||||
|         'is_deleted', |         'is_deleted', | ||||||
|         'po_number', |         'po_number', | ||||||
| @ -84,7 +83,7 @@ class PurchaseOrder extends BaseModel | |||||||
|         'balance', |         'balance', | ||||||
|         'partial', |         'partial', | ||||||
|         'paid_to_date', |         'paid_to_date', | ||||||
|         'subscription_id', |         // 'subscription_id',
 | ||||||
|         'vendor_id', |         'vendor_id', | ||||||
|         'last_viewed' |         'last_viewed' | ||||||
|     ]; |     ]; | ||||||
| @ -154,7 +153,7 @@ class PurchaseOrder extends BaseModel | |||||||
| 
 | 
 | ||||||
|     public function vendor() |     public function vendor() | ||||||
|     { |     { | ||||||
|         return $this->belongsTo(Vendor::class); |         return $this->belongsTo(Vendor::class)->withTrashed(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function history() |     public function history() | ||||||
|  | |||||||
| @ -106,6 +106,7 @@ class User extends Authenticatable implements MustVerifyEmail | |||||||
|         'updated_at'       => 'timestamp', |         'updated_at'       => 'timestamp', | ||||||
|         'created_at'       => 'timestamp', |         'created_at'       => 'timestamp', | ||||||
|         'deleted_at'       => 'timestamp', |         'deleted_at'       => 'timestamp', | ||||||
|  |         'oauth_user_token_expiry' => 'datetime', | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										93
									
								
								app/Notifications/Ninja/NewAccountNotification.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/Notifications/Ninja/NewAccountNotification.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Invoice Ninja (https://invoiceninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Notifications\Ninja; | ||||||
|  | 
 | ||||||
|  | use App\Models\Account; | ||||||
|  | use App\Models\Client; | ||||||
|  | use Illuminate\Bus\Queueable; | ||||||
|  | use Illuminate\Contracts\Queue\ShouldQueue; | ||||||
|  | use Illuminate\Foundation\Bus\Dispatchable; | ||||||
|  | use Illuminate\Notifications\Messages\MailMessage; | ||||||
|  | use Illuminate\Notifications\Messages\SlackMessage; | ||||||
|  | use Illuminate\Notifications\Notification; | ||||||
|  | use Illuminate\Queue\InteractsWithQueue; | ||||||
|  | use Illuminate\Queue\SerializesModels; | ||||||
|  | 
 | ||||||
|  | class NewAccountNotification extends Notification  | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create a new notification instance. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     protected Account $account; | ||||||
|  | 
 | ||||||
|  |     protected Client $client; | ||||||
|  | 
 | ||||||
|  |     public function __construct(Account $account, Client $client) | ||||||
|  |     { | ||||||
|  |         $this->account = $account; | ||||||
|  |         $this->client = $client; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the notification's delivery channels. | ||||||
|  |      * | ||||||
|  |      * @param  mixed  $notifiable | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     public function via($notifiable) | ||||||
|  |     { | ||||||
|  |         return ['slack']; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the mail representation of the notification. | ||||||
|  |      * | ||||||
|  |      * @param  mixed  $notifiable | ||||||
|  |      * @return MailMessage | ||||||
|  |      */ | ||||||
|  |     public function toMail($notifiable) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the array representation of the notification. | ||||||
|  |      * | ||||||
|  |      * @param  mixed  $notifiable | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     public function toArray($notifiable) | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             //
 | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function toSlack($notifiable) | ||||||
|  |     { | ||||||
|  |         $content = "New Trial Started\n"; | ||||||
|  |         $content = "{$this->client->name}\n"; | ||||||
|  |         $content = "Account key: {$this->account->key}\n"; | ||||||
|  |         $content = "Users: {$this->account->users()->pluck('email')}\n"; | ||||||
|  |         $content = "Contacts: {$this->client->contacts()->pluck('email')}\n"; | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         return (new SlackMessage) | ||||||
|  |                 ->success() | ||||||
|  |                 ->from(ctrans('texts.notification_bot')) | ||||||
|  |                 ->image('https://app.invoiceninja.com/favicon.png') | ||||||
|  |                 ->content($content); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										121
									
								
								app/Notifications/Ninja/SpamNotification.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								app/Notifications/Ninja/SpamNotification.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * Invoice Ninja (https://invoiceninja.com). | ||||||
|  |  * | ||||||
|  |  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||||
|  |  * | ||||||
|  |  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||||
|  |  * | ||||||
|  |  * @license https://www.elastic.co/licensing/elastic-license | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace App\Notifications\Ninja; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Bus\Queueable; | ||||||
|  | use Illuminate\Contracts\Queue\ShouldQueue; | ||||||
|  | use Illuminate\Foundation\Bus\Dispatchable; | ||||||
|  | use Illuminate\Notifications\Messages\MailMessage; | ||||||
|  | use Illuminate\Notifications\Messages\SlackMessage; | ||||||
|  | use Illuminate\Notifications\Notification; | ||||||
|  | use Illuminate\Queue\InteractsWithQueue; | ||||||
|  | use Illuminate\Queue\SerializesModels; | ||||||
|  | 
 | ||||||
|  | class SpamNotification extends Notification  | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create a new notification instance. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  | 
 | ||||||
|  |     protected array $spam_list; | ||||||
|  | 
 | ||||||
|  |     public function __construct($spam_list) | ||||||
|  |     { | ||||||
|  |         $this->spam_list = $spam_list; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the notification's delivery channels. | ||||||
|  |      * | ||||||
|  |      * @param  mixed  $notifiable | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     public function via($notifiable) | ||||||
|  |     { | ||||||
|  |         return ['slack']; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the mail representation of the notification. | ||||||
|  |      * | ||||||
|  |      * @param  mixed  $notifiable | ||||||
|  |      * @return MailMessage | ||||||
|  |      */ | ||||||
|  |     public function toMail($notifiable) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the array representation of the notification. | ||||||
|  |      * | ||||||
|  |      * @param  mixed  $notifiable | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     public function toArray($notifiable) | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             //
 | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function toSlack($notifiable) | ||||||
|  |     { | ||||||
|  |         $content = ''; | ||||||
|  | 
 | ||||||
|  |         foreach($this->spam_list as $spam_list) | ||||||
|  |         { | ||||||
|  | 
 | ||||||
|  |             if(array_key_exists('companies', $spam_list)) | ||||||
|  |             { | ||||||
|  |                 $content .= " Companies \n"; | ||||||
|  | 
 | ||||||
|  |                 foreach($spam_list['companies'] as $company) | ||||||
|  |                 { | ||||||
|  |                     $content .= "{$company['name']} - c_key={$company['company_key']} - a_key={$company['account_key']} - {$company['owner']} \n"; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(array_key_exists('templates', $spam_list)) | ||||||
|  |             { | ||||||
|  |                 $content .= " Templates \n"; | ||||||
|  | 
 | ||||||
|  |                 foreach($spam_list['templates'] as $company) | ||||||
|  |                 { | ||||||
|  |                     $content .= "{$company['name']} - c_key={$company['company_key']} - a_key={$company['account_key']} - {$company['owner']} \n"; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             if(array_key_exists('users', $spam_list)) | ||||||
|  |             { | ||||||
|  | 
 | ||||||
|  |                 $content .= ' Users \n'; | ||||||
|  | 
 | ||||||
|  |                 foreach($spam_list['users'] as $user) | ||||||
|  |                 { | ||||||
|  |                     $content .= "{$user['email']} - a_key={$user['account_key']} - created={$user['created']} \n"; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return (new SlackMessage) | ||||||
|  |                 ->success() | ||||||
|  |                 ->from(ctrans('texts.notification_bot')) | ||||||
|  |                 ->image('https://app.invoiceninja.com/favicon.png') | ||||||
|  |                 ->content($content); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										150
									
								
								app/PaymentDrivers/Forte/ACH.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								app/PaymentDrivers/Forte/ACH.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,150 @@ | |||||||
|  | <?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\PaymentDrivers\Forte; | ||||||
|  | 
 | ||||||
|  | use App\Models\Payment; | ||||||
|  | use App\Models\GatewayType; | ||||||
|  | use App\Models\PaymentHash; | ||||||
|  | use App\Models\PaymentType; | ||||||
|  | use App\Http\Requests\Request; | ||||||
|  | use App\Utils\Traits\MakesHash; | ||||||
|  | use Illuminate\Support\Facades\Validator; | ||||||
|  | use App\PaymentDrivers\FortePaymentDriver; | ||||||
|  | 
 | ||||||
|  | class ACH | ||||||
|  | { | ||||||
|  |     use MakesHash; | ||||||
|  |      | ||||||
|  |     public $forte; | ||||||
|  | 
 | ||||||
|  |     private $forte_base_uri=""; | ||||||
|  |     private $forte_api_access_id=""; | ||||||
|  |     private $forte_secure_key=""; | ||||||
|  |     private $forte_auth_organization_id=""; | ||||||
|  |     private $forte_organization_id=""; | ||||||
|  |     private $forte_location_id=""; | ||||||
|  |      | ||||||
|  |     public function __construct(FortePaymentDriver $forte) | ||||||
|  |     { | ||||||
|  |         $this->forte = $forte; | ||||||
|  | 
 | ||||||
|  |         $this->forte_base_uri = "https://sandbox.forte.net/api/v3/"; | ||||||
|  |         if($this->forte->company_gateway->getConfigField('testMode') == false){ | ||||||
|  |             $this->forte_base_uri = "https://api.forte.net/v3/"; | ||||||
|  |         } | ||||||
|  |         $this->forte_api_access_id = $this->forte->company_gateway->getConfigField('apiAccessId'); | ||||||
|  |         $this->forte_secure_key = $this->forte->company_gateway->getConfigField('secureKey'); | ||||||
|  |         $this->forte_auth_organization_id = $this->forte->company_gateway->getConfigField('authOrganizationId'); | ||||||
|  |         $this->forte_organization_id = $this->forte->company_gateway->getConfigField('organizationId'); | ||||||
|  |         $this->forte_location_id = $this->forte->company_gateway->getConfigField('locationId'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function authorizeView(array $data) | ||||||
|  |     { | ||||||
|  |         return render('gateways.forte.ach.authorize', $data); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function authorizeResponse(Request $request) | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |         $payment_meta = new \stdClass; | ||||||
|  |         $payment_meta->brand = (string)ctrans('texts.ach'); | ||||||
|  |         $payment_meta->last4 = (string) $request->last_4; | ||||||
|  |         $payment_meta->exp_year = '-'; | ||||||
|  |         $payment_meta->type = GatewayType::BANK_TRANSFER; | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'payment_meta' => $payment_meta, | ||||||
|  |             'token' => $request->one_time_token, | ||||||
|  |             'payment_method_id' => $request->gateway_type_id, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $this->forte->storeGatewayToken($data); | ||||||
|  | 
 | ||||||
|  |         return redirect()->route('client.payment_methods.index')->withSuccess('Payment Method added.'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function paymentView(array $data) | ||||||
|  |     { | ||||||
|  |         $this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data); | ||||||
|  |         $this->forte->payment_hash->save(); | ||||||
|  | 
 | ||||||
|  |         $data['gateway'] = $this; | ||||||
|  |         return render('gateways.forte.ach.pay', $data); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function paymentResponse($request) | ||||||
|  |     { | ||||||
|  |         $payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->firstOrFail(); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             $curl = curl_init(); | ||||||
|  |             curl_setopt_array($curl, array( | ||||||
|  |             CURLOPT_URL => $this->forte_base_uri.'organizations/'.$this->forte_organization_id.'/locations/'.$this->forte_location_id.'/transactions', | ||||||
|  |             CURLOPT_RETURNTRANSFER => true, | ||||||
|  |             CURLOPT_ENCODING => '', | ||||||
|  |             CURLOPT_MAXREDIRS => 10, | ||||||
|  |             CURLOPT_TIMEOUT => 0, | ||||||
|  |             CURLOPT_FOLLOWLOCATION => true, | ||||||
|  |             CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | ||||||
|  |             CURLOPT_CUSTOMREQUEST => 'POST', | ||||||
|  |             CURLOPT_POSTFIELDS =>'{ | ||||||
|  |                 "action":"sale", | ||||||
|  |                 "authorization_amount": '.$payment_hash->data->total->amount_with_fee.', | ||||||
|  |                 "echeck":{ | ||||||
|  |                     "sec_code":"PPD", | ||||||
|  |                 }, | ||||||
|  |                 "billing_address":{ | ||||||
|  |                     "first_name": "'.$this->forte->client->name.'", | ||||||
|  |                     "last_name": "'.$this->forte->client->name.'" | ||||||
|  |                 }, | ||||||
|  |                 "echeck":{ | ||||||
|  |                    "one_time_token":"'.$request->payment_token.'" | ||||||
|  |                 } | ||||||
|  |             }', | ||||||
|  |             CURLOPT_HTTPHEADER => array( | ||||||
|  |                 'X-Forte-Auth-Organization-Id: '.$this->forte_organization_id, | ||||||
|  |                 'Content-Type: application/json', | ||||||
|  |                 'Authorization: Basic '.base64_encode($this->forte_api_access_id.':'.$this->forte_secure_key), | ||||||
|  |                 'Cookie: visid_incap_621087=u18+3REYR/iISgzZxOF5s2ODW2IAAAAAQUIPAAAAAADuGqKgECQLS81FcSDrmhGe; nlbi_621087=YHngadhJ2VU+yr7/R1efXgAAAAD3mQyhqmnLls8PRu4iN58G; incap_ses_1136_621087=CVdrXUdhIlm9WJNDieLDD4QVXGIAAAAAvBwvkUcwhM0+OwvdPm2stg==' | ||||||
|  |             ), | ||||||
|  |             )); | ||||||
|  | 
 | ||||||
|  |             $response = curl_exec($curl); | ||||||
|  |             $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | ||||||
|  | 
 | ||||||
|  |             curl_close($curl); | ||||||
|  | 
 | ||||||
|  |             $response=json_decode($response); | ||||||
|  |         } catch (\Throwable $th) { | ||||||
|  |             throw $th; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if ($httpcode>299) { | ||||||
|  |             $error = Validator::make([], []); | ||||||
|  |             $error->getMessageBag()->add('gateway_error', $response->response->response_desc); | ||||||
|  |             return redirect('client/invoices')->withErrors($error); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'payment_method' => $request->payment_method_id, | ||||||
|  |             'payment_type' => PaymentType::ACH, | ||||||
|  |             'amount' => $payment_hash->data->amount_with_fee, | ||||||
|  |             'transaction_reference' => $response->transaction_id, | ||||||
|  |             'gateway_type_id' => GatewayType::BANK_TRANSFER, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $payment=$this->forte->createPayment($data, Payment::STATUS_COMPLETED); | ||||||
|  |         return redirect('client/invoices')->withSuccess('Invoice paid.'); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								app/PaymentDrivers/Forte/CreditCard.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								app/PaymentDrivers/Forte/CreditCard.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,160 @@ | |||||||
|  | <?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\PaymentDrivers\Forte; | ||||||
|  | 
 | ||||||
|  | use App\Models\Payment; | ||||||
|  | use App\Models\GatewayType; | ||||||
|  | use App\Models\PaymentHash; | ||||||
|  | use App\Models\PaymentType; | ||||||
|  | use App\Utils\Traits\MakesHash; | ||||||
|  | use Illuminate\Support\Facades\Session; | ||||||
|  | use Illuminate\Support\Facades\Validator; | ||||||
|  | use App\PaymentDrivers\FortePaymentDriver; | ||||||
|  | use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; | ||||||
|  | 
 | ||||||
|  | class CreditCard | ||||||
|  | { | ||||||
|  |     use MakesHash; | ||||||
|  |      | ||||||
|  |     public $forte; | ||||||
|  | 
 | ||||||
|  |     private $forte_base_uri=""; | ||||||
|  |     private $forte_api_access_id=""; | ||||||
|  |     private $forte_secure_key=""; | ||||||
|  |     private $forte_auth_organization_id=""; | ||||||
|  |     private $forte_organization_id=""; | ||||||
|  |     private $forte_location_id=""; | ||||||
|  |      | ||||||
|  |     public function __construct(FortePaymentDriver $forte) | ||||||
|  |     { | ||||||
|  |         $this->forte = $forte; | ||||||
|  | 
 | ||||||
|  |         $this->forte_base_uri = "https://sandbox.forte.net/api/v3/"; | ||||||
|  |             if($this->forte->company_gateway->getConfigField('testMode') == false){ | ||||||
|  |                 $this->forte_base_uri = "https://api.forte.net/v3/"; | ||||||
|  |             } | ||||||
|  |         $this->forte_api_access_id = $this->forte->company_gateway->getConfigField('apiAccessId'); | ||||||
|  |         $this->forte_secure_key = $this->forte->company_gateway->getConfigField('secureKey'); | ||||||
|  |         $this->forte_auth_organization_id = $this->forte->company_gateway->getConfigField('authOrganizationId'); | ||||||
|  |         $this->forte_organization_id = $this->forte->company_gateway->getConfigField('organizationId'); | ||||||
|  |         $this->forte_location_id = $this->forte->company_gateway->getConfigField('locationId'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function authorizeView(array $data) | ||||||
|  |     { | ||||||
|  |         return render('gateways.forte.credit_card.authorize', $data); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function authorizeResponse($request) | ||||||
|  |     { | ||||||
|  |         $payment_meta = new \stdClass; | ||||||
|  |         $payment_meta->exp_month = (string) $request->expire_month; | ||||||
|  |         $payment_meta->exp_year = (string) $request->expire_year; | ||||||
|  |         $payment_meta->brand = (string) $request->card_type; | ||||||
|  |         $payment_meta->last4 = (string) $request->last_4; | ||||||
|  |         $payment_meta->type = GatewayType::CREDIT_CARD; | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'payment_meta' => $payment_meta, | ||||||
|  |             'token' => $request->one_time_token, | ||||||
|  |             'payment_method_id' => $request->payment_method_id, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         $this->forte->storeGatewayToken($data); | ||||||
|  | 
 | ||||||
|  |         return redirect()->route('client.payment_methods.index')->withSuccess('Payment Method added.'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function paymentView(array $data) | ||||||
|  |     { | ||||||
|  |         $this->forte->payment_hash->data = array_merge((array) $this->forte->payment_hash->data, $data); | ||||||
|  |         $this->forte->payment_hash->save(); | ||||||
|  | 
 | ||||||
|  |         $data['gateway'] = $this; | ||||||
|  |         return render('gateways.forte.credit_card.pay', $data); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function paymentResponse(PaymentResponseRequest $request) | ||||||
|  |     { | ||||||
|  |         $payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->firstOrFail(); | ||||||
|  |         $amount_with_fee = $payment_hash->data->total->amount_with_fee; | ||||||
|  |         $invoice_totals = $payment_hash->data->total->invoice_totals; | ||||||
|  |         $fee_total = 0; | ||||||
|  | 
 | ||||||
|  |         for ($i = ($invoice_totals * 100) ; $i < ($amount_with_fee * 100); $i++) {  | ||||||
|  |             $calculated_fee = ( 3 * $i) / 100; | ||||||
|  |             $calculated_amount_with_fee = round(($i + $calculated_fee) / 100,2); | ||||||
|  |             if ($calculated_amount_with_fee == $amount_with_fee) { | ||||||
|  |                 $fee_total = round($calculated_fee / 100,2); | ||||||
|  |                 $amount_with_fee = $calculated_amount_with_fee; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         try { | ||||||
|  |             $curl = curl_init(); | ||||||
|  | 
 | ||||||
|  |             curl_setopt_array($curl, array( | ||||||
|  |                 CURLOPT_URL => $this->forte_base_uri.'organizations/'.$this->forte_organization_id.'/locations/'.$this->forte_location_id.'/transactions', | ||||||
|  |                 CURLOPT_RETURNTRANSFER => true, | ||||||
|  |                 CURLOPT_ENCODING => '', | ||||||
|  |                 CURLOPT_MAXREDIRS => 10, | ||||||
|  |                 CURLOPT_TIMEOUT => 0, | ||||||
|  |                 CURLOPT_FOLLOWLOCATION => true, | ||||||
|  |                 CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | ||||||
|  |                 CURLOPT_CUSTOMREQUEST => 'POST', | ||||||
|  |                 CURLOPT_POSTFIELDS =>'{ | ||||||
|  |                      "action":"sale",  | ||||||
|  |                      "authorization_amount":'.$amount_with_fee.', | ||||||
|  |                      "service_fee_amount":'.$fee_total.', | ||||||
|  |                      "billing_address":{ | ||||||
|  |                         "first_name":"'.$this->forte->client->name.'", | ||||||
|  |                         "last_name":"'.$this->forte->client->name.'" | ||||||
|  |                      }, | ||||||
|  |                      "card":{ | ||||||
|  |                         "one_time_token":"'.$request->payment_token.'" | ||||||
|  |                      } | ||||||
|  |               }', | ||||||
|  |                 CURLOPT_HTTPHEADER => array( | ||||||
|  |                   'Content-Type: application/json', | ||||||
|  |                   'X-Forte-Auth-Organization-Id: '.$this->forte_organization_id, | ||||||
|  |                   'Authorization: Basic '.base64_encode($this->forte_api_access_id.':'.$this->forte_secure_key) | ||||||
|  |                 ), | ||||||
|  |               )); | ||||||
|  | 
 | ||||||
|  |             $response = curl_exec($curl); | ||||||
|  |             $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | ||||||
|  | 
 | ||||||
|  |             curl_close($curl); | ||||||
|  | 
 | ||||||
|  |             $response=json_decode($response); | ||||||
|  |         } catch (\Throwable $th) { | ||||||
|  |             throw $th; | ||||||
|  |         } | ||||||
|  |         if ($httpcode>299) { | ||||||
|  |             $error = Validator::make([], []); | ||||||
|  |             $error->getMessageBag()->add('gateway_error', $response->response->response_desc); | ||||||
|  |             return redirect('client/invoices')->withErrors($error); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $data = [ | ||||||
|  |             'payment_method' => $request->payment_method_id, | ||||||
|  |             'payment_type' => PaymentType::parseCardType(strtolower($request->card_brand)) ?: PaymentType::CREDIT_CARD_OTHER, | ||||||
|  |             'amount' => $payment_hash->data->amount_with_fee, | ||||||
|  |             'transaction_reference' => $response->transaction_id, | ||||||
|  |             'gateway_type_id' => GatewayType::CREDIT_CARD, | ||||||
|  |         ]; | ||||||
|  |         $payment=$this->forte->createPayment($data, Payment::STATUS_COMPLETED); | ||||||
|  |         return redirect('client/invoices')->withSuccess('Invoice paid.'); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										90
									
								
								app/PaymentDrivers/FortePaymentDriver.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								app/PaymentDrivers/FortePaymentDriver.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | |||||||
|  | <?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 App\PaymentDrivers; | ||||||
|  | 
 | ||||||
|  | use App\Models\SystemLog; | ||||||
|  | use App\Models\GatewayType; | ||||||
|  | use App\Utils\Traits\MakesHash; | ||||||
|  | use App\PaymentDrivers\Forte\ACH; | ||||||
|  | use App\PaymentDrivers\Forte\CreditCard; | ||||||
|  | 
 | ||||||
|  | class FortePaymentDriver extends BaseDriver | ||||||
|  | { | ||||||
|  |     use MakesHash; | ||||||
|  | 
 | ||||||
|  |     public $refundable = true; //does this gateway support refunds?
 | ||||||
|  | 
 | ||||||
|  |     public $token_billing = true; //does this gateway support token billing?
 | ||||||
|  | 
 | ||||||
|  |     public $can_authorise_credit_card = true; //does this gateway support authorizations?
 | ||||||
|  | 
 | ||||||
|  |     public $gateway; //initialized gateway
 | ||||||
|  | 
 | ||||||
|  |     public $payment_method; //initialized payment method
 | ||||||
|  | 
 | ||||||
|  |     public static $methods = [ | ||||||
|  |         GatewayType::CREDIT_CARD => CreditCard::class, | ||||||
|  |         GatewayType::BANK_TRANSFER => ACH::class, | ||||||
|  |     ]; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the gateway types. | ||||||
|  |      */ | ||||||
|  |     public function gatewayTypes(): array | ||||||
|  |     { | ||||||
|  |         $types = []; | ||||||
|  | 
 | ||||||
|  |             $types[] = GatewayType::CREDIT_CARD; | ||||||
|  |             $types[] = GatewayType::BANK_TRANSFER; | ||||||
|  | 
 | ||||||
|  |         return $types; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
 | ||||||
|  | 
 | ||||||
|  |     public function setPaymentMethod($payment_method_id) | ||||||
|  |     { | ||||||
|  |         $class = self::$methods[$payment_method_id]; | ||||||
|  |         $this->payment_method = new $class($this); | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function authorizeView(array $data) | ||||||
|  |     { | ||||||
|  |         return $this->payment_method->authorizeView($data); //this is your custom implementation from here
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function authorizeResponse($request) | ||||||
|  |     { | ||||||
|  |         return $this->payment_method->authorizeResponse($request);  //this is your custom implementation from here
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function processPaymentView(array $data) | ||||||
|  |     { | ||||||
|  |         return $this->payment_method->paymentView($data);  //this is your custom implementation from here
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function processPaymentResponse($request) | ||||||
|  |     { | ||||||
|  |         return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // public function refund(Payment $payment, $amount, $return_client_response = false)
 | ||||||
|  |     // {
 | ||||||
|  |     //     return $this->payment_method->yourRefundImplementationHere(); //this is your custom implementation from here
 | ||||||
|  |     // }
 | ||||||
|  | 
 | ||||||
|  |     // public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
 | ||||||
|  |     // {
 | ||||||
|  |     //     return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
 | ||||||
|  |     // }
 | ||||||
|  | } | ||||||
| @ -121,7 +121,19 @@ class PaymentIntentWebhook implements ShouldQueue | |||||||
|             nlog("payment intent"); |             nlog("payment intent"); | ||||||
|             nlog($this->stripe_request); |             nlog($this->stripe_request); | ||||||
| 
 | 
 | ||||||
|             if(optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('card', $this->stripe_request['object']['allowed_source_types'])) |             if(array_key_exists('allowed_source_types', $this->stripe_request['object']) && optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('card', $this->stripe_request['object']['allowed_source_types'])) | ||||||
|  |             { | ||||||
|  |                 nlog("hash found"); | ||||||
|  | 
 | ||||||
|  |                 $hash = $this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']; | ||||||
|  | 
 | ||||||
|  |                 $payment_hash = PaymentHash::where('hash', $hash)->first(); | ||||||
|  |                 $invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id); | ||||||
|  |                 $client = $invoice->client; | ||||||
|  | 
 | ||||||
|  |                 $this->updateCreditCardPayment($payment_hash, $client); | ||||||
|  |             } | ||||||
|  |             elseif(array_key_exists('payment_method_types', $this->stripe_request['object']) && optional($this->stripe_request['object']['charges']['data'][0]['metadata']['payment_hash']) && in_array('card', $this->stripe_request['object']['payment_method_types'])) | ||||||
|             { |             { | ||||||
|                 nlog("hash found"); |                 nlog("hash found"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -220,6 +220,7 @@ class Statement | |||||||
|     protected function getInvoices(): \Illuminate\Support\LazyCollection |     protected function getInvoices(): \Illuminate\Support\LazyCollection | ||||||
|     { |     { | ||||||
|         return Invoice::withTrashed() |         return Invoice::withTrashed() | ||||||
|  |             ->with('payments.type') | ||||||
|             ->where('is_deleted', false) |             ->where('is_deleted', false) | ||||||
|             ->where('company_id', $this->client->company_id) |             ->where('company_id', $this->client->company_id) | ||||||
|             ->where('client_id', $this->client->id) |             ->where('client_id', $this->client->id) | ||||||
|  | |||||||
| @ -255,6 +255,7 @@ class InstantPayment | |||||||
|             'tokens' => $tokens, |             'tokens' => $tokens, | ||||||
|             'payment_method_id' => $payment_method_id, |             'payment_method_id' => $payment_method_id, | ||||||
|             'amount_with_fee' => $invoice_totals + $fee_totals, |             'amount_with_fee' => $invoice_totals + $fee_totals, | ||||||
|  |             'client' => $client, | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         if ($is_credit_payment || $totals <= 0) { |         if ($is_credit_payment || $totals <= 0) { | ||||||
|  | |||||||
| @ -60,6 +60,8 @@ class Design extends BaseDesign | |||||||
| 
 | 
 | ||||||
|     public $company; |     public $company; | ||||||
| 
 | 
 | ||||||
|  |     public $client_or_vendor_entity; | ||||||
|  | 
 | ||||||
|     /** @var array */ |     /** @var array */ | ||||||
|     public $aging = []; |     public $aging = []; | ||||||
| 
 | 
 | ||||||
| @ -173,9 +175,27 @@ class Design extends BaseDesign | |||||||
|                     $this->sharedFooterElements(), |                     $this->sharedFooterElements(), | ||||||
|                 ], |                 ], | ||||||
|             ], |             ], | ||||||
|  |             // 'swiss-qr' => [
 | ||||||
|  |             //     'id' => 'swiss-qr',
 | ||||||
|  |             //     'elements' => $this->swissQrCodeElement(),
 | ||||||
|  |             // ]
 | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     public function swissQrCodeElement() :array | ||||||
|  |     { | ||||||
|  |         if($this->type == self::DELIVERY_NOTE) | ||||||
|  |             return []; | ||||||
|  | 
 | ||||||
|  |         $elements = []; | ||||||
|  | 
 | ||||||
|  |         if(strlen($this->company->getSetting('qr_iban')) > 5 && strlen($this->company->getSetting('besr_id')) > 1) | ||||||
|  |             $elements[] = ['element' => 'qr_code', 'content' => '$swiss_qr', 'show_empty' => false, 'properties' => ['data-ref' => 'swiss-qr-code']]; | ||||||
|  | 
 | ||||||
|  |         return $elements;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public function companyDetails(): array |     public function companyDetails(): array | ||||||
|     { |     { | ||||||
|         $variables = $this->context['pdf_variables']['company_details']; |         $variables = $this->context['pdf_variables']['company_details']; | ||||||
| @ -312,7 +332,10 @@ class Design extends BaseDesign | |||||||
|             $_variable = explode('.', $variable)[1]; |             $_variable = explode('.', $variable)[1]; | ||||||
|             $_customs = ['custom1', 'custom2', 'custom3', 'custom4']; |             $_customs = ['custom1', 'custom2', 'custom3', 'custom4']; | ||||||
| 
 | 
 | ||||||
|             if (in_array($_variable, $_customs)) { |             /* 2/7/2022 don't show custom values if they are empty */ | ||||||
|  |             $var = str_replace("custom", "custom_value", $_variable); | ||||||
|  | 
 | ||||||
|  |             if (in_array($_variable, $_customs) && !empty($this->entity->{$var})) { | ||||||
|                 $elements[] = ['element' => 'tr', 'elements' => [ |                 $elements[] = ['element' => 'tr', 'elements' => [ | ||||||
|                     ['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']], |                     ['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']], | ||||||
|                     ['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]], |                     ['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]], | ||||||
| @ -785,7 +808,7 @@ class Design extends BaseDesign | |||||||
|                 foreach ($taxes as $i => $tax) { |                 foreach ($taxes as $i => $tax) { | ||||||
|                     $elements[1]['elements'][] = ['element' => 'div', 'elements' => [ |                     $elements[1]['elements'][] = ['element' => 'div', 'elements' => [ | ||||||
|                         ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i . '-label']], |                         ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i . '-label']], | ||||||
|                         ['element' => 'span', 'content', 'content' => Number::formatMoney($tax['total'], $this->context['client']), 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i]], |                         ['element' => 'span', 'content', 'content' => Number::formatMoney($tax['total'], $this->client_or_vendor_entity), 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i]], | ||||||
|                     ]]; |                     ]]; | ||||||
|                 } |                 } | ||||||
|             } elseif ($variable == '$line_taxes') { |             } elseif ($variable == '$line_taxes') { | ||||||
| @ -798,13 +821,13 @@ class Design extends BaseDesign | |||||||
|                 foreach ($taxes as $i => $tax) { |                 foreach ($taxes as $i => $tax) { | ||||||
|                     $elements[1]['elements'][] = ['element' => 'div', 'elements' => [ |                     $elements[1]['elements'][] = ['element' => 'div', 'elements' => [ | ||||||
|                         ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i . '-label']], |                         ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i . '-label']], | ||||||
|                         ['element' => 'span', 'content', 'content' => Number::formatMoney($tax['total'], $this->context['client']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]], |                         ['element' => 'span', 'content', 'content' => Number::formatMoney($tax['total'], $this->client_or_vendor_entity), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]], | ||||||
|                     ]]; |                     ]]; | ||||||
|                 } |                 } | ||||||
|             } elseif (Str::startsWith($variable, '$custom_surcharge')) { |             } elseif (Str::startsWith($variable, '$custom_surcharge')) { | ||||||
|                 $_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
 |                 $_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
 | ||||||
| 
 | 
 | ||||||
|                 $visible = (int)$this->entity->{$_variable} != 0 || $this->entity->{$_variable} != '0' || !$this->entity->{$_variable}; |                 $visible = (int)$this->entity->{$_variable} > 0 || (int)$this->entity->{$_variable} < 0 || !$this->entity->{$_variable}; | ||||||
| 
 | 
 | ||||||
|                 $elements[1]['elements'][] = ['element' => 'div', 'elements' => [ |                 $elements[1]['elements'][] = ['element' => 'div', 'elements' => [ | ||||||
|                     ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']], |                     ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']], | ||||||
|  | |||||||
| @ -32,10 +32,12 @@ trait DesignHelpers | |||||||
| 
 | 
 | ||||||
|         if (isset($this->context['vendor'])) { |         if (isset($this->context['vendor'])) { | ||||||
|             $this->vendor = $this->context['vendor']; |             $this->vendor = $this->context['vendor']; | ||||||
|  |             $this->client_or_vendor_entity = $this->context['vendor']; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (isset($this->context['client'])) { |         if (isset($this->context['client'])) { | ||||||
|             $this->client = $this->context['client']; |             $this->client = $this->context['client']; | ||||||
|  |             $this->client_or_vendor_entity = $this->context['client']; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (isset($this->context['entity'])) { |         if (isset($this->context['entity'])) { | ||||||
|  | |||||||
| @ -41,7 +41,20 @@ class ApplyNumber extends AbstractService | |||||||
|             return $this->purchase_order; |             return $this->purchase_order; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $this->trySaving(); |         switch ($this->vendor->company->getSetting('counter_number_applied')) { | ||||||
|  |             case 'when_saved': | ||||||
|  |                 $this->trySaving(); | ||||||
|  |                 break; | ||||||
|  |             case 'when_sent': | ||||||
|  |                 if ($this->invoice->status_id == PurchaseOrder::STATUS_SENT) { | ||||||
|  |                     $this->trySaving(); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|         return $this->purchase_order; |         return $this->purchase_order; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -51,7 +51,6 @@ class PurchaseOrderService | |||||||
| 
 | 
 | ||||||
|         // //TODO implement design, footer, terms
 |         // //TODO implement design, footer, terms
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         // /* If client currency differs from the company default currency, then insert the client exchange rate on the model.*/
 |         // /* If client currency differs from the company default currency, then insert the client exchange rate on the model.*/
 | ||||||
|         // if (!isset($this->purchase_order->exchange_rate) && $this->purchase_order->client->currency()->id != (int)$this->purchase_order->company->settings->currency_id)
 |         // if (!isset($this->purchase_order->exchange_rate) && $this->purchase_order->client->currency()->id != (int)$this->purchase_order->company->settings->currency_id)
 | ||||||
|         //     $this->purchase_order->exchange_rate = $this->purchase_order->client->currency()->exchange_rate;
 |         //     $this->purchase_order->exchange_rate = $this->purchase_order->client->currency()->exchange_rate;
 | ||||||
| @ -89,7 +88,6 @@ class PurchaseOrderService | |||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     public function touchPdf($force = false) |     public function touchPdf($force = false) | ||||||
|     { |     { | ||||||
|         try { |         try { | ||||||
|  | |||||||
| @ -89,7 +89,7 @@ class PurchaseOrderTransformer extends EntityTransformer | |||||||
|             'custom_surcharge_tax3' => (bool)$purchase_order->custom_surcharge_tax3, |             'custom_surcharge_tax3' => (bool)$purchase_order->custom_surcharge_tax3, | ||||||
|             'custom_surcharge_tax4' => (bool)$purchase_order->custom_surcharge_tax4, |             'custom_surcharge_tax4' => (bool)$purchase_order->custom_surcharge_tax4, | ||||||
|             'line_items' => $purchase_order->line_items ?: (array)[], |             'line_items' => $purchase_order->line_items ?: (array)[], | ||||||
|             'entity_type' => 'purchase_order', |             'entity_type' => 'purchaseOrder', | ||||||
|             'exchange_rate' => (float)$purchase_order->exchange_rate, |             'exchange_rate' => (float)$purchase_order->exchange_rate, | ||||||
|             'paid_to_date' => (float)$purchase_order->paid_to_date, |             'paid_to_date' => (float)$purchase_order->paid_to_date, | ||||||
|             'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id), |             'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id), | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ | |||||||
| 
 | 
 | ||||||
| namespace App\Utils; | namespace App\Utils; | ||||||
| 
 | 
 | ||||||
|  | use App\Helpers\SwissQr\SwissQrGenerator; | ||||||
| use App\Models\Country; | use App\Models\Country; | ||||||
| use App\Models\CreditInvitation; | use App\Models\CreditInvitation; | ||||||
| use App\Models\GatewayType; | use App\Models\GatewayType; | ||||||
| @ -162,6 +163,17 @@ class HtmlEngine | |||||||
|             if($this->entity->vendor) { |             if($this->entity->vendor) { | ||||||
|                 $data['$invoice.vendor'] = ['value' => $this->entity->vendor->present()->name(), 'label' => ctrans('texts.vendor_name')]; |                 $data['$invoice.vendor'] = ['value' => $this->entity->vendor->present()->name(), 'label' => ctrans('texts.vendor_name')]; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             if(strlen($this->company->getSetting('qr_iban')) > 5 && strlen($this->company->getSetting('besr_id')) > 1) | ||||||
|  |             { | ||||||
|  |                 try{ | ||||||
|  |                     $data['$swiss_qr'] = ['value' => (new SwissQrGenerator($this->entity, $this->company))->run(), 'label' => '']; | ||||||
|  |                 } | ||||||
|  |                 catch(\Exception $e){ | ||||||
|  |                     $data['$swiss_qr'] = ['value' => '', 'label' => '']; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ($this->entity_string == 'quote') { |         if ($this->entity_string == 'quote') { | ||||||
| @ -281,6 +293,7 @@ class HtmlEngine | |||||||
|         $data['$assigned_to_user'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->present()->name() : '', 'label' => ctrans('texts.name')]; |         $data['$assigned_to_user'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->present()->name() : '', 'label' => ctrans('texts.name')]; | ||||||
| 
 | 
 | ||||||
|         $data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')]; |         $data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')]; | ||||||
|  | 
 | ||||||
|         $data['$invoice.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')]; |         $data['$invoice.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')]; | ||||||
|         $data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')]; |         $data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')]; | ||||||
|         $data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')]; |         $data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')]; | ||||||
|  | |||||||
| @ -16,16 +16,22 @@ use App\Models\Client; | |||||||
| use App\Models\ClientContact; | use App\Models\ClientContact; | ||||||
| use App\Models\Invoice; | use App\Models\Invoice; | ||||||
| use App\Models\InvoiceInvitation; | use App\Models\InvoiceInvitation; | ||||||
|  | use App\Models\PurchaseOrder; | ||||||
|  | use App\Models\PurchaseOrderInvitation; | ||||||
| use App\Models\Quote; | use App\Models\Quote; | ||||||
| use App\Models\QuoteInvitation; | use App\Models\QuoteInvitation; | ||||||
|  | use App\Models\Vendor; | ||||||
|  | use App\Models\VendorContact; | ||||||
| use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; | use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; | ||||||
| use App\Utils\Ninja; | use App\Utils\Ninja; | ||||||
| use App\Utils\Traits\MakesHash; | use App\Utils\Traits\MakesHash; | ||||||
| use App\Utils\Traits\MakesInvoiceHtml; | use App\Utils\Traits\MakesInvoiceHtml; | ||||||
| use App\Utils\Traits\MakesTemplateData; | use App\Utils\Traits\MakesTemplateData; | ||||||
|  | use App\Utils\VendorHtmlEngine; | ||||||
| use DB; | use DB; | ||||||
| use Illuminate\Support\Facades\App; | use Illuminate\Support\Facades\App; | ||||||
| use Illuminate\Support\Facades\Lang; | use Illuminate\Support\Facades\Lang; | ||||||
|  | use Illuminate\Support\Str; | ||||||
| use League\CommonMark\CommonMarkConverter; | use League\CommonMark\CommonMarkConverter; | ||||||
| use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; | use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; | ||||||
| 
 | 
 | ||||||
| @ -78,6 +84,10 @@ class TemplateEngine | |||||||
| 
 | 
 | ||||||
|     public function build() |     public function build() | ||||||
|     { |     { | ||||||
|  |          | ||||||
|  |         if ($this->template == 'email_template_null') | ||||||
|  |             $this->template = 'email_template_purchase_order'; | ||||||
|  | 
 | ||||||
|         return $this->setEntity() |         return $this->setEntity() | ||||||
|                  ->setSettingsObject() |                  ->setSettingsObject() | ||||||
|                  ->setTemplates() |                  ->setTemplates() | ||||||
| @ -88,7 +98,7 @@ class TemplateEngine | |||||||
|     private function setEntity() |     private function setEntity() | ||||||
|     { |     { | ||||||
|         if (strlen($this->entity) > 1 && strlen($this->entity_id) > 1) { |         if (strlen($this->entity) > 1 && strlen($this->entity_id) > 1) { | ||||||
|             $class = 'App\Models\\'.ucfirst($this->entity); |             $class = 'App\Models\\'.ucfirst(Str::camel($this->entity)); | ||||||
|             $this->entity_obj = $class::withTrashed()->where('id', $this->decodePrimaryKey($this->entity_id))->company()->first(); |             $this->entity_obj = $class::withTrashed()->where('id', $this->decodePrimaryKey($this->entity_id))->company()->first(); | ||||||
|         } else { |         } else { | ||||||
|             $this->mockEntity(); |             $this->mockEntity(); | ||||||
| @ -99,7 +109,11 @@ class TemplateEngine | |||||||
| 
 | 
 | ||||||
|     private function setSettingsObject() |     private function setSettingsObject() | ||||||
|     { |     { | ||||||
|         if ($this->entity_obj) { |         if($this->entity == 'purchaseOrder'){ | ||||||
|  |             $this->settings_entity = auth()->user()->company(); | ||||||
|  |             $this->settings = $this->settings_entity->settings; | ||||||
|  |         } | ||||||
|  |         elseif ($this->entity_obj->client()->exists()) { | ||||||
|             $this->settings_entity = $this->entity_obj->client; |             $this->settings_entity = $this->entity_obj->client; | ||||||
|             $this->settings = $this->settings_entity->getMergedSettings(); |             $this->settings = $this->settings_entity->getMergedSettings(); | ||||||
|         } else { |         } else { | ||||||
| @ -143,9 +157,13 @@ class TemplateEngine | |||||||
|         $this->raw_body = $this->body; |         $this->raw_body = $this->body; | ||||||
|         $this->raw_subject  = $this->subject; |         $this->raw_subject  = $this->subject; | ||||||
| 
 | 
 | ||||||
|         if ($this->entity_obj) { |         if($this->entity == 'purchaseOrder'){ | ||||||
|  |             $this->fakerValues(); | ||||||
|  |         } | ||||||
|  |         elseif ($this->entity_obj->client()->exists()) { | ||||||
|             $this->entityValues($this->entity_obj->client->primary_contact()->first()); |             $this->entityValues($this->entity_obj->client->primary_contact()->first()); | ||||||
|         } else { |         }  | ||||||
|  |         else { | ||||||
|             $this->fakerValues(); |             $this->fakerValues(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -172,11 +190,13 @@ class TemplateEngine | |||||||
| 
 | 
 | ||||||
|     private function entityValues($contact) |     private function entityValues($contact) | ||||||
|     { |     { | ||||||
|         $this->labels_and_values = (new HtmlEngine($this->entity_obj->invitations->first()))->generateLabelsAndValues(); |         if($this->entity == 'purchaseOrder') | ||||||
|  |             $this->labels_and_values = (new VendorHtmlEngine($this->entity_obj->invitations->first()))->generateLabelsAndValues(); | ||||||
|  |         else | ||||||
|  |             $this->labels_and_values = (new HtmlEngine($this->entity_obj->invitations->first()))->generateLabelsAndValues(); | ||||||
| 
 | 
 | ||||||
|         $this->body = strtr($this->body, $this->labels_and_values['labels']); |         $this->body = strtr($this->body, $this->labels_and_values['labels']); | ||||||
|         $this->body = strtr($this->body, $this->labels_and_values['values']); |         $this->body = strtr($this->body, $this->labels_and_values['values']); | ||||||
| //        $this->body = str_replace("\n", "<br>", $this->body);
 |  | ||||||
| 
 | 
 | ||||||
|         $this->subject = strtr($this->subject, $this->labels_and_values['labels']); |         $this->subject = strtr($this->subject, $this->labels_and_values['labels']); | ||||||
|         $this->subject = strtr($this->subject, $this->labels_and_values['values']); |         $this->subject = strtr($this->subject, $this->labels_and_values['values']); | ||||||
| @ -198,7 +218,18 @@ class TemplateEngine | |||||||
|         $data['footer'] = ''; |         $data['footer'] = ''; | ||||||
|         $data['logo'] = auth()->user()->company()->present()->logo(); |         $data['logo'] = auth()->user()->company()->present()->logo(); | ||||||
| 
 | 
 | ||||||
|         $data = array_merge($data, Helpers::sharedEmailVariables($this->entity_obj->client)); |         if($this->entity_obj->client()->exists()) | ||||||
|  |             $data = array_merge($data, Helpers::sharedEmailVariables($this->entity_obj->client)); | ||||||
|  |         else{ | ||||||
|  | 
 | ||||||
|  |             $data['signature'] = $this->settings->email_signature; | ||||||
|  |             $data['settings'] = $this->settings; | ||||||
|  |             $data['whitelabel'] = $this->entity_obj ? $this->entity_obj->company->account->isPaid() : true; | ||||||
|  |             $data['company'] = $this->entity_obj ? $this->entity_obj->company : ''; | ||||||
|  |             $data['settings'] = $this->settings; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|         if ($email_style == 'custom') { |         if ($email_style == 'custom') { | ||||||
|             $wrapper = $this->settings_entity->getSetting('email_style_custom'); |             $wrapper = $this->settings_entity->getSetting('email_style_custom'); | ||||||
| @ -241,8 +272,13 @@ class TemplateEngine | |||||||
| 
 | 
 | ||||||
|     private function mockEntity() |     private function mockEntity() | ||||||
|     { |     { | ||||||
|  |         if(!$this->entity && $this->template && str_contains($this->template, 'purchase_order')) | ||||||
|  |             $this->entity = 'purchaseOrder'; | ||||||
|  | 
 | ||||||
|         DB::connection(config('database.default'))->beginTransaction(); |         DB::connection(config('database.default'))->beginTransaction(); | ||||||
| 
 | 
 | ||||||
|  |         $vendor = false; | ||||||
|  | 
 | ||||||
|         $client = Client::factory()->create([ |         $client = Client::factory()->create([ | ||||||
|                 'user_id' => auth()->user()->id, |                 'user_id' => auth()->user()->id, | ||||||
|                 'company_id' => auth()->user()->company()->id, |                 'company_id' => auth()->user()->company()->id, | ||||||
| @ -289,12 +325,60 @@ class TemplateEngine | |||||||
|             ]); |             ]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $this->entity_obj->setRelation('invitations', $invitation); | 
 | ||||||
|         $this->entity_obj->setRelation('client', $client); | 
 | ||||||
|         $this->entity_obj->setRelation('company', auth()->user()->company()); |         if($this->entity == 'purchaseOrder') | ||||||
|         $this->entity_obj->load('client'); |         { | ||||||
|         $client->setRelation('company', auth()->user()->company()); | 
 | ||||||
|         $client->load('company'); |             $vendor = Vendor::factory()->create([ | ||||||
|  |                     'user_id' => auth()->user()->id, | ||||||
|  |                     'company_id' => auth()->user()->company()->id, | ||||||
|  |                 ]); | ||||||
|  | 
 | ||||||
|  |             $contact = VendorContact::factory()->create([ | ||||||
|  |                     'user_id' => auth()->user()->id, | ||||||
|  |                     'company_id' => auth()->user()->company()->id, | ||||||
|  |                     'vendor_id' => $vendor->id, | ||||||
|  |                     'is_primary' => 1, | ||||||
|  |                     'send_email' => true, | ||||||
|  |                 ]); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             $this->entity_obj = PurchaseOrder::factory()->create([ | ||||||
|  |                         'user_id' => auth()->user()->id, | ||||||
|  |                         'company_id' => auth()->user()->company()->id, | ||||||
|  |                         'vendor_id' => $vendor->id, | ||||||
|  |                     ]); | ||||||
|  | 
 | ||||||
|  |             $invitation = PurchaseOrderInvitation::factory()->create([ | ||||||
|  |                         'user_id' => auth()->user()->id, | ||||||
|  |                         'company_id' => auth()->user()->company()->id, | ||||||
|  |                         'purchase_order_id' => $this->entity_obj->id, | ||||||
|  |                         'vendor_contact_id' => $contact->id, | ||||||
|  |             ]); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if($vendor) | ||||||
|  |         { | ||||||
|  | 
 | ||||||
|  |             $this->entity_obj->setRelation('invitations', $invitation); | ||||||
|  |             $this->entity_obj->setRelation('vendor', $vendor); | ||||||
|  |             $this->entity_obj->setRelation('company', auth()->user()->company()); | ||||||
|  |             $this->entity_obj->load('vendor'); | ||||||
|  |             $vendor->setRelation('company', auth()->user()->company()); | ||||||
|  |             $vendor->load('company'); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |         else  | ||||||
|  |         { | ||||||
|  |             $this->entity_obj->setRelation('invitations', $invitation); | ||||||
|  |             $this->entity_obj->setRelation('client', $client); | ||||||
|  |             $this->entity_obj->setRelation('company', auth()->user()->company()); | ||||||
|  |             $this->entity_obj->load('client'); | ||||||
|  |             $client->setRelation('company', auth()->user()->company()); | ||||||
|  |             $client->load('company'); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function tearDown() |     private function tearDown() | ||||||
|  | |||||||
| @ -111,6 +111,10 @@ trait AppSetup | |||||||
|                 'subject' => EmailTemplateDefaults::emailCreditSubject(), |                 'subject' => EmailTemplateDefaults::emailCreditSubject(), | ||||||
|                 'body' => EmailTemplateDefaults::emailCreditTemplate(), |                 'body' => EmailTemplateDefaults::emailCreditTemplate(), | ||||||
|             ], |             ], | ||||||
|  |             'purchase_order' => [ | ||||||
|  |                 'subject' => EmailTemplateDefaults::emailPurchaseOrderSubject(), | ||||||
|  |                 'body' => EmailTemplateDefaults::emailPurchaseOrderTemplate(), | ||||||
|  |             ], | ||||||
|         ]; |         ]; | ||||||
| 
 | 
 | ||||||
|         Cache::forever($name, $data); |         Cache::forever($name, $data); | ||||||
|  | |||||||
| @ -120,6 +120,9 @@ trait CompanySettingsSaver | |||||||
|             elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') { |             elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter') { | ||||||
|                 $value = 'integer'; |                 $value = 'integer'; | ||||||
| 
 | 
 | ||||||
|  |                 if($key == 'besr_id') | ||||||
|  |                     $value = 'string'; | ||||||
|  | 
 | ||||||
|                 if (! property_exists($settings, $key)) { |                 if (! property_exists($settings, $key)) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } elseif (! $this->checkAttribute($value, $settings->{$key})) { |                 } elseif (! $this->checkAttribute($value, $settings->{$key})) { | ||||||
| @ -187,6 +190,9 @@ trait CompanySettingsSaver | |||||||
|                 if($key == 'gmail_sending_user_id') |                 if($key == 'gmail_sending_user_id') | ||||||
|                     $value = 'string'; |                     $value = 'string'; | ||||||
| 
 | 
 | ||||||
|  |                 if($key == 'besr_id') | ||||||
|  |                     $value = 'string'; | ||||||
|  | 
 | ||||||
|                 if (! property_exists($settings, $key)) { |                 if (! property_exists($settings, $key)) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } elseif ($this->checkAttribute($value, $settings->{$key})) { |                 } elseif ($this->checkAttribute($value, $settings->{$key})) { | ||||||
|  | |||||||
| @ -200,56 +200,33 @@ trait MakesTemplateData | |||||||
|         $data['$task.tax_name3'] = ['value' => 'CA Sales Tax', 'label' => ctrans('texts.tax')]; |         $data['$task.tax_name3'] = ['value' => 'CA Sales Tax', 'label' => ctrans('texts.tax')]; | ||||||
|         $data['$task.line_total'] = ['value' => '$100.00', 'label' => ctrans('texts.line_total')]; |         $data['$task.line_total'] = ['value' => '$100.00', 'label' => ctrans('texts.line_total')]; | ||||||
| 
 | 
 | ||||||
|         //$data['$paid_to_date'] = ;
 |         $data['$vendor_name'] = ['value' => 'Joey Diaz Denkins', 'label' => ctrans('texts.vendor_name')];; | ||||||
|         // $data['$your_invoice'] = ;
 |         $data['$vendor.name'] = &$data['$vendor_name']; | ||||||
|         // $data['$quote'] = ;
 |         $data['$vendor'] = &$data['$vendor_name']; | ||||||
|         // $data['$your_quote'] = ;
 |  | ||||||
|         // $data['$invoice_issued_to'] = ;
 |  | ||||||
|         // $data['$quote_issued_to'] = ;
 |  | ||||||
|         // $data['$rate'] = ;
 |  | ||||||
|         // $data['$hours'] = ;
 |  | ||||||
|         // $data['$from'] = ;
 |  | ||||||
|         // $data['$to'] = ;
 |  | ||||||
|         // $data['$invoice_to'] = ;
 |  | ||||||
|         // $data['$quote_to'] = ;
 |  | ||||||
|         // $data['$details'] = ;
 |  | ||||||
|         // $data['custom_label1']              = ['value' => '', 'label' => ctrans('texts.')];
 |  | ||||||
|         // $data['custom_label2']              = ['value' => '', 'label' => ctrans('texts.')];
 |  | ||||||
|         // $data['custom_label3']              = ['value' => '', 'label' => ctrans('texts.')];
 |  | ||||||
|         // $data['custom_label4']              = ['value' => '', 'label' => ctrans('texts.')];
 |  | ||||||
|         //$data['$blank'] = ;
 |  | ||||||
|         //$data['$surcharge'] = ;
 |  | ||||||
|         /* |  | ||||||
|         $data['$tax_invoice'] = |  | ||||||
|         $data['$tax_quote'] = |  | ||||||
|         $data['$statement'] = ; |  | ||||||
|         $data['$statement_date'] = ; |  | ||||||
|         $data['$your_statement'] = ; |  | ||||||
|         $data['$statement_issued_to'] = ; |  | ||||||
|         $data['$statement_to'] = ; |  | ||||||
|         $data['$credit_note'] = ; |  | ||||||
|         $data['$credit_date'] = ; |  | ||||||
|         $data['$credit_issued_to'] = ; |  | ||||||
|         $data['$credit_to'] = ; |  | ||||||
|         $data['$your_credit'] = ; |  | ||||||
|         $data['$phone'] = ; |  | ||||||
| 
 | 
 | ||||||
|         $data['$outstanding'] = ; |         $data['$vendor.address1'] = &$data['$address1']; | ||||||
|         $data['$invoice_due_date'] = ; |         $data['$vendor.address2'] = &$data['$address2']; | ||||||
|         $data['$quote_due_date'] = ; |         $data['$vendor_address'] = ['value' => '5 Kalamazoo Way\n Jimbuckeroo\n USA 90210', 'label' => ctrans('texts.address')]; | ||||||
|         $data['$service'] = ; |         $data['$vendor.address'] = &$data['$vendor_address']; | ||||||
|         $data['$product_key'] = ; |         $data['$vendor.postal_code'] = ['value' => '90210', 'label' => ctrans('texts.postal_code')]; | ||||||
|         $data['$unit_cost'] = ; |         $data['$vendor.public_notes'] = $data['$invoice.public_notes']; | ||||||
|         $data['$custom_value1'] = ; |         $data['$vendor.city'] = &$data['$company.city']; | ||||||
|         $data['$custom_value2'] = ; |         $data['$vendor.state'] = &$data['$company.state']; | ||||||
|         $data['$delivery_note'] = ; |         $data['$vendor.id_number'] = &$data['$id_number']; | ||||||
|         $data['$date'] = ; |         $data['$vendor.vat_number'] = &$data['$vat_number']; | ||||||
|         $data['$method'] = ; |         $data['$vendor.website'] = &$data['$website']; | ||||||
|         $data['$payment_date'] = ; |         $data['$vendor.phone'] = &$data['$phone']; | ||||||
|         $data['$reference'] = ; |         $data['$vendor.city_state_postal'] = &$data['$city_state_postal']; | ||||||
|         $data['$amount'] = ; |         $data['$vendor.postal_city_state'] = &$data['$postal_city_state']; | ||||||
|         $data['$amount_paid'] =; |         $data['$vendor.country'] = &$data['$country']; | ||||||
|         	*/ |         $data['$vendor.email'] = &$data['$email']; | ||||||
|  |          | ||||||
|  |         $data['$vendor.billing_address1'] = &$data['$vendor.address1']; | ||||||
|  |         $data['$vendor.billing_address2'] = &$data['$vendor.address2']; | ||||||
|  |         $data['$vendor.billing_city'] = &$data['$vendor.city']; | ||||||
|  |         $data['$vendor.billing_state'] = &$data['$vendor.state']; | ||||||
|  |         $data['$vendor.billing_postal_code'] = &$data['$vendor.postal_code']; | ||||||
|  |         $data['$vendor.billing_country'] = &$data['$vendor.country']; | ||||||
| 
 | 
 | ||||||
|         $arrKeysLength = array_map('strlen', array_keys($data)); |         $arrKeysLength = array_map('strlen', array_keys($data)); | ||||||
|         array_multisort($arrKeysLength, SORT_DESC, $data); |         array_multisort($arrKeysLength, SORT_DESC, $data); | ||||||
|  | |||||||
| @ -55,7 +55,7 @@ trait SettingsSaver | |||||||
|             elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || ($key == 'payment_terms' && strlen($settings->{$key}) >= 1) || ($key == 'valid_until' && strlen($settings->{$key}) >= 1)) {     |             elseif (substr($key, -3) == '_id' || substr($key, -14) == 'number_counter' || ($key == 'payment_terms' && strlen($settings->{$key}) >= 1) || ($key == 'valid_until' && strlen($settings->{$key}) >= 1)) {     | ||||||
|                 $value = 'integer'; |                 $value = 'integer'; | ||||||
| 
 | 
 | ||||||
|                 if($key == 'gmail_sending_user_id') |                 if($key == 'gmail_sending_user_id' || $key == 'besr_id') | ||||||
|                     $value = 'string'; |                     $value = 'string'; | ||||||
| 
 | 
 | ||||||
|                 if (! property_exists($settings, $key)) { |                 if (! property_exists($settings, $key)) { | ||||||
|  | |||||||
| @ -63,6 +63,11 @@ class VendorHtmlEngine | |||||||
|          |          | ||||||
|         $this->vendor = $this->contact->vendor->load('company','country'); |         $this->vendor = $this->contact->vendor->load('company','country'); | ||||||
|          |          | ||||||
|  |         if(!$this->vendor->currency_id){ | ||||||
|  |             $this->vendor->currency_id = $this->company->settings->currency_id; | ||||||
|  |             $this->vendor->save(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         $this->entity->load('vendor'); |         $this->entity->load('vendor'); | ||||||
| 
 | 
 | ||||||
|         $this->settings = $this->company->settings; |         $this->settings = $this->company->settings; | ||||||
|  | |||||||
| @ -85,6 +85,7 @@ | |||||||
|         "setasign/fpdi": "^2.3", |         "setasign/fpdi": "^2.3", | ||||||
|         "socialiteproviders/apple": "^5.2", |         "socialiteproviders/apple": "^5.2", | ||||||
|         "socialiteproviders/microsoft": "^4.1", |         "socialiteproviders/microsoft": "^4.1", | ||||||
|  |         "sprain/swiss-qr-bill": "^3.2", | ||||||
|         "square/square": "13.0.0.20210721", |         "square/square": "13.0.0.20210721", | ||||||
|         "stripe/stripe-php": "^7.50", |         "stripe/stripe-php": "^7.50", | ||||||
|         "symfony/http-client": "^5.2", |         "symfony/http-client": "^5.2", | ||||||
|  | |||||||
							
								
								
									
										1167
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1167
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -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.4.4', |     'app_version' => '5.4.7', | ||||||
|     'app_tag' => '5.4.4', |     'app_tag' => '5.4.7', | ||||||
|     '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', ''), | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								database/factories/PurchaseOrderFactory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								database/factories/PurchaseOrderFactory.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | <?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 Database\Factories; | ||||||
|  | 
 | ||||||
|  | use App\Factory\InvoiceItemFactory; | ||||||
|  | use App\Models\Invoice; | ||||||
|  | use App\Models\PurchaseOrder; | ||||||
|  | use Illuminate\Database\Eloquent\Factories\Factory; | ||||||
|  | 
 | ||||||
|  | class PurchaseOrderFactory extends Factory | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * The name of the factory's corresponding model. | ||||||
|  |      * | ||||||
|  |      * @var string | ||||||
|  |      */ | ||||||
|  |     protected $model = PurchaseOrder::class; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Define the model's default state. | ||||||
|  |      * | ||||||
|  |      * @return array | ||||||
|  |      */ | ||||||
|  |     public function definition() | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             'status_id' => Invoice::STATUS_SENT, | ||||||
|  |             'number' => $this->faker->ean13(), | ||||||
|  |             'discount' => $this->faker->numberBetween(1, 10), | ||||||
|  |             'is_amount_discount' => (bool) random_int(0, 1), | ||||||
|  |             'tax_name1' => 'GST', | ||||||
|  |             'tax_rate1' => 10, | ||||||
|  |             'tax_name2' => 'VAT', | ||||||
|  |             'tax_rate2' => 17.5, | ||||||
|  |             'is_deleted' => false, | ||||||
|  |             'po_number' => $this->faker->text(10), | ||||||
|  |             'date' => $this->faker->date(), | ||||||
|  |             'due_date' => $this->faker->date(), | ||||||
|  |             'line_items' => InvoiceItemFactory::generate(5), | ||||||
|  |             'terms' => $this->faker->text(500), | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,50 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use App\Models\Gateway; | ||||||
|  | use App\Models\GatewayType; | ||||||
|  | use Illuminate\Support\Str; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | 
 | ||||||
|  | class FortePaymentGateway extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function up() | ||||||
|  |     { | ||||||
|  |         $fields = new \stdClass; | ||||||
|  |         $fields->testMode = false; | ||||||
|  |         $fields->apiLoginId = ""; | ||||||
|  |         $fields->apiAccessId = ""; | ||||||
|  |         $fields->secureKey = ""; | ||||||
|  |         $fields->authOrganizationId = ""; | ||||||
|  |         $fields->organizationId = ""; | ||||||
|  |         $fields->locationId = ""; | ||||||
|  | 
 | ||||||
|  |         $forte = new Gateway; | ||||||
|  |         $forte->id = 59; | ||||||
|  |         $forte->name = 'Forte';  | ||||||
|  |         $forte->key = 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs';  | ||||||
|  |         $forte->provider = 'Forte'; | ||||||
|  |         $forte->is_offsite = true; | ||||||
|  |         $forte->fields = \json_encode($fields); | ||||||
|  |         $forte->visible = 1; | ||||||
|  |         $forte->site_url = 'https://www.forte.net/'; | ||||||
|  |         $forte->default_gateway_type_id = GatewayType::CREDIT_CARD; | ||||||
|  |         $forte->save(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function down() | ||||||
|  |     { | ||||||
|  |         //
 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -24,7 +24,7 @@ class AddJobRelatedFieldsToSchedulersTable extends Migration | |||||||
|         Schema::table('schedulers', function (Blueprint $table) { |         Schema::table('schedulers', function (Blueprint $table) { | ||||||
|             $table->string('action_name')->index(); |             $table->string('action_name')->index(); | ||||||
|             $table->string('action_class'); |             $table->string('action_class'); | ||||||
|             $table->json('parameters')->nullable(); |             $table->mediumText('parameters')->nullable(); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -0,0 +1,28 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | class FixesForDescriptionInPdfDesigns extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function up() | ||||||
|  |     { | ||||||
|  |         \Illuminate\Support\Facades\Artisan::call('ninja:design-update'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function down() | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | class SetOauthExpiryColumn extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function up() | ||||||
|  |     { | ||||||
|  |         Schema::table('users', function (Blueprint $table) { | ||||||
|  |             $table->datetime('oauth_user_token_expiry')->nullable(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function down() | ||||||
|  |     { | ||||||
|  |         //
 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,31 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Migrations\Migration; | ||||||
|  | use Illuminate\Database\Schema\Blueprint; | ||||||
|  | use Illuminate\Support\Facades\Schema; | ||||||
|  | 
 | ||||||
|  | class AddFlagToAccountsTable extends Migration | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Run the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function up() | ||||||
|  |     { | ||||||
|  |         Schema::table('accounts', function (Blueprint $table) { | ||||||
|  |             $table->boolean('is_flagged')->default(0); | ||||||
|  |             $table->boolean('is_verified_account')->default(0); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Reverse the migrations. | ||||||
|  |      * | ||||||
|  |      * @return void | ||||||
|  |      */ | ||||||
|  |     public function down() | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -98,7 +98,7 @@ class PaymentLibrariesSeeder extends Seeder | |||||||
| 
 | 
 | ||||||
|         Gateway::query()->update(['visible' => 0]); |         Gateway::query()->update(['visible' => 0]); | ||||||
| 
 | 
 | ||||||
|         Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58])->update(['visible' => 1]); |         Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59])->update(['visible' => 1]); | ||||||
| 
 | 
 | ||||||
|         if (Ninja::isHosted()) { |         if (Ninja::isHosted()) { | ||||||
|             Gateway::whereIn('id', [20])->update(['visible' => 0]); |             Gateway::whereIn('id', [20])->update(['visible' => 0]); | ||||||
|  | |||||||
| @ -2157,6 +2157,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||||
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||||||
| attributed_text | attributed_text | ||||||
| super_editor | super_editor | ||||||
|  | super_text_layout | ||||||
| 
 | 
 | ||||||
| Copyright (c) 2021 Superlist, SuperDeclarative! and the contributors | Copyright (c) 2021 Superlist, SuperDeclarative! and the contributors | ||||||
| 
 | 
 | ||||||
| @ -5969,7 +5970,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||||||
| cross_file | cross_file | ||||||
| flutter_lints |  | ||||||
| flutter_plugin_android_lifecycle | flutter_plugin_android_lifecycle | ||||||
| google_sign_in | google_sign_in | ||||||
| google_sign_in_android | google_sign_in_android | ||||||
| @ -15096,37 +15096,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
| SOFTWARE. | SOFTWARE. | ||||||
| 
 | 
 | ||||||
| -------------------------------------------------------------------------------- |  | ||||||
| lints |  | ||||||
| 
 |  | ||||||
| Copyright 2021, the Dart project authors. |  | ||||||
| 
 |  | ||||||
| Redistribution and use in source and binary forms, with or without |  | ||||||
| modification, are permitted provided that the following conditions are |  | ||||||
| met: |  | ||||||
| 
 |  | ||||||
|     * Redistributions of source code must retain the above copyright |  | ||||||
|       notice, this list of conditions and the following disclaimer. |  | ||||||
|     * Redistributions in binary form must reproduce the above |  | ||||||
|       copyright notice, this list of conditions and the following |  | ||||||
|       disclaimer in the documentation and/or other materials provided |  | ||||||
|       with the distribution. |  | ||||||
|     * Neither the name of Google LLC nor the names of its |  | ||||||
|       contributors may be used to endorse or promote products derived |  | ||||||
|       from this software without specific prior written permission. |  | ||||||
| 
 |  | ||||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |  | ||||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |  | ||||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |  | ||||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |  | ||||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |  | ||||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |  | ||||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |  | ||||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |  | ||||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | ||||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |  | ||||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | ||||||
| 
 |  | ||||||
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||||||
| markdown | markdown | ||||||
| 
 | 
 | ||||||
| @ -15239,6 +15208,31 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
| 
 | 
 | ||||||
|  | -------------------------------------------------------------------------------- | ||||||
|  | msal_js | ||||||
|  | 
 | ||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2021 Ethan Lafrenais | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
|  | 
 | ||||||
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||||||
| msix | msix | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							| @ -3,43 +3,43 @@ const MANIFEST = 'flutter-app-manifest'; | |||||||
| const TEMP = 'flutter-temp-cache'; | const TEMP = 'flutter-temp-cache'; | ||||||
| const CACHE_NAME = 'flutter-app-cache'; | const CACHE_NAME = 'flutter-app-cache'; | ||||||
| const RESOURCES = { | const RESOURCES = { | ||||||
|   "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", |   "favicon.png": "dca91c54388f52eded692718d5a98b8b", | ||||||
| "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", |  | ||||||
| "/": "2e739a78eec983322924f724ebfa09ba", |  | ||||||
| "main.dart.js": "fa4a0263712be1ce1df7d59ca0ede10e", |  | ||||||
| "version.json": "d72bd323e3b8e22ce5acdc247f4e6f62", |  | ||||||
| "favicon.png": "dca91c54388f52eded692718d5a98b8b", |  | ||||||
| "flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67", |  | ||||||
| "favicon.ico": "51636d3a390451561744c42188ccd628", |  | ||||||
| "canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba", |  | ||||||
| "canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd", |  | ||||||
| "canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900", |  | ||||||
| "canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487", | "canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487", | ||||||
|  | "canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900", | ||||||
|  | "canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd", | ||||||
|  | "canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba", | ||||||
|  | "/": "0ba127947d7857ad97e39c264e308461", | ||||||
|  | "flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67", | ||||||
|  | "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", | ||||||
|  | "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", | ||||||
|  | "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", | ||||||
|  | "version.json": "d72bd323e3b8e22ce5acdc247f4e6f62", | ||||||
|  | "favicon.ico": "51636d3a390451561744c42188ccd628", | ||||||
| "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", | "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", | ||||||
| "assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5", |  | ||||||
| "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56", |  | ||||||
| "assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b", | "assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b", | ||||||
| "assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219", | "assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5", | ||||||
| "assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5", |  | ||||||
| "assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629", |  | ||||||
| "assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629", |  | ||||||
| "assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868", |  | ||||||
| "assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf", |  | ||||||
| "assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024", |  | ||||||
| "assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2", |  | ||||||
| "assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08", |  | ||||||
| "assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1", |  | ||||||
| "assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3", |  | ||||||
| "assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6", |  | ||||||
| "assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978", |  | ||||||
| "assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc", |  | ||||||
| "assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c", |  | ||||||
| "assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71", |  | ||||||
| "assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541", | "assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541", | ||||||
| "assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3", | "assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3", | ||||||
|  | "assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219", | ||||||
| "assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a", | "assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a", | ||||||
| "assets/NOTICES": "52d7174bb068ef86545951d5bc8c5744", | "assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c", | ||||||
| "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40" | "assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978", | ||||||
|  | "assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3", | ||||||
|  | "assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71", | ||||||
|  | "assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08", | ||||||
|  | "assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf", | ||||||
|  | "assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5", | ||||||
|  | "assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6", | ||||||
|  | "assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868", | ||||||
|  | "assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1", | ||||||
|  | "assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629", | ||||||
|  | "assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc", | ||||||
|  | "assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2", | ||||||
|  | "assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024", | ||||||
|  | "assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629", | ||||||
|  | "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56", | ||||||
|  | "assets/NOTICES": "9b6b63256d3a6491659b71127ee9f3b6", | ||||||
|  | "main.dart.js": "739fb98bd5601a93da8be6b4358508d2" | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // The application shell files that are downloaded before a service worker can
 | // The application shell files that are downloaded before a service worker can
 | ||||||
|  | |||||||
							
								
								
									
										81
									
								
								public/js/clients/payments/forte-ach-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								public/js/clients/payments/forte-ach-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | |||||||
|  | /******/ (() => { // webpackBootstrap
 | ||||||
|  | var __webpack_exports__ = {}; | ||||||
|  | /*!************************************************************!*\ | ||||||
|  |   !*** ./resources/js/clients/payments/forte-ach-payment.js ***! | ||||||
|  |   \************************************************************/ | ||||||
|  | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||||||
|  | 
 | ||||||
|  | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 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
 | ||||||
|  |  */ | ||||||
|  | var ForteAuthorizeACH = function ForteAuthorizeACH(apiLoginId) { | ||||||
|  |   var _this = this; | ||||||
|  | 
 | ||||||
|  |   _classCallCheck(this, ForteAuthorizeACH); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "handleAuthorization", function () { | ||||||
|  |     var account_number = document.getElementById('account-number').value; | ||||||
|  |     var routing_number = document.getElementById('routing-number').value; | ||||||
|  |     var data = { | ||||||
|  |       api_login_id: _this.apiLoginId, | ||||||
|  |       account_number: account_number, | ||||||
|  |       routing_number: routing_number, | ||||||
|  |       account_type: 'checking' | ||||||
|  |     }; | ||||||
|  |     var payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |     if (payNowButton) { | ||||||
|  |       document.getElementById('pay-now').disabled = true; | ||||||
|  |       document.querySelector('#pay-now > svg').classList.remove('hidden'); | ||||||
|  |       document.querySelector('#pay-now > span').classList.add('hidden'); | ||||||
|  |     } // console.log(data);
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     forte.createToken(data).success(_this.successResponseHandler).error(_this.failedResponseHandler); | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "successResponseHandler", function (response) { | ||||||
|  |     document.getElementById('payment_token').value = response.onetime_token; | ||||||
|  |     document.getElementById('server_response').submit(); | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "failedResponseHandler", function (response) { | ||||||
|  |     var errors = '<div class="alert alert-failure mb-4"><ul><li>' + response.response_description + '</li></ul></div>'; | ||||||
|  |     document.getElementById('forte_errors').innerHTML = errors; | ||||||
|  |     document.getElementById('pay-now').disabled = false; | ||||||
|  |     document.querySelector('#pay-now > svg').classList.add('hidden'); | ||||||
|  |     document.querySelector('#pay-now > span').classList.remove('hidden'); | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "handle", function () { | ||||||
|  |     var payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |     if (payNowButton) { | ||||||
|  |       payNowButton.addEventListener('click', function (e) { | ||||||
|  |         _this.handleAuthorization(); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return _this; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   this.apiLoginId = apiLoginId; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | var apiLoginId = document.querySelector('meta[name="forte-api-login-id"]').content; | ||||||
|  | /** @handle */ | ||||||
|  | 
 | ||||||
|  | new ForteAuthorizeACH(apiLoginId).handle(); | ||||||
|  | /******/ })() | ||||||
|  | ; | ||||||
							
								
								
									
										669
									
								
								public/js/clients/payments/forte-card-js.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										669
									
								
								public/js/clients/payments/forte-card-js.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,669 @@ | |||||||
|  | (() => { | ||||||
|  |     function t(t) { | ||||||
|  |         (this.elem = jQuery(t)), | ||||||
|  |             (this.captureName = | ||||||
|  |                 !!this.elem.data('capture-name') && | ||||||
|  |                 this.elem.data('capture-name')), | ||||||
|  |             (this.iconColour = | ||||||
|  |                 !!this.elem.data('icon-colour') && | ||||||
|  |                 this.elem.data('icon-colour')), | ||||||
|  |             (this.stripe = | ||||||
|  |                 !!this.elem.data('stripe') && this.elem.data('stripe')), | ||||||
|  |             this.stripe && (this.captureName = !1), | ||||||
|  |             this.initCardNumberInput(), | ||||||
|  |             this.initNameInput(), | ||||||
|  |             this.initExpiryMonthInput(), | ||||||
|  |             this.initExpiryYearInput(), | ||||||
|  |             this.initCvcInput(), | ||||||
|  |             this.elem.empty(), | ||||||
|  |             this.setupCardNumberInput(), | ||||||
|  |             this.setupNameInput(), | ||||||
|  |             this.setupExpiryInput(), | ||||||
|  |             this.setupCvcInput(), | ||||||
|  |             this.iconColour && this.setIconColour(this.iconColour), | ||||||
|  |             this.refreshCreditCardTypeIcon(); | ||||||
|  |     } | ||||||
|  |     !(function (e) { | ||||||
|  |         var r = { | ||||||
|  |             init: function () { | ||||||
|  |                 return this.data('cardjs', new t(this)), this; | ||||||
|  |             }, | ||||||
|  |             cardNumber: function () { | ||||||
|  |                 return this.data('cardjs').getCardNumber(); | ||||||
|  |             }, | ||||||
|  |             cardType: function () { | ||||||
|  |                 return this.data('cardjs').getCardType(); | ||||||
|  |             }, | ||||||
|  |             name: function () { | ||||||
|  |                 return this.data('cardjs').getName(); | ||||||
|  |             }, | ||||||
|  |             expiryMonth: function () { | ||||||
|  |                 return this.data('cardjs').getExpiryMonth(); | ||||||
|  |             }, | ||||||
|  |             expiryYear: function () { | ||||||
|  |                 return this.data('cardjs').getExpiryYear(); | ||||||
|  |             }, | ||||||
|  |             cvc: function () { | ||||||
|  |                 return this.data('cardjs').getCvc(); | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  |         e.fn.CardJs = function (t) { | ||||||
|  |             return r[t] | ||||||
|  |                 ? r[t].apply(this, Array.prototype.slice.call(arguments, 1)) | ||||||
|  |                 : 'object' != typeof t && t | ||||||
|  |                 ? void e.error( | ||||||
|  |                       'Method ' + t + ' does not exist on jQuery.CardJs' | ||||||
|  |                   ) | ||||||
|  |                 : r.init.apply(this, arguments); | ||||||
|  |         }; | ||||||
|  |     })(jQuery), | ||||||
|  |         $(function () { | ||||||
|  |             $('.card-js').each(function (t, e) { | ||||||
|  |                 $(e).CardJs(); | ||||||
|  |             }); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.constructor = t), | ||||||
|  |         (t.KEYS = { | ||||||
|  |             0: 48, | ||||||
|  |             9: 57, | ||||||
|  |             NUMPAD_0: 96, | ||||||
|  |             NUMPAD_9: 105, | ||||||
|  |             DELETE: 46, | ||||||
|  |             BACKSPACE: 8, | ||||||
|  |             ARROW_LEFT: 37, | ||||||
|  |             ARROW_RIGHT: 39, | ||||||
|  |             ARROW_UP: 38, | ||||||
|  |             ARROW_DOWN: 40, | ||||||
|  |             HOME: 36, | ||||||
|  |             END: 35, | ||||||
|  |             TAB: 9, | ||||||
|  |             A: 65, | ||||||
|  |             X: 88, | ||||||
|  |             C: 67, | ||||||
|  |             V: 86, | ||||||
|  |         }), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_DEFAULT_MASK = 'XXXX XXXX XXXX XXXX'), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_VISA_MASK = 'XXXX XXXX XXXX XXXX'), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_MASTERCARD_MASK = 'XXXX XXXX XXXX XXXX'), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_DISCOVER_MASK = 'XXXX XXXX XXXX XXXX'), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_JCB_MASK = 'XXXX XXXX XXXX XXXX'), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_AMEX_MASK = 'XXXX XXXXXX XXXXX'), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_DINERS_MASK = 'XXXX XXXX XXXX XX'), | ||||||
|  |         (t.prototype.creditCardNumberMask = t.CREDIT_CARD_NUMBER_DEFAULT_MASK), | ||||||
|  |         (t.CREDIT_CARD_NUMBER_PLACEHOLDER = 'Card number'), | ||||||
|  |         (t.NAME_PLACEHOLDER = 'Name on card'), | ||||||
|  |         (t.EXPIRY_MASK = 'XX / XXXX'), | ||||||
|  |         (t.EXPIRY_PLACEHOLDER = 'MM / YYYY'), | ||||||
|  |         (t.EXPIRY_USE_DROPDOWNS = !1), | ||||||
|  |         (t.EXPIRY_NUMBER_OF_YEARS = 10), | ||||||
|  |         (t.CVC_MASK_3 = 'XXX'), | ||||||
|  |         (t.CVC_MASK_4 = 'XXXX'), | ||||||
|  |         (t.CVC_PLACEHOLDER = 'CVC'), | ||||||
|  |         (t.CREDIT_CARD_SVG = | ||||||
|  |             '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="3px" width="24px" height="17px" viewBox="0 0 216 146" enable-background="new 0 0 216 146" xml:space="preserve"><g><path class="svg" d="M182.385,14.258c-2.553-2.553-5.621-3.829-9.205-3.829H42.821c-3.585,0-6.653,1.276-9.207,3.829c-2.553,2.553-3.829,5.621-3.829,9.206v99.071c0,3.585,1.276,6.654,3.829,9.207c2.554,2.553,5.622,3.829,9.207,3.829H173.18c3.584,0,6.652-1.276,9.205-3.829s3.83-5.622,3.83-9.207V23.464C186.215,19.879,184.938,16.811,182.385,14.258z M175.785,122.536c0,0.707-0.258,1.317-0.773,1.834c-0.516,0.515-1.127,0.772-1.832,0.772H42.821c-0.706,0-1.317-0.258-1.833-0.773c-0.516-0.518-0.774-1.127-0.774-1.834V73h135.571V122.536z M175.785,41.713H40.214v-18.25c0-0.706,0.257-1.316,0.774-1.833c0.516-0.515,1.127-0.773,1.833-0.773H173.18c0.705,0,1.316,0.257,1.832,0.773c0.516,0.517,0.773,1.127,0.773,1.833V41.713z"/><rect class="svg" x="50.643" y="104.285" width="20.857" height="10.429"/><rect class="svg" x="81.929" y="104.285" width="31.286" height="10.429"/></g></svg>'), | ||||||
|  |         (t.LOCK_SVG = | ||||||
|  |             '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="3px" width="24px" height="17px" viewBox="0 0 216 146" enable-background="new 0 0 216 146" xml:space="preserve"><path class="svg" d="M152.646,70.067c-1.521-1.521-3.367-2.281-5.541-2.281H144.5V52.142c0-9.994-3.585-18.575-10.754-25.745c-7.17-7.17-15.751-10.755-25.746-10.755s-18.577,3.585-25.746,10.755C75.084,33.567,71.5,42.148,71.5,52.142v15.644h-2.607c-2.172,0-4.019,0.76-5.54,2.281c-1.521,1.52-2.281,3.367-2.281,5.541v46.929c0,2.172,0.76,4.019,2.281,5.54c1.521,1.52,3.368,2.281,5.54,2.281h78.214c2.174,0,4.02-0.76,5.541-2.281c1.52-1.521,2.281-3.368,2.281-5.54V75.607C154.93,73.435,154.168,71.588,152.646,70.067z M128.857,67.786H87.143V52.142c0-5.757,2.037-10.673,6.111-14.746c4.074-4.074,8.989-6.11,14.747-6.11s10.673,2.036,14.746,6.11c4.073,4.073,6.11,8.989,6.11,14.746V67.786z"/></svg>'), | ||||||
|  |         (t.CALENDAR_SVG = | ||||||
|  |             '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="4px" width="24px" height="16px" viewBox="0 0 216 146" enable-background="new 0 0 216 146" xml:space="preserve"><path class="svg" d="M172.691,23.953c-2.062-2.064-4.508-3.096-7.332-3.096h-10.428v-7.822c0-3.584-1.277-6.653-3.83-9.206c-2.554-2.553-5.621-3.83-9.207-3.83h-5.213c-3.586,0-6.654,1.277-9.207,3.83c-2.554,2.553-3.83,5.622-3.83,9.206v7.822H92.359v-7.822c0-3.584-1.277-6.653-3.83-9.206c-2.553-2.553-5.622-3.83-9.207-3.83h-5.214c-3.585,0-6.654,1.277-9.207,3.83c-2.553,2.553-3.83,5.622-3.83,9.206v7.822H50.643c-2.825,0-5.269,1.032-7.333,3.096s-3.096,4.509-3.096,7.333v104.287c0,2.823,1.032,5.267,3.096,7.332c2.064,2.064,4.508,3.096,7.333,3.096h114.714c2.824,0,5.27-1.032,7.332-3.096c2.064-2.064,3.096-4.509,3.096-7.332V31.286C175.785,28.461,174.754,26.017,172.691,23.953z M134.073,13.036c0-0.761,0.243-1.386,0.731-1.874c0.488-0.488,1.113-0.733,1.875-0.733h5.213c0.762,0,1.385,0.244,1.875,0.733c0.488,0.489,0.732,1.114,0.732,1.874V36.5c0,0.761-0.244,1.385-0.732,1.874c-0.49,0.488-1.113,0.733-1.875,0.733h-5.213c-0.762,0-1.387-0.244-1.875-0.733s-0.731-1.113-0.731-1.874V13.036z M71.501,13.036c0-0.761,0.244-1.386,0.733-1.874c0.489-0.488,1.113-0.733,1.874-0.733h5.214c0.761,0,1.386,0.244,1.874,0.733c0.488,0.489,0.733,1.114,0.733,1.874V36.5c0,0.761-0.244,1.386-0.733,1.874c-0.489,0.488-1.113,0.733-1.874,0.733h-5.214c-0.761,0-1.386-0.244-1.874-0.733c-0.488-0.489-0.733-1.113-0.733-1.874V13.036z M165.357,135.572H50.643V52.143h114.714V135.572z"/></svg>'), | ||||||
|  |         (t.USER_SVG = | ||||||
|  |             '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="4px" width="24px" height="16px" viewBox="0 0 216 146" enable-background="new 0 0 216 146" xml:space="preserve"><g><path class="svg" d="M107.999,73c8.638,0,16.011-3.056,22.12-9.166c6.111-6.11,9.166-13.483,9.166-22.12c0-8.636-3.055-16.009-9.166-22.12c-6.11-6.11-13.484-9.165-22.12-9.165c-8.636,0-16.01,3.055-22.12,9.165c-6.111,6.111-9.166,13.484-9.166,22.12c0,8.637,3.055,16.01,9.166,22.12C91.99,69.944,99.363,73,107.999,73z"/><path class="svg" d="M165.07,106.037c-0.191-2.743-0.571-5.703-1.141-8.881c-0.57-3.178-1.291-6.124-2.16-8.84c-0.869-2.715-2.037-5.363-3.504-7.943c-1.466-2.58-3.15-4.78-5.052-6.6s-4.223-3.272-6.965-4.358c-2.744-1.086-5.772-1.63-9.085-1.63c-0.489,0-1.63,0.584-3.422,1.752s-3.815,2.472-6.069,3.911c-2.254,1.438-5.188,2.743-8.799,3.909c-3.612,1.168-7.237,1.752-10.877,1.752c-3.639,0-7.264-0.584-10.876-1.752c-3.611-1.166-6.545-2.471-8.799-3.909c-2.254-1.439-4.277-2.743-6.069-3.911c-1.793-1.168-2.933-1.752-3.422-1.752c-3.313,0-6.341,0.544-9.084,1.63s-5.065,2.539-6.966,4.358c-1.901,1.82-3.585,4.02-5.051,6.6s-2.634,5.229-3.503,7.943c-0.869,2.716-1.589,5.662-2.159,8.84c-0.571,3.178-0.951,6.137-1.141,8.881c-0.19,2.744-0.285,5.554-0.285,8.433c0,6.517,1.983,11.664,5.948,15.439c3.965,3.774,9.234,5.661,15.806,5.661h71.208c6.572,0,11.84-1.887,15.806-5.661c3.966-3.775,5.948-8.921,5.948-15.439C165.357,111.591,165.262,108.78,165.07,106.037z"/></g></svg>'), | ||||||
|  |         (t.MAIL_SVG = | ||||||
|  |             '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"x="0px" y="4px" width="24px" height="16px" viewBox="0 0 216 146" enable-background="new 0 0 216 146" xml:space="preserve"><path class="svg" d="M177.171,19.472c-2.553-2.553-5.622-3.829-9.206-3.829H48.036c-3.585,0-6.654,1.276-9.207,3.829C36.276,22.025,35,25.094,35,28.679v88.644c0,3.585,1.276,6.652,3.829,9.205c2.553,2.555,5.622,3.83,9.207,3.83h119.929c3.584,0,6.653-1.275,9.206-3.83c2.554-2.553,3.829-5.621,3.829-9.205V28.679C181,25.094,179.725,22.025,177.171,19.472zM170.57,117.321c0,0.706-0.258,1.317-0.774,1.833s-1.127,0.773-1.832,0.773H48.035c-0.706,0-1.317-0.257-1.833-0.773c-0.516-0.516-0.774-1.127-0.774-1.833V54.75c1.738,1.955,3.612,3.748,5.622,5.377c14.557,11.189,26.126,20.368,34.708,27.538c2.77,2.336,5.024,4.155,6.762,5.459s4.087,2.62,7.047,3.951s5.744,1.995,8.351,1.995H108h0.081c2.606,0,5.392-0.664,8.351-1.995c2.961-1.331,5.311-2.647,7.049-3.951c1.737-1.304,3.992-3.123,6.762-5.459c8.582-7.17,20.15-16.349,34.707-27.538c2.01-1.629,3.885-3.422,5.621-5.377V117.321z M170.57,30.797v0.896c0,3.204-1.262,6.776-3.787,10.713c-2.525,3.938-5.256,7.075-8.188,9.41c-10.484,8.257-21.373,16.865-32.672,25.827c-0.326,0.271-1.277,1.073-2.852,2.403c-1.574,1.331-2.824,2.351-3.748,3.056c-0.924,0.707-2.131,1.562-3.625,2.566s-2.865,1.752-4.114,2.24s-2.417,0.732-3.503,0.732H108h-0.082c-1.086,0-2.253-0.244-3.503-0.732c-1.249-0.488-2.621-1.236-4.114-2.24c-1.493-1.004-2.702-1.859-3.625-2.566c-0.923-0.705-2.173-1.725-3.748-3.056c-1.575-1.33-2.526-2.132-2.852-2.403c-11.297-8.962-22.187-17.57-32.67-25.827c-7.985-6.3-11.977-14.013-11.977-23.138c0-0.706,0.258-1.317,0.774-1.833c0.516-0.516,1.127-0.774,1.833-0.774h119.929c0.434,0.244,0.814,0.312,1.141,0.204c0.326-0.11,0.57,0.094,0.732,0.61c0.163,0.516,0.312,0.76,0.448,0.733c0.136-0.027,0.218,0.312,0.245,1.019c0.025,0.706,0.039,1.061,0.039,1.061V30.797z"/></svg>'), | ||||||
|  |         (t.INFORMATION_SVG = | ||||||
|  |             '<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="4px" width="24px" height="16px" viewBox="0 0 216 146" enable-background="new 0 0 216 146" xml:space="preserve"><g><path class="svg" d="M97.571,41.714h20.859c1.411,0,2.633-0.516,3.666-1.548c1.031-1.031,1.547-2.254,1.547-3.666V20.857c0-1.412-0.516-2.634-1.549-3.667c-1.031-1.031-2.254-1.548-3.666-1.548H97.571c-1.412,0-2.634,0.517-3.666,1.548c-1.032,1.032-1.548,2.255-1.548,3.667V36.5c0,1.412,0.516,2.635,1.548,3.666C94.937,41.198,96.159,41.714,97.571,41.714z"/><path class="svg" d="M132.523,111.048c-1.031-1.032-2.254-1.548-3.666-1.548h-5.215V62.571c0-1.412-0.516-2.634-1.547-3.666c-1.033-1.032-2.255-1.548-3.666-1.548H87.143c-1.412,0-2.634,0.516-3.666,1.548c-1.032,1.032-1.548,2.254-1.548,3.666V73c0,1.412,0.516,2.635,1.548,3.666c1.032,1.032,2.254,1.548,3.666,1.548h5.215V109.5h-5.215c-1.412,0-2.634,0.516-3.666,1.548c-1.032,1.032-1.548,2.254-1.548,3.666v10.429c0,1.412,0.516,2.635,1.548,3.668c1.032,1.03,2.254,1.547,3.666,1.547h41.714c1.412,0,2.634-0.517,3.666-1.547c1.031-1.033,1.547-2.256,1.547-3.668v-10.429C134.07,113.302,133.557,112.08,132.523,111.048z"/></g></svg>'), | ||||||
|  |         (t.keyCodeFromEvent = function (t) { | ||||||
|  |             return t.which || t.keyCode; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsCommandFromEvent = function (t) { | ||||||
|  |             return t.ctrlKey || t.metaKey; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsNumber = function (e) { | ||||||
|  |             return t.keyIsTopNumber(e) || t.keyIsKeypadNumber(e); | ||||||
|  |         }), | ||||||
|  |         (t.keyIsTopNumber = function (e) { | ||||||
|  |             var r = t.keyCodeFromEvent(e); | ||||||
|  |             return r >= t.KEYS[0] && r <= t.KEYS[9]; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsKeypadNumber = function (e) { | ||||||
|  |             var r = t.keyCodeFromEvent(e); | ||||||
|  |             return r >= t.KEYS.NUMPAD_0 && r <= t.KEYS.NUMPAD_9; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsDelete = function (e) { | ||||||
|  |             return t.keyCodeFromEvent(e) == t.KEYS.DELETE; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsBackspace = function (e) { | ||||||
|  |             return t.keyCodeFromEvent(e) == t.KEYS.BACKSPACE; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsDeletion = function (e) { | ||||||
|  |             return t.keyIsDelete(e) || t.keyIsBackspace(e); | ||||||
|  |         }), | ||||||
|  |         (t.keyIsArrow = function (e) { | ||||||
|  |             var r = t.keyCodeFromEvent(e); | ||||||
|  |             return r >= t.KEYS.ARROW_LEFT && r <= t.KEYS.ARROW_DOWN; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsNavigation = function (e) { | ||||||
|  |             var r = t.keyCodeFromEvent(e); | ||||||
|  |             return r == t.KEYS.HOME || r == t.KEYS.END; | ||||||
|  |         }), | ||||||
|  |         (t.keyIsKeyboardCommand = function (e) { | ||||||
|  |             var r = t.keyCodeFromEvent(e); | ||||||
|  |             return ( | ||||||
|  |                 t.keyIsCommandFromEvent(e) && | ||||||
|  |                 (r == t.KEYS.A || | ||||||
|  |                     r == t.KEYS.X || | ||||||
|  |                     r == t.KEYS.C || | ||||||
|  |                     r == t.KEYS.V) | ||||||
|  |             ); | ||||||
|  |         }), | ||||||
|  |         (t.keyIsTab = function (e) { | ||||||
|  |             return t.keyCodeFromEvent(e) == t.KEYS.TAB; | ||||||
|  |         }), | ||||||
|  |         (t.copyAllElementAttributes = function (t, e) { | ||||||
|  |             $.each(t[0].attributes, function (t, r) { | ||||||
|  |                 e.attr(r.nodeName, r.nodeValue); | ||||||
|  |             }); | ||||||
|  |         }), | ||||||
|  |         (t.numbersOnlyString = function (t) { | ||||||
|  |             for (var e = '', r = 0; r < t.length; r++) { | ||||||
|  |                 var n = t.charAt(r); | ||||||
|  |                 !isNaN(parseInt(n)) && (e += n); | ||||||
|  |             } | ||||||
|  |             return e; | ||||||
|  |         }), | ||||||
|  |         (t.applyFormatMask = function (t, e) { | ||||||
|  |             for (var r = '', n = 0, i = 0; i < e.length; i++) { | ||||||
|  |                 var a = e[i]; | ||||||
|  |                 if ('X' == a) { | ||||||
|  |                     if (!t.charAt(n)) break; | ||||||
|  |                     (r += t.charAt(n)), n++; | ||||||
|  |                 } else r += a; | ||||||
|  |             } | ||||||
|  |             return r; | ||||||
|  |         }), | ||||||
|  |         (t.cardTypeFromNumber = function (t) { | ||||||
|  |             if (((e = new RegExp('^30[0-5]')), null != t.match(e))) | ||||||
|  |                 return 'Diners - Carte Blanche'; | ||||||
|  |             if (((e = new RegExp('^(30[6-9]|36|38)')), null != t.match(e))) | ||||||
|  |                 return 'Diners'; | ||||||
|  |             if (((e = new RegExp('^35(2[89]|[3-8][0-9])')), null != t.match(e))) | ||||||
|  |                 return 'JCB'; | ||||||
|  |             if (((e = new RegExp('^3[47]')), null != t.match(e))) return 'AMEX'; | ||||||
|  |             if ( | ||||||
|  |                 ((e = new RegExp('^(4026|417500|4508|4844|491(3|7))')), | ||||||
|  |                 null != t.match(e)) | ||||||
|  |             ) | ||||||
|  |                 return 'Visa Electron'; | ||||||
|  |             var e = new RegExp('^4'); | ||||||
|  |             return null != t.match(e) | ||||||
|  |                 ? 'Visa' | ||||||
|  |                 : ((e = new RegExp('^5[1-5]')), | ||||||
|  |                   null != t.match(e) | ||||||
|  |                       ? 'Mastercard' | ||||||
|  |                       : ((e = new RegExp( | ||||||
|  |                             '^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)' | ||||||
|  |                         )), | ||||||
|  |                         null != t.match(e) ? 'Discover' : '')); | ||||||
|  |         }), | ||||||
|  |         (t.caretStartPosition = function (t) { | ||||||
|  |             return 'number' == typeof t.selectionStart && t.selectionStart; | ||||||
|  |         }), | ||||||
|  |         (t.caretEndPosition = function (t) { | ||||||
|  |             return 'number' == typeof t.selectionEnd && t.selectionEnd; | ||||||
|  |         }), | ||||||
|  |         (t.setCaretPosition = function (t, e) { | ||||||
|  |             if (null != t) | ||||||
|  |                 if (t.createTextRange) { | ||||||
|  |                     var r = t.createTextRange(); | ||||||
|  |                     r.move('character', e), r.select(); | ||||||
|  |                 } else | ||||||
|  |                     t.selectionStart | ||||||
|  |                         ? (t.focus(), t.setSelectionRange(e, e)) | ||||||
|  |                         : t.focus(); | ||||||
|  |         }), | ||||||
|  |         (t.normaliseCaretPosition = function (t, e) { | ||||||
|  |             var r = 0; | ||||||
|  |             if (0 > e || e > t.length) return 0; | ||||||
|  |             for (var n = 0; n < t.length; n++) { | ||||||
|  |                 if (n == e) return r; | ||||||
|  |                 'X' == t[n] && r++; | ||||||
|  |             } | ||||||
|  |             return r; | ||||||
|  |         }), | ||||||
|  |         (t.denormaliseCaretPosition = function (t, e) { | ||||||
|  |             var r = 0; | ||||||
|  |             if (0 > e || e > t.length) return 0; | ||||||
|  |             for (var n = 0; n < t.length; n++) { | ||||||
|  |                 if (r == e) return n; | ||||||
|  |                 'X' == t[n] && r++; | ||||||
|  |             } | ||||||
|  |             return t.length; | ||||||
|  |         }), | ||||||
|  |         (t.filterNumberOnlyKey = function (e) { | ||||||
|  |             var r = t.keyIsNumber(e), | ||||||
|  |                 n = t.keyIsDeletion(e), | ||||||
|  |                 i = t.keyIsArrow(e), | ||||||
|  |                 a = t.keyIsNavigation(e), | ||||||
|  |                 s = t.keyIsKeyboardCommand(e), | ||||||
|  |                 p = t.keyIsTab(e); | ||||||
|  |             r || n || i || a || s || p || e.preventDefault(); | ||||||
|  |         }), | ||||||
|  |         (t.digitFromKeyCode = function (e) { | ||||||
|  |             return e >= t.KEYS[0] && e <= t.KEYS[9] | ||||||
|  |                 ? e - t.KEYS[0] | ||||||
|  |                 : e >= t.KEYS.NUMPAD_0 && e <= t.KEYS.NUMPAD_9 | ||||||
|  |                 ? e - t.KEYS.NUMPAD_0 | ||||||
|  |                 : null; | ||||||
|  |         }), | ||||||
|  |         (t.handleMaskedNumberInputKey = function (e, r) { | ||||||
|  |             t.filterNumberOnlyKey(e); | ||||||
|  |             var n = e.which || e.keyCode, | ||||||
|  |                 i = e.target, | ||||||
|  |                 a = t.caretStartPosition(i), | ||||||
|  |                 s = t.caretEndPosition(i), | ||||||
|  |                 p = t.normaliseCaretPosition(r, a), | ||||||
|  |                 c = t.normaliseCaretPosition(r, s), | ||||||
|  |                 o = a, | ||||||
|  |                 u = t.keyIsNumber(e), | ||||||
|  |                 h = t.keyIsDelete(e), | ||||||
|  |                 d = t.keyIsBackspace(e); | ||||||
|  |             if (u || h || d) { | ||||||
|  |                 e.preventDefault(); | ||||||
|  |                 var l = $(i).val(), | ||||||
|  |                     y = t.numbersOnlyString(l), | ||||||
|  |                     m = t.digitFromKeyCode(n), | ||||||
|  |                     C = c > p; | ||||||
|  |                 C && (y = y.slice(0, p) + y.slice(c)), | ||||||
|  |                     a != r.length && | ||||||
|  |                         (u && | ||||||
|  |                             l.length <= r.length && | ||||||
|  |                             ((y = y.slice(0, p) + m + y.slice(p)), | ||||||
|  |                             (o = Math.max( | ||||||
|  |                                 t.denormaliseCaretPosition(r, p + 1), | ||||||
|  |                                 t.denormaliseCaretPosition(r, p + 2) - 1 | ||||||
|  |                             ))), | ||||||
|  |                         h && (y = y.slice(0, p) + y.slice(p + 1))), | ||||||
|  |                     0 != a && | ||||||
|  |                         d && | ||||||
|  |                         !C && | ||||||
|  |                         ((y = y.slice(0, p - 1) + y.slice(p)), | ||||||
|  |                         (o = t.denormaliseCaretPosition(r, p - 1))), | ||||||
|  |                     $(i).val(t.applyFormatMask(y, r)), | ||||||
|  |                     t.setCaretPosition(i, o); | ||||||
|  |             } | ||||||
|  |         }), | ||||||
|  |         (t.handleCreditCardNumberKey = function (e, r) { | ||||||
|  |             t.handleMaskedNumberInputKey(e, r); | ||||||
|  |         }), | ||||||
|  |         (t.handleCreditCardNumberChange = function (t) {}), | ||||||
|  |         (t.handleExpiryKey = function (e) { | ||||||
|  |             t.handleMaskedNumberInputKey(e, t.EXPIRY_MASK); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.getCardNumber = function () { | ||||||
|  |             return this.cardNumberInput.val(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.getCardType = function () { | ||||||
|  |             return t.cardTypeFromNumber(this.getCardNumber()); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.getName = function () { | ||||||
|  |             return this.nameInput.val(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.getExpiryMonth = function () { | ||||||
|  |             return this.expiryMonthInput.val(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.getExpiryYear = function () { | ||||||
|  |             return this.expiryYearInput.val(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.getCvc = function () { | ||||||
|  |             return this.cvcInput.val(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setIconColour = function (t) { | ||||||
|  |             this.elem.find('.icon .svg').css({ fill: t }); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setIconColour = function (t) { | ||||||
|  |             this.elem.find('.icon .svg').css({ fill: t }); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.refreshCreditCardTypeIcon = function () { | ||||||
|  |             this.setCardTypeIconFromNumber( | ||||||
|  |                 t.numbersOnlyString(this.cardNumberInput.val()) | ||||||
|  |             ); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.clearCardTypeIcon = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .removeClass('show'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconAsVisa = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .attr('class', 'card-type-icon show visa'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconAsMasterCard = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .attr('class', 'card-type-icon show master-card'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconAsAmericanExpress = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .attr('class', 'card-type-icon show american-express'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconAsDiscover = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .attr('class', 'card-type-icon show discover'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconAsDiners = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .attr('class', 'card-type-icon show diners'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconAsJcb = function () { | ||||||
|  |             this.elem | ||||||
|  |                 .find('.card-number-wrapper .card-type-icon') | ||||||
|  |                 .attr('class', 'card-type-icon show jcb'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeIconFromNumber = function (e) { | ||||||
|  |             switch (t.cardTypeFromNumber(e)) { | ||||||
|  |                 case 'Visa Electron': | ||||||
|  |                 case 'Visa': | ||||||
|  |                     this.setCardTypeAsVisa(); | ||||||
|  |                     break; | ||||||
|  |                 case 'Mastercard': | ||||||
|  |                     this.setCardTypeAsMasterCard(); | ||||||
|  |                     break; | ||||||
|  |                 case 'AMEX': | ||||||
|  |                     this.setCardTypeAsAmericanExpress(); | ||||||
|  |                     break; | ||||||
|  |                 case 'Discover': | ||||||
|  |                     this.setCardTypeAsDiscover(); | ||||||
|  |                     break; | ||||||
|  |                 case 'Diners - Carte Blanche': | ||||||
|  |                 case 'Diners': | ||||||
|  |                     this.setCardTypeAsDiners(); | ||||||
|  |                     break; | ||||||
|  |                 case 'JCB': | ||||||
|  |                     this.setCardTypeAsJcb(); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     this.clearCardType(); | ||||||
|  |             } | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardMask = function (t) { | ||||||
|  |             (this.creditCardNumberMask = t), | ||||||
|  |                 this.cardNumberInput.attr('maxlength', t.length); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCvc3 = function () { | ||||||
|  |             this.cvcInput.attr('maxlength', t.CVC_MASK_3.length); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCvc4 = function () { | ||||||
|  |             this.cvcInput.attr('maxlength', t.CVC_MASK_4.length); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.clearCardType = function () { | ||||||
|  |             this.clearCardTypeIcon(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_DEFAULT_MASK), | ||||||
|  |                 this.setCvc3(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeAsVisa = function () { | ||||||
|  |             this.setCardTypeIconAsVisa(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_VISA_MASK), | ||||||
|  |                 this.setCvc3(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeAsMasterCard = function () { | ||||||
|  |             this.setCardTypeIconAsMasterCard(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_MASTERCARD_MASK), | ||||||
|  |                 this.setCvc3(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeAsAmericanExpress = function () { | ||||||
|  |             this.setCardTypeIconAsAmericanExpress(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_AMEX_MASK), | ||||||
|  |                 this.setCvc4(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeAsDiscover = function () { | ||||||
|  |             this.setCardTypeIconAsDiscover(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_DISCOVER_MASK), | ||||||
|  |                 this.setCvc3(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeAsDiners = function () { | ||||||
|  |             this.setCardTypeIconAsDiners(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_DINERS_MASK), | ||||||
|  |                 this.setCvc3(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setCardTypeAsJcb = function () { | ||||||
|  |             this.setCardTypeIconAsJcb(), | ||||||
|  |                 this.setCardMask(t.CREDIT_CARD_NUMBER_JCB_MASK), | ||||||
|  |                 this.setCvc3(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.initCardNumberInput = function () { | ||||||
|  |             var e = this; | ||||||
|  |             (this.cardNumberInput = this.elem.find('.card-number')), | ||||||
|  |                 this.cardNumberInput[0] | ||||||
|  |                     ? this.cardNumberInput.detach() | ||||||
|  |                     : (this.cardNumberInput = $( | ||||||
|  |                           "<input class='card-number' />" | ||||||
|  |                       )), | ||||||
|  |                 this.cardNumberInput.attr('type', 'tel'), | ||||||
|  |                 this.cardNumberInput.attr('placeholder') || | ||||||
|  |                     this.cardNumberInput.attr( | ||||||
|  |                         'placeholder', | ||||||
|  |                         t.CREDIT_CARD_NUMBER_PLACEHOLDER | ||||||
|  |                     ), | ||||||
|  |                 this.cardNumberInput.attr( | ||||||
|  |                     'maxlength', | ||||||
|  |                     this.creditCardNumberMask.length | ||||||
|  |                 ), | ||||||
|  |                 this.cardNumberInput.attr('x-autocompletetype', 'cc-number'), | ||||||
|  |                 this.cardNumberInput.attr('autocompletetype', 'cc-number'), | ||||||
|  |                 this.cardNumberInput.attr('autocorrect', 'off'), | ||||||
|  |                 this.cardNumberInput.attr('spellcheck', 'off'), | ||||||
|  |                 this.cardNumberInput.attr('autocapitalize', 'off'), | ||||||
|  |                 this.cardNumberInput.keydown(function (r) { | ||||||
|  |                     t.handleCreditCardNumberKey(r, e.creditCardNumberMask); | ||||||
|  |                 }), | ||||||
|  |                 this.cardNumberInput.keyup(function (t) { | ||||||
|  |                     e.refreshCreditCardTypeIcon(); | ||||||
|  |                 }), | ||||||
|  |                 this.cardNumberInput.change(t.handleCreditCardNumberChange); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.initNameInput = function () { | ||||||
|  |             (this.nameInput = this.elem.find('.name')), | ||||||
|  |                 this.nameInput[0] | ||||||
|  |                     ? ((this.captureName = !0), this.nameInput.detach()) | ||||||
|  |                     : (this.nameInput = $("<input class='name' />")), | ||||||
|  |                 this.nameInput.attr('placeholder') || | ||||||
|  |                     this.nameInput.attr('placeholder', t.NAME_PLACEHOLDER); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.initExpiryMonthInput = function () { | ||||||
|  |             (this.expiryMonthInput = this.elem.find('.expiry-month')), | ||||||
|  |                 this.expiryMonthInput[0] | ||||||
|  |                     ? this.expiryMonthInput.detach() | ||||||
|  |                     : (this.expiryMonthInput = $( | ||||||
|  |                           "<input class='expiry-month' />" | ||||||
|  |                       )); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.initExpiryYearInput = function () { | ||||||
|  |             (this.expiryYearInput = this.elem.find('.expiry-year')), | ||||||
|  |                 this.expiryYearInput[0] | ||||||
|  |                     ? this.expiryYearInput.detach() | ||||||
|  |                     : (this.expiryYearInput = $( | ||||||
|  |                           "<input class='expiry-year' name='expiry-year' />" | ||||||
|  |                       )); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.initCvcInput = function () { | ||||||
|  |             (this.cvcInput = this.elem.find('.cvc')), | ||||||
|  |                 this.cvcInput[0] | ||||||
|  |                     ? this.cvcInput.detach() | ||||||
|  |                     : (this.cvcInput = $("<input class='cvc' />")), | ||||||
|  |                 this.cvcInput.attr('type', 'tel'), | ||||||
|  |                 this.cvcInput.attr('placeholder') || | ||||||
|  |                     this.cvcInput.attr('placeholder', t.CVC_PLACEHOLDER), | ||||||
|  |                 this.cvcInput.attr('maxlength', t.CVC_MASK_3.length), | ||||||
|  |                 this.cvcInput.attr('x-autocompletetype', 'cc-csc'), | ||||||
|  |                 this.cvcInput.attr('autocompletetype', 'cc-csc'), | ||||||
|  |                 this.cvcInput.attr('autocorrect', 'off'), | ||||||
|  |                 this.cvcInput.attr('spellcheck', 'off'), | ||||||
|  |                 this.cvcInput.attr('autocapitalize', 'off'), | ||||||
|  |                 this.cvcInput.keydown(t.filterNumberOnlyKey); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setupCardNumberInput = function () { | ||||||
|  |             this.stripe && this.cardNumberInput.attr('data-stripe', 'number'), | ||||||
|  |                 this.elem.append("<div class='card-number-wrapper'></div>"); | ||||||
|  |             var e = this.elem.find('.card-number-wrapper'); | ||||||
|  |             e.append(this.cardNumberInput), | ||||||
|  |                 e.append("<div class='card-type-icon'></div>"), | ||||||
|  |                 e.append("<div class='icon'></div>"), | ||||||
|  |                 e.find('.icon').append(t.CREDIT_CARD_SVG); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setupNameInput = function () { | ||||||
|  |             if (this.captureName) { | ||||||
|  |                 this.elem.append("<div class='name-wrapper'></div>"); | ||||||
|  |                 var e = this.elem.find('.name-wrapper'); | ||||||
|  |                 e.append(this.nameInput), | ||||||
|  |                     e.append("<div class='icon'></div>"), | ||||||
|  |                     e.find('.icon').append(t.USER_SVG); | ||||||
|  |             } | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setupExpiryInput = function () { | ||||||
|  |             this.elem.append( | ||||||
|  |                 "<div class='expiry-container'><div class='expiry-wrapper'></div></div>" | ||||||
|  |             ); | ||||||
|  |             var e, | ||||||
|  |                 r = this.elem.find('.expiry-wrapper'); | ||||||
|  |             if (this.EXPIRY_USE_DROPDOWNS) { | ||||||
|  |                 e = $('<div></div>'); | ||||||
|  |                 var n = $( | ||||||
|  |                         "<select><option value='any' selected='' hidden=''>MM</option><option value='1'>01</option><option value='2'>02</option><option value='3'>03</option><option value='4'>04</option><option value='5'>05</option><option value='6'>06</option><option value='7'>07</option><option value='8'>08</option><option value='9'>09</option><option value='10'>10</option><option value='11'>11</option><option value='12'>12</option></select>" | ||||||
|  |                     ), | ||||||
|  |                     i = this.expiryMonthInput; | ||||||
|  |                 t.copyAllElementAttributes(i, n), | ||||||
|  |                     this.expiryMonthInput.remove(), | ||||||
|  |                     (this.expiryMonthInput = n); | ||||||
|  |                 for ( | ||||||
|  |                     var a = $( | ||||||
|  |                             "<select><option value='any' selected='' hidden=''>YY</option></select>" | ||||||
|  |                         ), | ||||||
|  |                         s = parseInt( | ||||||
|  |                             new Date().getFullYear().toString().substring(2, 4) | ||||||
|  |                         ), | ||||||
|  |                         p = 0; | ||||||
|  |                     p < t.EXPIRY_NUMBER_OF_YEARS; | ||||||
|  |                     p++ | ||||||
|  |                 ) | ||||||
|  |                     a.append("<option value='" + s + "'>" + s + '</option>'), | ||||||
|  |                         (s = (s + 1) % 100); | ||||||
|  |                 var c = this.expiryYearInput; | ||||||
|  |                 t.copyAllElementAttributes(c, a), | ||||||
|  |                     this.expiryYearInput.remove(), | ||||||
|  |                     (this.expiryYearInput = a), | ||||||
|  |                     e.append(this.expiryMonthInput), | ||||||
|  |                     e.append(this.expiryYearInput); | ||||||
|  |             } else { | ||||||
|  |                 (e = $('<div></div>')), | ||||||
|  |                     (this.expiryMonthInput = $( | ||||||
|  |                         "<input type='hidden' name='expiry-month' />" | ||||||
|  |                     )), | ||||||
|  |                     (this.expiryYearInput = $( | ||||||
|  |                         "<input type='hidden' name='expiry-year' />" | ||||||
|  |                     )), | ||||||
|  |                     this.stripe && | ||||||
|  |                         (this.expiryMonthInput.attr('data-stripe', 'exp-month'), | ||||||
|  |                         this.expiryYearInput.attr('data-stripe', 'exp-year')), | ||||||
|  |                     (this.expiryMonthYearInput = $("<input class='expiry' />")), | ||||||
|  |                     this.expiryMonthYearInput.attr('type', 'tel'), | ||||||
|  |                     this.expiryMonthYearInput.attr('placeholder') || | ||||||
|  |                         this.expiryMonthYearInput.attr( | ||||||
|  |                             'placeholder', | ||||||
|  |                             t.EXPIRY_PLACEHOLDER | ||||||
|  |                         ), | ||||||
|  |                     this.expiryMonthYearInput.attr( | ||||||
|  |                         'maxlength', | ||||||
|  |                         t.EXPIRY_MASK.length | ||||||
|  |                     ), | ||||||
|  |                     this.expiryMonthYearInput.attr( | ||||||
|  |                         'x-autocompletetype', | ||||||
|  |                         'cc-exp' | ||||||
|  |                     ), | ||||||
|  |                     this.expiryMonthYearInput.attr( | ||||||
|  |                         'autocompletetype', | ||||||
|  |                         'cc-exp' | ||||||
|  |                     ), | ||||||
|  |                     this.expiryMonthYearInput.attr('autocorrect', 'off'), | ||||||
|  |                     this.expiryMonthYearInput.attr('spellcheck', 'off'), | ||||||
|  |                     this.expiryMonthYearInput.attr('autocapitalize', 'off'); | ||||||
|  |                 var o = this; | ||||||
|  |                 this.expiryMonthYearInput.keydown(function (e) { | ||||||
|  |                     t.handleExpiryKey(e); | ||||||
|  |                     var r = o.expiryMonthYearInput.val(); | ||||||
|  |                     1 == r.length && | ||||||
|  |                         parseInt(r) > 1 && | ||||||
|  |                         t.keyIsNumber(e) && | ||||||
|  |                         o.expiryMonthYearInput.val( | ||||||
|  |                             t.applyFormatMask('0' + r, t.EXPIRY_MASK) | ||||||
|  |                         ), | ||||||
|  |                         o.EXPIRY_USE_DROPDOWNS || | ||||||
|  |                             null == o.expiryMonthYearInput || | ||||||
|  |                             (o.expiryMonthInput.val(o.expiryMonth()), | ||||||
|  |                             o.expiryYearInput.val( | ||||||
|  |                                 9 == r.length ? r.substr(5, 4) : null | ||||||
|  |                             )); | ||||||
|  |                 }), | ||||||
|  |                     this.expiryMonthYearInput.blur(function (t) { | ||||||
|  |                         o.refreshExpiryMonthValidation(); | ||||||
|  |                     }), | ||||||
|  |                     e.append(this.expiryMonthYearInput), | ||||||
|  |                     e.append(this.expiryMonthInput), | ||||||
|  |                     e.append(this.expiryYearInput); | ||||||
|  |             } | ||||||
|  |             r.append(e), | ||||||
|  |                 r.append("<div class='icon'></div>"), | ||||||
|  |                 r.find('.icon').append(t.CALENDAR_SVG); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setupCvcInput = function () { | ||||||
|  |             this.stripe && this.cvcInput.attr('data-stripe', 'cvc'), | ||||||
|  |                 this.elem.append( | ||||||
|  |                     "<div class='cvc-container'><div class='cvc-wrapper'></div></div>" | ||||||
|  |                 ); | ||||||
|  |             var e = this.elem.find('.cvc-wrapper'); | ||||||
|  |             e.append(this.cvcInput), | ||||||
|  |                 e.append("<div class='icon'></div>"), | ||||||
|  |                 e.find('.icon').append(t.LOCK_SVG); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.expiryMonth = function () { | ||||||
|  |             if ( | ||||||
|  |                 !this.EXPIRY_USE_DROPDOWNS && | ||||||
|  |                 null != this.expiryMonthYearInput | ||||||
|  |             ) { | ||||||
|  |                 var t = this.expiryMonthYearInput.val(); | ||||||
|  |                 return t.length >= 2 ? parseInt(t.substr(0, 2)) : null; | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         }), | ||||||
|  |         (t.isValidMonth = function (t) { | ||||||
|  |             return t >= 1 && 12 >= t; | ||||||
|  |         }), | ||||||
|  |         (t.isExpiryValid = function (e, r) { | ||||||
|  |             var n = new Date(), | ||||||
|  |                 i = n.getMonth() + 1, | ||||||
|  |                 a = '' + n.getFullYear(); | ||||||
|  |             return ( | ||||||
|  |                 2 == ('' + r).length && (r = a.substring(0, 2) + '' + r), | ||||||
|  |                 (i = parseInt(i)), | ||||||
|  |                 (a = parseInt(a)), | ||||||
|  |                 (e = parseInt(e)), | ||||||
|  |                 (r = parseInt(r)), | ||||||
|  |                 t.isValidMonth(e) && (r > a || (r == a && e >= i)) | ||||||
|  |             ); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.refreshExpiryMonthValidation = function () { | ||||||
|  |             t.isExpiryValid(this.getExpiryMonth(), this.getExpiryYear()) | ||||||
|  |                 ? this.setExpiryMonthAsValid() | ||||||
|  |                 : this.setExpiryMonthAsInvalid(); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setExpiryMonthAsValid = function () { | ||||||
|  |             this.EXPIRY_USE_DROPDOWNS || | ||||||
|  |                 this.expiryMonthYearInput.parent().removeClass('has-error'); | ||||||
|  |         }), | ||||||
|  |         (t.prototype.setExpiryMonthAsInvalid = function () { | ||||||
|  |             this.EXPIRY_USE_DROPDOWNS || | ||||||
|  |                 this.expiryMonthYearInput.parent().addClass('has-error'); | ||||||
|  |         }); | ||||||
|  | })(); | ||||||
							
								
								
									
										82
									
								
								public/js/clients/payments/forte-credit-card-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								public/js/clients/payments/forte-credit-card-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | /******/ (() => { // webpackBootstrap
 | ||||||
|  | var __webpack_exports__ = {}; | ||||||
|  | /*!********************************************************************!*\ | ||||||
|  |   !*** ./resources/js/clients/payments/forte-credit-card-payment.js ***! | ||||||
|  |   \********************************************************************/ | ||||||
|  | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||||||
|  | 
 | ||||||
|  | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 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
 | ||||||
|  |  */ | ||||||
|  | var ForteAuthorizeCard = function ForteAuthorizeCard(apiLoginId) { | ||||||
|  |   var _this = this; | ||||||
|  | 
 | ||||||
|  |   _classCallCheck(this, ForteAuthorizeCard); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "handleAuthorization", function () { | ||||||
|  |     var myCard = $('#my-card'); | ||||||
|  |     var data = { | ||||||
|  |       api_login_id: _this.apiLoginId, | ||||||
|  |       card_number: myCard.CardJs('cardNumber').replace(/[^\d]/g, ''), | ||||||
|  |       expire_year: myCard.CardJs('expiryYear').replace(/[^\d]/g, ''), | ||||||
|  |       expire_month: myCard.CardJs('expiryMonth').replace(/[^\d]/g, ''), | ||||||
|  |       cvv: document.getElementById('cvv').value.replace(/[^\d]/g, '') | ||||||
|  |     }; | ||||||
|  |     var payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |     if (payNowButton) { | ||||||
|  |       document.getElementById('pay-now').disabled = true; | ||||||
|  |       document.querySelector('#pay-now > svg').classList.remove('hidden'); | ||||||
|  |       document.querySelector('#pay-now > span').classList.add('hidden'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     forte.createToken(data).success(_this.successResponseHandler).error(_this.failedResponseHandler); | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "successResponseHandler", function (response) { | ||||||
|  |     document.getElementById('payment_token').value = response.onetime_token; | ||||||
|  |     document.getElementById('card_brand').value = response.card_type; | ||||||
|  |     document.getElementById('server_response').submit(); | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "failedResponseHandler", function (response) { | ||||||
|  |     var errors = '<div class="alert alert-failure mb-4"><ul><li>' + response.response_description + '</li></ul></div>'; | ||||||
|  |     document.getElementById('forte_errors').innerHTML = errors; | ||||||
|  |     document.getElementById('pay-now').disabled = false; | ||||||
|  |     document.querySelector('#pay-now > svg').classList.add('hidden'); | ||||||
|  |     document.querySelector('#pay-now > span').classList.remove('hidden'); | ||||||
|  |     return false; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   _defineProperty(this, "handle", function () { | ||||||
|  |     var payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |     if (payNowButton) { | ||||||
|  |       payNowButton.addEventListener('click', function (e) { | ||||||
|  |         _this.handleAuthorization(); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return _this; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   this.apiLoginId = apiLoginId; | ||||||
|  |   this.cardHolderName = document.getElementById('cardholder_name'); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | var apiLoginId = document.querySelector('meta[name="forte-api-login-id"]').content; | ||||||
|  | /** @handle */ | ||||||
|  | 
 | ||||||
|  | new ForteAuthorizeCard(apiLoginId).handle(); | ||||||
|  | /******/ })() | ||||||
|  | ; | ||||||
							
								
								
									
										322468
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										322468
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										317492
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										317492
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										283989
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										283989
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										32019
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32019
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										81
									
								
								resources/js/clients/payments/forte-ach-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								resources/js/clients/payments/forte-ach-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | |||||||
|  | /** | ||||||
|  |  * 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
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | class ForteAuthorizeACH { | ||||||
|  |     constructor(apiLoginId) { | ||||||
|  |         this.apiLoginId = apiLoginId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     handleAuthorization = () => { | ||||||
|  |         var account_number = document.getElementById('account-number').value; | ||||||
|  |         var routing_number = document.getElementById('routing-number').value; | ||||||
|  | 
 | ||||||
|  |         var data = { | ||||||
|  |             api_login_id: this.apiLoginId, | ||||||
|  |             account_number: account_number, | ||||||
|  |             routing_number: routing_number, | ||||||
|  |             account_type: 'checking', | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |         if (payNowButton) { | ||||||
|  |             document.getElementById('pay-now').disabled = true; | ||||||
|  |             document.querySelector('#pay-now > svg').classList.remove('hidden'); | ||||||
|  |             document.querySelector('#pay-now > span').classList.add('hidden'); | ||||||
|  |         } | ||||||
|  |         // console.log(data);
 | ||||||
|  |         forte | ||||||
|  |             .createToken(data) | ||||||
|  |             .success(this.successResponseHandler) | ||||||
|  |             .error(this.failedResponseHandler); | ||||||
|  |         return false; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     successResponseHandler = (response) => { | ||||||
|  |         document.getElementById('payment_token').value = response.onetime_token; | ||||||
|  | 
 | ||||||
|  |         document.getElementById('server_response').submit(); | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     failedResponseHandler = (response) => { | ||||||
|  |         var errors = | ||||||
|  |             '<div class="alert alert-failure mb-4"><ul><li>' + | ||||||
|  |             response.response_description + | ||||||
|  |             '</li></ul></div>'; | ||||||
|  |         document.getElementById('forte_errors').innerHTML = errors; | ||||||
|  |         document.getElementById('pay-now').disabled = false; | ||||||
|  |         document.querySelector('#pay-now > svg').classList.add('hidden'); | ||||||
|  |         document.querySelector('#pay-now > span').classList.remove('hidden'); | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     handle = () => { | ||||||
|  |         let payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |         if (payNowButton) { | ||||||
|  |             payNowButton.addEventListener('click', (e) => { | ||||||
|  |                 this.handleAuthorization(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const apiLoginId = document.querySelector( | ||||||
|  |     'meta[name="forte-api-login-id"]' | ||||||
|  | ).content; | ||||||
|  | 
 | ||||||
|  | /** @handle */ | ||||||
|  | new ForteAuthorizeACH(apiLoginId).handle(); | ||||||
							
								
								
									
										83
									
								
								resources/js/clients/payments/forte-credit-card-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								resources/js/clients/payments/forte-credit-card-payment.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | |||||||
|  | /** | ||||||
|  |  * 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
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | class ForteAuthorizeCard { | ||||||
|  |     constructor(apiLoginId) { | ||||||
|  |         this.apiLoginId = apiLoginId; | ||||||
|  |         this.cardHolderName = document.getElementById('cardholder_name'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     handleAuthorization = () => { | ||||||
|  |         var myCard = $('#my-card'); | ||||||
|  | 
 | ||||||
|  |         var data = { | ||||||
|  |             api_login_id: this.apiLoginId, | ||||||
|  |             card_number: myCard.CardJs('cardNumber').replace(/[^\d]/g, ''), | ||||||
|  |             expire_year: myCard.CardJs('expiryYear').replace(/[^\d]/g, ''), | ||||||
|  |             expire_month: myCard.CardJs('expiryMonth').replace(/[^\d]/g, ''), | ||||||
|  |             cvv: document.getElementById('cvv').value.replace(/[^\d]/g, ''), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         let payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |         if (payNowButton) { | ||||||
|  |             document.getElementById('pay-now').disabled = true; | ||||||
|  |             document.querySelector('#pay-now > svg').classList.remove('hidden'); | ||||||
|  |             document.querySelector('#pay-now > span').classList.add('hidden'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         forte | ||||||
|  |             .createToken(data) | ||||||
|  |             .success(this.successResponseHandler) | ||||||
|  |             .error(this.failedResponseHandler); | ||||||
|  |         return false; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     successResponseHandler = (response) => { | ||||||
|  |         document.getElementById('payment_token').value = response.onetime_token; | ||||||
|  |         document.getElementById('card_brand').value = response.card_type; | ||||||
|  | 
 | ||||||
|  |         document.getElementById('server_response').submit(); | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     failedResponseHandler = (response) => { | ||||||
|  |         var errors = | ||||||
|  |             '<div class="alert alert-failure mb-4"><ul><li>' + | ||||||
|  |             response.response_description + | ||||||
|  |             '</li></ul></div>'; | ||||||
|  |         document.getElementById('forte_errors').innerHTML = errors; | ||||||
|  |         document.getElementById('pay-now').disabled = false; | ||||||
|  |         document.querySelector('#pay-now > svg').classList.add('hidden'); | ||||||
|  |         document.querySelector('#pay-now > span').classList.remove('hidden'); | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     handle = () => { | ||||||
|  |         let payNowButton = document.getElementById('pay-now'); | ||||||
|  | 
 | ||||||
|  |         if (payNowButton) { | ||||||
|  |             payNowButton.addEventListener('click', (e) => { | ||||||
|  |                 this.handleAuthorization(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const apiLoginId = document.querySelector( | ||||||
|  |     'meta[name="forte-api-login-id"]' | ||||||
|  | ).content; | ||||||
|  | 
 | ||||||
|  | /** @handle */ | ||||||
|  | new ForteAuthorizeCard(apiLoginId).handle(); | ||||||
| @ -1,5 +1,5 @@ | |||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html data-report-errors="{{ $report_errors }}" data-rc="{{ $rc }}" data-user-agent="{{ $user_agent }}" data-login="{{ $login }}"> | <html data-report-errors="{{ $report_errors }}" data-rc="{{ $rc }}" data-user-agent="{{ $user_agent }}" data-login="{{ $login }}" data-signup="{{ $signup }}"> | ||||||
| <head> | <head> | ||||||
|     <!-- Source: https://github.com/invoiceninja/invoiceninja --> |     <!-- Source: https://github.com/invoiceninja/invoiceninja --> | ||||||
|     <!-- Version: {{ config('ninja.app_version') }} --> |     <!-- Version: {{ config('ninja.app_version') }} --> | ||||||
| @ -152,6 +152,12 @@ | |||||||
| 
 | 
 | ||||||
|   <script defer src="{{ $path }}?v={{ config('ninja.app_version') }}" type="application/javascript"></script> |   <script defer src="{{ $path }}?v={{ config('ninja.app_version') }}" type="application/javascript"></script> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |   <script type="text/javascript"  | ||||||
|  |     src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js" | ||||||
|  |     integrity="sha384-ggh+EF1aSqm+Y4yvv2n17KpurNcZTeYtUZUvhPziElsstmIEubyEB6AIVpKLuZgr" | ||||||
|  |     crossorigin="anonymous"> | ||||||
|  |   </script> | ||||||
|   <center style="padding-top: 150px" id="loader"> |   <center style="padding-top: 150px" id="loader"> | ||||||
|     <div class="loader"></div> |     <div class="loader"></div> | ||||||
|   </center> |   </center> | ||||||
|  | |||||||
| @ -278,6 +278,13 @@ | |||||||
|         margin-top: 1rem; |         margin-top: 1rem; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -270,6 +270,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -240,6 +240,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
| @ -335,7 +341,7 @@ $entity_images | |||||||
|                     'product-table', 'task-table', 'delivery-note-table', |                     'product-table', 'task-table', 'delivery-note-table', | ||||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', |                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table', |                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table', | ||||||
|                     'client-details','vendor-details' |                     'client-details','vendor-details', 'swiss-qr' | ||||||
|                 ]; |                 ]; | ||||||
| 
 | 
 | ||||||
|                 tables.forEach((tableIdentifier) => { |                 tables.forEach((tableIdentifier) => { | ||||||
|  | |||||||
| @ -223,6 +223,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -228,6 +228,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -245,6 +245,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -272,6 +272,12 @@ | |||||||
|         white-space: nowrap; |         white-space: nowrap; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       /** Useful snippets, uncomment to enable. **/ |       /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|       /** Hide company logo **/ |       /** Hide company logo **/ | ||||||
|  | |||||||
| @ -215,6 +215,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -288,6 +288,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -252,6 +252,12 @@ | |||||||
|         bottom: 0; |         bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     [data-element='product_table-product.description-td'], td { | ||||||
|  |         min-width:100%; | ||||||
|  |         max-width: 300px; | ||||||
|  |         overflow-wrap: break-word;  | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** Useful snippets, uncomment to enable. **/ |     /** Useful snippets, uncomment to enable. **/ | ||||||
| 
 | 
 | ||||||
|     /** Hide company logo **/ |     /** Hide company logo **/ | ||||||
|  | |||||||
| @ -0,0 +1,131 @@ | |||||||
|  | @extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Bank Details', 'card_title' => 'Bank Details']) | ||||||
|  | 
 | ||||||
|  | @section('gateway_head') | ||||||
|  |     @if($gateway->getConfigField('testMode')) | ||||||
|  |         <script type="text/javascript" src="https://sandbox.forte.net/api/js/v1"></script> | ||||||
|  |     @else | ||||||
|  |         <script type="text/javascript" src="https://api.forte.net/js/v1"></script> | ||||||
|  |     @endif | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_content') | ||||||
|  |     @if(session()->has('ach_error')) | ||||||
|  |         <div class="alert alert-failure mb-4"> | ||||||
|  |             <p>{{ session('ach_error') }}</p> | ||||||
|  |         </div> | ||||||
|  |     @endif | ||||||
|  |     @if(Session::has('error')) | ||||||
|  |         <div class="alert alert-failure mb-4" id="errors">{{ Session::get('error') }}</div> | ||||||
|  |     @endif | ||||||
|  |     <div id="forte_errors"></div> | ||||||
|  |     @if ($errors->any()) | ||||||
|  |         <div class="alert alert-failure mb-4"> | ||||||
|  |             <ul> | ||||||
|  |                 @foreach ($errors->all() as $error) | ||||||
|  |                     <li>{{ $error }}</li> | ||||||
|  |                 @endforeach | ||||||
|  |             </ul> | ||||||
|  |         </div> | ||||||
|  |     @endif | ||||||
|  | 
 | ||||||
|  |     <form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::BANK_TRANSFER]) }}" method="post" id="server_response"> | ||||||
|  |         @csrf | ||||||
|  | 
 | ||||||
|  |         <input type="hidden" name="gateway_type_id" value="2"> | ||||||
|  |         <input type="hidden" name="gateway_response" id="gateway_response"> | ||||||
|  |         <input type="hidden" name="is_default" id="is_default"> | ||||||
|  |         <input type="hidden" name="last_4" id="last_4"> | ||||||
|  |         <input type="hidden" name="one_time_token" id="one_time_token"> | ||||||
|  | 
 | ||||||
|  |         <div class="alert alert-failure mb-4" hidden id="errors"></div> | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_type')]) | ||||||
|  |             <span class="flex items-center mr-4"> | ||||||
|  |                 <input class="form-radio mr-2" type="radio" value="individual" name="account-holder-type" checked> | ||||||
|  |                 <span>{{ __('texts.individual_account') }}</span> | ||||||
|  |             </span> | ||||||
|  |             <span class="flex items-center"> | ||||||
|  |                 <input class="form-radio mr-2" type="radio" value="company" name="account-holder-type"> | ||||||
|  |                 <span>{{ __('texts.company_account') }}</span> | ||||||
|  |             </span> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_name')]) | ||||||
|  |             <input class="input w-full" id="account-holder-name" type="text" name="account_holder_name" placeholder="{{ ctrans('texts.name') }}" required> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.country')]) | ||||||
|  |             <select name="countries" id="country" name="country" class="form-select input w-full" required> | ||||||
|  |                 @foreach($countries as $country) | ||||||
|  |                     <option value="{{ $country->iso_3166_2 }}" {{$country->iso_3166_2 == 'US' ? "selected" : ""}}>{{ $country->iso_3166_2 }} ({{ $country->name }})</option> | ||||||
|  |                 @endforeach | ||||||
|  |             </select> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.currency')]) | ||||||
|  |             <select name="currencies" id="currency" name="currency" class="form-select input w-full"> | ||||||
|  |                 @foreach($currencies as $currency) | ||||||
|  |                     <option value="{{ $currency->code }}" {{$currency->code == 'USD' ? "selected" : ""}}>{{ $currency->code }} ({{ $currency->name }})</option> | ||||||
|  |                 @endforeach | ||||||
|  |             </select> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.routing_number')]) | ||||||
|  |             <input class="input w-full" id="routing-number" type="text" required> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_number')]) | ||||||
|  |             <input class="input w-full" id="account-number" type="text" required> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element-single') | ||||||
|  |             <input type="checkbox" class="form-checkbox mr-1" name="accept_terms" id="accept-terms" required> | ||||||
|  |             <label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth('contact')->user()->client->company->settings->email]) }}</label> | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         <div class="bg-white px-4 py-5 flex justify-end"> | ||||||
|  |             <button type="button" | ||||||
|  |                 onclick="submitACH()" | ||||||
|  |                 class="button button-primary bg-primary {{ $class ?? '' }}"> | ||||||
|  |                     <svg class="animate-spin h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | ||||||
|  |                         <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | ||||||
|  |                         <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | ||||||
|  |                     </svg> | ||||||
|  |                 <span>{{ $slot ?? ctrans('texts.add_payment_method') }}</span> | ||||||
|  |             </button> | ||||||
|  |             <input type="submit" style="display: none" id="form_btn"> | ||||||
|  |         </div> | ||||||
|  |     </form> | ||||||
|  | 
 | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_footer') | ||||||
|  |     <script> | ||||||
|  |         function onTokenCreated(params) { | ||||||
|  |             document.getElementById('one_time_token').value=params.onetime_token; | ||||||
|  |             document.getElementById('last_4').value=params.last_4; | ||||||
|  |             let button = document.querySelector("#form_btn"); | ||||||
|  |             button.click(); | ||||||
|  |         } | ||||||
|  |         function onTokenFailed(params) { | ||||||
|  |             var errors = '<div class="alert alert-failure mb-4"><ul><li>'+ params.response_description +'</li></ul></div>'; | ||||||
|  |             document.getElementById("forte_errors").innerHTML = errors; | ||||||
|  |         } | ||||||
|  |         function submitACH(){ | ||||||
|  |             var account_number=document.getElementById('account-number').value; | ||||||
|  |             var routing_number=document.getElementById('routing-number').value; | ||||||
|  | 
 | ||||||
|  |             var data = { | ||||||
|  |                api_login_id: '{{$gateway->getConfigField("apiLoginId")}}', | ||||||
|  |                account_number: account_number, | ||||||
|  |                routing_number: routing_number,  | ||||||
|  |                account_type: "checking", | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             forte.createToken(data) | ||||||
|  |                .success(onTokenCreated) | ||||||
|  |                .error(onTokenFailed); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  | @endsection | ||||||
| @ -0,0 +1,53 @@ | |||||||
|  | @extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Bank Transfer', 'card_title' => 'Bank Transfer']) | ||||||
|  | 
 | ||||||
|  | @section('gateway_head') | ||||||
|  |     <meta name="forte-api-login-id" content="{{$gateway->forte->company_gateway->getConfigField("apiLoginId")}}"> | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_content') | ||||||
|  |     <form action="{{ route('client.payments.response') }}" method="post" id="server_response"> | ||||||
|  |         @csrf | ||||||
|  |         <input type="hidden" name="payment_hash" value="{{ $payment_hash }}"> | ||||||
|  |         <input type="hidden" name="company_gateway_id" value="{{ $gateway->forte->company_gateway->id }}"> | ||||||
|  |         <input type="hidden" name="payment_method_id" value="{{$payment_method_id}}"> | ||||||
|  |         <input type="hidden" name="gateway_response" id="gateway_response"> | ||||||
|  |         <input type="hidden" name="dataValue" id="dataValue"/> | ||||||
|  |         <input type="hidden" name="dataDescriptor" id="dataDescriptor"/> | ||||||
|  |         <input type="hidden" name="token" id="token"/> | ||||||
|  |         <input type="hidden" name="store_card" id="store_card"/> | ||||||
|  |         <input type="submit" style="display: none" id="form_btn"> | ||||||
|  |     </form> | ||||||
|  | 
 | ||||||
|  |     <div id="forte_errors"></div> | ||||||
|  | 
 | ||||||
|  |     @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')]) | ||||||
|  |         Bank Transfer | ||||||
|  |     @endcomponent | ||||||
|  | 
 | ||||||
|  |     @include('portal.ninja2020.gateways.includes.payment_details') | ||||||
|  | 
 | ||||||
|  |     @component('portal.ninja2020.components.general.card-element', ['title' => 'Pay with Bank Transfer']) | ||||||
|  |         <input type="hidden" name="payment_token" id="payment_token"> | ||||||
|  |         <div class="bg-white px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6" | ||||||
|  |             style="display: flex!important; justify-content: center!important;"> | ||||||
|  |             <input class="input w-full" id="routing-number" type="text" placeholder="{{ctrans('texts.routing_number')}}" required> | ||||||
|  |         </div> | ||||||
|  |         <div class="bg-white px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6" | ||||||
|  |             style="display: flex!important; justify-content: center!important;"> | ||||||
|  |             <input class="input w-full" id="account-number" type="text" placeholder="{{ctrans('texts.account_number')}}" required> | ||||||
|  |         </div> | ||||||
|  |     @endcomponent | ||||||
|  | 
 | ||||||
|  |     @include('portal.ninja2020.gateways.includes.pay_now') | ||||||
|  | 
 | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_footer') | ||||||
|  |     @if($gateway->forte->company_gateway->getConfigField('testMode')) | ||||||
|  |         <script type="text/javascript" src="https://sandbox.forte.net/api/js/v1"></script> | ||||||
|  |     @else | ||||||
|  |         <script type="text/javascript" src="https://api.forte.net/js/v1"></script> | ||||||
|  |     @endif | ||||||
|  |      | ||||||
|  |     <script src="{{ asset('js/clients/payments/forte-ach-payment.js') }}"></script> | ||||||
|  | @endsection | ||||||
| @ -0,0 +1,122 @@ | |||||||
|  | @extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')]) | ||||||
|  | 
 | ||||||
|  | @section('gateway_head') | ||||||
|  |     <meta name="year-invalid" content="{{ ctrans('texts.year_invalid') }}"> | ||||||
|  |     <meta name="month-invalid" content="{{ ctrans('texts.month_invalid') }}"> | ||||||
|  |     <meta name="credit-card-invalid" content="{{ ctrans('texts.credit_card_invalid') }}"> | ||||||
|  | 
 | ||||||
|  |     <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> | ||||||
|  |     <script src="{{ asset('js/clients/payments/forte-card-js.min.js') }}"></script> | ||||||
|  | 
 | ||||||
|  |     <link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css"> | ||||||
|  |     @if($gateway->getConfigField('testMode')) | ||||||
|  |         <script type="text/javascript" src="https://sandbox.forte.net/api/js/v1"></script> | ||||||
|  |     @else | ||||||
|  |         <script type="text/javascript" src="https://api.forte.net/js/v1"></script> | ||||||
|  |     @endif | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_content') | ||||||
|  |     <form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}" | ||||||
|  |           method="post" id="server_response"> | ||||||
|  |         @csrf | ||||||
|  | 
 | ||||||
|  |         <input type="hidden" name="payment_method_id" value="1"> | ||||||
|  |         <input type="hidden" name="one_time_token" id="one_time_token"> | ||||||
|  |         <input type="hidden" name="card_type" id="card_type"> | ||||||
|  |         <input type="hidden" name="expire_year" id="expire_year"> | ||||||
|  |         <input type="hidden" name="expire_month" id="expire_month"> | ||||||
|  |         <input type="hidden" name="last_4" id="last_4"> | ||||||
|  | 
 | ||||||
|  |         @if(!Request::isSecure()) | ||||||
|  |             <p class="alert alert-failure">{{ ctrans('texts.https_required') }}</p> | ||||||
|  |         @endif | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |         @if(Session::has('error')) | ||||||
|  |             <div class="alert alert-failure mb-4" id="errors">{{ Session::get('error') }}</div> | ||||||
|  |         @endif | ||||||
|  |         <div id="forte_errors"></div> | ||||||
|  |         @if ($errors->any()) | ||||||
|  |             <div class="alert alert-failure mb-4"> | ||||||
|  |                 <ul> | ||||||
|  |                     @foreach ($errors->all() as $error) | ||||||
|  |                         <li>{{ $error }}</li> | ||||||
|  |                     @endforeach | ||||||
|  |                 </ul> | ||||||
|  |             </div> | ||||||
|  |         @endif | ||||||
|  | 
 | ||||||
|  |         @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.method')]) | ||||||
|  |             {{ ctrans('texts.credit_card') }} | ||||||
|  |         @endcomponent | ||||||
|  | 
 | ||||||
|  |         @include('portal.ninja2020.gateways.forte.includes.credit_card') | ||||||
|  | 
 | ||||||
|  |         <div class="bg-white px-4 py-5 flex justify-end"> | ||||||
|  |             <button type="button" | ||||||
|  |                 onclick="submitCard()" | ||||||
|  |                 class="button button-primary bg-primary {{ $class ?? '' }}"> | ||||||
|  |                     <svg class="animate-spin h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | ||||||
|  |                         <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | ||||||
|  |                         <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | ||||||
|  |                     </svg> | ||||||
|  |                 <span>{{ $slot ?? ctrans('texts.add_payment_method') }}</span> | ||||||
|  |             </button> | ||||||
|  |             <input type="submit" style="display: none" id="form_btn"> | ||||||
|  |         </div> | ||||||
|  |          | ||||||
|  |     </form> | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_footer') | ||||||
|  |     <script> | ||||||
|  |         function onTokenCreated(params) { | ||||||
|  |             document.getElementById('one_time_token').value=params.onetime_token; | ||||||
|  |             document.getElementById('last_4').value=params.last_4; | ||||||
|  |             let button = document.querySelector("#form_btn"); | ||||||
|  |             button.click(); | ||||||
|  |         } | ||||||
|  |         function onTokenFailed(params) { | ||||||
|  |             var errors = '<div class="alert alert-failure mb-4"><ul><li>'+ params.response_description +'</li></ul></div>'; | ||||||
|  |             document.getElementById("forte_errors").innerHTML = errors; | ||||||
|  |         } | ||||||
|  |         function submitCard(){ | ||||||
|  |             var doc = document.getElementsByClassName("card-number-wrapper"); | ||||||
|  |             var cardType=doc[0].childNodes[1].classList[2]; | ||||||
|  |             if (cardType=='master-card') { | ||||||
|  |                 document.getElementById('card_type').value='mast'; | ||||||
|  |             } else if(cardType=='visa') { | ||||||
|  |                 document.getElementById('card_type').value='visa'; | ||||||
|  |             }else if(cardType=='jcb') { | ||||||
|  |                 document.getElementById('card_type').value='jcb'; | ||||||
|  |             }else if(cardType=='discover') { | ||||||
|  |                 document.getElementById('card_type').value='disc'; | ||||||
|  |             }else if(cardType=='american-express') { | ||||||
|  |                 document.getElementById('card_type').value='amex'; | ||||||
|  |             }else{ | ||||||
|  |                 document.getElementById('card_type').value=cardType; | ||||||
|  |             } | ||||||
|  |             var month=document.querySelector('input[name=expiry-month]').value; | ||||||
|  |             var year=document.querySelector('input[name=expiry-year]').value; | ||||||
|  |             var cc=document.getElementById('card_number').value.replaceAll(' ',''); | ||||||
|  |             var cvv=document.getElementById('cvv').value; | ||||||
|  |              | ||||||
|  |             document.getElementById('expire_year').value=year; | ||||||
|  |             document.getElementById('expire_month').value=month; | ||||||
|  |              | ||||||
|  |             var data = { | ||||||
|  |                api_login_id: '{{$gateway->getConfigField("apiLoginId")}}', | ||||||
|  |                card_number: cc, | ||||||
|  |                expire_year: year,  | ||||||
|  |                expire_month: month, | ||||||
|  |                cvv: cvv, | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             forte.createToken(data) | ||||||
|  |                .success(onTokenCreated) | ||||||
|  |                .error(onTokenFailed); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     </script> | ||||||
|  | @endsection | ||||||
| @ -0,0 +1,51 @@ | |||||||
|  | @extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')]) | ||||||
|  | 
 | ||||||
|  | @section('gateway_head') | ||||||
|  |     <meta name="forte-api-login-id" content="{{$gateway->forte->company_gateway->getConfigField("apiLoginId")}}"> | ||||||
|  |     <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> | ||||||
|  |     <script src="{{ asset('js/clients/payments/forte-card-js.min.js') }}"></script> | ||||||
|  | 
 | ||||||
|  |     <link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css"> | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_content') | ||||||
|  |     <form action="{{ route('client.payments.response') }}" method="post" id="server_response"> | ||||||
|  |         @csrf | ||||||
|  |         <input type="hidden" name="card_brand" id="card_brand"> | ||||||
|  |         <input type="hidden" name="payment_token" id="payment_token"> | ||||||
|  |         <input type="hidden" name="payment_hash" value="{{ $payment_hash }}"> | ||||||
|  |         <input type="hidden" name="company_gateway_id" value="{{ $gateway->forte->company_gateway->id }}"> | ||||||
|  |         <input type="hidden" name="payment_method_id" value="{{$payment_method_id}}"> | ||||||
|  |         <input type="hidden" name="gateway_response" id="gateway_response"> | ||||||
|  |         <input type="hidden" name="dataValue" id="dataValue"/> | ||||||
|  |         <input type="hidden" name="dataDescriptor" id="dataDescriptor"/> | ||||||
|  |         <input type="hidden" name="token" id="token"/> | ||||||
|  |         <input type="hidden" name="store_card" id="store_card"/> | ||||||
|  |         <input type="submit" style="display: none" id="form_btn"> | ||||||
|  |     </form> | ||||||
|  | 
 | ||||||
|  |     <div id="forte_errors"></div> | ||||||
|  | 
 | ||||||
|  |     @component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')]) | ||||||
|  |         {{ ctrans('texts.credit_card') }} | ||||||
|  |      @endcomponent | ||||||
|  | 
 | ||||||
|  |     @include('portal.ninja2020.gateways.includes.payment_details') | ||||||
|  | 
 | ||||||
|  |     @component('portal.ninja2020.components.general.card-element', ['title' => 'Pay with Credit Card']) | ||||||
|  |        @include('portal.ninja2020.gateways.forte.includes.credit_card') | ||||||
|  |     @endcomponent | ||||||
|  | 
 | ||||||
|  |     @include('portal.ninja2020.gateways.includes.pay_now') | ||||||
|  | 
 | ||||||
|  | @endsection | ||||||
|  | 
 | ||||||
|  | @section('gateway_footer') | ||||||
|  |     @if($gateway->forte->company_gateway->getConfigField('testMode')) | ||||||
|  |         <script type="text/javascript" src="https://sandbox.forte.net/api/js/v1"></script> | ||||||
|  |     @else | ||||||
|  |         <script type="text/javascript" src="https://api.forte.net/js/v1"></script> | ||||||
|  |     @endif | ||||||
|  |      | ||||||
|  |     <script src="{{ asset('js/clients/payments/forte-credit-card-payment.js') }}"></script> | ||||||
|  | @endsection | ||||||
| @ -0,0 +1,12 @@ | |||||||
|  | <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6" | ||||||
|  |      style="display: flex!important; justify-content: center!important;" id="authorize--credit-card-container"> | ||||||
|  |     <div class="card-js" id="my-card" data-capture-name="true"> | ||||||
|  |         <input class="name" id="cardholder_name" name="card_holders_name" placeholder="{{ ctrans('texts.name')}}"> | ||||||
|  |         <input class="card-number my-custom-class" id="card_number"> | ||||||
|  |         <input type="hidden" name="expiry_month" id="expiration_month"> | ||||||
|  |         <input type="hidden" name="expiry_year" id="expiration_year"> | ||||||
|  |         <input class="cvc" name="cvc" id="cvv"> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div id="errors"></div> | ||||||
|  | </div> | ||||||
| @ -3,7 +3,7 @@ | |||||||
| 
 | 
 | ||||||
| @push('head') | @push('head') | ||||||
|     <meta name="show-purchase_order-terms" content="false"> |     <meta name="show-purchase_order-terms" content="false"> | ||||||
|     <meta name="require-purchase_order-signature" content="{{ $purchase_order->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_purchase_order_signature }}"> |     <meta name="require-purchase_order-signature" content="{{ $purchase_order->company->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && property_exists($settings, 'require_purchase_order_signature') && $settings->require_purchase_order_signature }}"> | ||||||
|     @include('portal.ninja2020.components.no-cache') |     @include('portal.ninja2020.components.no-cache') | ||||||
|      |      | ||||||
|     <script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script> |     <script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script> | ||||||
|  | |||||||
| @ -129,6 +129,9 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale | |||||||
|     Route::post('preview', 'PreviewController@show')->name('preview.show'); |     Route::post('preview', 'PreviewController@show')->name('preview.show'); | ||||||
|     Route::post('live_preview', 'PreviewController@live')->name('preview.live'); |     Route::post('live_preview', 'PreviewController@live')->name('preview.live'); | ||||||
| 
 | 
 | ||||||
|  |     Route::post('preview/purchase_order', 'PreviewPurchaseOrderController@show')->name('preview_purchase_order.show'); | ||||||
|  |     Route::post('live_preview/purchase_order', 'PreviewPurchaseOrderController@live')->name('preview_purchase_order.live'); | ||||||
|  | 
 | ||||||
|     Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit
 |     Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit
 | ||||||
|     Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); |     Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk'); | ||||||
|     Route::put('products/{product}/upload', 'ProductController@upload'); |     Route::put('products/{product}/upload', 'ProductController@upload'); | ||||||
| @ -212,6 +215,7 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale | |||||||
|     Route::get('purchase_orders/{purchase_order}/{action}', 'PurchaseOrderController@action')->name('purchase_orders.action'); |     Route::get('purchase_orders/{purchase_order}/{action}', 'PurchaseOrderController@action')->name('purchase_orders.action'); | ||||||
| 
 | 
 | ||||||
|     Route::get('users', 'UserController@index'); |     Route::get('users', 'UserController@index'); | ||||||
|  |     Route::get('users/create', 'UserController@create')->middleware('password_protected'); | ||||||
|     Route::get('users/{user}', 'UserController@show')->middleware('password_protected'); |     Route::get('users/{user}', 'UserController@show')->middleware('password_protected'); | ||||||
|     Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); |     Route::put('users/{user}', 'UserController@update')->middleware('password_protected'); | ||||||
|     Route::post('users', 'UserController@store')->middleware('password_protected'); |     Route::post('users', 'UserController@store')->middleware('password_protected'); | ||||||
|  | |||||||
| @ -20,6 +20,8 @@ Route::get('vendors', [VendorContactLoginController::class, 'catch'])->name('ven | |||||||
| Route::group(['middleware' => ['invite_db'], 'prefix' => 'vendor', 'as' => 'vendor.'], function () { | Route::group(['middleware' => ['invite_db'], 'prefix' => 'vendor', 'as' => 'vendor.'], function () { | ||||||
|     /*Invitation catches*/ |     /*Invitation catches*/ | ||||||
|     Route::get('purchase_order/{invitation_key}', [InvitationController::class, 'purchaseOrder']); |     Route::get('purchase_order/{invitation_key}', [InvitationController::class, 'purchaseOrder']); | ||||||
|  |     Route::get('purchase_order/{invitation_key}/download', [InvitationController::class, 'download']); | ||||||
|  | 
 | ||||||
|  //   Route::get('purchase_order/{invitation_key}/download_pdf', 'PurchaseOrderController@downloadPdf')->name('recurring_invoice.download_invitation_key');
 |  //   Route::get('purchase_order/{invitation_key}/download_pdf', 'PurchaseOrderController@downloadPdf')->name('recurring_invoice.download_invitation_key');
 | ||||||
|  //   Route::get('purchase_order/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
 |  //   Route::get('purchase_order/{invitation_key}/download', 'ClientPortal\InvitationController@routerForDownload');
 | ||||||
| 
 | 
 | ||||||
| @ -40,4 +42,7 @@ Route::group(['middleware' => ['auth:vendor', 'vendor_locale', 'domain_db'], 'pr | |||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| Route::fallback('BaseController@notFoundVendor'); | Route::fallback('BaseController@notFoundVendor'); | ||||||
| @ -56,7 +56,7 @@ class CompanySettingsTest extends TestCase | |||||||
|             $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, | ||||||
|             ])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); |             ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); | ||||||
|         } catch (ValidationException $e) { |         } catch (ValidationException $e) { | ||||||
|             $message = json_decode($e->validator->getMessageBag(), 1); |             $message = json_decode($e->validator->getMessageBag(), 1); | ||||||
|         } |         } | ||||||
| @ -78,11 +78,13 @@ class CompanySettingsTest extends TestCase | |||||||
| 
 | 
 | ||||||
|         $this->company->saveSettings($settings, $this->company); |         $this->company->saveSettings($settings, $this->company); | ||||||
| 
 | 
 | ||||||
|  |         $response = false; | ||||||
|  |          | ||||||
|         try { |         try { | ||||||
|         $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, | ||||||
|             ])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); |             ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); | ||||||
|         } catch (ValidationException $e) { |         } catch (ValidationException $e) { | ||||||
|             $message = json_decode($e->validator->getMessageBag(), 1); |             $message = json_decode($e->validator->getMessageBag(), 1); | ||||||
|             nlog($message); |             nlog($message); | ||||||
| @ -109,7 +111,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|             ])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); |             ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| @ -135,7 +137,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|             ])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); |             ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| @ -162,7 +164,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|             ])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); |             ])->putJson('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| @ -185,7 +187,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/companies?include=company', $this->company->toArray()); |         ])->postJson('/api/v1/companies?include=company', $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $arr = $response->json(); |         $arr = $response->json(); | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
| @ -203,7 +205,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/companies?include=company', $this->company->toArray()); |         ])->postJson('/api/v1/companies?include=company', $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $arr = $response->json(); |         $arr = $response->json(); | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
| @ -221,7 +223,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/companies?include=company', $this->company->toArray()); |         ])->postJson('/api/v1/companies?include=company', $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $arr = $response->json(); |         $arr = $response->json(); | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
| @ -239,7 +241,7 @@ class CompanySettingsTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/companies?include=company', $this->company->toArray()); |         ])->postJson('/api/v1/companies?include=company', $this->company->toArray()); | ||||||
| 
 | 
 | ||||||
|         $arr = $response->json(); |         $arr = $response->json(); | ||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
|  | |||||||
| @ -47,6 +47,31 @@ class PreviewTest extends TestCase | |||||||
|         $response->assertStatus(200); |         $response->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function testPurchaseOrderPreviewRoute() | ||||||
|  |     { | ||||||
|  |         $data = $this->getData(); | ||||||
|  | 
 | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |                 'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |                 'X-API-TOKEN' => $this->token, | ||||||
|  |             ])->post('/api/v1/preview/purchase_order', $data); | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(200); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testPurchaseOrderPreviewHtmlResponse() | ||||||
|  |     { | ||||||
|  |         $data = $this->getData(); | ||||||
|  | 
 | ||||||
|  |         $response = $this->withHeaders([ | ||||||
|  |                 'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|  |                 'X-API-TOKEN' => $this->token, | ||||||
|  |             ])->post('/api/v1/preview/purchase_order?html=true', $data); | ||||||
|  | 
 | ||||||
|  |         $response->assertStatus(200); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     public function testPreviewHtmlResponse() |     public function testPreviewHtmlResponse() | ||||||
|     { |     { | ||||||
|         $data = $this->getData(); |         $data = $this->getData(); | ||||||
|  | |||||||
| @ -124,7 +124,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/expenses/', $data) |         ])->postJson('/api/v1/expenses/', $data) | ||||||
|             ->assertStatus(200); |             ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -137,7 +137,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/expenses/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/expenses/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -148,19 +148,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/expenses/bulk?action=archive', $data) |         ])->postJson('/api/v1/expenses/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/expenses/bulk?action=restore', $data) |         ])->postJson('/api/v1/expenses/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/expenses/bulk?action=delete', $data) |         ])->postJson('/api/v1/expenses/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -183,7 +183,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/vendors/', $data) |         ])->postJson('/api/v1/vendors/', $data) | ||||||
|             ->assertStatus(200); |             ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -197,7 +197,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/vendors/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/vendors/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -208,19 +208,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/vendors/bulk?action=archive', $data) |         ])->postJson('/api/v1/vendors/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/vendors/bulk?action=restore', $data) |         ])->postJson('/api/v1/vendors/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/vendors/bulk?action=delete', $data) |         ])->postJson('/api/v1/vendors/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -245,7 +245,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/tasks/', $data) |         ])->postJson('/api/v1/tasks/', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -259,7 +259,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/tasks/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/tasks/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -270,19 +270,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/tasks/bulk?action=archive', $data) |         ])->postJson('/api/v1/tasks/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/tasks/bulk?action=restore', $data) |         ])->postJson('/api/v1/tasks/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/tasks/bulk?action=delete', $data) |         ])->postJson('/api/v1/tasks/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -306,7 +306,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/credits/', $data) |         ])->postJson('/api/v1/credits/', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -320,7 +320,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/credits/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/credits/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -331,19 +331,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/credits/bulk?action=archive', $data) |         ])->postJson('/api/v1/credits/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/credits/bulk?action=restore', $data) |         ])->postJson('/api/v1/credits/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/credits/bulk?action=delete', $data) |         ])->postJson('/api/v1/credits/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -368,7 +368,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/quotes/', $data) |         ])->postJson('/api/v1/quotes/', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -382,7 +382,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/quotes/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/quotes/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -397,25 +397,25 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/quotes/bulk?action=archive', $data) |         ])->postJson('/api/v1/quotes/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/quotes/bulk?action=restore', $data) |         ])->postJson('/api/v1/quotes/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/quotes/bulk?action=approve', $data) |         ])->postJson('/api/v1/quotes/bulk?action=approve', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/quotes/bulk?action=delete', $data) |         ])->postJson('/api/v1/quotes/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -449,7 +449,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/payments?include=invoices', $data) |         ])->postJson('/api/v1/payments?include=invoices', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $arr = $response->json(); |         $arr = $response->json(); | ||||||
| @ -461,7 +461,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/payments/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/payments/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $data = [ |         $data = [ | ||||||
| @ -471,19 +471,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/payments/bulk?action=archive', $data) |         ])->postJson('/api/v1/payments/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/payments/bulk?action=restore', $data) |         ])->postJson('/api/v1/payments/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/payments/bulk?action=delete', $data) |         ])->postJson('/api/v1/payments/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -507,7 +507,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/invoices/', $data) |         ])->postJson('/api/v1/invoices/', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -521,7 +521,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/invoices/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/invoices/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -532,19 +532,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/invoices/bulk?action=archive', $data) |         ])->postJson('/api/v1/invoices/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/invoices/bulk?action=restore', $data) |         ])->postJson('/api/v1/invoices/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/invoices/bulk?action=delete', $data) |         ])->postJson('/api/v1/invoices/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -571,7 +571,7 @@ class EventTest extends TestCase | |||||||
|             $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, | ||||||
|             ])->post('/api/v1/recurring_invoices/', $data); |             ])->postJson('/api/v1/recurring_invoices/', $data); | ||||||
|         } catch (ValidationException $e) { |         } catch (ValidationException $e) { | ||||||
|             $message = json_decode($e->validator->getMessageBag(), 1); |             $message = json_decode($e->validator->getMessageBag(), 1); | ||||||
|         } |         } | ||||||
| @ -590,7 +590,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/recurring_invoices/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/recurring_invoices/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -601,19 +601,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/recurring_invoices/bulk?action=archive', $data) |         ])->postJson('/api/v1/recurring_invoices/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/recurring_invoices/bulk?action=restore', $data) |         ])->postJson('/api/v1/recurring_invoices/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/recurring_invoices/bulk?action=delete', $data) |         ])->postJson('/api/v1/recurring_invoices/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -636,7 +636,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/clients/', $data) |         ])->postJson('/api/v1/clients/', $data) | ||||||
|             ->assertStatus(200); |             ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -650,7 +650,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/clients/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/clients/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -661,19 +661,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/clients/bulk?action=archive', $data) |         ])->postJson('/api/v1/clients/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/clients/bulk?action=restore', $data) |         ])->postJson('/api/v1/clients/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/clients/bulk?action=delete', $data) |         ])->postJson('/api/v1/clients/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -705,7 +705,7 @@ class EventTest extends TestCase | |||||||
|                 'X-API-SECRET' => config('ninja.api_secret'), |                 'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|                 'X-API-TOKEN' => $this->token, |                 'X-API-TOKEN' => $this->token, | ||||||
|                 'X-API-PASSWORD' => 'ALongAndBriliantPassword', |                 'X-API-PASSWORD' => 'ALongAndBriliantPassword', | ||||||
|         ])->post('/api/v1/users?include=company_user', $data) |         ])->postJson('/api/v1/users?include=company_user', $data) | ||||||
|           ->assertStatus(200); |           ->assertStatus(200); | ||||||
|         |         | ||||||
|         $arr = $response->json(); |         $arr = $response->json(); | ||||||
| @ -725,7 +725,7 @@ class EventTest extends TestCase | |||||||
|             'X-API-SECRET' => config('ninja.api_secret'), |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|             'X-API-TOKEN' => $this->token, |             'X-API-TOKEN' => $this->token, | ||||||
|             'X-API-PASSWORD' => 'ALongAndBriliantPassword', |             'X-API-PASSWORD' => 'ALongAndBriliantPassword', | ||||||
|         ])->put('/api/v1/users/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/users/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -737,21 +737,21 @@ class EventTest extends TestCase | |||||||
|             'X-API-SECRET' => config('ninja.api_secret'), |             'X-API-SECRET' => config('ninja.api_secret'), | ||||||
|             'X-API-TOKEN' => $this->token, |             'X-API-TOKEN' => $this->token, | ||||||
|             'X-API-PASSWORD' => 'ALongAndBriliantPassword', |             'X-API-PASSWORD' => 'ALongAndBriliantPassword', | ||||||
|         ])->post('/api/v1/users/bulk?action=archive', $data) |         ])->postJson('/api/v1/users/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|             'X-API-PASSWORD' => 'ALongAndBriliantPassword', |             'X-API-PASSWORD' => 'ALongAndBriliantPassword', | ||||||
|         ])->post('/api/v1/users/bulk?action=restore', $data) |         ])->postJson('/api/v1/users/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|             'X-API-PASSWORD' => 'ALongAndBriliantPassword', |             'X-API-PASSWORD' => 'ALongAndBriliantPassword', | ||||||
|         ])->post('/api/v1/users/bulk?action=delete', $data) |         ])->postJson('/api/v1/users/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -772,7 +772,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/subscriptions/', $data) |         ])->postJson('/api/v1/subscriptions/', $data) | ||||||
|             ->assertStatus(200); |             ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -785,7 +785,7 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/subscriptions/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/subscriptions/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -796,19 +796,19 @@ class EventTest extends TestCase | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/subscriptions/bulk?action=archive', $data) |         ])->postJson('/api/v1/subscriptions/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/subscriptions/bulk?action=restore', $data) |         ])->postJson('/api/v1/subscriptions/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/subscriptions/bulk?action=delete', $data) |         ])->postJson('/api/v1/subscriptions/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -833,7 +833,7 @@ public function PurchaseOrderEvents() | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/purchase_orders/', $data) |         ])->postJson('/api/v1/purchase_orders/', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -847,7 +847,7 @@ public function PurchaseOrderEvents() | |||||||
|         $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, | ||||||
|         ])->put('/api/v1/purchase_orders/' . $arr['data']['id'], $data) |         ])->putJson('/api/v1/purchase_orders/' . $arr['data']['id'], $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -862,25 +862,25 @@ public function PurchaseOrderEvents() | |||||||
|         $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, | ||||||
|         ])->post('/api/v1/purchase_orders/bulk?action=archive', $data) |         ])->postJson('/api/v1/purchase_orders/bulk?action=archive', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/purchase_orders/bulk?action=restore', $data) |         ])->postJson('/api/v1/purchase_orders/bulk?action=restore', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/purchase_orders/bulk?action=approve', $data) |         ])->postJson('/api/v1/purchase_orders/bulk?action=approve', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
| 
 | 
 | ||||||
|         $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, | ||||||
|         ])->post('/api/v1/purchase_orders/bulk?action=delete', $data) |         ])->postJson('/api/v1/purchase_orders/bulk?action=delete', $data) | ||||||
|         ->assertStatus(200); |         ->assertStatus(200); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								webpack.mix.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								webpack.mix.js
									
									
									
									
										vendored
									
									
								
							| @ -10,6 +10,14 @@ mix.js("resources/js/app.js", "public/js") | |||||||
|         "resources/js/clients/payments/authorize-credit-card-payment.js", |         "resources/js/clients/payments/authorize-credit-card-payment.js", | ||||||
|         "public/js/clients/payments/authorize-credit-card-payment.js" |         "public/js/clients/payments/authorize-credit-card-payment.js" | ||||||
|     ) |     ) | ||||||
|  |     .js( | ||||||
|  |         "resources/js/clients/payments/forte-credit-card-payment.js", | ||||||
|  |         "public/js/clients/payments/forte-credit-card-payment.js" | ||||||
|  |     ) | ||||||
|  |     .js( | ||||||
|  |         "resources/js/clients/payments/forte-ach-payment.js", | ||||||
|  |         "public/js/clients/payments/forte-ach-payment.js" | ||||||
|  |     ) | ||||||
|     .js( |     .js( | ||||||
|         "resources/js/clients/payments/stripe-ach.js", |         "resources/js/clients/payments/stripe-ach.js", | ||||||
|         "public/js/clients/payments/stripe-ach.js" |         "public/js/clients/payments/stripe-ach.js" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user