mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 23:57:33 -05:00 
			
		
		
		
	Merge branch 'v5-develop' into v5-stable
This commit is contained in:
		
						commit
						e5595cf914
					
				@ -61,3 +61,11 @@ SENTRY_LARAVEL_DSN=https://39389664f3f14969b4c43dadda00a40b@sentry2.invoicing.co
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
GOOGLE_PLAY_PACKAGE_NAME=
 | 
					GOOGLE_PLAY_PACKAGE_NAME=
 | 
				
			||||||
APPSTORE_PASSWORD=
 | 
					APPSTORE_PASSWORD=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MICROSOFT_CLIENT_ID=
 | 
				
			||||||
 | 
					MICROSOFT_CLIENT_SECRET=
 | 
				
			||||||
 | 
					MICROSOFT_REDIRECT_URI=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APPLE_CLIENT_ID=
 | 
				
			||||||
 | 
					APPLE_CLIENT_SECRET=
 | 
				
			||||||
 | 
					APPLE_REDIRECT_URI=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,7 @@
 | 
				
			|||||||
/node_modules
 | 
					/node_modules
 | 
				
			||||||
/public/hot
 | 
					/public/hot
 | 
				
			||||||
/public/storage
 | 
					/public/storage
 | 
				
			||||||
 | 
					/public/react
 | 
				
			||||||
/storage/*.key
 | 
					/storage/*.key
 | 
				
			||||||
/vendor
 | 
					/vendor
 | 
				
			||||||
/.idea
 | 
					/.idea
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +1 @@
 | 
				
			|||||||
5.3.98
 | 
					5.4.0
 | 
				
			||||||
@ -62,7 +62,7 @@ class EmailTemplateDefaults
 | 
				
			|||||||
            case 'email_template_custom3':
 | 
					            case 'email_template_custom3':
 | 
				
			||||||
                return self::emailInvoiceTemplate();
 | 
					                return self::emailInvoiceTemplate();
 | 
				
			||||||
            case 'email_template_purchase_order':
 | 
					            case 'email_template_purchase_order':
 | 
				
			||||||
                return self::emailPurchaseOrderSubject();
 | 
					                return self::emailPurchaseOrderTemplate();
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* Subject */
 | 
					            /* Subject */
 | 
				
			||||||
@ -157,7 +157,7 @@ class EmailTemplateDefaults
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public static function emailPurchaseOrderSubject()
 | 
					    public static function emailPurchaseOrderSubject()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return ctrans('texts.purchase_order_subject', ['number' => '$number']);
 | 
					        return ctrans('texts.purchase_order_subject', ['number' => '$number', 'account' => '$account']);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static function emailPurchaseOrderTemplate()
 | 
					    public static function emailPurchaseOrderTemplate()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										51
									
								
								app/Events/PurchaseOrder/PurchaseOrderWasAccepted.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/Events/PurchaseOrder/PurchaseOrderWasAccepted.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					<?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\Events\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrderInvitation;
 | 
				
			||||||
 | 
					use App\Models\VendorContact;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class PurchaseOrderWasAccepted.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class PurchaseOrderWasAccepted
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @var PurchaseOrder
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public $purchase_order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $event_vars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $contact;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new event instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param PurchaseOrder $purchase_order
 | 
				
			||||||
 | 
					     * @param Company $company
 | 
				
			||||||
 | 
					     * @param array $event_vars
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct(PurchaseOrder $purchase_order, VendorContact $contact, Company $company, array $event_vars)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->purchase_order = $purchase_order;
 | 
				
			||||||
 | 
					        $this->contact = $contact;
 | 
				
			||||||
 | 
					        $this->company = $company;
 | 
				
			||||||
 | 
					        $this->event_vars = $event_vars;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -13,6 +13,7 @@ namespace App\Events\PurchaseOrder;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Models\Company;
 | 
					use App\Models\Company;
 | 
				
			||||||
use App\Models\PurchaseOrder;
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrderInvitation;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -25,7 +26,7 @@ class PurchaseOrderWasEmailed
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @var PurchaseOrder
 | 
					     * @var PurchaseOrder
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public $purchase_order;
 | 
					    public $invitation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $company;
 | 
					    public $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,9 +39,9 @@ class PurchaseOrderWasEmailed
 | 
				
			|||||||
     * @param Company $company
 | 
					     * @param Company $company
 | 
				
			||||||
     * @param array $event_vars
 | 
					     * @param array $event_vars
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function __construct(PurchaseOrder $purchase_order, Company $company, array $event_vars)
 | 
					    public function __construct(PurchaseOrderInvitation $invitation, Company $company, array $event_vars)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->purchase_order = $purchase_order;
 | 
					        $this->invitation = $invitation;
 | 
				
			||||||
        $this->company = $company;
 | 
					        $this->company = $company;
 | 
				
			||||||
        $this->event_vars = $event_vars;
 | 
					        $this->event_vars = $event_vars;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ namespace App\Events\PurchaseOrder;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Models\Company;
 | 
					use App\Models\Company;
 | 
				
			||||||
use App\Models\PurchaseOrder;
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrderInvitation;
 | 
				
			||||||
use Illuminate\Queue\SerializesModels;
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -25,7 +26,7 @@ class PurchaseOrderWasViewed
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @var PurchaseOrder
 | 
					     * @var PurchaseOrder
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public $purchase_order;
 | 
					    public $invitation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $company;
 | 
					    public $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,9 +39,9 @@ class PurchaseOrderWasViewed
 | 
				
			|||||||
     * @param Company $company
 | 
					     * @param Company $company
 | 
				
			||||||
     * @param array $event_vars
 | 
					     * @param array $event_vars
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function __construct(PurchaseOrder $purchase_order, Company $company, array $event_vars)
 | 
					    public function __construct(PurchaseOrderInvitation $invitation, Company $company, array $event_vars)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->purchase_order = $purchase_order;
 | 
					        $this->invitation = $invitation;
 | 
				
			||||||
        $this->company = $company;
 | 
					        $this->company = $company;
 | 
				
			||||||
        $this->event_vars = $event_vars;
 | 
					        $this->event_vars = $event_vars;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -222,6 +222,9 @@ class Handler extends ExceptionHandler
 | 
				
			|||||||
            case 'user':
 | 
					            case 'user':
 | 
				
			||||||
                $login = 'login';
 | 
					                $login = 'login';
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'vendor':
 | 
				
			||||||
 | 
					                $login = 'vendor.catchall';
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                $login = 'default';
 | 
					                $login = 'default';
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
				
			|||||||
@ -12,9 +12,11 @@
 | 
				
			|||||||
namespace App\Http\Controllers;
 | 
					namespace App\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Http\Requests\Account\CreateAccountRequest;
 | 
					use App\Http\Requests\Account\CreateAccountRequest;
 | 
				
			||||||
 | 
					use App\Http\Requests\Account\UpdateAccountRequest;
 | 
				
			||||||
use App\Jobs\Account\CreateAccount;
 | 
					use App\Jobs\Account\CreateAccount;
 | 
				
			||||||
use App\Models\Account;
 | 
					use App\Models\Account;
 | 
				
			||||||
use App\Models\CompanyUser;
 | 
					use App\Models\CompanyUser;
 | 
				
			||||||
 | 
					use App\Transformers\AccountTransformer;
 | 
				
			||||||
use App\Transformers\CompanyUserTransformer;
 | 
					use App\Transformers\CompanyUserTransformer;
 | 
				
			||||||
use App\Utils\TruthSource;
 | 
					use App\Utils\TruthSource;
 | 
				
			||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
 | 
					use Illuminate\Foundation\Bus\DispatchesJobs;
 | 
				
			||||||
@ -157,4 +159,22 @@ class AccountController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return $this->listResponse($ct);
 | 
					        return $this->listResponse($ct);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function update(UpdateAccountRequest $request, Account $account)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $fi = new \FilesystemIterator(public_path('react'), \FilesystemIterator::SKIP_DOTS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(iterator_count($fi) < 30)
 | 
				
			||||||
 | 
					            return response()->json(['message' => 'React App Not Installed, Please install the React app before attempting to switch.'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $account->fill($request->all());
 | 
				
			||||||
 | 
					        $account->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->entity_type = Account::class;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->entity_transformer = AccountTransformer::class;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->itemResponse($account);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -92,7 +92,7 @@ class LoginController extends BaseController
 | 
				
			|||||||
     * @return void
 | 
					     * @return void
 | 
				
			||||||
     * deprecated .1 API ONLY we don't need to set any session variables
 | 
					     * deprecated .1 API ONLY we don't need to set any session variables
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function authenticated(Request $request, User $user) : void
 | 
					    public function authenticated(Request $request, User $user): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        //$this->setCurrentCompanyId($user->companies()->first()->account->default_company_id);
 | 
					        //$this->setCurrentCompanyId($user->companies()->first()->account->default_company_id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -168,9 +168,9 @@ class LoginController extends BaseController
 | 
				
			|||||||
            $this->fireLockoutEvent($request);
 | 
					            $this->fireLockoutEvent($request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return response()
 | 
					            return response()
 | 
				
			||||||
            ->json(['message' => 'Too many login attempts, you are being throttled'], 401)
 | 
					                ->json(['message' => 'Too many login attempts, you are being throttled'], 401)
 | 
				
			||||||
            ->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'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->attemptLogin($request)) {
 | 
					        if ($this->attemptLogin($request)) {
 | 
				
			||||||
@ -250,7 +250,7 @@ class LoginController extends BaseController
 | 
				
			|||||||
            // $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $user->account->default_company->id)->first());
 | 
					            // $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $user->account->default_company->id)->first());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /*On the hosted platform, only owners can login for free/pro accounts*/
 | 
					            /*On the hosted platform, only owners can login for free/pro accounts*/
 | 
				
			||||||
            if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
 | 
					            if (Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
 | 
				
			||||||
                return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
					                return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
 | 
					            event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
 | 
				
			||||||
@ -267,9 +267,9 @@ class LoginController extends BaseController
 | 
				
			|||||||
            $this->incrementLoginAttempts($request);
 | 
					            $this->incrementLoginAttempts($request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return response()
 | 
					            return response()
 | 
				
			||||||
            ->json(['message' => ctrans('texts.invalid_credentials')], 401)
 | 
					                ->json(['message' => ctrans('texts.invalid_credentials')], 401)
 | 
				
			||||||
            ->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'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -317,29 +317,28 @@ class LoginController extends BaseController
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $truth = app()->make(TruthSource::class);
 | 
					        $truth = app()->make(TruthSource::class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($truth->getCompanyToken())
 | 
					        if ($truth->getCompanyToken())
 | 
				
			||||||
            $company_token = $truth->getCompanyToken();
 | 
					            $company_token = $truth->getCompanyToken();
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
            $company_token = CompanyToken::where('token', $request->header('X-API-TOKEN'))->first();
 | 
					            $company_token = CompanyToken::where('token', $request->header('X-API-TOKEN'))->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $cu = CompanyUser::query()
 | 
					        $cu = CompanyUser::query()
 | 
				
			||||||
                          ->where('user_id', $company_token->user_id);
 | 
					            ->where('user_id', $company_token->user_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($cu->count() == 0)
 | 
					        if ($cu->count() == 0)
 | 
				
			||||||
            return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
					            return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $cu->first()->account->companies->each(function ($company) use($cu, $request){
 | 
					        $cu->first()->account->companies->each(function ($company) use ($cu, $request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if($company->tokens()->where('is_system', true)->count() == 0)
 | 
					            if ($company->tokens()->where('is_system', true)->count() == 0) {
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT'));
 | 
					                CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT'));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($request->has('current_company') && $request->input('current_company') == 'true')
 | 
					        if ($request->has('current_company') && $request->input('current_company') == 'true')
 | 
				
			||||||
          $cu->where("company_id", $company_token->company_id);
 | 
					            $cu->where("company_id", $company_token->company_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient())
 | 
					        if (Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient())
 | 
				
			||||||
            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
					            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this->refreshResponse($cu);
 | 
					        return $this->refreshResponse($cu);
 | 
				
			||||||
@ -359,24 +358,134 @@ class LoginController extends BaseController
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function oauthApiLogin()
 | 
					    public function oauthApiLogin()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $message = 'Provider not supported';
 | 
				
			||||||
        if (request()->input('provider') == 'google') {
 | 
					        if (request()->input('provider') == 'google') {
 | 
				
			||||||
            return $this->handleGoogleOauth();
 | 
					            return $this->handleGoogleOauth();
 | 
				
			||||||
 | 
					        } 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';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } elseif (request()->input('provider') == 'apple') {
 | 
				
			||||||
 | 
					            if (request()->has('token')) {
 | 
				
			||||||
 | 
					                return $this->handleSocialiteLogin('apple', request()->get('token'));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                $message = 'Token is missing for the apple login';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return response()
 | 
					        return response()
 | 
				
			||||||
        ->json(['message' => 'Provider not supported'], 400)
 | 
					            ->json(['message' => $message], 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 hydrateCompanyUser() :Builder
 | 
					    private function getSocialiteUser(string $provider, string $token)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Socialite::driver($provider)->userFromToken($token);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function handleSocialiteLogin($provider, $token)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $user = $this->getSocialiteUser($provider, $token);
 | 
				
			||||||
 | 
					        if ($user) {
 | 
				
			||||||
 | 
					            return $this->loginOrCreateFromSocialite($user, $provider);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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 loginOrCreateFromSocialite($user, $provider)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $query = [
 | 
				
			||||||
 | 
					            'oauth_user_id' => $user->id,
 | 
				
			||||||
 | 
					            'oauth_provider_id' => $provider,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        if ($existing_user = MultiDB::hasUser($query)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!$existing_user->account)
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Auth::login($existing_user, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $cu = $this->hydrateCompanyUser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($cu->count() == 0)
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return $this->timeConstrainedResponse($cu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        //If this is a result user/email combo - lets add their OAuth details details
 | 
				
			||||||
 | 
					        if ($existing_login_user = MultiDB::hasUser(['email' => $user->email])) {
 | 
				
			||||||
 | 
					            if (!$existing_login_user->account)
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Auth::login($existing_login_user, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auth()->user()->update([
 | 
				
			||||||
 | 
					                'oauth_user_id' => $user->id,
 | 
				
			||||||
 | 
					                'oauth_provider_id' => $provider,
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $cu = $this->hydrateCompanyUser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($cu->count() == 0)
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
 | 
				
			||||||
 | 
					                return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return $this->timeConstrainedResponse($cu);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $name = OAuth::splitName($user->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $new_account = [
 | 
				
			||||||
 | 
					            'first_name' => $name[0],
 | 
				
			||||||
 | 
					            'last_name' => $name[1],
 | 
				
			||||||
 | 
					            'password' => '',
 | 
				
			||||||
 | 
					            'email' => $user->email,
 | 
				
			||||||
 | 
					            'oauth_user_id' => $user->id,
 | 
				
			||||||
 | 
					            'oauth_provider_id' => $provider,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MultiDB::setDefaultDatabase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Auth::login($account->default_company->owner(), true);
 | 
				
			||||||
 | 
					        auth()->user()->email_verified_at = now();
 | 
				
			||||||
 | 
					        auth()->user()->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $cu = $this->hydrateCompanyUser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($cu->count() == 0)
 | 
				
			||||||
 | 
					            return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
 | 
				
			||||||
 | 
					            return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->timeConstrainedResponse($cu);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function hydrateCompanyUser(): Builder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $cu = CompanyUser::query()->where('user_id', auth()->user()->id);
 | 
					        $cu = CompanyUser::query()->where('user_id', auth()->user()->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists())
 | 
					        if (CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists())
 | 
				
			||||||
            $set_company = auth()->user()->account->default_company;
 | 
					            $set_company = auth()->user()->account->default_company;
 | 
				
			||||||
        else{
 | 
					        else {
 | 
				
			||||||
            $set_company = $cu->first()->company;
 | 
					            $set_company = $cu->first()->company;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -392,14 +501,13 @@ class LoginController extends BaseController
 | 
				
			|||||||
        if($cu->count() == 0)
 | 
					        if($cu->count() == 0)
 | 
				
			||||||
            return $cu;
 | 
					            return $cu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count())
 | 
					        if (auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) {
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          auth()->user()->companies->each(function($company){
 | 
					            auth()->user()->companies->each(function ($company) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()){
 | 
					                if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth");
 | 
					                    CreateCompanyToken::dispatchNow($company, auth()->user(), "Google_O_Auth");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -494,7 +602,7 @@ class LoginController extends BaseController
 | 
				
			|||||||
                // $cu = CompanyUser::query()
 | 
					                // $cu = CompanyUser::query()
 | 
				
			||||||
                //                   ->where('user_id', auth()->user()->id);
 | 
					                //                   ->where('user_id', auth()->user()->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if($cu->count() == 0)
 | 
					                if ($cu->count() == 0)
 | 
				
			||||||
                    return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
					                    return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
 | 
					                if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										58
									
								
								app/Http/Controllers/Auth/VendorContactLoginController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/Http/Controllers/Auth/VendorContactLoginController.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					<?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\Auth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Events\Contact\ContactLoggedIn;
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Http\ViewComposers\PortalComposer;
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Models\Account;
 | 
				
			||||||
 | 
					use App\Models\ClientContact;
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use Auth;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Auth\AuthenticatesUsers;
 | 
				
			||||||
 | 
					use Illuminate\Http\JsonResponse;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Hash;
 | 
				
			||||||
 | 
					use Route;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VendorContactLoginController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use AuthenticatesUsers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected $redirectTo = '/vendor/purchase_orders';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->middleware('guest:vendor', ['except' => ['logout']]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function catch()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->render('purchase_orders.catch');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function logout()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Auth::guard('vendor')->logout();
 | 
				
			||||||
 | 
					        request()->session()->invalidate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return redirect('/vendors');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -84,6 +84,7 @@ class BaseController extends Controller
 | 
				
			|||||||
          'company.products.documents',
 | 
					          'company.products.documents',
 | 
				
			||||||
          'company.payments.paymentables',
 | 
					          'company.payments.paymentables',
 | 
				
			||||||
          'company.payments.documents',
 | 
					          'company.payments.documents',
 | 
				
			||||||
 | 
					          'company.purchase_orders.documents',
 | 
				
			||||||
          'company.payment_terms.company',
 | 
					          'company.payment_terms.company',
 | 
				
			||||||
          'company.projects.documents',
 | 
					          'company.projects.documents',
 | 
				
			||||||
          'company.recurring_expenses',
 | 
					          'company.recurring_expenses',
 | 
				
			||||||
@ -171,7 +172,12 @@ class BaseController extends Controller
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function notFoundClient()
 | 
					    public function notFoundClient()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        abort(404, 'Page not found in client portal.');
 | 
					        abort(404, 'Page not found in the client portal.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function notFoundVendor()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        abort(404, 'Page not found in the vendor portal.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -296,6 +302,13 @@ class BaseController extends Controller
 | 
				
			|||||||
                if(!$user->hasPermission('view_project'))
 | 
					                if(!$user->hasPermission('view_project'))
 | 
				
			||||||
                  $query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
 | 
					                  $query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            'company.purchase_orders'=> function ($query) use ($updated_at, $user) {
 | 
				
			||||||
 | 
					                $query->where('updated_at', '>=', $updated_at)->with('documents');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(!$user->hasPermission('view_purchase_order'))
 | 
				
			||||||
 | 
					                  $query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            'company.quotes'=> function ($query) use ($updated_at, $user) {
 | 
					            'company.quotes'=> function ($query) use ($updated_at, $user) {
 | 
				
			||||||
                $query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
 | 
					                $query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
 | 
				
			||||||
@ -533,6 +546,13 @@ class BaseController extends Controller
 | 
				
			|||||||
                if(!$user->hasPermission('view_project'))
 | 
					                if(!$user->hasPermission('view_project'))
 | 
				
			||||||
                  $query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
 | 
					                  $query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            'company.purchase_orders'=> function ($query) use ($created_at, $user) {
 | 
				
			||||||
 | 
					                $query->where('created_at', '>=', $created_at)->with('documents');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(!$user->hasPermission('view_purchase_order'))
 | 
				
			||||||
 | 
					                  $query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            'company.quotes'=> function ($query) use ($created_at, $user) {
 | 
					            'company.quotes'=> function ($query) use ($created_at, $user) {
 | 
				
			||||||
                $query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
 | 
					                $query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
 | 
				
			||||||
@ -780,7 +800,7 @@ class BaseController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            $this->buildCache();
 | 
					            $this->buildCache();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(config('ninja.react_app_enabled'))
 | 
					            if(Ninja::isSelfHost() && $account->set_react_as_default_ap)
 | 
				
			||||||
                return response()->view('react.index', $data)->header('X-Frame-Options', 'SAMEORIGIN', false);
 | 
					                return response()->view('react.index', $data)->header('X-Frame-Options', 'SAMEORIGIN', false);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                return response()->view('index.index', $data)->header('X-Frame-Options', 'SAMEORIGIN', false);
 | 
					                return response()->view('index.index', $data)->header('X-Frame-Options', 'SAMEORIGIN', false);
 | 
				
			||||||
 | 
				
			|||||||
@ -154,9 +154,11 @@ class EntityViewController extends Controller
 | 
				
			|||||||
        if (! $invitation->viewed_date) {
 | 
					        if (! $invitation->viewed_date) {
 | 
				
			||||||
            $invitation->markViewed();
 | 
					            $invitation->markViewed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            event(new InvitationWasViewed($invitation->{$request->entity_type}, $invitation, $invitation->{$request->entity_type}->company, Ninja::eventVars()));
 | 
					            if(!session()->get('is_silent'))
 | 
				
			||||||
 | 
					                event(new InvitationWasViewed($invitation->{$request->entity_type}, $invitation, $invitation->{$request->entity_type}->company, Ninja::eventVars()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->fireEntityViewedEvent($invitation, $request->entity_type);
 | 
					            if(!session()->get('is_silent'))
 | 
				
			||||||
 | 
					                $this->fireEntityViewedEvent($invitation, $request->entity_type);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return redirect()->route('client.'.$request->entity_type.'.show', [$request->entity_type => $this->encodePrimaryKey($invitation->{$key})]);
 | 
					        return redirect()->route('client.'.$request->entity_type.'.show', [$request->entity_type => $this->encodePrimaryKey($invitation->{$key})]);
 | 
				
			||||||
 | 
				
			|||||||
@ -129,9 +129,11 @@ class InvitationController extends Controller
 | 
				
			|||||||
        if (auth()->guard('contact')->user() && ! request()->has('silent') && ! $invitation->viewed_date) {
 | 
					        if (auth()->guard('contact')->user() && ! request()->has('silent') && ! $invitation->viewed_date) {
 | 
				
			||||||
            $invitation->markViewed();
 | 
					            $invitation->markViewed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            event(new InvitationWasViewed($invitation->{$entity}, $invitation, $invitation->{$entity}->company, Ninja::eventVars()));
 | 
					            if(!session()->get('is_silent'))
 | 
				
			||||||
 | 
					                event(new InvitationWasViewed($invitation->{$entity}, $invitation, $invitation->{$entity}->company, Ninja::eventVars()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->fireEntityViewedEvent($invitation, $entity);
 | 
					            if(!session()->get('is_silent'))
 | 
				
			||||||
 | 
					                $this->fireEntityViewedEvent($invitation, $entity);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else{
 | 
					        else{
 | 
				
			||||||
            $is_silent = 'true';
 | 
					            $is_silent = 'true';
 | 
				
			||||||
 | 
				
			|||||||
@ -61,7 +61,7 @@ class InvoiceController extends Controller
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
 | 
					        $invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) {
 | 
					            if ($invitation && auth()->guard('contact') && !session()->get('is_silent') && ! $invitation->viewed_date) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $invitation->markViewed();
 | 
					                $invitation->markViewed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -154,6 +154,7 @@ class NinjaPlanController extends Controller
 | 
				
			|||||||
        $recurring_invoice->auto_bill_enabled =  $this->setAutoBillFlag($recurring_invoice->auto_bill);
 | 
					        $recurring_invoice->auto_bill_enabled =  $this->setAutoBillFlag($recurring_invoice->auto_bill);
 | 
				
			||||||
        $recurring_invoice->due_date_days = 'terms';
 | 
					        $recurring_invoice->due_date_days = 'terms';
 | 
				
			||||||
        $recurring_invoice->next_send_date = now()->addDays(14)->format('Y-m-d');
 | 
					        $recurring_invoice->next_send_date = now()->addDays(14)->format('Y-m-d');
 | 
				
			||||||
 | 
					        $recurring_invoice->next_send_date_client = now()->addDays(14)->format('Y-m-d');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $recurring_invoice->save();
 | 
					        $recurring_invoice->save();
 | 
				
			||||||
        $r = $recurring_invoice->calc()->getRecurringInvoice();
 | 
					        $r = $recurring_invoice->calc()->getRecurringInvoice();
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,8 @@ use App\Http\Requests\PurchaseOrder\ShowPurchaseOrderRequest;
 | 
				
			|||||||
use App\Http\Requests\PurchaseOrder\StorePurchaseOrderRequest;
 | 
					use App\Http\Requests\PurchaseOrder\StorePurchaseOrderRequest;
 | 
				
			||||||
use App\Http\Requests\PurchaseOrder\UpdatePurchaseOrderRequest;
 | 
					use App\Http\Requests\PurchaseOrder\UpdatePurchaseOrderRequest;
 | 
				
			||||||
use App\Jobs\Invoice\ZipInvoices;
 | 
					use App\Jobs\Invoice\ZipInvoices;
 | 
				
			||||||
 | 
					use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
 | 
				
			||||||
 | 
					use App\Jobs\PurchaseOrder\ZipPurchaseOrders;
 | 
				
			||||||
use App\Models\Client;
 | 
					use App\Models\Client;
 | 
				
			||||||
use App\Models\PurchaseOrder;
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
use App\Repositories\PurchaseOrderRepository;
 | 
					use App\Repositories\PurchaseOrderRepository;
 | 
				
			||||||
@ -31,6 +33,7 @@ use App\Transformers\PurchaseOrderTransformer;
 | 
				
			|||||||
use App\Utils\Ninja;
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
use Illuminate\Http\Response;
 | 
					use Illuminate\Http\Response;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PurchaseOrderController extends BaseController
 | 
					class PurchaseOrderController extends BaseController
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -183,6 +186,7 @@ class PurchaseOrderController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $purchase_order = $purchase_order->service()
 | 
					        $purchase_order = $purchase_order->service()
 | 
				
			||||||
            ->fillDefaults()
 | 
					            ->fillDefaults()
 | 
				
			||||||
 | 
					            ->triggeredActions($request)
 | 
				
			||||||
            ->save();
 | 
					            ->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        event(new PurchaseOrderWasCreated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
 | 
					        event(new PurchaseOrderWasCreated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
 | 
				
			||||||
@ -485,7 +489,7 @@ class PurchaseOrderController extends BaseController
 | 
				
			|||||||
         * Download Purchase Order/s
 | 
					         * Download Purchase Order/s
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($action == 'bulk_download' && $purchase_orders->count() > 1) {
 | 
					        if ($action == 'bulk_download' && $purchase_orders->count() >= 1) {
 | 
				
			||||||
            $purchase_orders->each(function ($purchase_order) {
 | 
					            $purchase_orders->each(function ($purchase_order) {
 | 
				
			||||||
                if (auth()->user()->cannot('view', $purchase_order)) {
 | 
					                if (auth()->user()->cannot('view', $purchase_order)) {
 | 
				
			||||||
                    nlog("access denied");
 | 
					                    nlog("access denied");
 | 
				
			||||||
@ -493,7 +497,7 @@ class PurchaseOrderController extends BaseController
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ZipInvoices::dispatch($purchase_orders, $purchase_orders->first()->company, auth()->user());
 | 
					            ZipPurchaseOrders::dispatch($purchase_orders, $purchase_orders->first()->company, auth()->user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return response()->json(['message' => ctrans('texts.sent_message')], 200);
 | 
					            return response()->json(['message' => ctrans('texts.sent_message')], 200);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -579,7 +583,7 @@ class PurchaseOrderController extends BaseController
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function action(ActionPurchaseOrderRequest $request, PurchaseOrder $purchase_order, $action)
 | 
					    public function action(ActionPurchaseOrderRequest $request, PurchaseOrder $purchase_order, $action)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->performAction($invoice, $action);
 | 
					        return $this->performAction($purchase_order, $action);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function performAction(PurchaseOrder $purchase_order, $action, $bulk = false)
 | 
					    private function performAction(PurchaseOrder $purchase_order, $action, $bulk = false)
 | 
				
			||||||
@ -627,8 +631,13 @@ class PurchaseOrderController extends BaseController
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            case 'email':
 | 
					            case 'email':
 | 
				
			||||||
                //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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (! $bulk) {
 | 
				
			||||||
 | 
					                    return response()->json(['message' => 'email sent'], 200);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            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;
 | 
				
			||||||
 | 
				
			|||||||
@ -135,6 +135,9 @@ class SelfUpdateController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        nlog("Extracting zip");
 | 
					        nlog("Extracting zip");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //clean up old snappdf installations
 | 
				
			||||||
 | 
					        $this->cleanOldSnapChromeBinaries();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        // try{
 | 
					        // try{
 | 
				
			||||||
        //     $s = new Snappdf;
 | 
					        //     $s = new Snappdf;
 | 
				
			||||||
        //     $s->getChromiumPath();
 | 
					        //     $s->getChromiumPath();
 | 
				
			||||||
@ -190,6 +193,46 @@ class SelfUpdateController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function cleanOldSnapChromeBinaries()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $current_revision =  base_path('vendor/beganovich/snappdf/versions/revision.txt');
 | 
				
			||||||
 | 
					        $current_revision_text = file_get_contents($current_revision);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $iterator = new \DirectoryIterator(base_path('vendor/beganovich/snappdf/versions'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($iterator as $file) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if($file->isDir() && !$file->isDot() && ($current_revision_text != $file->getFileName()))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $directoryIterator = new \RecursiveDirectoryIterator(base_path('vendor/beganovich/snappdf/versions/'.$file->getFileName()), \RecursiveDirectoryIterator::SKIP_DOTS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach (new \RecursiveIteratorIterator($directoryIterator) as $filex) 
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                  unlink($filex->getPathName());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $this->deleteDirectory(base_path('vendor/beganovich/snappdf/versions/'.$file->getFileName()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function deleteDirectory($dir) {
 | 
				
			||||||
 | 
					        if (!file_exists($dir)) return true;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        if (!is_dir($dir) || is_link($dir)) return unlink($dir);
 | 
				
			||||||
 | 
					            foreach (scandir($dir) as $item) {
 | 
				
			||||||
 | 
					                if ($item == '.' || $item == '..') continue;
 | 
				
			||||||
 | 
					                if (!$this->deleteDirectory($dir . "/" . $item)) {
 | 
				
			||||||
 | 
					                    if (!$this->deleteDirectory($dir . "/" . $item)) return false;
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return rmdir($dir);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function postHookUpdate()
 | 
					    private function postHookUpdate()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if(config('ninja.app_version') == '5.3.82')
 | 
					        if(config('ninja.app_version') == '5.3.82')
 | 
				
			||||||
 | 
				
			|||||||
@ -35,6 +35,7 @@ class SubdomainController extends BaseController
 | 
				
			|||||||
        'html',
 | 
					        'html',
 | 
				
			||||||
        'lb',
 | 
					        'lb',
 | 
				
			||||||
        'shopify',
 | 
					        'shopify',
 | 
				
			||||||
 | 
					        'beta',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct()
 | 
					    public function __construct()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										144
									
								
								app/Http/Controllers/VendorPortal/InvitationController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								app/Http/Controllers/VendorPortal/InvitationController.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					<?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\VendorPortal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Events\Credit\CreditWasViewed;
 | 
				
			||||||
 | 
					use App\Events\Invoice\InvoiceWasViewed;
 | 
				
			||||||
 | 
					use App\Events\Misc\InvitationWasViewed;
 | 
				
			||||||
 | 
					use App\Events\Quote\QuoteWasViewed;
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Jobs\Entity\CreateRawPdf;
 | 
				
			||||||
 | 
					use App\Models\Client;
 | 
				
			||||||
 | 
					use App\Models\ClientContact;
 | 
				
			||||||
 | 
					use App\Models\CreditInvitation;
 | 
				
			||||||
 | 
					use App\Models\InvoiceInvitation;
 | 
				
			||||||
 | 
					use App\Models\Payment;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrderInvitation;
 | 
				
			||||||
 | 
					use App\Models\QuoteInvitation;
 | 
				
			||||||
 | 
					use App\Services\ClientPortal\InstantPayment;
 | 
				
			||||||
 | 
					use App\Utils\CurlUtils;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesDates;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Auth;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class InvitationController.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class InvitationController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use MakesHash;
 | 
				
			||||||
 | 
					    use MakesDates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function purchaseOrder(string $invitation_key)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Auth::logout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $invitation = PurchaseOrderInvitation::where('key', $invitation_key)
 | 
				
			||||||
 | 
					                                    ->whereHas('purchase_order', function ($query) {
 | 
				
			||||||
 | 
					                                         $query->where('is_deleted',0);
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                                    ->with('contact.vendor')
 | 
				
			||||||
 | 
					                                    ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!$invitation)
 | 
				
			||||||
 | 
					            return abort(404,'The resource is no longer available.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($invitation->contact->trashed())
 | 
				
			||||||
 | 
					            $invitation->contact->restore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $vendor_contact = $invitation->contact;
 | 
				
			||||||
 | 
					        $entity = 'purchase_order';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(empty($vendor_contact->email))
 | 
				
			||||||
 | 
					            $vendor_contact->email = Str::random(15) . "@example.com"; $vendor_contact->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (request()->has('vendor_hash') && request()->input('vendor_hash') == $invitation->contact->vendor->vendor_hash) {
 | 
				
			||||||
 | 
					            request()->session()->invalidate();
 | 
				
			||||||
 | 
					            auth()->guard('vendor')->loginUsingId($vendor_contact->id, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            nlog("else - default - login contact");
 | 
				
			||||||
 | 
					            request()->session()->invalidate();
 | 
				
			||||||
 | 
					            auth()->guard('vendor')->loginUsingId($vendor_contact->id, true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        session()->put('is_silent', request()->has('silent'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (auth()->guard('vendor')->user() && ! session()->get('is_silent') && ! $invitation->viewed_date) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $invitation->markViewed();
 | 
				
			||||||
 | 
					            event(new InvitationWasViewed($invitation->purchase_order, $invitation, $invitation->company, Ninja::eventVars()));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return redirect()->route('vendor.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->purchase_order_id), 'silent' => session()->get('is_silent')]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return redirect()->route('vendor.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->purchase_order_id)]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // public function routerForDownload(string $entity, string $invitation_key)
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     set_time_limit(45);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     if(Ninja::isHosted())
 | 
				
			||||||
 | 
					    //         return $this->returnRawPdf($entity, $invitation_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     return redirect('client/'.$entity.'/'.$invitation_key.'/download_pdf');
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // private function returnRawPdf(string $entity, string $invitation_key)
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     if(!in_array($entity, ['invoice', 'credit', 'quote', 'recurring_invoice']))
 | 
				
			||||||
 | 
					    //         return response()->json(['message' => 'Invalid resource request']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										229
									
								
								app/Http/Controllers/VendorPortal/PurchaseOrderController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								app/Http/Controllers/VendorPortal/PurchaseOrderController.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,229 @@
 | 
				
			|||||||
 | 
					<?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\VendorPortal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Events\Misc\InvitationWasViewed;
 | 
				
			||||||
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasAccepted;
 | 
				
			||||||
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Http\Requests\VendorPortal\PurchaseOrders\ProcessPurchaseOrdersInBulkRequest;
 | 
				
			||||||
 | 
					use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrderRequest;
 | 
				
			||||||
 | 
					use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrdersRequest;
 | 
				
			||||||
 | 
					use App\Jobs\Invoice\InjectSignature;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesDates;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\View\Factory;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					use Illuminate\View\View;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrderController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use MakesHash, MakesDates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_INVOICES = 1;
 | 
				
			||||||
 | 
					    public const MODULE_CREDITS = 2;
 | 
				
			||||||
 | 
					    public const MODULE_QUOTES = 4;
 | 
				
			||||||
 | 
					    public const MODULE_TASKS = 8;
 | 
				
			||||||
 | 
					    public const MODULE_EXPENSES = 16;
 | 
				
			||||||
 | 
					    public const MODULE_PROJECTS = 32;
 | 
				
			||||||
 | 
					    public const MODULE_VENDORS = 64;
 | 
				
			||||||
 | 
					    public const MODULE_TICKETS = 128;
 | 
				
			||||||
 | 
					    public const MODULE_PROPOSALS = 256;
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_EXPENSES = 512;
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_TASKS = 1024;
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_QUOTES = 2048;
 | 
				
			||||||
 | 
					    public const MODULE_INVOICES = 4096;
 | 
				
			||||||
 | 
					    public const MODULE_PROFORMAL_INVOICES = 8192;
 | 
				
			||||||
 | 
					    public const MODULE_PURCHASE_ORDERS = 16384;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Display list of invoices.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Factory|View
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function index(ShowPurchaseOrdersRequest $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->render('purchase_orders.index', ['company' => auth()->user()->company, 'settings' => auth()->user()->company->settings, 'sidebar' => $this->sidebarMenu()]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Show specific invoice.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ShowInvoiceRequest $request
 | 
				
			||||||
 | 
					     * @param Invoice $invoice
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Factory|View
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function show(ShowPurchaseOrderRequest $request, PurchaseOrder $purchase_order)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        set_time_limit(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $invitation = $purchase_order->invitations()->where('vendor_contact_id', auth()->guard('vendor')->user()->id)->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($invitation && auth()->guard('vendor') && !session()->get('is_silent') && ! $invitation->viewed_date) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $invitation->markViewed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                event(new InvitationWasViewed($purchase_order, $invitation, $purchase_order->company, Ninja::eventVars()));
 | 
				
			||||||
 | 
					                event(new PurchaseOrderWasViewed($invitation, $invitation->company, Ninja::eventVars()));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'purchase_order' => $purchase_order,
 | 
				
			||||||
 | 
					            'key' => $invitation ? $invitation->key : false,
 | 
				
			||||||
 | 
					            'settings' => $purchase_order->company->settings,
 | 
				
			||||||
 | 
					            'sidebar' => $this->sidebarMenu(),
 | 
				
			||||||
 | 
					            'company' => $purchase_order->company
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($request->query('mode') === 'fullscreen') {
 | 
				
			||||||
 | 
					            return render('purchase_orders.show-fullscreen', $data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->render('purchase_orders.show', $data);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function sidebarMenu() :array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;
 | 
				
			||||||
 | 
					        $data = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: Enable dashboard once it's completed.
 | 
				
			||||||
 | 
					        // $this->settings->enable_client_portal_dashboard
 | 
				
			||||||
 | 
					        // $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (self::MODULE_PURCHASE_ORDERS & $enabled_modules) {
 | 
				
			||||||
 | 
					            $data[] = ['title' => ctrans('texts.purchase_orders'), 'url' => 'vendor.purchase_orders.index', 'icon' => 'file-text'];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // $data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function bulk(ProcessPurchaseOrdersInBulkRequest $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $transformed_ids = $this->transformKeys($request->purchase_orders);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($request->input('action') == 'download') {
 | 
				
			||||||
 | 
					            return $this->downloadInvoices((array) $transformed_ids);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        elseif ($request->input('action') == 'accept'){
 | 
				
			||||||
 | 
					            return $this->acceptPurchaseOrder($request->all());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return redirect()
 | 
				
			||||||
 | 
					            ->back()
 | 
				
			||||||
 | 
					            ->with('message', ctrans('texts.no_action_provided'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function acceptPurchaseOrder($data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $purchase_orders = PurchaseOrder::query()
 | 
				
			||||||
 | 
					                                        ->whereIn('id', $this->transformKeys($data['purchase_orders']))
 | 
				
			||||||
 | 
					                                        ->where('company_id', auth()->guard('vendor')->user()->vendor->company_id)
 | 
				
			||||||
 | 
					                                        ->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT])
 | 
				
			||||||
 | 
					                                        ->cursor()->each(function ($purchase_order){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        $purchase_order->service()
 | 
				
			||||||
 | 
					                                                       ->markSent()
 | 
				
			||||||
 | 
					                                                       ->applyNumber()
 | 
				
			||||||
 | 
					                                                       ->setStatus(PurchaseOrder::STATUS_ACCEPTED)
 | 
				
			||||||
 | 
					                                                       ->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        if (request()->has('signature') && !is_null(request()->signature) && !empty(request()->signature)) {
 | 
				
			||||||
 | 
					                                            InjectSignature::dispatch($purchase_order, request()->signature);
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                        
 | 
				
			||||||
 | 
					                                        event(new PurchaseOrderWasAccepted($purchase_order, auth()->guard('vendor')->user(), $purchase_order->company, Ninja::eventVars()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(count($data['purchase_orders']) == 1){ 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $purchase_order = PurchaseOrder::whereIn('id', $this->transformKeys($data['purchase_orders']))->first();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return redirect()->route('vendor.purchase_order.show', ['purchase_order' => $purchase_order->hashed_id]);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return redirect()->route('vendor.purchase_orders.index');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function downloadInvoices($ids)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $purchase_orders = PurchaseOrder::whereIn('id', $ids)
 | 
				
			||||||
 | 
					                            ->where('vendor_id', auth()->guard('vendor')->user()->vendor_id)
 | 
				
			||||||
 | 
					                            ->withTrashed()
 | 
				
			||||||
 | 
					                            ->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(count($purchase_orders) == 0)
 | 
				
			||||||
 | 
					            return back()->with(['message' => ctrans('texts.no_items_selected')]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(count($purchase_orders) == 1){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           $purchase_order = $purchase_orders->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           $file = $purchase_order->service()->getPurchaseOrderPdf(auth()->guard('vendor')->user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return response()->streamDownload(function () use($file) {
 | 
				
			||||||
 | 
					                    echo Storage::get($file);
 | 
				
			||||||
 | 
					            },  basename($file), ['Content-Type' => 'application/pdf']);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->buildZip($purchase_orders);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function buildZip($purchase_orders)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // create new archive
 | 
				
			||||||
 | 
					        $zipFile = new \PhpZip\ZipFile();
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            foreach ($purchase_orders as $purchase_order) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                #add it to the zip
 | 
				
			||||||
 | 
					                $zipFile->addFromString(basename($purchase_order->pdf_file_path()), file_get_contents($purchase_order->pdf_file_path(null, 'url', true)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $filename = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.purchase_orders')).'.zip';
 | 
				
			||||||
 | 
					            $filepath = sys_get_temp_dir() . '/' . $filename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           $zipFile->saveAsFile($filepath) // save the archive to a file
 | 
				
			||||||
 | 
					                   ->close(); // close archive
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					           return response()->download($filepath, $filename)->deleteFileAfterSend(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(\PhpZip\Exception\ZipException $e){
 | 
				
			||||||
 | 
					            // handle exception
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        finally{
 | 
				
			||||||
 | 
					            $zipFile->close();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					<?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\VendorPortal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Controllers\Controller;
 | 
				
			||||||
 | 
					use App\Models\VendorContact;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use App\Utils\TranslationHelper;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VendorContactController extends Controller
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use MakesHash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_INVOICES = 1;
 | 
				
			||||||
 | 
					    public const MODULE_CREDITS = 2;
 | 
				
			||||||
 | 
					    public const MODULE_QUOTES = 4;
 | 
				
			||||||
 | 
					    public const MODULE_TASKS = 8;
 | 
				
			||||||
 | 
					    public const MODULE_EXPENSES = 16;
 | 
				
			||||||
 | 
					    public const MODULE_PROJECTS = 32;
 | 
				
			||||||
 | 
					    public const MODULE_VENDORS = 64;
 | 
				
			||||||
 | 
					    public const MODULE_TICKETS = 128;
 | 
				
			||||||
 | 
					    public const MODULE_PROPOSALS = 256;
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_EXPENSES = 512;
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_TASKS = 1024;
 | 
				
			||||||
 | 
					    public const MODULE_RECURRING_QUOTES = 2048;
 | 
				
			||||||
 | 
					    public const MODULE_INVOICES = 4096;
 | 
				
			||||||
 | 
					    public const MODULE_PROFORMAL_INVOICES = 8192;
 | 
				
			||||||
 | 
					    public const MODULE_PURCHASE_ORDERS = 16384;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function edit(VendorContact $vendor_contact)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->render('vendor_profile.edit', [
 | 
				
			||||||
 | 
					            'contact' => $vendor_contact,
 | 
				
			||||||
 | 
					            'vendor' => $vendor_contact->vendor,
 | 
				
			||||||
 | 
					            'settings' => $vendor_contact->vendor->company->settings,
 | 
				
			||||||
 | 
					            'company' => $vendor_contact->vendor->company,
 | 
				
			||||||
 | 
					            'sidebar' => $this->sidebarMenu(),
 | 
				
			||||||
 | 
					            'countries' => TranslationHelper::getCountries()
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function update(VendorContact $vendor_contact)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $vendor_contact->fill(request()->all());
 | 
				
			||||||
 | 
					        $vendor_contact->vendor->fill(request()->all());
 | 
				
			||||||
 | 
					        $vendor_contact->push();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         return back()->withSuccess(ctrans('texts.profile_updated_successfully'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function sidebarMenu() :array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;
 | 
				
			||||||
 | 
					        $data = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: Enable dashboard once it's completed.
 | 
				
			||||||
 | 
					        // $this->settings->enable_client_portal_dashboard
 | 
				
			||||||
 | 
					        // $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (self::MODULE_PURCHASE_ORDERS & $enabled_modules) {
 | 
				
			||||||
 | 
					            $data[] = ['title' => ctrans('texts.purchase_orders'), 'url' => 'vendor.purchase_orders.index', 'icon' => 'file-text'];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // $data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -29,7 +29,7 @@ class WePayController extends BaseController
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function signup(string $token)
 | 
					    public function signup(string $token)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return render('gateways.wepay.signup.finished');
 | 
					        // return render('gateways.wepay.signup.finished');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $hash = Cache::get($token);
 | 
					        $hash = Cache::get($token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,7 @@ use App\Http\Middleware\TrimStrings;
 | 
				
			|||||||
use App\Http\Middleware\TrustProxies;
 | 
					use App\Http\Middleware\TrustProxies;
 | 
				
			||||||
use App\Http\Middleware\UrlSetDb;
 | 
					use App\Http\Middleware\UrlSetDb;
 | 
				
			||||||
use App\Http\Middleware\UserVerified;
 | 
					use App\Http\Middleware\UserVerified;
 | 
				
			||||||
 | 
					use App\Http\Middleware\VendorLocale;
 | 
				
			||||||
use App\Http\Middleware\VerifyCsrfToken;
 | 
					use App\Http\Middleware\VerifyCsrfToken;
 | 
				
			||||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
 | 
					use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
 | 
				
			||||||
use Illuminate\Auth\Middleware\Authorize;
 | 
					use Illuminate\Auth\Middleware\Authorize;
 | 
				
			||||||
@ -158,6 +159,7 @@ class Kernel extends HttpKernel
 | 
				
			|||||||
        'api_db' => SetDb::class,
 | 
					        'api_db' => SetDb::class,
 | 
				
			||||||
        'company_key_db' => SetDbByCompanyKey::class,
 | 
					        'company_key_db' => SetDbByCompanyKey::class,
 | 
				
			||||||
        'locale' => Locale::class,
 | 
					        'locale' => Locale::class,
 | 
				
			||||||
 | 
					        'vendor_locale' => VendorLocale::class,
 | 
				
			||||||
        'contact_register' => ContactRegister::class,
 | 
					        'contact_register' => ContactRegister::class,
 | 
				
			||||||
        'shop_token_auth' => ShopTokenAuth::class,
 | 
					        'shop_token_auth' => ShopTokenAuth::class,
 | 
				
			||||||
        'phantom_secret' => PhantomSecret::class,
 | 
					        'phantom_secret' => PhantomSecret::class,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										76
									
								
								app/Http/Livewire/PurchaseOrdersTable.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								app/Http/Livewire/PurchaseOrdersTable.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					<?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\Livewire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Models\Invoice;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Utils\Traits\WithSorting;
 | 
				
			||||||
 | 
					use Carbon\Carbon;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					use Livewire\WithPagination;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrdersTable extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use WithPagination, WithSorting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $per_page = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $status = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $company;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public function mount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::setDb($this->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->sort_asc = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->sort_field = 'date';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $local_status = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $query = PurchaseOrder::query()
 | 
				
			||||||
 | 
					            ->with('vendor.contacts')
 | 
				
			||||||
 | 
					            ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
 | 
				
			||||||
 | 
					            ->whereIn('status_id', [PurchaseOrder::STATUS_SENT, PurchaseOrder::STATUS_ACCEPTED])
 | 
				
			||||||
 | 
					            ->where('company_id', $this->company->id)
 | 
				
			||||||
 | 
					            ->where('is_deleted', false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (in_array('sent', $this->status)) {
 | 
				
			||||||
 | 
					            $local_status[] = PurchaseOrder::STATUS_SENT;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (in_array('accepted', $this->status)) {
 | 
				
			||||||
 | 
					            $local_status[] = PurchaseOrder::STATUS_ACCEPTED;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (count($local_status) > 0) {
 | 
				
			||||||
 | 
					            $query = $query->whereIn('status_id', array_unique($local_status));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $query = $query
 | 
				
			||||||
 | 
					            ->where('vendor_id', auth()->guard('vendor')->user()->vendor_id)
 | 
				
			||||||
 | 
					            // ->where('status_id', '<>', Invoice::STATUS_DRAFT)
 | 
				
			||||||
 | 
					            // ->where('status_id', '<>', Invoice::STATUS_CANCELLED)
 | 
				
			||||||
 | 
					            ->withTrashed()
 | 
				
			||||||
 | 
					            ->paginate($this->per_page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return render('components.livewire.purchase-orders-table', [
 | 
				
			||||||
 | 
					            'purchase_orders' => $query
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -60,6 +60,8 @@ class CheckClientExistence
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        session()->put('multiple_contacts', $multiple_contacts);
 | 
					        session()->put('multiple_contacts', $multiple_contacts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        session()->put('is_silent', request()->has('silent'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $next($request);
 | 
					        return $next($request);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -20,32 +20,38 @@ class RedirectIfAuthenticated
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handle an incoming request.
 | 
					     * Handle an incoming request.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param  Request  $request
 | 
					     * @param Request $request
 | 
				
			||||||
     * @param Closure $next
 | 
					     * @param Closure $next
 | 
				
			||||||
     * @param  string|null  $guard
 | 
					     * @param string|null $guard
 | 
				
			||||||
     * @return mixed
 | 
					     * @return mixed
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle($request, Closure $next, $guard = null)
 | 
					    public function handle($request, Closure $next, $guard = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        switch ($guard) {
 | 
					        switch ($guard) {
 | 
				
			||||||
        case 'contact':
 | 
					            case 'contact':
 | 
				
			||||||
          if (Auth::guard($guard)->check()) {
 | 
					                if (Auth::guard($guard)->check()) {
 | 
				
			||||||
              return redirect()->route('client.dashboard');
 | 
					                    return redirect()->route('client.dashboard');
 | 
				
			||||||
          }
 | 
					                }
 | 
				
			||||||
          break;
 | 
					                break;
 | 
				
			||||||
          case 'user':
 | 
					            case 'user':
 | 
				
			||||||
          Auth::logout();
 | 
					                Auth::logout();
 | 
				
			||||||
          // if (Auth::guard($guard)->check()) {
 | 
					                // if (Auth::guard($guard)->check()) {
 | 
				
			||||||
          //     return redirect()->route('dashboard.index');
 | 
					                //     return redirect()->route('dashboard.index');
 | 
				
			||||||
          // }
 | 
					                // }
 | 
				
			||||||
          break;
 | 
					                break;
 | 
				
			||||||
        default:
 | 
					            case 'vendor':
 | 
				
			||||||
        Auth::logout();
 | 
					                if (Auth::guard($guard)->check()) {
 | 
				
			||||||
          // if (Auth::guard($guard)->check()) {
 | 
					                    //TODO create routes for vendor
 | 
				
			||||||
          //     return redirect('/');
 | 
					                  //  return redirect()->route('vendor.dashboard');
 | 
				
			||||||
          // }
 | 
					                }
 | 
				
			||||||
          break;
 | 
					                break;
 | 
				
			||||||
      }
 | 
					            default:
 | 
				
			||||||
 | 
					                Auth::logout();
 | 
				
			||||||
 | 
					                // if (Auth::guard($guard)->check()) {
 | 
				
			||||||
 | 
					                //     return redirect('/');
 | 
				
			||||||
 | 
					                // }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $next($request);
 | 
					        return $next($request);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -46,7 +46,7 @@ class SetInviteDb
 | 
				
			|||||||
        if($entity == "pay")
 | 
					        if($entity == "pay")
 | 
				
			||||||
            $entity = "invoice";
 | 
					            $entity = "invoice";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(!in_array($entity, ['invoice','quote','credit','recurring_invoice']))
 | 
					        if(!in_array($entity, ['invoice','quote','credit','recurring_invoice','purchase_order']))
 | 
				
			||||||
            abort(404,'I could not find this resource.');
 | 
					            abort(404,'I could not find this resource.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Try and determine the DB from the invitation key STRING*/
 | 
					        /* Try and determine the DB from the invitation key STRING*/
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										57
									
								
								app/Http/Middleware/VendorLocale.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								app/Http/Middleware/VendorLocale.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					<?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\Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Closure;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Auth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VendorLocale
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle an incoming request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  Request  $request
 | 
				
			||||||
 | 
					     * @param Closure $next
 | 
				
			||||||
 | 
					     * @return mixed
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle($request, Closure $next)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (auth()->guard('contact')->check()) {
 | 
				
			||||||
 | 
					            auth()->guard('contact')->logout();
 | 
				
			||||||
 | 
					            $request->session()->invalidate();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /*LOCALE SET */
 | 
				
			||||||
 | 
					        if ($request->has('lang')) {
 | 
				
			||||||
 | 
					            $locale = $request->input('lang');
 | 
				
			||||||
 | 
					            App::setLocale($locale);
 | 
				
			||||||
 | 
					        } elseif (auth()->guard('vendor')->user()) {
 | 
				
			||||||
 | 
					            App::setLocale(auth()->guard('vendor')->user()->company->locale());
 | 
				
			||||||
 | 
					        } elseif (auth()->user()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try{
 | 
				
			||||||
 | 
					                App::setLocale(auth()->user()->company()->getLocale());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch(\Exception $e){
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            App::setLocale(config('ninja.i18n.locale'));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $next($request);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										54
									
								
								app/Http/Requests/Account/UpdateAccountRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								app/Http/Requests/Account/UpdateAccountRequest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					<?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\Account;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Requests\Request;
 | 
				
			||||||
 | 
					use App\Http\ValidationRules\Account\BlackListRule;
 | 
				
			||||||
 | 
					use App\Http\ValidationRules\Account\EmailBlackListRule;
 | 
				
			||||||
 | 
					use App\Http\ValidationRules\NewUniqueUserRule;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UpdateAccountRequest extends Request
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Determine if the user is authorized to make this request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return bool
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function authorize()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (auth()->user()->isAdmin() || auth()->user()->isOwner()) && (int)$this->account->id === auth()->user()->account_id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the validation rules that apply to the request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function rules()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            'set_react_as_default_ap' => 'required|bail|bool'
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Only allow single field to update account table */
 | 
				
			||||||
 | 
					    protected function prepareForValidation()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $input = $this->all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $cleaned_input = array_intersect_key( $input, array_flip(['set_react_as_default_ap']));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->replace($cleaned_input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -23,7 +23,7 @@ class ShowInvoiceRequest extends Request
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function authorize() : bool
 | 
					    public function authorize() : bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return auth()->guard('contact')->user()->client_id === $this->invoice->client_id
 | 
					        return (int)auth()->guard('contact')->user()->client_id === (int)$this->invoice->client_id
 | 
				
			||||||
            &&  auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES;
 | 
					            &&  auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,9 +27,8 @@ class CreatePaymentMethodRequest extends FormRequest
 | 
				
			|||||||
                $available_methods[] = $method['gateway_type_id'];
 | 
					                $available_methods[] = $method['gateway_type_id'];
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (in_array($this->query('method'), $available_methods)) {
 | 
					        if (in_array($this->query('method'), $available_methods)) 
 | 
				
			||||||
            return true;        
 | 
					            return true;        
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ class ShowQuoteRequest extends FormRequest
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public function authorize()
 | 
					    public function authorize()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
         return auth()->guard('contact')->user()->client->id === $this->quote->client_id
 | 
					         return auth()->guard('contact')->user()->client->id === (int)$this->quote->client_id
 | 
				
			||||||
             && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_QUOTES;
 | 
					             && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_QUOTES;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -39,21 +39,28 @@ class Checkout3dsRequest extends FormRequest
 | 
				
			|||||||
    public function getCompany()
 | 
					    public function getCompany()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        MultiDB::findAndSetDbByCompanyKey($this->company_key);
 | 
					        MultiDB::findAndSetDbByCompanyKey($this->company_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Company::where('company_key', $this->company_key)->first();
 | 
					        return Company::where('company_key', $this->company_key)->first();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getCompanyGateway()
 | 
					    public function getCompanyGateway()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::findAndSetDbByCompanyKey($this->company_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id));
 | 
					        return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getPaymentHash()
 | 
					    public function getPaymentHash()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::findAndSetDbByCompanyKey($this->company_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return PaymentHash::where('hash', $this->hash)->first();
 | 
					        return PaymentHash::where('hash', $this->hash)->first();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getClient()
 | 
					    public function getClient()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Client::find($this->getPaymentHash()->data->client_id);
 | 
					        MultiDB::findAndSetDbByCompanyKey($this->company_key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Client::withTrashed()->find($this->getPaymentHash()->data->client_id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@ class StorePurchaseOrderRequest extends Request
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $rules = [];
 | 
					        $rules = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $rules['vendor_id'] = 'required';
 | 
					        $rules['vendor_id'] = 'bail|required|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $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';
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					<?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\VendorPortal\PurchaseOrders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\ViewComposers\PortalComposer;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Http\FormRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProcessPurchaseOrdersInBulkRequest extends FormRequest
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function authorize()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return auth()->guard('vendor')->user()->vendor->company->enabled_modules & PortalComposer::MODULE_PURCHASE_ORDERS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function rules()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            'purchase_orders' => ['array'],
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?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\VendorPortal\PurchaseOrders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Requests\Request;
 | 
				
			||||||
 | 
					use App\Http\ViewComposers\PortalComposer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ShowPurchaseOrderRequest extends Request
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Determine if the user is authorized to make this request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return bool
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function authorize() : bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (int)auth()->guard('vendor')->user()->vendor_id === (int)$this->purchase_order->vendor_id
 | 
				
			||||||
 | 
					            &&  auth()->guard('vendor')->user()->company->enabled_modules & PortalComposer::MODULE_PURCHASE_ORDERS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?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\VendorPortal\PurchaseOrders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Http\Requests\Request;
 | 
				
			||||||
 | 
					use App\Http\ViewComposers\PortalComposer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ShowPurchaseOrdersRequest extends Request
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Determine if the user is authorized to make this request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return bool
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function authorize() : bool
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return auth()->guard('vendor')->user()->company->enabled_modules & PortalComposer::MODULE_PURCHASE_ORDERS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -70,7 +70,7 @@ class CreateEntityPdf implements ShouldQueue
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param $invitation
 | 
					     * @param $invitation
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function __construct($invitation, $disk = 'public')
 | 
					    public function __construct($invitation, $disk = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->invitation = $invitation;
 | 
					        $this->invitation = $invitation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,7 +99,7 @@ class CreateEntityPdf implements ShouldQueue
 | 
				
			|||||||
        $this->client = $invitation->contact->client;
 | 
					        $this->client = $invitation->contact->client;
 | 
				
			||||||
        $this->client->load('company');
 | 
					        $this->client->load('company');
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        $this->disk = Ninja::isHosted() ? config('filesystems.default') : $disk;
 | 
					        $this->disk = $disk ?? config('filesystems.default');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle() :void
 | 
					    public function handle() :void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        nlog("Updating company ledger for client ". $this->client->id);
 | 
					        // nlog("Updating company ledger for client ". $this->client->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        MultiDB::setDb($this->company->db);
 | 
					        MultiDB::setDb($this->company->db);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										102
									
								
								app/Jobs/PurchaseOrder/PurchaseOrderEmail.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								app/Jobs/PurchaseOrder/PurchaseOrderEmail.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					<?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\Jobs\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailerJob;
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailerObject;
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Mail\Engine\PurchaseOrderEmailEngine;
 | 
				
			||||||
 | 
					use App\Mail\VendorTemplateEmail;
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use Exception;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrderEmail implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PurchaseOrder $purchase_order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Company $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $template_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $tries = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(PurchaseOrder $purchase_order, Company $company, $template_data = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->purchase_order = $purchase_order;
 | 
				
			||||||
 | 
					        $this->company = $company;
 | 
				
			||||||
 | 
					        $this->template_data = $template_data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the job.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::setDb($this->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->purchase_order->last_sent_date = now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->purchase_order->invitations->load('contact.vendor.country', 'purchase_order.vendor.country', 'purchase_order.company')->each(function ($invitation) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Don't fire emails if the company is disabled */
 | 
				
			||||||
 | 
					        if ($this->company->is_disabled) 
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            /* Set DB */
 | 
				
			||||||
 | 
					            MultiDB::setDB($this->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            App::forgetInstance('translator');
 | 
				
			||||||
 | 
					            $t = app('translator');
 | 
				
			||||||
 | 
					            App::setLocale($invitation->contact->preferredLocale());
 | 
				
			||||||
 | 
					            $t->replace(Ninja::transformTranslations($this->company->settings));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Mark entity sent */
 | 
				
			||||||
 | 
					            $invitation->purchase_order->service()->markSent()->save();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            $email_builder = (new PurchaseOrderEmailEngine($invitation, 'purchase_order', $this->template_data))->build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $nmo = new NinjaMailerObject;
 | 
				
			||||||
 | 
					            $nmo->mailable = new VendorTemplateEmail($email_builder, $invitation->contact, $invitation);
 | 
				
			||||||
 | 
					            $nmo->company = $this->company;
 | 
				
			||||||
 | 
					            $nmo->settings = $this->company->settings;
 | 
				
			||||||
 | 
					            $nmo->to_user = $invitation->contact;
 | 
				
			||||||
 | 
					            $nmo->entity_string = 'purchase_order';
 | 
				
			||||||
 | 
					            $nmo->invitation = $invitation;
 | 
				
			||||||
 | 
					            $nmo->reminder_template = 'purchase_order';
 | 
				
			||||||
 | 
					            $nmo->entity = $invitation->purchase_order;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            NinjaMailerJob::dispatchNow($nmo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->purchase_order->invitations->count() >= 1) {
 | 
				
			||||||
 | 
					            event(new PurchaseOrderWasEmailed($this->purchase_order->invitations->first(), $this->purchase_order->invitations->first()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										126
									
								
								app/Jobs/PurchaseOrder/ZipPurchaseOrders.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								app/Jobs/PurchaseOrder/ZipPurchaseOrders.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					<?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\Jobs\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Jobs\Entity\CreateEntityPdf;
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailerJob;
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailerObject;
 | 
				
			||||||
 | 
					use App\Jobs\Util\UnlinkFile;
 | 
				
			||||||
 | 
					use App\Jobs\Vendor\CreatePurchaseOrderPdf;
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Mail\DownloadInvoices;
 | 
				
			||||||
 | 
					use App\Mail\DownloadPurchaseOrders;
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use App\Utils\TempFile;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\InteractsWithQueue;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Mail;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					use ZipArchive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ZipPurchaseOrders implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $purchase_orders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $tries = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @param $purchase_orders
 | 
				
			||||||
 | 
					     * @param Company $company
 | 
				
			||||||
 | 
					     * @param $email
 | 
				
			||||||
 | 
					     * @deprecated confirm to be deleted
 | 
				
			||||||
 | 
					     * Create a new job instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct($purchase_orders, Company $company, User $user)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->purchase_orders = $purchase_orders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->company = $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->user = $user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->settings = $company->settings;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Execute the job.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     * @throws \ZipStream\Exception\FileNotFoundException
 | 
				
			||||||
 | 
					     * @throws \ZipStream\Exception\FileNotReadableException
 | 
				
			||||||
 | 
					     * @throws \ZipStream\Exception\OverflowException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::setDb($this->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # create new zip object
 | 
				
			||||||
 | 
					        $zipFile = new \PhpZip\ZipFile();
 | 
				
			||||||
 | 
					        $file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
 | 
				
			||||||
 | 
					        $invitation = $this->purchase_orders->first()->invitations->first();
 | 
				
			||||||
 | 
					        $path = $this->purchase_orders->first()->vendor->purchase_order_filepath($invitation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->purchase_orders->each(function ($purchase_order){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CreatePurchaseOrderPdf::dispatchNow($purchase_order->invitations()->first());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            foreach ($this->purchase_orders as $purchase_order) {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					                $file = $purchase_order->service()->getPurchaseOrderPdf();
 | 
				
			||||||
 | 
					                $zip_file_name = basename($file);
 | 
				
			||||||
 | 
					                $zipFile->addFromString($zip_file_name, Storage::get($file));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Storage::put($path.$file_name, $zipFile->outputAsString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $nmo = new NinjaMailerObject;
 | 
				
			||||||
 | 
					            $nmo->mailable = new DownloadPurchaseOrders(Storage::url($path.$file_name), $this->company);
 | 
				
			||||||
 | 
					            $nmo->to_user = $this->user;
 | 
				
			||||||
 | 
					            $nmo->settings = $this->settings;
 | 
				
			||||||
 | 
					            $nmo->company = $this->company;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            NinjaMailerJob::dispatch($nmo);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(\PhpZip\Exception\ZipException $e){
 | 
				
			||||||
 | 
					            nlog("could not make zip => ". $e->getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        finally{
 | 
				
			||||||
 | 
					            $zipFile->close();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										4
									
								
								app/Jobs/Vendor/CreatePurchaseOrderPdf.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								app/Jobs/Vendor/CreatePurchaseOrderPdf.php
									
									
									
									
										vendored
									
									
								
							@ -70,7 +70,7 @@ class CreatePurchaseOrderPdf implements ShouldQueue
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param $invitation
 | 
					     * @param $invitation
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function __construct($invitation, $disk = 'public')
 | 
					    public function __construct($invitation, $disk = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->invitation = $invitation;
 | 
					        $this->invitation = $invitation;
 | 
				
			||||||
        $this->company = $invitation->company;
 | 
					        $this->company = $invitation->company;
 | 
				
			||||||
@ -83,7 +83,7 @@ class CreatePurchaseOrderPdf implements ShouldQueue
 | 
				
			|||||||
        $this->vendor = $invitation->contact->vendor;
 | 
					        $this->vendor = $invitation->contact->vendor;
 | 
				
			||||||
        $this->vendor->load('company');
 | 
					        $this->vendor->load('company');
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        $this->disk = Ninja::isHosted() ? config('filesystems.default') : $disk;
 | 
					        $this->disk = $disk ?? config('filesystems.default');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,8 @@ class OAuth
 | 
				
			|||||||
    const SOCIAL_LINKEDIN = 4;
 | 
					    const SOCIAL_LINKEDIN = 4;
 | 
				
			||||||
    const SOCIAL_TWITTER = 5;
 | 
					    const SOCIAL_TWITTER = 5;
 | 
				
			||||||
    const SOCIAL_BITBUCKET = 6;
 | 
					    const SOCIAL_BITBUCKET = 6;
 | 
				
			||||||
 | 
					    const SOCIAL_MICROSOFT = 7;
 | 
				
			||||||
 | 
					    const SOCIAL_APPLE = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param Socialite $user
 | 
					     * @param Socialite $user
 | 
				
			||||||
@ -38,8 +40,8 @@ class OAuth
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        /** 1. Ensure user arrives on the correct provider **/
 | 
					        /** 1. Ensure user arrives on the correct provider **/
 | 
				
			||||||
        $query = [
 | 
					        $query = [
 | 
				
			||||||
            'oauth_user_id' =>$socialite_user->getId(),
 | 
					            'oauth_user_id' => $socialite_user->getId(),
 | 
				
			||||||
            'oauth_provider_id'=>$provider,
 | 
					            'oauth_provider_id' => $provider,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($user = MultiDB::hasUser($query)) {
 | 
					        if ($user = MultiDB::hasUser($query)) {
 | 
				
			||||||
@ -54,12 +56,12 @@ class OAuth
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $name = trim($name);
 | 
					        $name = trim($name);
 | 
				
			||||||
        $last_name = (strpos($name, ' ') === false) ? '' : preg_replace('#.*\s([\w-]*)$#', '$1', $name);
 | 
					        $last_name = (strpos($name, ' ') === false) ? '' : preg_replace('#.*\s([\w-]*)$#', '$1', $name);
 | 
				
			||||||
        $first_name = trim(preg_replace('#'.preg_quote($last_name, '/').'#', '', $name));
 | 
					        $first_name = trim(preg_replace('#' . preg_quote($last_name, '/') . '#', '', $name));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return [$first_name, $last_name];
 | 
					        return [$first_name, $last_name];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static function providerToString(int $social_provider) : string
 | 
					    public static function providerToString(int $social_provider): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        switch ($social_provider) {
 | 
					        switch ($social_provider) {
 | 
				
			||||||
            case SOCIAL_GOOGLE:
 | 
					            case SOCIAL_GOOGLE:
 | 
				
			||||||
@ -74,10 +76,14 @@ class OAuth
 | 
				
			|||||||
                return 'twitter';
 | 
					                return 'twitter';
 | 
				
			||||||
            case SOCIAL_BITBUCKET:
 | 
					            case SOCIAL_BITBUCKET:
 | 
				
			||||||
                return 'bitbucket';
 | 
					                return 'bitbucket';
 | 
				
			||||||
 | 
					            case SOCIAL_MICROSOFT:
 | 
				
			||||||
 | 
					                return 'microsoft';
 | 
				
			||||||
 | 
					            case SOCIAL_APPLE:
 | 
				
			||||||
 | 
					                return 'apple';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static function providerToInt(string $social_provider) : int
 | 
					    public static function providerToInt(string $social_provider): int
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        switch ($social_provider) {
 | 
					        switch ($social_provider) {
 | 
				
			||||||
            case 'google':
 | 
					            case 'google':
 | 
				
			||||||
@ -92,6 +98,10 @@ class OAuth
 | 
				
			|||||||
                return SOCIAL_TWITTER;
 | 
					                return SOCIAL_TWITTER;
 | 
				
			||||||
            case 'bitbucket':
 | 
					            case 'bitbucket':
 | 
				
			||||||
                return SOCIAL_BITBUCKET;
 | 
					                return SOCIAL_BITBUCKET;
 | 
				
			||||||
 | 
					            case 'microsoft':
 | 
				
			||||||
 | 
					                return SOCIAL_MICROSOFT;
 | 
				
			||||||
 | 
					            case 'apple':
 | 
				
			||||||
 | 
					                return SOCIAL_APPLE;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -103,7 +113,6 @@ class OAuth
 | 
				
			|||||||
                $this->provider_id = self::SOCIAL_GOOGLE;
 | 
					                $this->provider_id = self::SOCIAL_GOOGLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return $this;
 | 
					                return $this;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
				
			|||||||
@ -49,6 +49,8 @@ class InvitationViewedListener implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if($entity_name == 'recurringInvoice')
 | 
					        if($entity_name == 'recurringInvoice')
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 | 
					        elseif($entity_name == 'purchaseOrder')
 | 
				
			||||||
 | 
					            $entity_name = 'purchase_order';
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        $nmo = new NinjaMailerObject;
 | 
					        $nmo = new NinjaMailerObject;
 | 
				
			||||||
        $nmo->mailable = new NinjaMailer( (new EntityViewedObject($invitation, $entity_name))->build() );
 | 
					        $nmo->mailable = new NinjaMailer( (new EntityViewedObject($invitation, $entity_name))->build() );
 | 
				
			||||||
@ -60,6 +62,8 @@ class InvitationViewedListener implements ShouldQueue
 | 
				
			|||||||
            $entity_viewed = "{$entity_name}_viewed";
 | 
					            $entity_viewed = "{$entity_name}_viewed";
 | 
				
			||||||
            $entity_viewed_all = "{$entity_name}_viewed_all";
 | 
					            $entity_viewed_all = "{$entity_name}_viewed_all";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed, $entity_viewed_all]);
 | 
					            $methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed, $entity_viewed_all]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (($key = array_search('mail', $methods)) !== false) {
 | 
					            if (($key = array_search('mail', $methods)) !== false) {
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					<?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\Listeners\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Models\Activity;
 | 
				
			||||||
 | 
					use App\Repositories\ActivityRepository;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					use stdClass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrderAcceptedActivity implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    protected $activity_repo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $delay = 5;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create the event listener.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ActivityRepository $activity_repo
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function __construct(ActivityRepository $activity_repo)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->activity_repo = $activity_repo;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle the event.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  object  $event
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle($event)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::setDb($event->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $fields = new stdClass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->purchase_order->user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $event->purchase_order->service()->markSent()->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $fields->user_id = $user_id;
 | 
				
			||||||
 | 
					        $fields->company_id = $event->purchase_order->company_id;
 | 
				
			||||||
 | 
					        $fields->activity_type_id = Activity::ACCEPT_PURCHASE_ORDER;
 | 
				
			||||||
 | 
					        $fields->vendor_id = $event->purchase_order->vendor_id;
 | 
				
			||||||
 | 
					        $fields->vendor_contact_id = $event->contact->id;
 | 
				
			||||||
 | 
					        $fields->purchase_order_id = $event->purchase_order->id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->activity_repo->save($fields, $event->purchase_order, $event->event_vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Quote Ninja (https://quoteninja.com).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @link https://github.com/quoteninja/quoteninja source repository
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @copyright Copyright (c) 2022. Quote Ninja LLC (https://quoteninja.com)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @license https://www.elastic.co/licensing/elastic-license
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Listeners\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailer;
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailerJob;
 | 
				
			||||||
 | 
					use App\Jobs\Mail\NinjaMailerObject;
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Mail\Admin\EntityCreatedObject;
 | 
				
			||||||
 | 
					use App\Mail\Admin\PurchaseOrderAcceptedObject;
 | 
				
			||||||
 | 
					use App\Notifications\Admin\EntitySentNotification;
 | 
				
			||||||
 | 
					use App\Utils\Traits\Notifications\UserNotifies;
 | 
				
			||||||
 | 
					use Illuminate\Contracts\Queue\ShouldQueue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrderAcceptedNotification implements ShouldQueue
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use UserNotifies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $delay = 5;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public function __construct()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle the event.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param  object  $event
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function handle($event)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::setDb($event->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $first_notification_sent = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $purchase_order = $event->purchase_order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $nmo = new NinjaMailerObject;
 | 
				
			||||||
 | 
					        $nmo->mailable = new NinjaMailer( (new PurchaseOrderAcceptedObject($purchase_order, $event->company))->build() );
 | 
				
			||||||
 | 
					        $nmo->company = $event->company;
 | 
				
			||||||
 | 
					        $nmo->settings = $event->company->settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* We loop through each user and determine whether they need to be notified */
 | 
				
			||||||
 | 
					        foreach ($event->company->company_users as $company_user) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* The User */
 | 
				
			||||||
 | 
					            $user = $company_user->user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(!$user)
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* Returns an array of notification methods */
 | 
				
			||||||
 | 
					            $methods = $this->findUserNotificationTypes($purchase_order->invitations()->first(), $company_user, 'purchase_order', ['all_notifications', 'purchase_order_accepted', 'purchase_order_accepted_all']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* If one of the methods is email then we fire the EntitySentMailer */
 | 
				
			||||||
 | 
					            if (($key = array_search('mail', $methods)) !== false) {
 | 
				
			||||||
 | 
					                unset($methods[$key]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $nmo->to_user = $user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                NinjaMailerJob::dispatch($nmo);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                /* This prevents more than one notification being sent */
 | 
				
			||||||
 | 
					                $first_notification_sent = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -65,7 +65,12 @@ class EntityViewedObject
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private function getAmount()
 | 
					    private function getAmount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Number::formatMoney($this->entity->amount, $this->entity->client);
 | 
					        if($this->entity->client)
 | 
				
			||||||
 | 
					            $currency_entity = $this->entity->client;
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            $currency_entity = $this->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Number::formatMoney($this->entity->amount, $currency_entity);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function getSubject()
 | 
					    private function getSubject()
 | 
				
			||||||
@ -82,7 +87,10 @@ class EntityViewedObject
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private function getData()
 | 
					    private function getData()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $settings = $this->entity->client->getMergedSettings();
 | 
					        if($this->entity->client)
 | 
				
			||||||
 | 
					            $settings = $this->entity->client->getMergedSettings();
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            $settings = $this->company->settings; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = [
 | 
					        $data = [
 | 
				
			||||||
            'title' => $this->getSubject(),
 | 
					            'title' => $this->getSubject(),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										103
									
								
								app/Mail/Admin/PurchaseOrderAcceptedObject.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								app/Mail/Admin/PurchaseOrderAcceptedObject.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					<?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\Mail\Admin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Number;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
 | 
					use stdClass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrderAcceptedObject
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $purchase_order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(PurchaseOrder $purchase_order, Company $company)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->purchase_order = $purchase_order;
 | 
				
			||||||
 | 
					        $this->company = $company;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function build()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        MultiDB::setDb($this->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!$this->purchase_order)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        App::forgetInstance('translator');
 | 
				
			||||||
 | 
					        /* Init a new copy of the translator*/
 | 
				
			||||||
 | 
					        $t = app('translator');
 | 
				
			||||||
 | 
					        /* Set the locale*/
 | 
				
			||||||
 | 
					        App::setLocale($this->company->getLocale());
 | 
				
			||||||
 | 
					        /* Set customized translations _NOW_ */
 | 
				
			||||||
 | 
					        $t->replace(Ninja::transformTranslations($this->company->settings));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $mail_obj = new stdClass;
 | 
				
			||||||
 | 
					        $mail_obj->amount = $this->getAmount();
 | 
				
			||||||
 | 
					        $mail_obj->subject = $this->getSubject();
 | 
				
			||||||
 | 
					        $mail_obj->data = $this->getData();
 | 
				
			||||||
 | 
					        $mail_obj->markdown = 'email.admin.generic';
 | 
				
			||||||
 | 
					        $mail_obj->tag = $this->company->company_key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $mail_obj;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function getAmount()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Number::formatMoney($this->purchase_order->amount, $this->company);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function getSubject()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					            ctrans(
 | 
				
			||||||
 | 
					                "texts.notification_purchase_order_accepted_subject",
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                        'vendor' => $this->purchase_order->vendor->present()->name(),
 | 
				
			||||||
 | 
					                        'purchase_order' => $this->purchase_order->number,
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function getData()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $settings = $this->company->settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'title' => $this->getSubject(),
 | 
				
			||||||
 | 
					            'message' => ctrans(
 | 
				
			||||||
 | 
					                "texts.notification_purchase_order_accepted",
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    'amount' => $this->getAmount(),
 | 
				
			||||||
 | 
					                    'vendor' => $this->purchase_order->vendor->present()->name(),
 | 
				
			||||||
 | 
					                    'purchase_order' => $this->purchase_order->number,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            'url' => $this->purchase_order->invitations->first()->getAdminLink(),
 | 
				
			||||||
 | 
					            'button' => ctrans("texts.view_purchase_order"),
 | 
				
			||||||
 | 
					            'signature' => $settings->email_signature,
 | 
				
			||||||
 | 
					            'logo' => $this->company->present()->logo(),
 | 
				
			||||||
 | 
					            'settings' => $settings,
 | 
				
			||||||
 | 
					            'whitelabel' => $this->company->account->isPaid() ? true : false,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										56
									
								
								app/Mail/DownloadPurchaseOrders.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								app/Mail/DownloadPurchaseOrders.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					<?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\Mail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Mail\Mailable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DownloadPurchaseOrders extends Mailable
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // use Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $file_path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct($file_path, Company $company)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->file_path = $file_path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->company = $company;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Build the message.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function build()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        App::setLocale($this->company->getLocale());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->from(config('mail.from.address'), config('mail.from.name'))
 | 
				
			||||||
 | 
					            ->subject(ctrans('texts.download_files'))
 | 
				
			||||||
 | 
					            ->text('email.admin.download_invoices_text', [
 | 
				
			||||||
 | 
					                'url' => $this->file_path,
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					            ->view('email.admin.download_purchase_orders', [
 | 
				
			||||||
 | 
					                'url' => $this->file_path,
 | 
				
			||||||
 | 
					                'logo' => $this->company->present()->logo,
 | 
				
			||||||
 | 
					                'whitelabel' => $this->company->account->isPaid() ? true : false,
 | 
				
			||||||
 | 
					                'settings' => $this->company->settings,
 | 
				
			||||||
 | 
					                'greeting' => $this->company->present()->name(),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										207
									
								
								app/Mail/Engine/PurchaseOrderEmailEngine.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								app/Mail/Engine/PurchaseOrderEmailEngine.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,207 @@
 | 
				
			|||||||
 | 
					<?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\Mail\Engine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\DataMapper\EmailTemplateDefaults;
 | 
				
			||||||
 | 
					use App\Jobs\Entity\CreateEntityPdf;
 | 
				
			||||||
 | 
					use App\Models\Account;
 | 
				
			||||||
 | 
					use App\Models\Expense;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Models\Task;
 | 
				
			||||||
 | 
					use App\Models\Vendor;
 | 
				
			||||||
 | 
					use App\Models\VendorContact;
 | 
				
			||||||
 | 
					use App\Utils\HtmlEngine;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Number;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use App\Utils\VendorHtmlEngine;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Lang;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PurchaseOrderEmailEngine extends BaseEmailEngine
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use MakesHash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $invitation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Vendor $vendor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PurchaseOrder $purchase_order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $reminder_template;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $template_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct($invitation, $reminder_template, $template_data)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->invitation = $invitation;
 | 
				
			||||||
 | 
					        $this->reminder_template = $reminder_template; //'purchase_order'
 | 
				
			||||||
 | 
					        $this->vendor = $invitation->contact->vendor;
 | 
				
			||||||
 | 
					        $this->purchase_order = $invitation->purchase_order;
 | 
				
			||||||
 | 
					        $this->contact = $invitation->contact;
 | 
				
			||||||
 | 
					        $this->template_data = $template_data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function build()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        App::forgetInstance('translator');
 | 
				
			||||||
 | 
					        $t = app('translator');
 | 
				
			||||||
 | 
					        $t->replace(Ninja::transformTranslations($this->vendor->company->settings));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (is_array($this->template_data) &&  array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
 | 
				
			||||||
 | 
					            $body_template = $this->template_data['body'];
 | 
				
			||||||
 | 
					        } elseif (strlen($this->vendor->getSetting('email_template_'.$this->reminder_template)) > 0) {
 | 
				
			||||||
 | 
					            $body_template = $this->vendor->getSetting('email_template_'.$this->reminder_template);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $body_template = EmailTemplateDefaults::getDefaultTemplate('email_template_'.$this->reminder_template, $this->vendor->company->locale());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Use default translations if a custom message has not been set*/
 | 
				
			||||||
 | 
					        if (iconv_strlen($body_template) == 0) {
 | 
				
			||||||
 | 
					            $body_template = trans(
 | 
				
			||||||
 | 
					                'texts.invoice_message',
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    'invoice' => $this->purchase_order->number,
 | 
				
			||||||
 | 
					                    'company' => $this->purchase_order->company->present()->name(),
 | 
				
			||||||
 | 
					                    'amount' => Number::formatMoney($this->purchase_order->balance, $this->vendor),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                null,
 | 
				
			||||||
 | 
					                $this->vendor->company->locale()
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $body_template .= '<div class="center">$view_button</div>';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $text_body = trans(
 | 
				
			||||||
 | 
					                'texts.purchase_order_message',
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    'purchase_order' => $this->purchase_order->number,
 | 
				
			||||||
 | 
					                    'company' => $this->purchase_order->company->present()->name(),
 | 
				
			||||||
 | 
					                    'amount' => Number::formatMoney($this->purchase_order->balance, $this->vendor),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                null,
 | 
				
			||||||
 | 
					                $this->vendor->company->locale()
 | 
				
			||||||
 | 
					            ) . "\n\n" . $this->invitation->getLink();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (is_array($this->template_data) &&  array_key_exists('subject', $this->template_data) && strlen($this->template_data['subject']) > 0) {
 | 
				
			||||||
 | 
					            $subject_template = $this->template_data['subject'];
 | 
				
			||||||
 | 
					        } elseif (strlen($this->vendor->getSetting('email_subject_'.$this->reminder_template)) > 0) {
 | 
				
			||||||
 | 
					            $subject_template = $this->vendor->getSetting('email_subject_'.$this->reminder_template);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $subject_template = EmailTemplateDefaults::getDefaultTemplate('email_subject_'.$this->reminder_template, $this->vendor->company->locale());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (iconv_strlen($subject_template) == 0) {
 | 
				
			||||||
 | 
					            $subject_template = trans(
 | 
				
			||||||
 | 
					                'texts.purchase_order_subject',
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    'number' => $this->purchase_order->number,
 | 
				
			||||||
 | 
					                    'account' => $this->purchase_order->company->present()->name(),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                null,
 | 
				
			||||||
 | 
					                $this->vendor->company->locale()
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->setTemplate($this->vendor->getSetting('email_style'))
 | 
				
			||||||
 | 
					            ->setContact($this->contact)
 | 
				
			||||||
 | 
					            ->setVariables((new VendorHtmlEngine($this->invitation))->makeValues())//move make values into the htmlengine
 | 
				
			||||||
 | 
					            ->setSubject($subject_template)
 | 
				
			||||||
 | 
					            ->setBody($body_template)
 | 
				
			||||||
 | 
					            ->setFooter("<a href='{$this->invitation->getLink()}'>".ctrans('texts.view_purchase_order').'</a>')
 | 
				
			||||||
 | 
					            ->setViewLink($this->invitation->getLink())
 | 
				
			||||||
 | 
					            ->setViewText(ctrans('texts.view_purchase_order'))
 | 
				
			||||||
 | 
					            ->setInvitation($this->invitation)
 | 
				
			||||||
 | 
					            ->setTextBody($text_body);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->vendor->getSetting('pdf_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(Ninja::isHosted())
 | 
				
			||||||
 | 
					                $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]);
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation)]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //attach third party documents
 | 
				
			||||||
 | 
					        if($this->vendor->getSetting('document_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Storage::url
 | 
				
			||||||
 | 
					            foreach($this->purchase_order->documents as $document){
 | 
				
			||||||
 | 
					                $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach($this->purchase_order->company->documents as $document){
 | 
				
			||||||
 | 
					                $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // $line_items = $this->purchase_order->line_items;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // foreach($line_items as $item)
 | 
				
			||||||
 | 
					            // {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //     $expense_ids = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //     if(property_exists($item, 'expense_id'))
 | 
				
			||||||
 | 
					            //     {
 | 
				
			||||||
 | 
					            //         $expense_ids[] = $item->expense_id;
 | 
				
			||||||
 | 
					            //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //     if(count($expense_ids) > 0){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //         $expenses = Expense::whereIn('id', $this->transformKeys($expense_ids))
 | 
				
			||||||
 | 
					            //                            ->where('invoice_documents', 1)
 | 
				
			||||||
 | 
					            //                            ->cursor()
 | 
				
			||||||
 | 
					            //                            ->each(function ($expense){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //                                 foreach($expense->documents as $document)
 | 
				
			||||||
 | 
					            //                                 {
 | 
				
			||||||
 | 
					            //                                     $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
 | 
				
			||||||
 | 
					            //                                 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //                            });
 | 
				
			||||||
 | 
					            //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //     $task_ids = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //     if(property_exists($item, 'task_id'))
 | 
				
			||||||
 | 
					            //     {
 | 
				
			||||||
 | 
					            //         $task_ids[] = $item->task_id;
 | 
				
			||||||
 | 
					            //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //     if(count($task_ids) > 0 && $this->purchase_order->company->purchase_order_task_documents){
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					            //         $tasks = Task::whereIn('id', $this->transformKeys($task_ids))
 | 
				
			||||||
 | 
					            //                            ->cursor()
 | 
				
			||||||
 | 
					            //                            ->each(function ($task){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //                                 foreach($task->documents as $document)
 | 
				
			||||||
 | 
					            //                                 {
 | 
				
			||||||
 | 
					            //                                     $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
 | 
				
			||||||
 | 
					            //                                 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //                            });
 | 
				
			||||||
 | 
					            //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								app/Mail/VendorTemplateEmail.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								app/Mail/VendorTemplateEmail.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					<?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\Mail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Jobs\Invoice\CreateUbl;
 | 
				
			||||||
 | 
					use App\Models\Account;
 | 
				
			||||||
 | 
					use App\Models\Client;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use App\Models\VendorContact;
 | 
				
			||||||
 | 
					use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
 | 
				
			||||||
 | 
					use App\Utils\HtmlEngine;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\TemplateEngine;
 | 
				
			||||||
 | 
					use App\Utils\VendorHtmlEngine;
 | 
				
			||||||
 | 
					use Illuminate\Bus\Queueable;
 | 
				
			||||||
 | 
					use Illuminate\Mail\Mailable;
 | 
				
			||||||
 | 
					use Illuminate\Queue\SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VendorTemplateEmail extends Mailable
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $build_email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $vendor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $invitation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct($build_email, VendorContact $contact, $invitation = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->build_email = $build_email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->contact = $contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->vendor = $contact->vendor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->company = $contact->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->invitation = $invitation;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function build()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        $template_name = 'email.template.'.$this->build_email->getTemplate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->build_email->getTemplate() == 'light' || $this->build_email->getTemplate() == 'dark') {
 | 
				
			||||||
 | 
					            $template_name = 'email.template.client';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($this->build_email->getTemplate() == 'custom') {
 | 
				
			||||||
 | 
					            $this->build_email->setBody(str_replace('$body', $this->build_email->getBody(), $this->client->getSetting('email_style_custom')));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $settings = $this->company->settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->build_email->getTemplate() !== 'custom') {
 | 
				
			||||||
 | 
					            $this->build_email->setBody(
 | 
				
			||||||
 | 
					                DesignHelpers::parseMarkdownToHtml($this->build_email->getBody())
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($this->invitation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            $html_variables = (new VendorHtmlEngine($this->invitation))->makeValues();
 | 
				
			||||||
 | 
					            $signature = str_replace(array_keys($html_variables), array_values($html_variables), $settings->email_signature);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            $signature = $settings->email_signature;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(property_exists($settings, 'email_from_name') && strlen($settings->email_from_name) > 1)
 | 
				
			||||||
 | 
					            $email_from_name = $settings->email_from_name;
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            $email_from_name = $this->company->present()->name();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->from(config('mail.from.address'), $email_from_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (strlen($settings->bcc_email) > 1)
 | 
				
			||||||
 | 
					            $this->bcc(explode(",",str_replace(" ", "", $settings->bcc_email)));//remove whitespace if any has been inserted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->subject($this->build_email->getSubject())
 | 
				
			||||||
 | 
					            ->text('email.template.text', [
 | 
				
			||||||
 | 
					                'text_body' => $this->build_email->getTextBody(),
 | 
				
			||||||
 | 
					                'whitelabel' => $this->vendor->user->account->isPaid() ? true : false,
 | 
				
			||||||
 | 
					                'settings' => $settings,
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					            ->view($template_name, [
 | 
				
			||||||
 | 
					                'greeting' => ctrans('texts.email_salutation', ['name' => $this->contact->present()->name()]),
 | 
				
			||||||
 | 
					                'body' => $this->build_email->getBody(),
 | 
				
			||||||
 | 
					                'footer' => $this->build_email->getFooter(),
 | 
				
			||||||
 | 
					                'view_link' => $this->build_email->getViewLink(),
 | 
				
			||||||
 | 
					                'view_text' => $this->build_email->getViewText(),
 | 
				
			||||||
 | 
					                'title' => '',
 | 
				
			||||||
 | 
					                'signature' => $signature,
 | 
				
			||||||
 | 
					                'settings' => $settings,
 | 
				
			||||||
 | 
					                'company' => $this->company,
 | 
				
			||||||
 | 
					                'whitelabel' => $this->vendor->user->account->isPaid() ? true : false,
 | 
				
			||||||
 | 
					                'logo' => $this->company->present()->logo($settings),
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					            ->withSwiftMessage(function ($message) {
 | 
				
			||||||
 | 
					                $message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
 | 
				
			||||||
 | 
					                $message->invitation = $this->invitation;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /*In the hosted platform we need to slow things down a little for Storage to catch up.*/
 | 
				
			||||||
 | 
					            if(Ninja::isHosted())
 | 
				
			||||||
 | 
					                sleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach ($this->build_email->getAttachments() as $file) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if(is_string($file))
 | 
				
			||||||
 | 
					                    $this->attach($file);
 | 
				
			||||||
 | 
					                elseif(is_array($file))
 | 
				
			||||||
 | 
					                    $this->attach($file['path'], ['as' => $file['name'], 'mime' => $file['mime']]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -11,6 +11,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Models;
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Exceptions\ModelNotFoundException;
 | 
				
			||||||
use App\Jobs\Mail\NinjaMailerJob;
 | 
					use App\Jobs\Mail\NinjaMailerJob;
 | 
				
			||||||
use App\Jobs\Mail\NinjaMailerObject;
 | 
					use App\Jobs\Mail\NinjaMailerObject;
 | 
				
			||||||
use App\Mail\Ninja\EmailQuotaExceeded;
 | 
					use App\Mail\Ninja\EmailQuotaExceeded;
 | 
				
			||||||
@ -57,6 +58,7 @@ class Account extends BaseModel
 | 
				
			|||||||
        'utm_content',
 | 
					        'utm_content',
 | 
				
			||||||
        'user_agent',
 | 
					        'user_agent',
 | 
				
			||||||
        'platform',
 | 
					        'platform',
 | 
				
			||||||
 | 
					        'set_react_as_default_ap',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -74,7 +76,8 @@ class Account extends BaseModel
 | 
				
			|||||||
        'updated_at' => 'timestamp',
 | 
					        'updated_at' => 'timestamp',
 | 
				
			||||||
        'created_at' => 'timestamp',
 | 
					        'created_at' => 'timestamp',
 | 
				
			||||||
        'deleted_at' => 'timestamp',
 | 
					        'deleted_at' => 'timestamp',
 | 
				
			||||||
        'onboarding' => 'object'
 | 
					        'onboarding' => 'object',
 | 
				
			||||||
 | 
					        'set_react_as_default_ap' => 'bool'
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const PLAN_FREE = 'free';
 | 
					    const PLAN_FREE = 'free';
 | 
				
			||||||
@ -87,6 +90,7 @@ class Account extends BaseModel
 | 
				
			|||||||
    const FEATURE_TASKS = 'tasks';
 | 
					    const FEATURE_TASKS = 'tasks';
 | 
				
			||||||
    const FEATURE_EXPENSES = 'expenses';
 | 
					    const FEATURE_EXPENSES = 'expenses';
 | 
				
			||||||
    const FEATURE_QUOTES = 'quotes';
 | 
					    const FEATURE_QUOTES = 'quotes';
 | 
				
			||||||
 | 
					    const FEATURE_PURCHASE_ORDERS = 'purchase_orders';
 | 
				
			||||||
    const FEATURE_CUSTOMIZE_INVOICE_DESIGN = 'custom_designs';
 | 
					    const FEATURE_CUSTOMIZE_INVOICE_DESIGN = 'custom_designs';
 | 
				
			||||||
    const FEATURE_DIFFERENT_DESIGNS = 'different_designs';
 | 
					    const FEATURE_DIFFERENT_DESIGNS = 'different_designs';
 | 
				
			||||||
    const FEATURE_EMAIL_TEMPLATES_REMINDERS = 'template_reminders';
 | 
					    const FEATURE_EMAIL_TEMPLATES_REMINDERS = 'template_reminders';
 | 
				
			||||||
@ -162,6 +166,7 @@ class Account extends BaseModel
 | 
				
			|||||||
            case self::FEATURE_TASKS:
 | 
					            case self::FEATURE_TASKS:
 | 
				
			||||||
            case self::FEATURE_EXPENSES:
 | 
					            case self::FEATURE_EXPENSES:
 | 
				
			||||||
            case self::FEATURE_QUOTES:
 | 
					            case self::FEATURE_QUOTES:
 | 
				
			||||||
 | 
					            case self::FEATURE_PURCHASE_ORDERS:
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case self::FEATURE_CUSTOMIZE_INVOICE_DESIGN:
 | 
					            case self::FEATURE_CUSTOMIZE_INVOICE_DESIGN:
 | 
				
			||||||
@ -468,4 +473,14 @@ class Account extends BaseModel
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function resolveRouteBinding($value, $field = null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (is_numeric($value)) {
 | 
				
			||||||
 | 
					            throw new ModelNotFoundException("Record with value {$value} not found");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this
 | 
				
			||||||
 | 
					            ->where('id', $this->decodePrimaryKey($value))->firstOrFail();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -116,6 +116,7 @@ class Activity extends StaticModel
 | 
				
			|||||||
    const RESTORE_PURCHASE_ORDER = 134;
 | 
					    const RESTORE_PURCHASE_ORDER = 134;
 | 
				
			||||||
    const EMAIL_PURCHASE_ORDER = 135; 
 | 
					    const EMAIL_PURCHASE_ORDER = 135; 
 | 
				
			||||||
    const VIEW_PURCHASE_ORDER = 136;
 | 
					    const VIEW_PURCHASE_ORDER = 136;
 | 
				
			||||||
 | 
					    const ACCEPT_PURCHASE_ORDER = 137;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    protected $casts = [
 | 
					    protected $casts = [
 | 
				
			||||||
        'is_system' => 'boolean',
 | 
					        'is_system' => 'boolean',
 | 
				
			||||||
 | 
				
			|||||||
@ -486,7 +486,7 @@ class Client extends BaseModel implements HasLocalePreference
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($this->currency()->code == 'EUR' && in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id'))){
 | 
					        if($this->currency()->code == 'EUR' && (in_array(GatewayType::BANK_TRANSFER, array_column($pms, 'gateway_type_id')) || in_array(GatewayType::SEPA, array_column($pms, 'gateway_type_id'))) ){
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
            foreach($pms as $pm){
 | 
					            foreach($pms as $pm){
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -501,18 +501,6 @@ class Client extends BaseModel implements HasLocalePreference
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // if ($this->currency()->code == 'EUR' && in_array(GatewayType::SEPA, array_column($pms, 'gateway_type_id'))) {
 | 
					 | 
				
			||||||
        //     foreach ($pms as $pm) {
 | 
					 | 
				
			||||||
        //         if ($pm['gateway_type_id'] == GatewayType::SEPA) {
 | 
					 | 
				
			||||||
        //             $cg = CompanyGateway::find($pm['company_gateway_id']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //             if ($cg && $cg->fees_and_limits->{GatewayType::SEPA}->is_enabled) {
 | 
					 | 
				
			||||||
        //                 return $cg;
 | 
					 | 
				
			||||||
        //             }
 | 
					 | 
				
			||||||
        //         }
 | 
					 | 
				
			||||||
        //     }
 | 
					 | 
				
			||||||
        // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if ($this->country && $this->country->iso_3166_3 == 'GBR' && in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) {
 | 
					        if ($this->country && $this->country->iso_3166_3 == 'GBR' && in_array(GatewayType::DIRECT_DEBIT, array_column($pms, 'gateway_type_id'))) {
 | 
				
			||||||
            foreach ($pms as $pm) {
 | 
					            foreach ($pms as $pm) {
 | 
				
			||||||
                if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) {
 | 
					                if ($pm['gateway_type_id'] == GatewayType::DIRECT_DEBIT) {
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ namespace App\Models;
 | 
				
			|||||||
use App\DataMapper\CompanySettings;
 | 
					use App\DataMapper\CompanySettings;
 | 
				
			||||||
use App\Models\Language;
 | 
					use App\Models\Language;
 | 
				
			||||||
use App\Models\Presenters\CompanyPresenter;
 | 
					use App\Models\Presenters\CompanyPresenter;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
use App\Services\Notification\NotificationService;
 | 
					use App\Services\Notification\NotificationService;
 | 
				
			||||||
use App\Utils\Ninja;
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
@ -192,6 +193,11 @@ class Company extends BaseModel
 | 
				
			|||||||
        return $this->hasMany(Subscription::class)->withTrashed();
 | 
					        return $this->hasMany(Subscription::class)->withTrashed();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function purchase_orders()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(PurchaseOrder::class)->withTrashed();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function task_statuses()
 | 
					    public function task_statuses()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->hasMany(TaskStatus::class)->withTrashed();
 | 
					        return $this->hasMany(TaskStatus::class)->withTrashed();
 | 
				
			||||||
 | 
				
			|||||||
@ -346,7 +346,7 @@ class Invoice extends BaseModel
 | 
				
			|||||||
                return '<h5><span class="badge badge-danger">'.ctrans('texts.overdue').'</span></h5>';
 | 
					                return '<h5><span class="badge badge-danger">'.ctrans('texts.overdue').'</span></h5>';
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case self::STATUS_UNPAID:
 | 
					            case self::STATUS_UNPAID:
 | 
				
			||||||
                return '<h5><span class="badge badge-warning">'.ctrans('texts.unpaid').'</span></h5>';
 | 
					                return '<h5><span class="badge badge-warning text-white">'.ctrans('texts.unpaid').'</span></h5>';
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case self::STATUS_REVERSED:
 | 
					            case self::STATUS_REVERSED:
 | 
				
			||||||
                return '<h5><span class="badge badge-info">'.ctrans('texts.reversed').'</span></h5>';
 | 
					                return '<h5><span class="badge badge-info">'.ctrans('texts.reversed').'</span></h5>';
 | 
				
			||||||
 | 
				
			|||||||
@ -212,7 +212,7 @@ class Payment extends BaseModel
 | 
				
			|||||||
                return '<h6><span class="badge badge-secondary">'.ctrans('texts.payment_status_1').'</span></h6>';
 | 
					                return '<h6><span class="badge badge-secondary">'.ctrans('texts.payment_status_1').'</span></h6>';
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case self::STATUS_CANCELLED:
 | 
					            case self::STATUS_CANCELLED:
 | 
				
			||||||
                return '<h6><span class="badge badge-warning">'.ctrans('texts.payment_status_2').'</span></h6>';
 | 
					                return '<h6><span class="badge badge-warning text-white">'.ctrans('texts.payment_status_2').'</span></h6>';
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case self::STATUS_FAILED:
 | 
					            case self::STATUS_FAILED:
 | 
				
			||||||
                return '<h6><span class="badge badge-danger">'.ctrans('texts.payment_status_3').'</span></h6>';
 | 
					                return '<h6><span class="badge badge-danger">'.ctrans('texts.payment_status_3').'</span></h6>';
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@ use App\Jobs\Entity\CreateEntityPdf;
 | 
				
			|||||||
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
 | 
					use App\Jobs\Vendor\CreatePurchaseOrderPdf;
 | 
				
			||||||
use App\Services\PurchaseOrder\PurchaseOrderService;
 | 
					use App\Services\PurchaseOrder\PurchaseOrderService;
 | 
				
			||||||
use App\Utils\Ninja;
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Traits\MakesDates;
 | 
				
			||||||
use Illuminate\Database\Eloquent\SoftDeletes;
 | 
					use Illuminate\Database\Eloquent\SoftDeletes;
 | 
				
			||||||
use Illuminate\Support\Carbon;
 | 
					use Illuminate\Support\Carbon;
 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
@ -26,6 +27,7 @@ class PurchaseOrder extends BaseModel
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    use Filterable;
 | 
					    use Filterable;
 | 
				
			||||||
    use SoftDeletes;
 | 
					    use SoftDeletes;
 | 
				
			||||||
 | 
					    use MakesDates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $fillable = [
 | 
					    protected $fillable = [
 | 
				
			||||||
        'number',
 | 
					        'number',
 | 
				
			||||||
@ -99,8 +101,51 @@ class PurchaseOrder extends BaseModel
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const STATUS_DRAFT = 1;
 | 
					    const STATUS_DRAFT = 1;
 | 
				
			||||||
    const STATUS_SENT = 2;
 | 
					    const STATUS_SENT = 2;
 | 
				
			||||||
    const STATUS_PARTIAL = 3;
 | 
					    const STATUS_ACCEPTED = 3;
 | 
				
			||||||
    const STATUS_APPLIED = 4;
 | 
					    const STATUS_CANCELLED = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static function stringStatus(int $status)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        switch ($status) {
 | 
				
			||||||
 | 
					            case self::STATUS_DRAFT:
 | 
				
			||||||
 | 
					                return ctrans('texts.draft');
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case self::STATUS_SENT:
 | 
				
			||||||
 | 
					                return ctrans('texts.sent');
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case self::STATUS_ACCEPTED:
 | 
				
			||||||
 | 
					                return ctrans('texts.accepted');
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case self::STATUS_CANCELLED:
 | 
				
			||||||
 | 
					                return ctrans('texts.cancelled');
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					                // code...
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static function badgeForStatus(int $status)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        switch ($status) {
 | 
				
			||||||
 | 
					            case self::STATUS_DRAFT:
 | 
				
			||||||
 | 
					                return '<h5><span class="badge badge-light">'.ctrans('texts.draft').'</span></h5>';
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case self::STATUS_SENT:
 | 
				
			||||||
 | 
					                return '<h5><span class="badge badge-primary">'.ctrans('texts.sent').'</span></h5>';
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case self::STATUS_ACCEPTED:
 | 
				
			||||||
 | 
					                return '<h5><span class="badge badge-primary">'.ctrans('texts.accepted').'</span></h5>';
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case self::STATUS_CANCELLED:
 | 
				
			||||||
 | 
					                return '<h5><span class="badge badge-secondary">'.ctrans('texts.cancelled').'</span></h5>';
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                // code...
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function assigned_user()
 | 
					    public function assigned_user()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,13 @@
 | 
				
			|||||||
<?php
 | 
					<?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\Models;
 | 
					namespace App\Models;
 | 
				
			||||||
@ -9,6 +18,7 @@ use App\Utils\Traits\Inviteable;
 | 
				
			|||||||
use App\Utils\Traits\MakesDates;
 | 
					use App\Utils\Traits\MakesDates;
 | 
				
			||||||
use Carbon\Carbon;
 | 
					use Carbon\Carbon;
 | 
				
			||||||
use Illuminate\Database\Eloquent\SoftDeletes;
 | 
					use Illuminate\Database\Eloquent\SoftDeletes;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PurchaseOrderInvitation extends BaseModel
 | 
					class PurchaseOrderInvitation extends BaseModel
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -104,4 +114,37 @@ class PurchaseOrderInvitation extends BaseModel
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getLink() :string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $entity_type = Str::snake(class_basename($this->entityType()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(Ninja::isHosted()){
 | 
				
			||||||
 | 
					            $domain = $this->company->domain();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            $domain = config('ninja.app_url');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch ($this->company->portal_mode) {
 | 
				
			||||||
 | 
					            case 'subdomain':
 | 
				
			||||||
 | 
					                return $domain.'/vendor/'.$entity_type.'/'.$this->key;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'iframe':
 | 
				
			||||||
 | 
					                return $domain.'/vendor/'.$entity_type.'/'.$this->key;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case 'domain':
 | 
				
			||||||
 | 
					                return $domain.'/vendor/'.$entity_type.'/'.$this->key;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                return '';
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getAdminLink() :string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->getLink().'?silent=true';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -73,6 +73,24 @@ class VendorContact extends Authenticatable implements HasLocalePreference
 | 
				
			|||||||
        'vendor_id',
 | 
					        'vendor_id',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function avatar()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->avatar) {
 | 
				
			||||||
 | 
					            return $this->avatar;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return asset('images/svg/user.svg');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public function setAvatarAttribute($value)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (! filter_var($value, FILTER_VALIDATE_URL) && $value) {
 | 
				
			||||||
 | 
					            $this->attributes['avatar'] = url('/').$value;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->attributes['avatar'] = $value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getEntityType()
 | 
					    public function getEntityType()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return self::class;
 | 
					        return self::class;
 | 
				
			||||||
@ -110,7 +128,7 @@ class VendorContact extends Authenticatable implements HasLocalePreference
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function sendPasswordResetNotification($token)
 | 
					    public function sendPasswordResetNotification($token)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->notify(new ClientContactResetPassword($token));
 | 
					        // $this->notify(new ClientContactResetPassword($token));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function preferredLocale()
 | 
					    public function preferredLocale()
 | 
				
			||||||
@ -118,12 +136,9 @@ class VendorContact extends Authenticatable implements HasLocalePreference
 | 
				
			|||||||
        $languages = Cache::get('languages');
 | 
					        $languages = Cache::get('languages');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $languages->filter(function ($item) {
 | 
					        return $languages->filter(function ($item) {
 | 
				
			||||||
            return $item->id == $this->client->getSetting('language_id');
 | 
					            return $item->id == $this->company->getSetting('language_id');
 | 
				
			||||||
        })->first()->locale;
 | 
					        })->first()->locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //$lang = Language::find($this->client->getSetting('language_id'));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //return $lang->locale;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
@ -407,12 +407,7 @@ class BaseDriver extends AbstractPaymentDriver
 | 
				
			|||||||
            $this->unWindGatewayFees($this->payment_hash);
 | 
					            $this->unWindGatewayFees($this->payment_hash);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($e instanceof CheckoutHttpException) {
 | 
					        $error = $e->getMessage();
 | 
				
			||||||
            $error = $e->getBody();
 | 
					 | 
				
			||||||
        } else if ($e instanceof Exception) {
 | 
					 | 
				
			||||||
            $error = $e->getMessage();
 | 
					 | 
				
			||||||
        } else
 | 
					 | 
				
			||||||
            $error = $e->getMessage();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(!$this->payment_hash)
 | 
					        if(!$this->payment_hash)
 | 
				
			||||||
            throw new PaymentFailed($error, $e->getCode());
 | 
					            throw new PaymentFailed($error, $e->getCode());
 | 
				
			||||||
 | 
				
			|||||||
@ -14,18 +14,24 @@ namespace App\PaymentDrivers\CheckoutCom;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Exceptions\PaymentFailed;
 | 
					use App\Exceptions\PaymentFailed;
 | 
				
			||||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
 | 
					use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
 | 
				
			||||||
use Illuminate\Http\Request;
 | 
					 | 
				
			||||||
use App\Models\ClientGatewayToken;
 | 
					use App\Models\ClientGatewayToken;
 | 
				
			||||||
use App\Models\GatewayType;
 | 
					use App\Models\GatewayType;
 | 
				
			||||||
 | 
					use App\Models\Payment;
 | 
				
			||||||
use App\PaymentDrivers\CheckoutComPaymentDriver;
 | 
					use App\PaymentDrivers\CheckoutComPaymentDriver;
 | 
				
			||||||
use App\PaymentDrivers\Common\MethodInterface;
 | 
					use App\PaymentDrivers\Common\MethodInterface;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use Checkout\CheckoutApiException;
 | 
				
			||||||
 | 
					use Checkout\CheckoutArgumentException;
 | 
				
			||||||
 | 
					use Checkout\CheckoutAuthorizationException;
 | 
				
			||||||
use Checkout\Library\Exceptions\CheckoutHttpException;
 | 
					use Checkout\Library\Exceptions\CheckoutHttpException;
 | 
				
			||||||
use Checkout\Models\Payments\IdSource;
 | 
					use Checkout\Models\Payments\IdSource;
 | 
				
			||||||
use Checkout\Models\Payments\Payment;
 | 
					use Checkout\Payments\Four\Request\Source\RequestTokenSource;
 | 
				
			||||||
use Checkout\Models\Payments\TokenSource;
 | 
					use Checkout\Payments\Source\RequestTokenSource as SourceRequestTokenSource;
 | 
				
			||||||
use Illuminate\Contracts\View\Factory;
 | 
					use Illuminate\Contracts\View\Factory;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
use Illuminate\View\View;
 | 
					use Illuminate\View\View;
 | 
				
			||||||
 | 
					use Checkout\Payments\PaymentRequest as PaymentsPaymentRequest;
 | 
				
			||||||
 | 
					use Checkout\Payments\Four\Request\PaymentRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreditCard implements MethodInterface
 | 
					class CreditCard implements MethodInterface
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -57,6 +63,31 @@ class CreditCard implements MethodInterface
 | 
				
			|||||||
        return render('gateways.checkout.credit_card.authorize', $data);
 | 
					        return render('gateways.checkout.credit_card.authorize', $data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function bootRequest($token)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($this->checkout->is_four_api){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $token_source = new RequestTokenSource();
 | 
				
			||||||
 | 
					            $token_source->token = $token;
 | 
				
			||||||
 | 
					            $request = new PaymentRequest();
 | 
				
			||||||
 | 
					            $request->source = $token_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $token_source = new SourceRequestTokenSource();
 | 
				
			||||||
 | 
					            $token_source->token = $token;
 | 
				
			||||||
 | 
					            $request = new PaymentsPaymentRequest();
 | 
				
			||||||
 | 
					            $request->source = $token_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handle authorization for credit card.
 | 
					     * Handle authorization for credit card.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -67,41 +98,54 @@ class CreditCard implements MethodInterface
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $gateway_response = \json_decode($request->gateway_response);
 | 
					        $gateway_response = \json_decode($request->gateway_response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $method = new TokenSource(
 | 
					        $customerRequest = $this->checkout->getCustomer();
 | 
				
			||||||
            $gateway_response->token
 | 
					        $request = $this->bootRequest($gateway_response->token);
 | 
				
			||||||
        );
 | 
					        $request->capture = false;
 | 
				
			||||||
 | 
					        $request->reference = '$1 payment for authorization.';
 | 
				
			||||||
 | 
					        $request->amount = 100;
 | 
				
			||||||
 | 
					        $request->currency = $this->checkout->client->getCurrencyCode();
 | 
				
			||||||
 | 
					        $request->customer = $customerRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $payment = new Payment($method, 'USD');
 | 
					 | 
				
			||||||
        $payment->amount = 100; // $1
 | 
					 | 
				
			||||||
        $payment->reference = '$1 payment for authorization.';
 | 
					 | 
				
			||||||
        $payment->capture = false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $response = $this->checkout->gateway->payments()->request($payment);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($response->approved && $response->status === 'Authorized') {
 | 
					            $response = $this->checkout->gateway->getPaymentsClient()->requestPayment($request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($response['approved'] && $response['status'] === 'Authorized') {
 | 
				
			||||||
                $payment_meta = new \stdClass;
 | 
					                $payment_meta = new \stdClass;
 | 
				
			||||||
                $payment_meta->exp_month = (string) $response->source['expiry_month'];
 | 
					                $payment_meta->exp_month = (string) $response['source']['expiry_month'];
 | 
				
			||||||
                $payment_meta->exp_year = (string) $response->source['expiry_year'];
 | 
					                $payment_meta->exp_year = (string) $response['source']['expiry_year'];
 | 
				
			||||||
                $payment_meta->brand = (string) $response->source['scheme'];
 | 
					                $payment_meta->brand = (string) $response['source']['scheme'];
 | 
				
			||||||
                $payment_meta->last4 = (string) $response->source['last4'];
 | 
					                $payment_meta->last4 = (string) $response['source']['last4'];
 | 
				
			||||||
                $payment_meta->type = (int) GatewayType::CREDIT_CARD;
 | 
					                $payment_meta->type = (int) GatewayType::CREDIT_CARD;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $data = [
 | 
					                $data = [
 | 
				
			||||||
                    'payment_meta' => $payment_meta,
 | 
					                    'payment_meta' => $payment_meta,
 | 
				
			||||||
                    'token' => $response->source['id'],
 | 
					                    'token' => $response['source']['id'],
 | 
				
			||||||
                    'payment_method_id' => GatewayType::CREDIT_CARD,  
 | 
					                    'payment_method_id' => GatewayType::CREDIT_CARD,  
 | 
				
			||||||
                ];
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $payment_method = $this->checkout->storeGatewayToken($data);
 | 
					                $payment_method = $this->checkout->storeGatewayToken($data,['gateway_customer_reference' => $customerRequest['id']]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return redirect()->route('client.payment_methods.show', $payment_method->hashed_id);
 | 
					                return redirect()->route('client.payment_methods.show', $payment_method->hashed_id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (CheckoutHttpException $exception) {
 | 
					
 | 
				
			||||||
            throw new PaymentFailed(
 | 
					
 | 
				
			||||||
                $exception->getMessage()
 | 
					        } catch (CheckoutApiException $e) {
 | 
				
			||||||
            );
 | 
					            // API error
 | 
				
			||||||
 | 
					            $request_id = $e->request_id;
 | 
				
			||||||
 | 
					            $http_status_code = $e->http_status_code;
 | 
				
			||||||
 | 
					            $error_details = $e->error_details;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new PaymentFailed($e->getMessage());
 | 
				
			||||||
 | 
					        } catch (CheckoutArgumentException $e) {
 | 
				
			||||||
 | 
					            // Bad arguments
 | 
				
			||||||
 | 
					            throw new PaymentFailed($e->getMessage());
 | 
				
			||||||
 | 
					        } catch (CheckoutAuthorizationException $e) {
 | 
				
			||||||
 | 
					            // Bad Invalid authorization
 | 
				
			||||||
 | 
					            throw new PaymentFailed($e->getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function paymentView($data)
 | 
					    public function paymentView($data)
 | 
				
			||||||
@ -145,89 +189,102 @@ class CreditCard implements MethodInterface
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $cgt = ClientGatewayToken::query()
 | 
					        $cgt = ClientGatewayToken::query()
 | 
				
			||||||
            ->where('id', $this->decodePrimaryKey($request->input('token')))
 | 
					            ->where('id', $this->decodePrimaryKey($request->input('token')))
 | 
				
			||||||
            ->where('company_id', auth()->guard('contact')->user()->client->company->id)
 | 
					            ->where('company_id', auth()->guard('contact')->user()->client->company_id)
 | 
				
			||||||
            ->first();
 | 
					            ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!$cgt) {
 | 
					        if (!$cgt) {
 | 
				
			||||||
            throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401);
 | 
					            throw new PaymentFailed(ctrans('texts.payment_token_not_found'), 401);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $method = new IdSource($cgt->token);
 | 
					        $paymentRequest = $this->checkout->bootTokenRequest($cgt->token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this->completePayment($method, $request);
 | 
					        return $this->completePayment($paymentRequest, $request);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function attemptPaymentUsingCreditCard(PaymentResponseRequest $request)
 | 
					    private function attemptPaymentUsingCreditCard(PaymentResponseRequest $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $checkout_response = $this->checkout->payment_hash->data->server_response;
 | 
					        $checkout_response = $this->checkout->payment_hash->data->server_response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $method = new TokenSource(
 | 
					        $paymentRequest = $this->bootRequest($checkout_response->token);
 | 
				
			||||||
            $checkout_response->token
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this->completePayment($method, $request);
 | 
					        return $this->completePayment($paymentRequest, $request);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function completePayment($method, PaymentResponseRequest $request)
 | 
					    private function completePayment($paymentRequest, PaymentResponseRequest $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $payment = new Payment($method, $this->checkout->payment_hash->data->currency);
 | 
					        $paymentRequest->amount = $this->checkout->payment_hash->data->value;
 | 
				
			||||||
        $payment->amount = $this->checkout->payment_hash->data->value;
 | 
					        $paymentRequest->reference = $this->checkout->getDescription();
 | 
				
			||||||
        $payment->reference = $this->checkout->getDescription();
 | 
					        $paymentRequest->customer = $this->checkout->getCustomer();
 | 
				
			||||||
        $payment->customer = [
 | 
					        $paymentRequest->metadata = ['udf1' => "Invoice Ninja"];
 | 
				
			||||||
            'name' => $this->checkout->client->present()->name() ,
 | 
					        $paymentRequest->currency = $this->checkout->client->getCurrencyCode();
 | 
				
			||||||
            'email' => $this->checkout->client->present()->email(),
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $payment->metadata = [
 | 
					        $this->checkout->payment_hash->data = array_merge((array)$this->checkout->payment_hash->data, ['checkout_payment_ref' => $paymentRequest]);
 | 
				
			||||||
            'udf1' => "Invoice Ninja",
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->checkout->payment_hash->data = array_merge((array)$this->checkout->payment_hash->data, ['checkout_payment_ref' => $payment]);
 | 
					 | 
				
			||||||
        $this->checkout->payment_hash->save();
 | 
					        $this->checkout->payment_hash->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->checkout->client->currency()->code == 'EUR' || $this->checkout->company_gateway->getConfigField('threeds')) {
 | 
					        if ($this->checkout->client->currency()->code == 'EUR' || $this->checkout->company_gateway->getConfigField('threeds')) {
 | 
				
			||||||
            $payment->{'3ds'} = ['enabled' => true];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $payment->{'success_url'} = route('checkout.3ds_redirect', [
 | 
					            $paymentRequest->{'3ds'} = ['enabled' => true];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $paymentRequest->{'success_url'} = route('checkout.3ds_redirect', [
 | 
				
			||||||
                'company_key' => $this->checkout->client->company->company_key,
 | 
					                'company_key' => $this->checkout->client->company->company_key,
 | 
				
			||||||
                'company_gateway_id' => $this->checkout->company_gateway->hashed_id,
 | 
					                'company_gateway_id' => $this->checkout->company_gateway->hashed_id,
 | 
				
			||||||
                'hash' => $this->checkout->payment_hash->hash,
 | 
					                'hash' => $this->checkout->payment_hash->hash,
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $payment->{'failure_url'} = route('checkout.3ds_redirect', [
 | 
					            $paymentRequest->{'failure_url'} = route('checkout.3ds_redirect', [
 | 
				
			||||||
                'company_key' => $this->checkout->client->company->company_key,
 | 
					                'company_key' => $this->checkout->client->company->company_key,
 | 
				
			||||||
                'company_gateway_id' => $this->checkout->company_gateway->hashed_id,
 | 
					                'company_gateway_id' => $this->checkout->company_gateway->hashed_id,
 | 
				
			||||||
                'hash' => $this->checkout->payment_hash->hash,
 | 
					                'hash' => $this->checkout->payment_hash->hash,
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $response = $this->checkout->gateway->payments()->request($payment);
 | 
					            // $response = $this->checkout->gateway->payments()->request($payment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($response->status == 'Authorized') {
 | 
					            $response = $this->checkout->gateway->getPaymentsClient()->requestPayment($paymentRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if ($response['status'] == 'Authorized') {
 | 
				
			||||||
                return $this->processSuccessfulPayment($response);
 | 
					                return $this->processSuccessfulPayment($response);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($response->status == 'Pending') {
 | 
					            if ($response['status'] == 'Pending') {
 | 
				
			||||||
                $this->checkout->confirmGatewayFee();
 | 
					                $this->checkout->confirmGatewayFee();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return $this->processPendingPayment($response);
 | 
					                return $this->processPendingPayment($response);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($response->status == 'Declined') {
 | 
					            if ($response['status'] == 'Declined') {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $this->checkout->unWindGatewayFees($this->checkout->payment_hash);
 | 
					                $this->checkout->unWindGatewayFees($this->checkout->payment_hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // $this->checkout->sendFailureMail($response->response_summary);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                //@todo - this will double up the checkout . com failed mails
 | 
					 | 
				
			||||||
                // $this->checkout->clientPaymentFailureMailer($response->status);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                return $this->processUnsuccessfulPayment($response);
 | 
					                return $this->processUnsuccessfulPayment($response);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (CheckoutHttpException $e) {
 | 
					        } 
 | 
				
			||||||
 | 
					        catch (CheckoutApiException $e) {
 | 
				
			||||||
 | 
					            // API error
 | 
				
			||||||
 | 
					            $request_id = $e->request_id;
 | 
				
			||||||
 | 
					            $http_status_code = $e->http_status_code;
 | 
				
			||||||
 | 
					            $error_details = $e->error_details;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
            $this->checkout->unWindGatewayFees($this->checkout->payment_hash);
 | 
					            $this->checkout->unWindGatewayFees($this->checkout->payment_hash);
 | 
				
			||||||
            return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
 | 
					            return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (CheckoutArgumentException $e) {
 | 
				
			||||||
 | 
					            // Bad arguments
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->checkout->unWindGatewayFees($this->checkout->payment_hash);
 | 
				
			||||||
 | 
					            return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (CheckoutAuthorizationException $e) {
 | 
				
			||||||
 | 
					            // Bad Invalid authorization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->checkout->unWindGatewayFees($this->checkout->payment_hash);
 | 
				
			||||||
 | 
					            return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -54,17 +54,18 @@ trait Utilities
 | 
				
			|||||||
        return round($amount * 100);
 | 
					        return round($amount * 100);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function processSuccessfulPayment(Payment $_payment)
 | 
					    private function processSuccessfulPayment($_payment)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->getParent()->payment_hash->data->store_card) {
 | 
					        if ($this->getParent()->payment_hash->data->store_card) {
 | 
				
			||||||
            $this->storeLocalPaymentMethod($_payment);
 | 
					            $this->storeLocalPaymentMethod($_payment);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = [
 | 
					        $data = [
 | 
				
			||||||
            'payment_method' => $_payment->source['id'],
 | 
					            'payment_method' => $_payment['source']['id'],
 | 
				
			||||||
            'payment_type' => 12,
 | 
					            'payment_type' => 12,
 | 
				
			||||||
            'amount' => $this->getParent()->payment_hash->data->raw_value,
 | 
					            'amount' => $this->getParent()->payment_hash->data->raw_value,
 | 
				
			||||||
            'transaction_reference' => $_payment->id,
 | 
					            'transaction_reference' => $_payment['id'],
 | 
				
			||||||
            'gateway_type_id' => GatewayType::CREDIT_CARD,
 | 
					            'gateway_type_id' => GatewayType::CREDIT_CARD,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,15 +83,15 @@ trait Utilities
 | 
				
			|||||||
        return redirect()->route('client.payments.show', ['payment' => $this->getParent()->encodePrimaryKey($payment->id)]);
 | 
					        return redirect()->route('client.payments.show', ['payment' => $this->getParent()->encodePrimaryKey($payment->id)]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function processUnsuccessfulPayment(Payment $_payment, $throw_exception = true)
 | 
					    public function processUnsuccessfulPayment($_payment, $throw_exception = true)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $error_message = '';
 | 
					        $error_message = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(property_exists($_payment, 'server_response'))
 | 
					        if(array_key_exists('response_summary',$_payment))
 | 
				
			||||||
            $error_message = $_payment->response_summary;
 | 
					            $error_message = $_payment['response_summary'];
 | 
				
			||||||
        elseif(property_exists($_payment, 'status'))
 | 
					        elseif(array_key_exists('status',$_payment))
 | 
				
			||||||
            $error_message = $_payment->status;
 | 
					            $error_message = $_payment['status'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->getParent()->sendFailureMail($error_message);
 | 
					        $this->getParent()->sendFailureMail($error_message);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -110,36 +111,37 @@ trait Utilities
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if ($throw_exception) {
 | 
					        if ($throw_exception) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            throw new PaymentFailed($_payment->status . " " . $error_message, $_payment->http_code);
 | 
					            throw new PaymentFailed($_payment['status'] . " " . $error_message, 500);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function processPendingPayment(Payment $_payment)
 | 
					    private function processPendingPayment($_payment)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            return redirect($_payment->_links['redirect']['href']);
 | 
					            return redirect($_payment['_links']['redirect']['href']);
 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            return $this->processInternallyFailedPayment($this->getParent(), $e);
 | 
					            return $this->getParent()->processInternallyFailedPayment($this->getParent(), $e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function storeLocalPaymentMethod(Payment $response)
 | 
					    private function storeLocalPaymentMethod($response)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $payment_meta = new stdClass;
 | 
					            $payment_meta = new stdClass;
 | 
				
			||||||
            $payment_meta->exp_month = (string) $response->source['expiry_month'];
 | 
					            $payment_meta->exp_month = (string) $response['source']['expiry_month'];
 | 
				
			||||||
            $payment_meta->exp_year = (string) $response->source['expiry_year'];
 | 
					            $payment_meta->exp_year = (string) $response['source']['expiry_year'];
 | 
				
			||||||
            $payment_meta->brand = (string) $response->source['scheme'];
 | 
					            $payment_meta->brand = (string) $response['source']['scheme'];
 | 
				
			||||||
            $payment_meta->last4 = (string) $response->source['last4'];
 | 
					            $payment_meta->last4 = (string) $response['source']['last4'];
 | 
				
			||||||
            $payment_meta->type = (int) GatewayType::CREDIT_CARD;
 | 
					            $payment_meta->type = (int) GatewayType::CREDIT_CARD;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $data = [
 | 
					            $data = [
 | 
				
			||||||
                'payment_meta' => $payment_meta,
 | 
					                'payment_meta' => $payment_meta,
 | 
				
			||||||
                'token' => $response->source['id'],
 | 
					                'token' => $response['source']['id'],
 | 
				
			||||||
                'payment_method_id' => $this->getParent()->payment_hash->data->payment_method_id,
 | 
					                'payment_method_id' => $this->getParent()->payment_hash->data->payment_method_id,
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return $this->getParent()->storePaymentMethod($data);
 | 
					            return $this->getParent()->storePaymentMethod($data, ['gateway_customer_reference' => $response['customer']['id']]);
 | 
				
			||||||
        } catch (Exception $e) {
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
            session()->flash('message', ctrans('texts.payment_method_saving_failed'));
 | 
					            session()->flash('message', ctrans('texts.payment_method_saving_failed'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -28,10 +28,22 @@ use App\PaymentDrivers\CheckoutCom\CreditCard;
 | 
				
			|||||||
use App\PaymentDrivers\CheckoutCom\Utilities;
 | 
					use App\PaymentDrivers\CheckoutCom\Utilities;
 | 
				
			||||||
use App\Utils\Traits\SystemLogTrait;
 | 
					use App\Utils\Traits\SystemLogTrait;
 | 
				
			||||||
use Checkout\CheckoutApi;
 | 
					use Checkout\CheckoutApi;
 | 
				
			||||||
 | 
					use Checkout\CheckoutApiException;
 | 
				
			||||||
 | 
					use Checkout\CheckoutArgumentException;
 | 
				
			||||||
 | 
					use Checkout\CheckoutAuthorizationException;
 | 
				
			||||||
 | 
					use Checkout\CheckoutDefaultSdk;
 | 
				
			||||||
 | 
					use Checkout\CheckoutFourSdk;
 | 
				
			||||||
 | 
					use Checkout\Environment;
 | 
				
			||||||
use Checkout\Library\Exceptions\CheckoutHttpException;
 | 
					use Checkout\Library\Exceptions\CheckoutHttpException;
 | 
				
			||||||
use Checkout\Models\Payments\IdSource;
 | 
					use Checkout\Models\Payments\IdSource;
 | 
				
			||||||
use Checkout\Models\Payments\Refund;
 | 
					use Checkout\Models\Payments\Refund;
 | 
				
			||||||
use Exception;
 | 
					use Exception;
 | 
				
			||||||
 | 
					use Checkout\Payments\Four\Request\PaymentRequest;
 | 
				
			||||||
 | 
					use Checkout\Payments\Four\Request\Source\RequestIdSource as SourceRequestIdSource;
 | 
				
			||||||
 | 
					use Checkout\Payments\PaymentRequest as PaymentsPaymentRequest;
 | 
				
			||||||
 | 
					use Checkout\Payments\Source\RequestIdSource;
 | 
				
			||||||
 | 
					use Checkout\Common\CustomerRequest;
 | 
				
			||||||
 | 
					use Checkout\Payments\RefundRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CheckoutComPaymentDriver extends BaseDriver
 | 
					class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -52,6 +64,8 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
    /* Authorise payment methods */
 | 
					    /* Authorise payment methods */
 | 
				
			||||||
    public $can_authorise_credit_card = true;
 | 
					    public $can_authorise_credit_card = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $is_four_api = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @var CheckoutApi;
 | 
					     * @var CheckoutApi;
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -109,7 +123,22 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
            'sandbox' => $this->company_gateway->getConfigField('testMode'),
 | 
					            'sandbox' => $this->company_gateway->getConfigField('testMode'),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->gateway = new CheckoutApi($config['secret'], $config['sandbox'], $config['public']);
 | 
					
 | 
				
			||||||
 | 
					        if(strlen($config['secret']) <= 38){
 | 
				
			||||||
 | 
					            $this->is_four_api = true;
 | 
				
			||||||
 | 
					            $builder = CheckoutFourSdk::staticKeys();
 | 
				
			||||||
 | 
					            $builder->setPublicKey($config['public']); // optional, only required for operations related with tokens
 | 
				
			||||||
 | 
					            $builder->setSecretKey($config['secret']);
 | 
				
			||||||
 | 
					            $builder->setEnvironment($config['sandbox'] ? Environment::sandbox(): Environment::production());
 | 
				
			||||||
 | 
					            $this->gateway = $builder->build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else    {
 | 
				
			||||||
 | 
					            $builder = CheckoutDefaultSdk::staticKeys();
 | 
				
			||||||
 | 
					            $builder->setPublicKey($config['public']); // optional, only required for operations related with tokens
 | 
				
			||||||
 | 
					            $builder->setSecretKey($config['secret']);
 | 
				
			||||||
 | 
					            $builder->setEnvironment($config['sandbox'] ? Environment::sandbox(): Environment::production());
 | 
				
			||||||
 | 
					            $this->gateway = $builder->build();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -121,9 +150,6 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function viewForType($gateway_type_id)
 | 
					    public function viewForType($gateway_type_id)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // At the moment Checkout.com payment
 | 
					 | 
				
			||||||
        // driver only supports payments using credit card.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return 'gateways.checkout.credit_card.pay';
 | 
					        return 'gateways.checkout.credit_card.pay';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -211,22 +237,40 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->init();
 | 
					        $this->init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $checkout_payment = new Refund($payment->transaction_reference);
 | 
					        $request = new RefundRequest();
 | 
				
			||||||
 | 
					        $request->reference = "{$payment->transaction_reference} " . now();
 | 
				
			||||||
 | 
					        $request->amount = $this->convertToCheckoutAmount($amount, $this->client->getCurrencyCode());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $refund = $this->gateway->payments()->refund($checkout_payment);
 | 
					            // or, refundPayment("payment_id") for a full refund
 | 
				
			||||||
            $checkout_payment = $this->gateway->payments()->details($refund->id);
 | 
					            $response = $this->gateway->getPaymentsClient()->refundPayment($payment->transaction_reference, $request);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            $response = ['refund_response' => $refund, 'checkout_payment_fetch' => $checkout_payment];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return [
 | 
					            return [
 | 
				
			||||||
                'transaction_reference' => $refund->action_id,
 | 
					                'transaction_reference' => $response['action_id'],
 | 
				
			||||||
                'transaction_response' => json_encode($response),
 | 
					                'transaction_response' => json_encode($response),
 | 
				
			||||||
                'success' => $checkout_payment->status == 'Refunded',
 | 
					                'success' => true,
 | 
				
			||||||
                'description' => $checkout_payment->status,
 | 
					                'description' => $response['reference'],
 | 
				
			||||||
                'code' => $checkout_payment->http_code,
 | 
					                'code' => 202,
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
        } catch (CheckoutHttpException $e) {
 | 
					
 | 
				
			||||||
 | 
					        } catch (CheckoutApiException $e) {
 | 
				
			||||||
 | 
					            // API error
 | 
				
			||||||
 | 
					            $request_id = $e->request_id;
 | 
				
			||||||
 | 
					            $http_status_code = $e->http_status_code;
 | 
				
			||||||
 | 
					            $error_details = $e->error_details;
 | 
				
			||||||
 | 
					        } catch (CheckoutArgumentException $e) {
 | 
				
			||||||
 | 
					            // Bad arguments
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return [
 | 
				
			||||||
 | 
					                'transaction_reference' => null,
 | 
				
			||||||
 | 
					                'transaction_response' => json_encode($e->getMessage()),
 | 
				
			||||||
 | 
					                'success' => false,
 | 
				
			||||||
 | 
					                'description' => $e->getMessage(),
 | 
				
			||||||
 | 
					                'code' => $e->getCode(),
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        } catch (CheckoutAuthorizationException $e) {
 | 
				
			||||||
 | 
					            // Bad Invalid authorization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return [
 | 
					            return [
 | 
				
			||||||
                'transaction_reference' => null,
 | 
					                'transaction_reference' => null,
 | 
				
			||||||
                'transaction_response' => json_encode($e->getMessage()),
 | 
					                'transaction_response' => json_encode($e->getMessage()),
 | 
				
			||||||
@ -235,6 +279,49 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
                'code' => $e->getCode(),
 | 
					                'code' => $e->getCode(),
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getCustomer()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        $response = $this->gateway->getCustomersClient()->get($this->client->present()->email());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return $response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(\Exception $e){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $request = new CustomerRequest();
 | 
				
			||||||
 | 
					            $request->email = $this->client->present()->email();
 | 
				
			||||||
 | 
					            $request->name = $this->client->present()->name();
 | 
				
			||||||
 | 
					            return $request;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function bootTokenRequest($token)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($this->is_four_api){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $token_source = new SourceRequestIdSource();
 | 
				
			||||||
 | 
					            $token_source->id = $token;
 | 
				
			||||||
 | 
					            $request = new PaymentRequest();
 | 
				
			||||||
 | 
					            $request->source = $token_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $token_source = new RequestIdSource();
 | 
				
			||||||
 | 
					            $token_source->id = $token;
 | 
				
			||||||
 | 
					            $request = new PaymentsPaymentRequest();
 | 
				
			||||||
 | 
					            $request->source = $token_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
 | 
					    public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
 | 
				
			||||||
@ -244,27 +331,29 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->init();
 | 
					        $this->init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $method = new IdSource($cgt->token);
 | 
					        $paymentRequest = $this->bootTokenRequest($cgt->token);
 | 
				
			||||||
 | 
					        $paymentRequest->amount = $this->convertToCheckoutAmount($amount, $this->client->getCurrencyCode());
 | 
				
			||||||
        $payment = new \Checkout\Models\Payments\Payment($method, $this->client->getCurrencyCode());
 | 
					        $paymentRequest->reference = '#' . $invoice->number . ' - ' . now();
 | 
				
			||||||
        $payment->amount = $this->convertToCheckoutAmount($amount, $this->client->getCurrencyCode());
 | 
					        $paymentRequest->customer = $this->getCustomer();
 | 
				
			||||||
        $payment->reference = $invoice->number . '-' . now();
 | 
					        $paymentRequest->metadata = ['udf1' => "Invoice Ninja"];
 | 
				
			||||||
 | 
					        $paymentRequest->currency = $this->client->getCurrencyCode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $request = new PaymentResponseRequest();
 | 
					        $request = new PaymentResponseRequest();
 | 
				
			||||||
        $request->setMethod('POST');
 | 
					        $request->setMethod('POST');
 | 
				
			||||||
        $request->request->add(['payment_hash' => $payment_hash->hash]);
 | 
					        $request->request->add(['payment_hash' => $payment_hash->hash]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $response = $this->gateway->payments()->request($payment);
 | 
					            // $response = $this->gateway->payments()->request($payment);
 | 
				
			||||||
 | 
					            $response = $this->gateway->getPaymentsClient()->requestPayment($paymentRequest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($response->status == 'Authorized') {
 | 
					            if ($response['status'] == 'Authorized') {
 | 
				
			||||||
                $this->confirmGatewayFee($request);
 | 
					                $this->confirmGatewayFee($request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $data = [
 | 
					                $data = [
 | 
				
			||||||
                    'payment_method' => $response->source['id'],
 | 
					                    'payment_method' => $response['source']['id'],
 | 
				
			||||||
                    'payment_type' => PaymentType::parseCardType(strtolower($response->source['scheme'])),
 | 
					                    'payment_type' => PaymentType::parseCardType(strtolower($response['source']['scheme'])),
 | 
				
			||||||
                    'amount' => $amount,
 | 
					                    'amount' => $amount,
 | 
				
			||||||
                    'transaction_reference' => $response->id,
 | 
					                    'transaction_reference' => $response['id'],
 | 
				
			||||||
                ];
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $payment = $this->createPayment($data, Payment::STATUS_COMPLETED);
 | 
					                $payment = $this->createPayment($data, Payment::STATUS_COMPLETED);
 | 
				
			||||||
@ -280,10 +369,10 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
                return $payment;
 | 
					                return $payment;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($response->status == 'Declined') {
 | 
					            if ($response['status'] == 'Declined') {
 | 
				
			||||||
                $this->unWindGatewayFees($payment_hash);
 | 
					                $this->unWindGatewayFees($payment_hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $this->sendFailureMail($response->status . " " . $response->response_summary);
 | 
					                $this->sendFailureMail($response['status'] . " " . $response['response_summary']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $message = [
 | 
					                $message = [
 | 
				
			||||||
                    'server_response' => $response,
 | 
					                    'server_response' => $response,
 | 
				
			||||||
@ -300,11 +389,9 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Exception | CheckoutHttpException $e) {
 | 
					        } catch (Exception | CheckoutApiException $e) {
 | 
				
			||||||
            $this->unWindGatewayFees($payment_hash);
 | 
					            $this->unWindGatewayFees($payment_hash);
 | 
				
			||||||
            $message = $e instanceof CheckoutHttpException
 | 
					            $message = $e->getMessage();
 | 
				
			||||||
                ? $e->getBody()
 | 
					 | 
				
			||||||
                : $e->getMessage();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $data = [
 | 
					            $data = [
 | 
				
			||||||
                'status' => '',
 | 
					                'status' => '',
 | 
				
			||||||
@ -334,20 +421,21 @@ class CheckoutComPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function process3dsConfirmation(Checkout3dsRequest $request)
 | 
					    public function process3dsConfirmation(Checkout3dsRequest $request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->init();
 | 
					        $this->init();
 | 
				
			||||||
        $this->setPaymentHash($request->getPaymentHash());
 | 
					        $this->setPaymentHash($request->getPaymentHash());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            $payment = $this->gateway->payments()->details(
 | 
					            $payment = $this->gateway->getPaymentsClient()->getPaymentDetails(
 | 
				
			||||||
                $request->query('cko-session-id')
 | 
					                $request->query('cko-session-id')
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($payment->approved) {
 | 
					            if ($payment['approved']) {
 | 
				
			||||||
                return $this->processSuccessfulPayment($payment);
 | 
					                return $this->processSuccessfulPayment($payment);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                return $this->processUnsuccessfulPayment($payment);
 | 
					                return $this->processUnsuccessfulPayment($payment);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (CheckoutHttpException | Exception $e) {
 | 
					        } catch (CheckoutApiException | Exception $e) {
 | 
				
			||||||
            return $this->processInternallyFailedPayment($this, $e);
 | 
					            return $this->processInternallyFailedPayment($this, $e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -235,7 +235,7 @@ class GoCardlessPaymentDriver extends BaseDriver
 | 
				
			|||||||
        nlog("GoCardless Event");
 | 
					        nlog("GoCardless Event");
 | 
				
			||||||
        nlog($request->all());
 | 
					        nlog($request->all());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(!is_array($request->events) || !is_object($request->events)){
 | 
					        if(!$request->has("events")){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            nlog("No GoCardless events to process in response?");
 | 
					            nlog("No GoCardless events to process in response?");
 | 
				
			||||||
            return response()->json([], 200);
 | 
					            return response()->json([], 200);
 | 
				
			||||||
@ -245,18 +245,19 @@ class GoCardlessPaymentDriver extends BaseDriver
 | 
				
			|||||||
        sleep(1);
 | 
					        sleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($request->events as $event) {
 | 
					        foreach ($request->events as $event) {
 | 
				
			||||||
            if ($event['action'] === 'confirmed' || $event['action'] === 'paid_out' || $event['action'] === 'paid') {
 | 
					            if ($event['action'] === 'confirmed' || $event['action'] === 'paid_out') {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                nlog("Searching for transaction reference");
 | 
					                nlog("Searching for transaction reference");
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                $payment = Payment::query()
 | 
					                $payment = Payment::query()
 | 
				
			||||||
                    ->where('transaction_reference', $event['links']['payment'])
 | 
					                    ->where('transaction_reference', $event['links']['payment'])
 | 
				
			||||||
                    // ->where('company_id', $request->getCompany()->id)
 | 
					                    ->where('company_id', $request->getCompany()->id)
 | 
				
			||||||
                    ->first();
 | 
					                    ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if ($payment) {
 | 
					                if ($payment) {
 | 
				
			||||||
                    $payment->status_id = Payment::STATUS_COMPLETED;
 | 
					                    $payment->status_id = Payment::STATUS_COMPLETED;
 | 
				
			||||||
                    $payment->save();
 | 
					                    $payment->save();
 | 
				
			||||||
 | 
					                    nlog("GoCardless completed");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    nlog("I was unable to find the payment for this reference");
 | 
					                    nlog("I was unable to find the payment for this reference");
 | 
				
			||||||
@ -268,12 +269,13 @@ class GoCardlessPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                $payment = Payment::query()
 | 
					                $payment = Payment::query()
 | 
				
			||||||
                    ->where('transaction_reference', $event['links']['payment'])
 | 
					                    ->where('transaction_reference', $event['links']['payment'])
 | 
				
			||||||
                    // ->where('company_id', $request->getCompany()->id)
 | 
					                    ->where('company_id', $request->getCompany()->id)
 | 
				
			||||||
                    ->first();
 | 
					                    ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if ($payment) {
 | 
					                if ($payment) {
 | 
				
			||||||
                    $payment->status_id = Payment::STATUS_FAILED;
 | 
					                    $payment->status_id = Payment::STATUS_FAILED;
 | 
				
			||||||
                    $payment->save();
 | 
					                    $payment->save();
 | 
				
			||||||
 | 
					                    nlog("GoCardless completed");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -60,11 +60,12 @@ use App\Events\Payment\PaymentWasRefunded;
 | 
				
			|||||||
use App\Events\Payment\PaymentWasRestored;
 | 
					use App\Events\Payment\PaymentWasRestored;
 | 
				
			||||||
use App\Events\Payment\PaymentWasUpdated;
 | 
					use App\Events\Payment\PaymentWasUpdated;
 | 
				
			||||||
use App\Events\Payment\PaymentWasVoided;
 | 
					use App\Events\Payment\PaymentWasVoided;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasMarkedSent;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasAccepted;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasArchived;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasArchived;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasCreated;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasCreated;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasDeleted;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasDeleted;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
 | 
				
			||||||
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasMarkedSent;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasRestored;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasRestored;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasUpdated;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasUpdated;
 | 
				
			||||||
use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
 | 
				
			||||||
@ -179,6 +180,8 @@ use App\Listeners\Payment\PaymentEmailedActivity;
 | 
				
			|||||||
use App\Listeners\Payment\PaymentNotification;
 | 
					use App\Listeners\Payment\PaymentNotification;
 | 
				
			||||||
use App\Listeners\Payment\PaymentRestoredActivity;
 | 
					use App\Listeners\Payment\PaymentRestoredActivity;
 | 
				
			||||||
use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity;
 | 
					use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity;
 | 
				
			||||||
 | 
					use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedActivity;
 | 
				
			||||||
 | 
					use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedNotification;
 | 
				
			||||||
use App\Listeners\PurchaseOrder\PurchaseOrderArchivedActivity;
 | 
					use App\Listeners\PurchaseOrder\PurchaseOrderArchivedActivity;
 | 
				
			||||||
use App\Listeners\PurchaseOrder\PurchaseOrderDeletedActivity;
 | 
					use App\Listeners\PurchaseOrder\PurchaseOrderDeletedActivity;
 | 
				
			||||||
use App\Listeners\PurchaseOrder\PurchaseOrderEmailActivity;
 | 
					use App\Listeners\PurchaseOrder\PurchaseOrderEmailActivity;
 | 
				
			||||||
@ -264,9 +267,9 @@ class EventServiceProvider extends ServiceProvider
 | 
				
			|||||||
     * @var array
 | 
					     * @var array
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected $listen = [
 | 
					    protected $listen = [
 | 
				
			||||||
        AccountCreated::class =>[
 | 
					        AccountCreated::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        MessageSending::class =>[
 | 
					        MessageSending::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        MessageSent::class => [
 | 
					        MessageSent::class => [
 | 
				
			||||||
            MailSentListener::class,
 | 
					            MailSentListener::class,
 | 
				
			||||||
@ -312,35 +315,35 @@ class EventServiceProvider extends ServiceProvider
 | 
				
			|||||||
        PaymentWasVoided::class => [
 | 
					        PaymentWasVoided::class => [
 | 
				
			||||||
            PaymentVoidedActivity::class,
 | 
					            PaymentVoidedActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        PaymentWasRestored::class =>[
 | 
					        PaymentWasRestored::class => [
 | 
				
			||||||
            PaymentRestoredActivity::class,
 | 
					            PaymentRestoredActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        // Clients
 | 
					        // Clients
 | 
				
			||||||
        ClientWasCreated::class =>[
 | 
					        ClientWasCreated::class => [
 | 
				
			||||||
            CreatedClientActivity::class,
 | 
					            CreatedClientActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        ClientWasArchived::class =>[
 | 
					        ClientWasArchived::class => [
 | 
				
			||||||
            ArchivedClientActivity::class,
 | 
					            ArchivedClientActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        ClientWasUpdated::class =>[
 | 
					        ClientWasUpdated::class => [
 | 
				
			||||||
            ClientUpdatedActivity::class,
 | 
					            ClientUpdatedActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        ClientWasDeleted::class =>[
 | 
					        ClientWasDeleted::class => [
 | 
				
			||||||
            DeleteClientActivity::class,
 | 
					            DeleteClientActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        ClientWasRestored::class =>[
 | 
					        ClientWasRestored::class => [
 | 
				
			||||||
            RestoreClientActivity::class,
 | 
					            RestoreClientActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        // Documents
 | 
					        // Documents
 | 
				
			||||||
        DocumentWasCreated::class =>[
 | 
					        DocumentWasCreated::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        DocumentWasArchived::class =>[
 | 
					        DocumentWasArchived::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        DocumentWasUpdated::class =>[
 | 
					        DocumentWasUpdated::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        DocumentWasDeleted::class =>[
 | 
					        DocumentWasDeleted::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        DocumentWasRestored::class =>[
 | 
					        DocumentWasRestored::class => [
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        CreditWasCreated::class => [
 | 
					        CreditWasCreated::class => [
 | 
				
			||||||
            CreatedCreditActivity::class,
 | 
					            CreatedCreditActivity::class,
 | 
				
			||||||
@ -404,11 +407,11 @@ class EventServiceProvider extends ServiceProvider
 | 
				
			|||||||
        InvoiceWasCreated::class => [
 | 
					        InvoiceWasCreated::class => [
 | 
				
			||||||
            CreateInvoiceActivity::class,
 | 
					            CreateInvoiceActivity::class,
 | 
				
			||||||
            InvoiceCreatedNotification::class,
 | 
					            InvoiceCreatedNotification::class,
 | 
				
			||||||
        //    CreateInvoicePdf::class,
 | 
					            //    CreateInvoicePdf::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        InvoiceWasPaid::class => [
 | 
					        InvoiceWasPaid::class => [
 | 
				
			||||||
           InvoicePaidActivity::class,
 | 
					            InvoicePaidActivity::class,
 | 
				
			||||||
           CreateInvoicePdf::class,
 | 
					            CreateInvoicePdf::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        InvoiceWasViewed::class => [
 | 
					        InvoiceWasViewed::class => [
 | 
				
			||||||
            InvoiceViewedActivity::class,
 | 
					            InvoiceViewedActivity::class,
 | 
				
			||||||
@ -471,6 +474,10 @@ class EventServiceProvider extends ServiceProvider
 | 
				
			|||||||
        PurchaseOrderWasViewed::class => [
 | 
					        PurchaseOrderWasViewed::class => [
 | 
				
			||||||
            PurchaseOrderViewedActivity::class,
 | 
					            PurchaseOrderViewedActivity::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					        PurchaseOrderWasAccepted::class => [
 | 
				
			||||||
 | 
					            PurchaseOrderAcceptedActivity::class,
 | 
				
			||||||
 | 
					            PurchaseOrderAcceptedNotification::class
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
        CompanyDocumentsDeleted::class => [
 | 
					        CompanyDocumentsDeleted::class => [
 | 
				
			||||||
            DeleteCompanyDocuments::class,
 | 
					            DeleteCompanyDocuments::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
@ -593,7 +600,12 @@ class EventServiceProvider extends ServiceProvider
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        VendorWasUpdated::class => [
 | 
					        VendorWasUpdated::class => [
 | 
				
			||||||
            VendorUpdatedActivity::class,
 | 
					            VendorUpdatedActivity::class,
 | 
				
			||||||
        ]
 | 
					        ],
 | 
				
			||||||
 | 
					        \SocialiteProviders\Manager\SocialiteWasCalled::class => [
 | 
				
			||||||
 | 
					            // ... Manager won't register drivers that are not added to this listener.
 | 
				
			||||||
 | 
					            \SocialiteProviders\Apple\AppleExtendSocialite::class . '@handle',
 | 
				
			||||||
 | 
					            \SocialiteProviders\Microsoft\MicrosoftExtendSocialite::class . '@handle',
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,8 @@ class RouteServiceProvider extends ServiceProvider
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->mapContactApiRoutes();
 | 
					        $this->mapContactApiRoutes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->mapVendorsApiRoutes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->mapClientApiRoutes();
 | 
					        $this->mapClientApiRoutes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->mapShopApiRoutes();
 | 
					        $this->mapShopApiRoutes();
 | 
				
			||||||
@ -121,4 +123,12 @@ class RouteServiceProvider extends ServiceProvider
 | 
				
			|||||||
             ->namespace($this->namespace)
 | 
					             ->namespace($this->namespace)
 | 
				
			||||||
             ->group(base_path('routes/shop.php'));
 | 
					             ->group(base_path('routes/shop.php'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function mapVendorsApiRoutes()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Route::prefix('')
 | 
				
			||||||
 | 
					            ->middleware('client')
 | 
				
			||||||
 | 
					            ->namespace($this->namespace)
 | 
				
			||||||
 | 
					            ->group(base_path('routes/vendor.php'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -49,8 +49,6 @@ class GenerateDeliveryNote
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->contact = $contact;
 | 
					        $this->contact = $contact;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // $this->disk = 'public';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->disk = $disk ?? config('filesystems.default');
 | 
					        $this->disk = $disk ?? config('filesystems.default');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -341,7 +341,7 @@ class InvoiceService
 | 
				
			|||||||
            if(Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf'))
 | 
					            if(Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf'))
 | 
				
			||||||
                Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
 | 
					                Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if(Ninja::isHosted() && Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf')) {
 | 
					            if(Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf')) {
 | 
				
			||||||
                Storage::disk('public')->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
 | 
					                Storage::disk('public')->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -56,6 +56,10 @@ class Design extends BaseDesign
 | 
				
			|||||||
    /** @var Payment[] */
 | 
					    /** @var Payment[] */
 | 
				
			||||||
    public $payments;
 | 
					    public $payments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $settings_object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @var array */
 | 
					    /** @var array */
 | 
				
			||||||
    public $aging = [];
 | 
					    public $aging = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -80,6 +84,7 @@ class Design extends BaseDesign
 | 
				
			|||||||
        Str::endsWith('.html', $design) ? $this->design = $design : $this->design = "{$design}.html";
 | 
					        Str::endsWith('.html', $design) ? $this->design = $design : $this->design = "{$design}.html";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->options = $options;
 | 
					        $this->options = $options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function html(): ?string
 | 
					    public function html(): ?string
 | 
				
			||||||
@ -574,19 +579,19 @@ class Design extends BaseDesign
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        foreach ($this->context['pdf_variables']["{$type}_columns"] as $column) {
 | 
					        foreach ($this->context['pdf_variables']["{$type}_columns"] as $column) {
 | 
				
			||||||
            if (array_key_exists($column, $aliases)) {
 | 
					            if (array_key_exists($column, $aliases)) {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($aliases[$column], 1) . '-th', 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
 | 
					                $elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($aliases[$column], 1) . '-th', 'hidden' => $this->settings_object->getSetting('hide_empty_columns_on_pdf')]];
 | 
				
			||||||
            } elseif ($column == '$product.discount' && !$this->client->company->enable_product_discount) {
 | 
					            } elseif ($column == '$product.discount' && !$this->company->enable_product_discount) {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
 | 
					                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
 | 
				
			||||||
            } elseif ($column == '$product.quantity' && !$this->client->company->enable_product_quantity) {
 | 
					            } elseif ($column == '$product.quantity' && !$this->company->enable_product_quantity) {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
 | 
					                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
 | 
				
			||||||
            } elseif ($column == '$product.tax_rate1') {
 | 
					            } elseif ($column == '$product.tax_rate1') {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax1-th", 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
 | 
					                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax1-th", 'hidden' => $this->settings_object->getSetting('hide_empty_columns_on_pdf')]];
 | 
				
			||||||
            } elseif ($column == '$product.tax_rate2') {
 | 
					            } elseif ($column == '$product.tax_rate2') {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax2-th", 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
 | 
					                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax2-th", 'hidden' => $this->settings_object->getSetting('hide_empty_columns_on_pdf')]];
 | 
				
			||||||
            } elseif ($column == '$product.tax_rate3') {
 | 
					            } elseif ($column == '$product.tax_rate3') {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax3-th", 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
 | 
					                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax3-th", 'hidden' => $this->settings_object->getSetting('hide_empty_columns_on_pdf')]];
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'hidden' => $this->client->getSetting('hide_empty_columns_on_pdf')]];
 | 
					                $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'hidden' => $this->settings_object->getSetting('hide_empty_columns_on_pdf')]];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -677,9 +682,9 @@ class Design extends BaseDesign
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if ($cell == '$task.rate') {
 | 
					                    if ($cell == '$task.rate') {
 | 
				
			||||||
                        $element['elements'][] = ['element' => 'td', 'content' => $row['$task.cost'], 'properties' => ['data-ref' => 'task_table-task.cost-td']];
 | 
					                        $element['elements'][] = ['element' => 'td', 'content' => $row['$task.cost'], 'properties' => ['data-ref' => 'task_table-task.cost-td']];
 | 
				
			||||||
                    } elseif ($cell == '$product.discount' && !$this->client->company->enable_product_discount) {
 | 
					                    } elseif ($cell == '$product.discount' && !$this->company->enable_product_discount) {
 | 
				
			||||||
                        $element['elements'][] = ['element' => 'td', 'content' => $row['$product.discount'], 'properties' => ['data-ref' => 'product_table-product.discount-td', 'style' => 'display: none;']];
 | 
					                        $element['elements'][] = ['element' => 'td', 'content' => $row['$product.discount'], 'properties' => ['data-ref' => 'product_table-product.discount-td', 'style' => 'display: none;']];
 | 
				
			||||||
                    } elseif ($cell == '$product.quantity' && !$this->client->company->enable_product_quantity) {
 | 
					                    } elseif ($cell == '$product.quantity' && !$this->company->enable_product_quantity) {
 | 
				
			||||||
                        $element['elements'][] = ['element' => 'td', 'content' => $row['$product.quantity'], 'properties' => ['data-ref' => 'product_table-product.quantity-td', 'style' => 'display: none;']];
 | 
					                        $element['elements'][] = ['element' => 'td', 'content' => $row['$product.quantity'], 'properties' => ['data-ref' => 'product_table-product.quantity-td', 'style' => 'display: none;']];
 | 
				
			||||||
                    } elseif ($cell == '$task.hours') {
 | 
					                    } elseif ($cell == '$task.hours') {
 | 
				
			||||||
                        $element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity'], 'properties' => ['data-ref' => 'task_table-task.hours-td']];
 | 
					                        $element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity'], 'properties' => ['data-ref' => 'task_table-task.hours-td']];
 | 
				
			||||||
@ -799,7 +804,7 @@ class Design extends BaseDesign
 | 
				
			|||||||
            } 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 = $this->entity->{$_variable} != 0 || $this->entity->{$_variable} != '0';
 | 
					                $visible = (int)$this->entity->{$_variable} != 0 || $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']],
 | 
				
			||||||
@ -807,7 +812,7 @@ class Design extends BaseDesign
 | 
				
			|||||||
                ]];
 | 
					                ]];
 | 
				
			||||||
            } elseif (Str::startsWith($variable, '$custom')) {
 | 
					            } elseif (Str::startsWith($variable, '$custom')) {
 | 
				
			||||||
                $field = explode('_', $variable);
 | 
					                $field = explode('_', $variable);
 | 
				
			||||||
                $visible = is_object($this->client->company->custom_fields) && property_exists($this->client->company->custom_fields, $field[1]) && !empty($this->client->company->custom_fields->{$field[1]});
 | 
					                $visible = is_object($this->company->custom_fields) && property_exists($this->company->custom_fields, $field[1]) && !empty($this->company->custom_fields->{$field[1]});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $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']],
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,10 @@ trait DesignHelpers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->document();
 | 
					        $this->document();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->settings_object = $this->vendor ? $this->vendor->company : $this->client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->company = $this->vendor ? $this->vendor->company : $this->client->company;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -180,7 +184,7 @@ trait DesignHelpers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            $key = array_search(sprintf('%s%s.tax', '$', $type), $this->context['pdf_variables']["{$type}_columns"], true);
 | 
					            $key = array_search(sprintf('%s%s.tax', '$', $type), $this->context['pdf_variables']["{$type}_columns"], true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if ($key) {
 | 
					            if ($key !== false) {
 | 
				
			||||||
                array_splice($this->context['pdf_variables']["{$type}_columns"], $key, 1, $taxes);
 | 
					                array_splice($this->context['pdf_variables']["{$type}_columns"], $key, 1, $taxes);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -338,7 +342,7 @@ document.addEventListener('DOMContentLoaded', function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $key = array_search(sprintf('%s%s.description', '$', $type), $this->context['pdf_variables']["{$type}_columns"], true);
 | 
					        $key = array_search(sprintf('%s%s.description', '$', $type), $this->context['pdf_variables']["{$type}_columns"], true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($key) {
 | 
					        if ($key !== false) {
 | 
				
			||||||
            array_splice($this->context['pdf_variables']["{$type}_columns"], $key + 1, 0, $custom_columns);
 | 
					            array_splice($this->context['pdf_variables']["{$type}_columns"], $key + 1, 0, $custom_columns);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ class CreateInvitations extends AbstractService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function run()
 | 
					    public function run()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $contacts = $this->purchase_order->vendor->contacts()->where('send_email', true)->get();
 | 
					        $contacts = $this->purchase_order->vendor->contacts()->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($contacts->count() == 0){
 | 
					        if($contacts->count() == 0){
 | 
				
			||||||
            $this->createBlankContact();
 | 
					            $this->createBlankContact();
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ class GetPurchaseOrderPdf extends AbstractService
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! $this->contact) {
 | 
					        if (! $this->contact) {
 | 
				
			||||||
            $this->contact = $this->purchase_order->vendor->contacts()->where('send_email', true)->first();
 | 
					            $this->contact = $this->purchase_order->vendor->contacts()->orderBy('send_email', 'DESC')->first();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $invitation = $this->purchase_order->invitations()->where('vendor_contact_id', $this->contact->id)->first();
 | 
					        $invitation = $this->purchase_order->invitations()->where('vendor_contact_id', $this->contact->id)->first();
 | 
				
			||||||
 | 
				
			|||||||
@ -11,11 +11,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Services\PurchaseOrder;
 | 
					namespace App\Services\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Jobs\Vendor\CreatePurchaseOrderPdf;
 | 
				
			||||||
use App\Models\PurchaseOrder;
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
use App\Services\PurchaseOrder\ApplyNumber;
 | 
					use App\Services\PurchaseOrder\ApplyNumber;
 | 
				
			||||||
use App\Services\PurchaseOrder\CreateInvitations;
 | 
					use App\Services\PurchaseOrder\CreateInvitations;
 | 
				
			||||||
use App\Services\PurchaseOrder\GetPurchaseOrderPdf;
 | 
					use App\Services\PurchaseOrder\GetPurchaseOrderPdf;
 | 
				
			||||||
 | 
					use App\Services\PurchaseOrder\TriggeredActions;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PurchaseOrderService
 | 
					class PurchaseOrderService
 | 
				
			||||||
@ -62,6 +63,13 @@ class PurchaseOrderService
 | 
				
			|||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function triggeredActions($request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->purchase_order = (new TriggeredActions($this->purchase_order->load('invitations'), $request))->run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getPurchaseOrderPdf($contact = null)
 | 
					    public function getPurchaseOrderPdf($contact = null)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return (new GetPurchaseOrderPdf($this->purchase_order, $contact))->run();
 | 
					        return (new GetPurchaseOrderPdf($this->purchase_order, $contact))->run();
 | 
				
			||||||
@ -81,6 +89,34 @@ class PurchaseOrderService
 | 
				
			|||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function touchPdf($force = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            if($force){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $this->purchase_order->invitations->each(function ($invitation) {
 | 
				
			||||||
 | 
					                    CreatePurchaseOrderPdf::dispatchNow($invitation);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return $this;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->purchase_order->invitations->each(function ($invitation) {
 | 
				
			||||||
 | 
					                CreatePurchaseOrderPdf::dispatch($invitation);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(\Exception $e){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            nlog("failed creating purchase orders in Touch PDF");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Saves the purchase order.
 | 
					     * Saves the purchase order.
 | 
				
			||||||
     * @return \App\Models\PurchaseOrder object
 | 
					     * @return \App\Models\PurchaseOrder object
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										65
									
								
								app/Services/PurchaseOrder/TriggeredActions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								app/Services/PurchaseOrder/TriggeredActions.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					<?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\Services\PurchaseOrder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Events\Invoice\InvoiceWasEmailed;
 | 
				
			||||||
 | 
					use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
 | 
				
			||||||
 | 
					use App\Jobs\Entity\EmailEntity;
 | 
				
			||||||
 | 
					use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
 | 
				
			||||||
 | 
					use App\Models\Invoice;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Services\AbstractService;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Traits\GeneratesCounter;
 | 
				
			||||||
 | 
					use Illuminate\Http\Request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TriggeredActions extends AbstractService
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use GeneratesCounter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private $purchase_order;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function __construct(PurchaseOrder $purchase_order, Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->request = $request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->purchase_order = $purchase_order;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function run()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') {
 | 
				
			||||||
 | 
					            $this->purchase_order->service()->markSent()->touchPdf()->save();
 | 
				
			||||||
 | 
					            $this->sendEmail();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
 | 
				
			||||||
 | 
					            $this->purchase_order = $this->purchase_order->service()->markSent()->touchPdf()->save();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if ($this->request->has('cancel') && $this->request->input('cancel') == 'true') {
 | 
				
			||||||
 | 
					        //     $this->purchase_order = $this->purchase_order->service()->handleCancellation()->save();
 | 
				
			||||||
 | 
					        // }        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->purchase_order;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function sendEmail()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PurchaseOrderEmail::dispatch($this->purchase_order, $this->purchase_order->company);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -164,6 +164,7 @@ class SubscriptionService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $recurring_invoice = $this->convertInvoiceToRecurring($client_contact->client_id);
 | 
					        $recurring_invoice = $this->convertInvoiceToRecurring($client_contact->client_id);
 | 
				
			||||||
        $recurring_invoice->next_send_date = now()->addSeconds($this->subscription->trial_duration);
 | 
					        $recurring_invoice->next_send_date = now()->addSeconds($this->subscription->trial_duration);
 | 
				
			||||||
 | 
					        $recurring_invoice->next_send_date_client = now()->addSeconds($this->subscription->trial_duration);
 | 
				
			||||||
        $recurring_invoice->backup = 'is_trial';
 | 
					        $recurring_invoice->backup = 'is_trial';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(array_key_exists('coupon', $data) && ($data['coupon'] == $this->subscription->promo_code) && $this->subscription->promo_discount > 0)
 | 
					        if(array_key_exists('coupon', $data) && ($data['coupon'] == $this->subscription->promo_code) && $this->subscription->promo_discount > 0)
 | 
				
			||||||
@ -620,7 +621,9 @@ class SubscriptionService
 | 
				
			|||||||
            $recurring_invoice = $this->convertInvoiceToRecurring($old_recurring_invoice->client_id);
 | 
					            $recurring_invoice = $this->convertInvoiceToRecurring($old_recurring_invoice->client_id);
 | 
				
			||||||
            $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
 | 
					            $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
 | 
				
			||||||
            $recurring_invoice->next_send_date = now()->format('Y-m-d');
 | 
					            $recurring_invoice->next_send_date = now()->format('Y-m-d');
 | 
				
			||||||
 | 
					            $recurring_invoice->next_send_date_client = now()->format('Y-m-d');
 | 
				
			||||||
            $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
 | 
					            $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
 | 
				
			||||||
 | 
					            $recurring_invoice->next_send_date_client = $recurring_invoice->nextSendDateClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* Start the recurring service */
 | 
					            /* Start the recurring service */
 | 
				
			||||||
            $recurring_invoice->service()
 | 
					            $recurring_invoice->service()
 | 
				
			||||||
@ -754,8 +757,9 @@ class SubscriptionService
 | 
				
			|||||||
        $recurring_invoice->auto_bill_enabled =  $this->setAutoBillFlag($recurring_invoice->auto_bill);
 | 
					        $recurring_invoice->auto_bill_enabled =  $this->setAutoBillFlag($recurring_invoice->auto_bill);
 | 
				
			||||||
        $recurring_invoice->due_date_days = 'terms';
 | 
					        $recurring_invoice->due_date_days = 'terms';
 | 
				
			||||||
        $recurring_invoice->next_send_date = now()->format('Y-m-d');
 | 
					        $recurring_invoice->next_send_date = now()->format('Y-m-d');
 | 
				
			||||||
 | 
					        $recurring_invoice->next_send_date_client = now()->format('Y-m-d');
 | 
				
			||||||
        $recurring_invoice->next_send_date =  $recurring_invoice->nextSendDate();
 | 
					        $recurring_invoice->next_send_date =  $recurring_invoice->nextSendDate();
 | 
				
			||||||
 | 
					        $recurring_invoice->next_send_date_client = $recurring_invoice->nextSendDateClient();
 | 
				
			||||||
        return $recurring_invoice;
 | 
					        return $recurring_invoice;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,10 @@ class ZeroCostProduct extends AbstractService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            $recurring_invoice->next_send_date = now();
 | 
					            $recurring_invoice->next_send_date = now();
 | 
				
			||||||
            $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
 | 
					            $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
 | 
				
			||||||
 | 
					            $recurring_invoice->next_send_date = now();
 | 
				
			||||||
 | 
					            $recurring_invoice->next_send_date_client = now();
 | 
				
			||||||
            $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
 | 
					            $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
 | 
				
			||||||
 | 
					            $recurring_invoice->next_send_date_client = $recurring_invoice->nextSendDateClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* Start the recurring service */
 | 
					            /* Start the recurring service */
 | 
				
			||||||
            $recurring_invoice->service()
 | 
					            $recurring_invoice->service()
 | 
				
			||||||
 | 
				
			|||||||
@ -86,6 +86,7 @@ class AccountTransformer extends EntityTransformer
 | 
				
			|||||||
            'hosted_client_count' => (int) $account->hosted_client_count,
 | 
					            'hosted_client_count' => (int) $account->hosted_client_count,
 | 
				
			||||||
            'hosted_company_count' => (int) $account->hosted_company_count,
 | 
					            'hosted_company_count' => (int) $account->hosted_company_count,
 | 
				
			||||||
            'is_hosted' => (bool) Ninja::isHosted(),
 | 
					            'is_hosted' => (bool) Ninja::isHosted(),
 | 
				
			||||||
 | 
					            'set_react_as_default_ap' => (bool) $account->set_react_as_default_ap
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -65,7 +65,7 @@ class ActivityTransformer extends EntityTransformer
 | 
				
			|||||||
            'created_at' => (int) $activity->created_at,
 | 
					            'created_at' => (int) $activity->created_at,
 | 
				
			||||||
            'expense_id' => $activity->expense_id ? (string) $this->encodePrimaryKey($activity->expense_id) : '',
 | 
					            'expense_id' => $activity->expense_id ? (string) $this->encodePrimaryKey($activity->expense_id) : '',
 | 
				
			||||||
            'is_system' => (bool) $activity->is_system,
 | 
					            'is_system' => (bool) $activity->is_system,
 | 
				
			||||||
            'contact_id' => $activity->contact_id ? (string) $this->encodePrimaryKey($activity->contact_id) : '',
 | 
					            'contact_id' => $activity->client_contact_id ? (string) $this->encodePrimaryKey($activity->client_contact_id) : '',
 | 
				
			||||||
            'task_id' => $activity->task_id ? (string) $this->encodePrimaryKey($activity->task_id) : '',
 | 
					            'task_id' => $activity->task_id ? (string) $this->encodePrimaryKey($activity->task_id) : '',
 | 
				
			||||||
            'token_id' => $activity->token_id ? (string) $this->encodePrimaryKey($activity->token_id) : '',
 | 
					            'token_id' => $activity->token_id ? (string) $this->encodePrimaryKey($activity->token_id) : '',
 | 
				
			||||||
            'notes' => $activity->notes ? (string) $activity->notes : '',
 | 
					            'notes' => $activity->notes ? (string) $activity->notes : '',
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,7 @@ use App\Models\Payment;
 | 
				
			|||||||
use App\Models\PaymentTerm;
 | 
					use App\Models\PaymentTerm;
 | 
				
			||||||
use App\Models\Product;
 | 
					use App\Models\Product;
 | 
				
			||||||
use App\Models\Project;
 | 
					use App\Models\Project;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
use App\Models\Quote;
 | 
					use App\Models\Quote;
 | 
				
			||||||
use App\Models\RecurringExpense;
 | 
					use App\Models\RecurringExpense;
 | 
				
			||||||
use App\Models\RecurringInvoice;
 | 
					use App\Models\RecurringInvoice;
 | 
				
			||||||
@ -39,6 +40,7 @@ use App\Models\TaskStatus;
 | 
				
			|||||||
use App\Models\TaxRate;
 | 
					use App\Models\TaxRate;
 | 
				
			||||||
use App\Models\User;
 | 
					use App\Models\User;
 | 
				
			||||||
use App\Models\Webhook;
 | 
					use App\Models\Webhook;
 | 
				
			||||||
 | 
					use App\Transformers\PurchaseOrderTransformer;
 | 
				
			||||||
use App\Transformers\RecurringExpenseTransformer;
 | 
					use App\Transformers\RecurringExpenseTransformer;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
use stdClass;
 | 
					use stdClass;
 | 
				
			||||||
@ -95,6 +97,7 @@ class CompanyTransformer extends EntityTransformer
 | 
				
			|||||||
        'task_statuses',
 | 
					        'task_statuses',
 | 
				
			||||||
        'subscriptions',
 | 
					        'subscriptions',
 | 
				
			||||||
        'recurring_expenses',
 | 
					        'recurring_expenses',
 | 
				
			||||||
 | 
					        'purchase_orders',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -391,4 +394,11 @@ class CompanyTransformer extends EntityTransformer
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return $this->includeCollection($company->subscriptions, $transformer, Subscription::class);
 | 
					        return $this->includeCollection($company->subscriptions, $transformer, Subscription::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function includePurchaseOrders(Company $company)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $transformer = new PurchaseOrderTransformer($this->serializer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->includeCollection($company->purchase_orders, $transformer, PurchaseOrder::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -265,6 +265,8 @@ trait MakesInvoiceValues
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function transformLineItems($items, $table_type = '$product') :array
 | 
					    public function transformLineItems($items, $table_type = '$product') :array
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $entity = $this->client ? $this->client : $this->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = [];
 | 
					        $data = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! is_array($items)) {
 | 
					        if (! is_array($items)) {
 | 
				
			||||||
@ -294,23 +296,23 @@ trait MakesInvoiceValues
 | 
				
			|||||||
            $data[$key][$table_type.'.item'] = is_null(optional($item)->item) ? $item->product_key : $item->item;
 | 
					            $data[$key][$table_type.'.item'] = is_null(optional($item)->item) ? $item->product_key : $item->item;
 | 
				
			||||||
            $data[$key][$table_type.'.service'] = is_null(optional($item)->service) ? $item->product_key : $item->service;
 | 
					            $data[$key][$table_type.'.service'] = is_null(optional($item)->service) ? $item->product_key : $item->service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $this->client);
 | 
					            $data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $entity);
 | 
				
			||||||
            $data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $this->client);
 | 
					            $data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $entity);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
            $data[$key][$table_type . ".{$_table_type}1"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}1", $item->custom_value1, $this->client);
 | 
					            $data[$key][$table_type . ".{$_table_type}1"] = $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}1", $item->custom_value1, $entity);
 | 
				
			||||||
            $data[$key][$table_type . ".{$_table_type}2"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}2", $item->custom_value2, $this->client);
 | 
					            $data[$key][$table_type . ".{$_table_type}2"] = $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}2", $item->custom_value2, $entity);
 | 
				
			||||||
            $data[$key][$table_type . ".{$_table_type}3"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}3", $item->custom_value3, $this->client);
 | 
					            $data[$key][$table_type . ".{$_table_type}3"] = $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}3", $item->custom_value3, $entity);
 | 
				
			||||||
            $data[$key][$table_type . ".{$_table_type}4"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->client);
 | 
					            $data[$key][$table_type . ".{$_table_type}4"] = $helpers->formatCustomFieldValue($this->company->custom_fields, "{$_table_type}4", $item->custom_value4, $entity);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if($item->quantity > 0 || $item->cost > 0){
 | 
					            if($item->quantity > 0 || $item->cost > 0){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $data[$key][$table_type.'.quantity'] = Number::formatValueNoTrailingZeroes($item->quantity, $this->client->currency());
 | 
					                $data[$key][$table_type.'.quantity'] = Number::formatValueNoTrailingZeroes($item->quantity, $entity->currency());
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                $data[$key][$table_type.'.unit_cost'] = Number::formatMoneyNoRounding($item->cost, $this->client);
 | 
					                $data[$key][$table_type.'.unit_cost'] = Number::formatMoneyNoRounding($item->cost, $entity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $data[$key][$table_type.'.cost'] = Number::formatMoney($item->cost, $this->client);
 | 
					                $data[$key][$table_type.'.cost'] = Number::formatMoney($item->cost, $entity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $data[$key][$table_type.'.line_total'] =  Number::formatMoney($item->line_total, $this->client);
 | 
					                $data[$key][$table_type.'.line_total'] =  Number::formatMoney($item->line_total, $entity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else {
 | 
					            else {
 | 
				
			||||||
@ -326,13 +328,13 @@ trait MakesInvoiceValues
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(property_exists($item, 'gross_line_total'))
 | 
					            if(property_exists($item, 'gross_line_total'))
 | 
				
			||||||
                $data[$key][$table_type.'.gross_line_total'] =  ($item->gross_line_total == 0) ? '' :Number::formatMoney($item->gross_line_total, $this->client);
 | 
					                $data[$key][$table_type.'.gross_line_total'] =  ($item->gross_line_total == 0) ? '' :Number::formatMoney($item->gross_line_total, $entity);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                $data[$key][$table_type.'.gross_line_total'] = '';
 | 
					                $data[$key][$table_type.'.gross_line_total'] = '';
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
            if (isset($item->discount) && $item->discount > 0) {
 | 
					            if (isset($item->discount) && $item->discount > 0) {
 | 
				
			||||||
                if ($item->is_amount_discount) {
 | 
					                if ($item->is_amount_discount) {
 | 
				
			||||||
                    $data[$key][$table_type.'.discount'] = Number::formatMoney($item->discount, $this->client);
 | 
					                    $data[$key][$table_type.'.discount'] = Number::formatMoney($item->discount, $entity);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    $data[$key][$table_type.'.discount'] = floatval($item->discount).'%';
 | 
					                    $data[$key][$table_type.'.discount'] = floatval($item->discount).'%';
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -376,13 +378,14 @@ trait MakesInvoiceValues
 | 
				
			|||||||
    private function makeLineTaxes() :string
 | 
					    private function makeLineTaxes() :string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $tax_map = $this->calc()->getTaxMap();
 | 
					        $tax_map = $this->calc()->getTaxMap();
 | 
				
			||||||
 | 
					        $entity = $this->client ? $this->client : $this->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = '';
 | 
					        $data = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($tax_map as $tax) {
 | 
					        foreach ($tax_map as $tax) {
 | 
				
			||||||
            $data .= '<tr class="line_taxes">';
 | 
					            $data .= '<tr class="line_taxes">';
 | 
				
			||||||
            $data .= '<td>'.$tax['name'].'</td>';
 | 
					            $data .= '<td>'.$tax['name'].'</td>';
 | 
				
			||||||
            $data .= '<td>'.Number::formatMoney($tax['total'], $this->client).'</td></tr>';
 | 
					            $data .= '<td>'.Number::formatMoney($tax['total'], $entity).'</td></tr>';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $data;
 | 
					        return $data;
 | 
				
			||||||
@ -395,6 +398,7 @@ trait MakesInvoiceValues
 | 
				
			|||||||
    private function makeTotalTaxes() :string
 | 
					    private function makeTotalTaxes() :string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $data = '';
 | 
					        $data = '';
 | 
				
			||||||
 | 
					        $entity = $this->client ? $this->client : $this->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! $this->calc()->getTotalTaxMap()) {
 | 
					        if (! $this->calc()->getTotalTaxMap()) {
 | 
				
			||||||
            return $data;
 | 
					            return $data;
 | 
				
			||||||
@ -403,7 +407,7 @@ trait MakesInvoiceValues
 | 
				
			|||||||
        foreach ($this->calc()->getTotalTaxMap() as $tax) {
 | 
					        foreach ($this->calc()->getTotalTaxMap() as $tax) {
 | 
				
			||||||
            $data .= '<tr class="total_taxes">';
 | 
					            $data .= '<tr class="total_taxes">';
 | 
				
			||||||
            $data .= '<td>'.$tax['name'].'</td>';
 | 
					            $data .= '<td>'.$tax['name'].'</td>';
 | 
				
			||||||
            $data .= '<td>'.Number::formatMoney($tax['total'], $this->client).'</td></tr>';
 | 
					            $data .= '<td>'.Number::formatMoney($tax['total'], $entity).'</td></tr>';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $data;
 | 
					        return $data;
 | 
				
			||||||
@ -427,13 +431,14 @@ trait MakesInvoiceValues
 | 
				
			|||||||
    private function totalTaxValues() :string
 | 
					    private function totalTaxValues() :string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $data = '';
 | 
					        $data = '';
 | 
				
			||||||
 | 
					        $entity = $this->client ? $this->client : $this->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! $this->calc()->getTotalTaxMap()) {
 | 
					        if (! $this->calc()->getTotalTaxMap()) {
 | 
				
			||||||
            return $data;
 | 
					            return $data;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($this->calc()->getTotalTaxMap() as $tax) {
 | 
					        foreach ($this->calc()->getTotalTaxMap() as $tax) {
 | 
				
			||||||
            $data .= '<span>'.Number::formatMoney($tax['total'], $this->client).'</span>';
 | 
					            $data .= '<span>'.Number::formatMoney($tax['total'], $entity).'</span>';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $data;
 | 
					        return $data;
 | 
				
			||||||
@ -455,11 +460,12 @@ trait MakesInvoiceValues
 | 
				
			|||||||
    private function lineTaxValues() :string
 | 
					    private function lineTaxValues() :string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $tax_map = $this->calc()->getTaxMap();
 | 
					        $tax_map = $this->calc()->getTaxMap();
 | 
				
			||||||
 | 
					        $entity = $this->client ? $this->client : $this->company;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = '';
 | 
					        $data = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($tax_map as $tax) {
 | 
					        foreach ($tax_map as $tax) {
 | 
				
			||||||
            $data .= '<span>'.Number::formatMoney($tax['total'], $this->client).'</span>';
 | 
					            $data .= '<span>'.Number::formatMoney($tax['total'], $entity).'</span>';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $data;
 | 
					        return $data;
 | 
				
			||||||
@ -481,7 +487,8 @@ trait MakesInvoiceValues
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function generateCustomCSS() :string
 | 
					    public function generateCustomCSS() :string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $settings = $this->client->getMergedSettings();
 | 
					
 | 
				
			||||||
 | 
					        $settings = $this->client ? $this->client->getMergedSettings() :  $this->company->settings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $header_and_footer = '
 | 
					        $header_and_footer = '
 | 
				
			||||||
.header, .header-space {
 | 
					.header, .header-space {
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ use App\Models\Client;
 | 
				
			|||||||
use App\Models\Credit;
 | 
					use App\Models\Credit;
 | 
				
			||||||
use App\Models\Invoice;
 | 
					use App\Models\Invoice;
 | 
				
			||||||
use App\Models\Payment;
 | 
					use App\Models\Payment;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
use App\Models\Quote;
 | 
					use App\Models\Quote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -100,6 +101,9 @@ trait UserNotifies
 | 
				
			|||||||
            case ($entity instanceof Credit):
 | 
					            case ($entity instanceof Credit):
 | 
				
			||||||
                return array_merge($required_permissions, ["all_notifications","all_user_notifications","credit_created_user","credit_sent_user","credit_viewed_user"]);
 | 
					                return array_merge($required_permissions, ["all_notifications","all_user_notifications","credit_created_user","credit_sent_user","credit_viewed_user"]);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case ($entity instanceof PurchaseOrder):
 | 
				
			||||||
 | 
					                return array_merge($required_permissions, ["all_notifications","all_user_notifications","purchase_order_created_user","purchase_order_sent_user","purchase_order_viewed_user"]);
 | 
				
			||||||
 | 
					                break;      
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                return [];
 | 
					                return [];
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
@ -123,6 +127,9 @@ trait UserNotifies
 | 
				
			|||||||
            case ($entity instanceof Credit):
 | 
					            case ($entity instanceof Credit):
 | 
				
			||||||
                return array_diff($required_permissions, ["all_user_notifications","credit_created_user","credit_sent_user","credit_viewed_user"]);
 | 
					                return array_diff($required_permissions, ["all_user_notifications","credit_created_user","credit_sent_user","credit_viewed_user"]);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					            case ($entity instanceof PurchaseOrder):
 | 
				
			||||||
 | 
					                return array_diff($required_permissions, ["all_user_notifications","purchase_order_created_user","purchase_order_sent_user","purchase_order_viewed_user"]);
 | 
				
			||||||
 | 
					                break;                
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                // code...
 | 
					                // code...
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
				
			|||||||
@ -134,20 +134,15 @@ class VendorHtmlEngine
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $data['$entity.datetime'] = ['value' => $this->formatDatetime($this->entity->created_at, $this->company->date_format(), $this->company->locale()), 'label' => ctrans('texts.date')];
 | 
					        $data['$entity.datetime'] = ['value' => $this->formatDatetime($this->entity->created_at, $this->company->date_format(), $this->company->locale()), 'label' => ctrans('texts.date')];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data['$payment_button'] = ['value' => '<a class="button" href="'.$this->invitation->getPaymentLink().'">'.ctrans('texts.pay_now').'</a>', 'label' => ctrans('texts.pay_now')];
 | 
					 | 
				
			||||||
        $data['$payment_link'] = ['value' => $this->invitation->getPaymentLink(), 'label' => ctrans('texts.pay_now')];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $data['$entity'] = ['value' => '', 'label' => ctrans('texts.purchase_order')];
 | 
					        $data['$entity'] = ['value' => '', 'label' => ctrans('texts.purchase_order')];
 | 
				
			||||||
        $data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.purchase_order_number')];
 | 
					        $data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.purchase_order_number')];
 | 
				
			||||||
        $data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.purchase_order_number_short')];
 | 
					        $data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.purchase_order_number_short')];
 | 
				
			||||||
        $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->company) ?: '', 'label' => ctrans('texts.invoice_terms')];
 | 
					        $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->company) ?: '', 'label' => ctrans('texts.invoice_terms')];
 | 
				
			||||||
        $data['$terms'] = &$data['$entity.terms'];
 | 
					        $data['$terms'] = &$data['$entity.terms'];
 | 
				
			||||||
        $data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_invoice').'</a>', 'label' => ctrans('texts.view_invoice')];
 | 
					        $data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_purchase_order').'</a>', 'label' => ctrans('texts.view_purchase_order')];
 | 
				
			||||||
        $data['$viewLink'] = &$data['$view_link'];
 | 
					        $data['$viewLink'] = &$data['$view_link'];
 | 
				
			||||||
        $data['$viewButton'] = &$data['$view_link'];
 | 
					        $data['$viewButton'] = &$data['$view_link'];
 | 
				
			||||||
        $data['$view_button'] = &$data['$view_link'];
 | 
					        $data['$view_button'] = &$data['$view_link'];
 | 
				
			||||||
        $data['$paymentButton'] = &$data['$payment_button'];
 | 
					 | 
				
			||||||
        $data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
 | 
					        $data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
 | 
				
			||||||
        $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->company->date_format(), $this->company->locale()) ?: ' ', 'label' => ctrans('texts.date')];
 | 
					        $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->company->date_format(), $this->company->locale()) ?: ' ', 'label' => ctrans('texts.date')];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -390,11 +385,6 @@ class VendorHtmlEngine
 | 
				
			|||||||
        $data['$autoBill'] = ['value' => ctrans('texts.auto_bill_notification_placeholder'), 'label' => ''];
 | 
					        $data['$autoBill'] = ['value' => ctrans('texts.auto_bill_notification_placeholder'), 'label' => ''];
 | 
				
			||||||
        $data['$auto_bill'] = &$data['$autoBill'];
 | 
					        $data['$auto_bill'] = &$data['$autoBill'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /*Payment Aliases*/
 | 
					 | 
				
			||||||
        $data['$paymentLink'] = &$data['$payment_link'];
 | 
					 | 
				
			||||||
        $data['$payment_url'] = &$data['$payment_link'];
 | 
					 | 
				
			||||||
        $data['$portalButton'] = &$data['$paymentLink'];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $data['$dir'] = ['value' => optional($this->company->language())->locale === 'ar' ? 'rtl' : 'ltr', 'label' => ''];
 | 
					        $data['$dir'] = ['value' => optional($this->company->language())->locale === 'ar' ? 'rtl' : 'ltr', 'label' => ''];
 | 
				
			||||||
        $data['$dir_text_align'] = ['value' => optional($this->company->language())->locale === 'ar' ? 'right' : 'left', 'label' => ''];
 | 
					        $data['$dir_text_align'] = ['value' => optional($this->company->language())->locale === 'ar' ? 'right' : 'left', 'label' => ''];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,12 @@
 | 
				
			|||||||
        "Credit card billing",
 | 
					        "Credit card billing",
 | 
				
			||||||
        "projects",
 | 
					        "projects",
 | 
				
			||||||
        "tasks",
 | 
					        "tasks",
 | 
				
			||||||
        "freelancer"
 | 
					        "freelancer",
 | 
				
			||||||
 | 
					        "quotes",
 | 
				
			||||||
 | 
					        "purchase orders",
 | 
				
			||||||
 | 
					        "stripe billing",
 | 
				
			||||||
 | 
					        "invoices",
 | 
				
			||||||
 | 
					        "subscriptions"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "license": "Elastic License",
 | 
					    "license": "Elastic License",
 | 
				
			||||||
    "authors": [
 | 
					    "authors": [
 | 
				
			||||||
@ -37,7 +42,7 @@
 | 
				
			|||||||
        "bacon/bacon-qr-code": "^2.0",
 | 
					        "bacon/bacon-qr-code": "^2.0",
 | 
				
			||||||
        "beganovich/snappdf": "^1.7",
 | 
					        "beganovich/snappdf": "^1.7",
 | 
				
			||||||
        "braintree/braintree_php": "^6.0",
 | 
					        "braintree/braintree_php": "^6.0",
 | 
				
			||||||
        "checkout/checkout-sdk-php": "^1.0",
 | 
					        "checkout/checkout-sdk-php": "^2.5",
 | 
				
			||||||
        "cleverit/ubl_invoice": "^1.3",
 | 
					        "cleverit/ubl_invoice": "^1.3",
 | 
				
			||||||
        "coconutcraig/laravel-postmark": "^2.10",
 | 
					        "coconutcraig/laravel-postmark": "^2.10",
 | 
				
			||||||
        "doctrine/dbal": "^3.0",
 | 
					        "doctrine/dbal": "^3.0",
 | 
				
			||||||
@ -77,6 +82,8 @@
 | 
				
			|||||||
        "sentry/sentry-laravel": "^2",
 | 
					        "sentry/sentry-laravel": "^2",
 | 
				
			||||||
        "setasign/fpdf": "^1.8",
 | 
					        "setasign/fpdf": "^1.8",
 | 
				
			||||||
        "setasign/fpdi": "^2.3",
 | 
					        "setasign/fpdi": "^2.3",
 | 
				
			||||||
 | 
					        "socialiteproviders/apple": "^5.2",
 | 
				
			||||||
 | 
					        "socialiteproviders/microsoft": "^4.1",
 | 
				
			||||||
        "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",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1012
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1012
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -56,6 +56,10 @@ return [
 | 
				
			|||||||
            'driver' => 'session',
 | 
					            'driver' => 'session',
 | 
				
			||||||
            'provider' => 'contacts',
 | 
					            'provider' => 'contacts',
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					        'vendor' => [
 | 
				
			||||||
 | 
					            'driver' => 'session',
 | 
				
			||||||
 | 
					            'provider' => 'vendors',
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
@ -85,6 +89,11 @@ return [
 | 
				
			|||||||
            'driver' => 'eloquent',
 | 
					            'driver' => 'eloquent',
 | 
				
			||||||
            'model' => App\Models\ClientContact::class,
 | 
					            'model' => App\Models\ClientContact::class,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					        'vendors' => [
 | 
				
			||||||
 | 
					            'driver' => 'eloquent',
 | 
				
			||||||
 | 
					            'model' => App\Models\VendorContact::class,
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 'users' => [
 | 
					        // 'users' => [
 | 
				
			||||||
        //     'driver' => 'database',
 | 
					        //     'driver' => 'database',
 | 
				
			||||||
@ -120,6 +129,11 @@ return [
 | 
				
			|||||||
            'table' => 'password_resets',
 | 
					            'table' => 'password_resets',
 | 
				
			||||||
            'expire' => 60,
 | 
					            'expire' => 60,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
 | 
					        'vendors' => [
 | 
				
			||||||
 | 
					            'provider' => 'vendors',
 | 
				
			||||||
 | 
					            'table' => 'password_resets',
 | 
				
			||||||
 | 
					            'expire' => 60,
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
 | 
				
			|||||||
@ -14,8 +14,8 @@ return [
 | 
				
			|||||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
					    'require_https' => env('REQUIRE_HTTPS', true),
 | 
				
			||||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
					    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
				
			||||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
					    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
				
			||||||
    'app_version' => '5.3.98',
 | 
					    'app_version' => '5.4.0',
 | 
				
			||||||
    'app_tag' => '5.3.98',
 | 
					    'app_tag' => '5.4.0',
 | 
				
			||||||
    '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', ''),
 | 
				
			||||||
 | 
				
			|||||||
@ -80,4 +80,14 @@ return [
 | 
				
			|||||||
    'postmark' => [
 | 
					    'postmark' => [
 | 
				
			||||||
        'token' => env('POSTMARK_SECRET'),
 | 
					        'token' => env('POSTMARK_SECRET'),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					    'microsoft' => [
 | 
				
			||||||
 | 
					        'client_id' => env('MICROSOFT_CLIENT_ID'),
 | 
				
			||||||
 | 
					        'client_secret' => env('MICROSOFT_CLIENT_SECRET'),
 | 
				
			||||||
 | 
					        'redirect' => env('MICROSOFT_REDIRECT_URI')
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    'apple' => [
 | 
				
			||||||
 | 
					        'client_id' => env('APPLE_CLIENT_ID'),
 | 
				
			||||||
 | 
					        'client_secret' => env('APPLE_CLIENT_SECRET'),
 | 
				
			||||||
 | 
					        'redirect' => env('APPLE_REDIRECT_URI')
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					<?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
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SetAccountFlagForReact 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 AddReactSwitchingFlag extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('accounts', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->boolean('set_react_as_default_ap')->default(0);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return void
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/app.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							@ -5,9 +5,9 @@ const CACHE_NAME = 'flutter-app-cache';
 | 
				
			|||||||
const RESOURCES = {
 | 
					const RESOURCES = {
 | 
				
			||||||
  "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
 | 
					  "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
 | 
				
			||||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
					"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
				
			||||||
"/": "f4932ba24bfa72c97f7578b8951891c2",
 | 
					"/": "2e739a78eec983322924f724ebfa09ba",
 | 
				
			||||||
"main.dart.js": "68a04477f6ce39dcf894f583120e1c46",
 | 
					"main.dart.js": "fa4a0263712be1ce1df7d59ca0ede10e",
 | 
				
			||||||
"version.json": "3afb81924daf4f751571755436069115",
 | 
					"version.json": "d72bd323e3b8e22ce5acdc247f4e6f62",
 | 
				
			||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
 | 
					"favicon.png": "dca91c54388f52eded692718d5a98b8b",
 | 
				
			||||||
"flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67",
 | 
					"flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67",
 | 
				
			||||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
 | 
					"favicon.ico": "51636d3a390451561744c42188ccd628",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								public/js/clients/purchase_orders/accept.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								public/js/clients/purchase_orders/accept.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					/*! For license information please see accept.js.LICENSE.txt */
 | 
				
			||||||
 | 
					(()=>{function e(e,t){for(var n=0;n<t.length;n++){var a=t[n];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}var t=function(){function t(e,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),this.shouldDisplaySignature=e,this.shouldDisplayTerms=n,this.termsAccepted=!1}var n,a,r;return n=t,(a=[{key:"submitForm",value:function(){document.getElementById("approve-form").submit()}},{key:"displaySignature",value:function(){document.getElementById("displaySignatureModal").removeAttribute("style");var e=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"});this.signaturePad=e}},{key:"displayTerms",value:function(){document.getElementById("displayTermsModal").removeAttribute("style")}},{key:"handle",value:function(){var e=this;document.getElementById("approve-button").addEventListener("click",(function(){e.shouldDisplaySignature&&e.shouldDisplayTerms&&(e.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",(function(){e.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",(function(){document.querySelector('input[name="signature"').value=e.signaturePad.toDataURL(),e.termsAccepted=!0,e.submitForm()}))}))),e.shouldDisplaySignature&&!e.shouldDisplayTerms&&(e.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",(function(){document.querySelector('input[name="signature"').value=e.signaturePad.toDataURL(),e.submitForm()}))),!e.shouldDisplaySignature&&e.shouldDisplayTerms&&(e.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",(function(){e.termsAccepted=!0,e.submitForm()}))),e.shouldDisplaySignature||e.shouldDisplayTerms||e.submitForm()}))}}])&&e(n.prototype,a),r&&e(n,r),Object.defineProperty(n,"prototype",{writable:!1}),t}(),n=document.querySelector('meta[name="require-purchase_order-signature"]').content,a=document.querySelector('meta[name="show-purchase_order-terms"]').content;new t(Boolean(+n),Boolean(+a)).handle()})();
 | 
				
			||||||
							
								
								
									
										9
									
								
								public/js/clients/purchase_orders/accept.js.LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								public/js/clients/purchase_orders/accept.js.LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
							
								
								
									
										2
									
								
								public/js/clients/purchase_orders/action-selectors.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								public/js/clients/purchase_orders/action-selectors.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					/*! For license information please see action-selectors.js.LICENSE.txt */
 | 
				
			||||||
 | 
					(()=>{function e(e,n){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,n){if(!e)return;if("string"==typeof e)return t(e,n);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return t(e,n)}(e))||n&&e&&"number"==typeof e.length){r&&(e=r);var o=0,c=function(){};return{s:c,n:function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:c}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,l=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return a=e.done,e},e:function(e){l=!0,i=e},f:function(){try{a||null==r.return||r.return()}finally{if(l)throw i}}}}function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function t(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),this.parentElement=document.querySelector(".form-check-parent"),this.parentForm=document.getElementById("bulkActions")}var r,o,c;return r=t,o=[{key:"watchCheckboxes",value:function(e){var t=this;document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})),document.querySelectorAll(".form-check-child").forEach((function(n){e.checked?(n.checked=e.checked,t.processChildItem(n,document.getElementById("bulkActions"))):(n.checked=!1,document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})))}))}},{key:"processChildItem",value:function(t,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(r.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})),!1!==t.checked){var o=document.createElement("INPUT");o.setAttribute("name","purchase_orders[]"),o.setAttribute("value",t.dataset.value),o.setAttribute("class","child-hidden-input"),o.hidden=!0,n.append(o)}else{var c,i=document.querySelectorAll("input.child-hidden-input"),a=e(i);try{for(a.s();!(c=a.n()).done;){var l=c.value;l.value==t.dataset.value&&l.remove()}}catch(e){a.e(e)}finally{a.f()}}}},{key:"handle",value:function(){var t=this;this.parentElement.addEventListener("click",(function(){t.watchCheckboxes(t.parentElement)}));var n,r=e(document.querySelectorAll(".form-check-child"));try{var o=function(){var e=n.value;e.addEventListener("click",(function(){t.processChildItem(e,t.parentForm)}))};for(r.s();!(n=r.n()).done;)o()}catch(e){r.e(e)}finally{r.f()}}}],o&&n(r.prototype,o),c&&n(r,c),Object.defineProperty(r,"prototype",{writable:!1}),t}())).handle()})();
 | 
				
			||||||
@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
							
								
								
									
										297796
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										297796
									
								
								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
											
										
									
								
							
							
								
								
									
										295618
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										295618
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user