diff --git a/VERSION.txt b/VERSION.txt index 8444e431a72b..cf3ecc59ddfb 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.62 \ No newline at end of file +5.5.63 \ No newline at end of file diff --git a/app/Filters/BankIntegrationFilters.php b/app/Filters/BankIntegrationFilters.php index 9774af605711..2f5730387b12 100644 --- a/app/Filters/BankIntegrationFilters.php +++ b/app/Filters/BankIntegrationFilters.php @@ -39,14 +39,14 @@ class BankIntegrationFilters extends QueryFilters * @return Builder * @deprecated */ - public function filter(string $filter = '') : Builder + public function filter(string $filter = ''): Builder { if (strlen($filter) == 0) { return $this->builder; } return $this->builder->where(function ($query) use ($filter) { - $query->where('bank_integrations.bank_account_name', 'like', '%'.$filter.'%'); + $query->where('bank_account_name', 'like', '%'.$filter.'%'); }); } @@ -58,7 +58,7 @@ class BankIntegrationFilters extends QueryFilters * @param string filter * @return Builder */ - public function status(string $filter = '') : Builder + public function status(string $filter = ''): Builder { if (strlen($filter) == 0) { return $this->builder; @@ -90,7 +90,7 @@ class BankIntegrationFilters extends QueryFilters * @param string sort formatted as column|asc * @return Builder */ - public function sort(string $sort) : Builder + public function sort(string $sort): Builder { $sort_col = explode('|', $sort); @@ -102,9 +102,8 @@ class BankIntegrationFilters extends QueryFilters * * @return Illuminate\Database\Query\Builder */ - public function entityFilter() + public function entityFilter(): Builder { - //return $this->builder->whereCompanyId(auth()->user()->company()->id); return $this->builder->company(); } } diff --git a/app/Helpers/Mail/GmailTransport.php b/app/Helpers/Mail/GmailTransport.php index 479fbdbdeb59..a8bf46e0ba83 100644 --- a/app/Helpers/Mail/GmailTransport.php +++ b/app/Helpers/Mail/GmailTransport.php @@ -68,12 +68,13 @@ class GmailTransport extends AbstractTransport try{ $service->users_messages->send('me', $body, []); } - catch(Google\Service\Exception $e) { + catch(\Google\Service\Exception $e) { /* Need to slow down */ if($e->getCode() == '429') { - sleep(5); + sleep(rand(5,10)); + nlog("429 google - retrying "); $service->users_messages->send('me', $body, []); } diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 73903e1a8373..75126c91a6b2 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -15,8 +15,6 @@ use App\Models\Account; use App\Models\BankIntegration; use App\Models\BankTransaction; use App\Models\BankTransactionRule; -use App\Models\ClientGatewayToken; -use App\Models\Company; use App\Models\CompanyGateway; use App\Models\Design; use App\Models\ExpenseCategory; @@ -31,10 +29,8 @@ use App\Transformers\EntityTransformer; use App\Utils\Ninja; use App\Utils\Statics; use App\Utils\Traits\AppSetup; -use App\Utils\TruthSource; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Http\Request; use Illuminate\Support\Str; use League\Fractal\Manager; use League\Fractal\Pagination\IlluminatePaginatorAdapter; @@ -56,20 +52,20 @@ class BaseController extends Controller * * @var array */ - public $forced_includes; + public $forced_includes = []; /** * Passed from the parent when we need to force * the key of the response object. * @var string */ - public $forced_index; + public $forced_index = 'data'; /** * Fractal manager. * @var object */ - protected $manager; + protected Manager $manager; private $first_load = [ 'account', @@ -146,10 +142,6 @@ class BaseController extends Controller public function __construct() { $this->manager = new Manager(); - - $this->forced_includes = []; - - $this->forced_index = 'data'; } private function buildManager() @@ -165,6 +157,8 @@ class BaseController extends Controller $include = implode(',', $this->forced_includes); } + // $include = $this->filterIncludes($include); + $this->manager->parseIncludes($include); $this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY; @@ -187,6 +181,36 @@ class BaseController extends Controller ->header('X-APP-VERSION', config('ninja.app_version')); } + /** + * Filters the includes to ensure the + * end user has the correct permissions to + * view the includes + * + * @param array $includes The includes for the object + * @return string The filtered array of includes + */ + private function filterIncludes(string $includes): string + { + $permissions_array = [ + 'payments' => 'view_payment', + 'client' => 'view_client', + 'clients' => 'view_client', + 'vendor' => 'view_vendor', + 'vendors' => 'view_vendors', + 'expense' => 'view_expense', + 'expenses' => 'view_expense', + ]; + + $collection = collect(explode(",", $includes)); + + $filtered_includes = $collection->filter(function ($include) use ($permissions_array){ + return auth()->user()->hasPermission($permissions_array[$include]); + }); + + return $filtered_includes->implode(","); + + } + /** * 404 for the client portal. * @return Response 404 response @@ -219,6 +243,12 @@ class BaseController extends Controller return response()->make($error, $httpErrorCode, $headers); } + /** + * Refresh API response with latest cahnges + * @param Builer $query + * @property App\Models\User auth()->user() + * @return Builer + */ protected function refreshResponse($query) { $user = auth()->user(); @@ -245,7 +275,7 @@ class BaseController extends Controller $query->with( [ - 'company' => function ($query) use ($updated_at, $user) { + 'company' => function ($query) { $query->whereNotNull('updated_at')->with('documents', 'users'); }, 'company.clients' => function ($query) use ($updated_at, $user) { @@ -283,7 +313,7 @@ class BaseController extends Controller $query->where('designs.user_id', $user->id); } }, - 'company.documents'=> function ($query) use ($updated_at, $user) { + 'company.documents'=> function ($query) { $query->where('updated_at', '>=', $updated_at); }, 'company.expenses'=> function ($query) use ($updated_at, $user) { @@ -296,7 +326,7 @@ class BaseController extends Controller }); } }, - 'company.groups' => function ($query) use ($updated_at, $user) { + 'company.groups' => function ($query) { $query->whereNotNull('updated_at')->with('documents'); }, @@ -323,7 +353,7 @@ class BaseController extends Controller } }, - 'company.payment_terms'=> function ($query) use ($updated_at, $user) { + 'company.payment_terms'=> function ($query) use ($user) { $query->whereNotNull('updated_at'); if (! $user->isAdmin()) { @@ -408,7 +438,7 @@ class BaseController extends Controller } }, - 'company.tax_rates'=> function ($query) use ($updated_at, $user) { + 'company.tax_rates'=> function ($query) { $query->whereNotNull('updated_at'); }, 'company.vendors'=> function ($query) use ($updated_at, $user) { @@ -422,10 +452,10 @@ class BaseController extends Controller } }, - 'company.expense_categories'=> function ($query) use ($updated_at, $user) { + 'company.expense_categories'=> function ($query) { $query->whereNotNull('updated_at'); }, - 'company.task_statuses'=> function ($query) use ($updated_at, $user) { + 'company.task_statuses'=> function ($query) { $query->whereNotNull('updated_at'); }, 'company.activities'=> function ($query) use ($user) { @@ -433,19 +463,26 @@ class BaseController extends Controller $query->where('activities.user_id', $user->id); } }, - 'company.subscriptions'=> function ($query) use ($updated_at, $user) { + 'company.subscriptions'=> function ($query) use ($user) { $query->whereNotNull('updated_at'); if (! $user->isAdmin()) { $query->where('subscriptions.user_id', $user->id); } }, - 'company.bank_integrations'=> function ($query) use ($updated_at, $user) { + 'company.bank_integrations'=> function ($query) use ($user) { $query->whereNotNull('updated_at'); - if (! $user->isAdmin()) { + //scopes down permissions for users with no permissions + if (! $user->hasPermission('view_bank_transaction')) { $query->where('bank_integrations.user_id', $user->id); } + + //allows us to return integrations for users who can create bank transactions + if(!$user->isSuperUser() && $user->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) { + $query->exclude(["balance"]); + } + }, 'company.bank_transactions'=> function ($query) use ($updated_at, $user) { $query->where('updated_at', '>=', $updated_at); @@ -513,22 +550,22 @@ class BaseController extends Controller $query->with( [ - 'company' => function ($query) use ($created_at, $user) { + 'company' => function ($query) { $query->whereNotNull('created_at')->with('documents', 'users'); }, - 'company.designs'=> function ($query) use ($created_at, $user) { + 'company.designs'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at)->with('company'); }, - 'company.documents'=> function ($query) use ($created_at, $user) { + 'company.documents'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at); }, - 'company.groups'=> function ($query) use ($created_at, $user) { + 'company.groups'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at)->with('documents'); }, - 'company.payment_terms'=> function ($query) use ($created_at, $user) { + 'company.payment_terms'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at); }, - 'company.tax_rates'=> function ($query) use ($created_at, $user) { + 'company.tax_rates'=> function ($query) { $query->whereNotNull('created_at'); }, 'company.activities'=> function ($query) use ($user) { @@ -536,11 +573,16 @@ class BaseController extends Controller $query->where('activities.user_id', $user->id); } }, - 'company.bank_integrations'=> function ($query) use ($created_at, $user) { + 'company.bank_integrations'=> function ($query) use ($user) { - if (! $user->isAdmin()) { + if (! $user->hasPermission('view_bank_transaction')) { $query->where('bank_integrations.user_id', $user->id); } + + if(!$user->isSuperUser() && $user->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) { + $query->exclude(["balance"]); + } + }, 'company.bank_transaction_rules'=> function ($query) use ($user) { @@ -598,7 +640,7 @@ class BaseController extends Controller $query->with( [ - 'company' => function ($query) use ($created_at, $user) { + 'company' => function ($query) { $query->whereNotNull('created_at')->with('documents', 'users'); }, 'company.clients' => function ($query) use ($created_at, $user) { @@ -629,7 +671,7 @@ class BaseController extends Controller }); } }, - 'company.documents'=> function ($query) use ($created_at, $user) { + 'company.documents'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at); }, 'company.expenses'=> function ($query) use ($created_at, $user) { @@ -642,7 +684,7 @@ class BaseController extends Controller }); } }, - 'company.groups' => function ($query) use ($created_at, $user) { + 'company.groups' => function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at)->with('documents'); }, 'company.invoices'=> function ($query) use ($created_at, $user) { @@ -667,7 +709,7 @@ class BaseController extends Controller } }, - 'company.payment_terms'=> function ($query) use ($created_at, $user) { + 'company.payment_terms'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at); }, 'company.products' => function ($query) use ($created_at, $user) { @@ -734,7 +776,7 @@ class BaseController extends Controller } }, - 'company.tax_rates' => function ($query) use ($created_at, $user) { + 'company.tax_rates' => function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at); }, 'company.vendors'=> function ($query) use ($created_at, $user) { @@ -748,10 +790,10 @@ class BaseController extends Controller } }, - 'company.expense_categories'=> function ($query) use ($created_at, $user) { + 'company.expense_categories'=> function ($query) { $query->whereNotNull('created_at'); }, - 'company.task_statuses'=> function ($query) use ($created_at, $user) { + 'company.task_statuses'=> function ($query) use ($created_at) { $query->where('created_at', '>=', $created_at); }, 'company.activities'=> function ($query) use ($user) { @@ -789,9 +831,14 @@ class BaseController extends Controller 'company.bank_integrations'=> function ($query) use ($created_at, $user) { $query->where('created_at', '>=', $created_at); - if (! $user->isAdmin()) { + if (! $user->hasPermission('view_bank_transaction')) { $query->where('bank_integrations.user_id', $user->id); } + + if(!$user->isSuperUser() && $user->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) { + $query->exclude(["balance"]); + } + }, 'company.bank_transactions'=> function ($query) use ($created_at, $user) { $query->where('created_at', '>=', $created_at); @@ -836,38 +883,17 @@ class BaseController extends Controller $query->with($includes); - - - /*Restore here if refactor produces unexpected edge cases*/ -/* - if (auth()->user() && ! auth()->user()->hasPermission('view'.lcfirst(class_basename(Str::snake($this->entity_type))))) { - //06-10-2022 - some entities do not have assigned_user_id - this becomes an issue when we have a large company and low permission users - if(lcfirst(class_basename(Str::snake($this->entity_type))) == 'user') - $query->where('id', auth()->user()->id); - elseif($this->entity_type == BankTransaction::class){ //table without assigned_user_id - $query->where('user_id', '=', auth()->user()->id); - } - elseif(in_array(lcfirst(class_basename(Str::snake($this->entity_type))),['design','group_setting','payment_term'])){ - //need to pass these back regardless - nlog($this->entity_type); - } - else - $query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id); - - } -*/ - - /*21-01-2023*/ -/**/ - // 10-01-2022 need to ensure we snake case properly here to ensure permissions work as expected - // 28-03-2022 this is definitely correct here, do not append _ to the view, it resolved correctly when snake cased if (auth()->user() && ! auth()->user()->hasPermission('view_'.Str::snake(class_basename($this->entity_type)))) { //06-10-2022 - some entities do not have assigned_user_id - this becomes an issue when we have a large company and low permission users if(in_array($this->entity_type, [User::class])){ $query->where('id', auth()->user()->id); } elseif(in_array($this->entity_type, [BankTransactionRule::class,CompanyGateway::class, TaxRate::class, BankIntegration::class, Scheduler::class, BankTransaction::class, Webhook::class, ExpenseCategory::class])){ //table without assigned_user_id - $query->where('user_id', '=', auth()->user()->id); + + if($this->entity_type == BankIntegration::class && !auth()->user()->isSuperUser() && auth()->user()->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) + $query->exclude(["balance"]); //allows us to selective display bank integrations back to the user if they can view / create bank transactions but without the bank balance being present in the response + else + $query->where('user_id', '=', auth()->user()->id); } elseif(in_array($this->entity_type,[Design::class, GroupSetting::class, PaymentTerm::class])){ // nlog($this->entity_type); @@ -876,8 +902,6 @@ class BaseController extends Controller $query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id); } -/**/ - // $query->exclude(['balance','credit_balance','paid_to_date']); if (request()->has('updated_at') && request()->input('updated_at') > 0) { @@ -951,7 +975,7 @@ class BaseController extends Controller return $this->response($this->manager->createData($resource)->toArray()); } - public static function getApiHeaders($count = 0) + public static function getApiHeaders() { return [ 'Content-Type' => 'application/json', diff --git a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php index 94fbd0ed7685..0083347a5504 100644 --- a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php @@ -112,8 +112,8 @@ class UpdateRecurringInvoiceRequest extends Request $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; } - if (array_key_exists('auto_bill', $input) && isset($input['auto_bill']) && $this->setAutoBillFlag($input['auto_bill'])) { - $input['auto_bill_enabled'] = true; + if (array_key_exists('auto_bill', $input) && isset($input['auto_bill'])) { + $input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']); } if (array_key_exists('documents', $input)) { @@ -132,9 +132,9 @@ class UpdateRecurringInvoiceRequest extends Request * * @return bool */ - private function setAutoBillFlag($auto_bill) :bool + private function setAutoBillFlag($auto_bill) { - if ($auto_bill == 'always') { + if ($auto_bill == 'always' || $auto_bill == 'optout') { return true; } diff --git a/app/Http/Requests/Token/UpdateTokenRequest.php b/app/Http/Requests/Token/UpdateTokenRequest.php index 087bd1fb601f..a10a5a592af1 100644 --- a/app/Http/Requests/Token/UpdateTokenRequest.php +++ b/app/Http/Requests/Token/UpdateTokenRequest.php @@ -27,4 +27,12 @@ class UpdateTokenRequest extends Request { return auth()->user()->isAdmin(); } + + public function rules() + { + return [ + 'name' => 'required', + ]; + } + } diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php index 5302a41d6397..d3915f2e40e7 100644 --- a/app/Jobs/Mail/NinjaMailerJob.php +++ b/app/Jobs/Mail/NinjaMailerJob.php @@ -463,7 +463,7 @@ class NinjaMailerJob implements ShouldQueue $google->getClient()->setAccessToken(json_encode($user->oauth_user_token)); - sleep(rand(2,4)); + sleep(rand(1,6)); } catch(\Exception $e) { $this->logMailError('Gmail Token Invalid', $this->company->clients()->first()); diff --git a/app/Models/BankIntegration.php b/app/Models/BankIntegration.php index 042d134ce19b..23d13fe1d841 100644 --- a/app/Models/BankIntegration.php +++ b/app/Models/BankIntegration.php @@ -12,13 +12,15 @@ namespace App\Models; use App\Models\Filterable; +use App\Models\Traits\Excludable; use Illuminate\Database\Eloquent\SoftDeletes; class BankIntegration extends BaseModel { use SoftDeletes; use Filterable; - + use Excludable; + protected $fillable = [ 'bank_account_name', 'provider_name', diff --git a/app/Models/User.php b/app/Models/User.php index 7ece8fd65535..542eedfc5ffc 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -317,6 +317,16 @@ class User extends Authenticatable implements MustVerifyEmail // return $this->company_user->is_owner; } + /** + * Returns true is user is an admin _or_ owner + * + * @return boolean + */ + public function isSuperUser() :bool + { + return $this->token()->cu->is_owner || $this->token()->cu->is_admin; + } + /** * Returns all user created contacts. * @@ -386,21 +396,75 @@ class User extends Authenticatable implements MustVerifyEmail * @param string $permission '["view_all"]' * @return boolean */ - public function hasExactPermission(string $permission = ''): bool + public function hasExactPermission(string $permission = '___'): bool { $parts = explode('_', $permission); - $all_permission = ''; + $all_permission = '__'; if (count($parts) > 1) { $all_permission = $parts[0].'_all'; } - return (is_int(stripos($this->token()->cu->permissions, $all_permission))) || - (is_int(stripos($this->token()->cu->permissions, $permission))); + return (stripos($this->token()->cu->permissions, $all_permission) !== false) || + (stripos($this->token()->cu->permissions, $permission) !== false); } + /** + * Used when we need to match a range of permissions + * the user + * + * This method is used when we need to scope down the query + * and display a limited subset. + * + * @param array $permissions + * @return boolean + */ + public function hasIntersectPermissions(array $permissions = []): bool + { + + foreach($permissions as $permission) + { + + if($this->hasExactPermission($permission)) + return true; + + } + + return false; + + } + + /** + * Used when we need to match a range of permissions + * the user + * + * This method is used when we need to scope down the query + * and display a limited subset. + * + * @param array $permissions + * @return boolean + */ + public function hasIntersectPermissionsOrAdmin(array $permissions = []): bool + { + + if($this->isSuperUser()) + return true; + + foreach($permissions as $permission) + { + + if($this->hasExactPermission($permission)) + return true; + + } + + return false; + + } + + public function documents() { return $this->morphMany(Document::class, 'documentable'); diff --git a/app/Policies/BankTransactionPolicy.php b/app/Policies/BankTransactionPolicy.php index 9819e0768bc6..b58390df846e 100644 --- a/app/Policies/BankTransactionPolicy.php +++ b/app/Policies/BankTransactionPolicy.php @@ -26,6 +26,6 @@ class BankTransactionPolicy extends EntityPolicy */ public function create(User $user) : bool { - return $user->isAdmin() || $user->hasPermission('create_invoice') || $user->hasPermission('create_all'); + return $user->isAdmin() || $user->hasPermission('create_bank_transaction') || $user->hasPermission('create_all'); } } diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index e202ab75693e..c8bbe14ab8f0 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -540,6 +540,7 @@ class HtmlEngine $data['$task.tax_name2'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$task.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$task.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')]; + $data['$task.tax_amount'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$task.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')]; $data['$task.service'] = ['value' => '', 'label' => ctrans('texts.service')]; $data['$task.task1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'task1')]; diff --git a/config/ninja.php b/config/ninja.php index e095532bd1d0..dca28a9ec4d0 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -14,8 +14,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.62', - 'app_tag' => '5.5.62', + 'app_version' => '5.5.63', + 'app_tag' => '5.5.63', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php b/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php index e319effdcfd4..7a4ea8b41a3f 100644 --- a/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php @@ -10,8 +10,13 @@ $token_billing = false; $token_billing_string = 'false'; } - - if($gateway_instance->token_billing == 'optout' || $gateway_instance->token_billing == 'always'){ + + if($gateway_instance->token_billing == 'always'){ + $token_billing = false; + $token_billing_string = 'true'; + } + + if($gateway_instance->token_billing == 'optout'){ $checked_on = 'checked'; $checked_off = ''; } diff --git a/tests/Unit/PermissionsTest.php b/tests/Unit/PermissionsTest.php index 484a65dc1d04..5226178ec037 100644 --- a/tests/Unit/PermissionsTest.php +++ b/tests/Unit/PermissionsTest.php @@ -79,6 +79,40 @@ class PermissionsTest extends TestCase } + public function testIntersectPermissions() + { + + $low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first(); + $low_cu->permissions = '["view_client"]'; + $low_cu->save(); + + $this->assertFalse($this->user->hasIntersectPermissions(["viewclient"])); + $this->assertTrue($this->user->hasIntersectPermissions(["view_client"])); + + + $low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first(); + $low_cu->permissions = '["view_all"]'; + $low_cu->save(); + + $this->assertFalse($this->user->hasIntersectPermissions(["viewclient"])); + $this->assertTrue($this->user->hasIntersectPermissions(["view_client"])); + + $this->assertFalse($this->user->hasIntersectPermissions(["viewbank_transaction"])); + $this->assertTrue($this->user->hasIntersectPermissions(["view_bank_transaction"])); + + $low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first(); + $low_cu->permissions = '["create_all"]'; + $low_cu->save(); + + $this->assertFalse($this->user->hasIntersectPermissions(["createclient"])); + $this->assertTrue($this->user->hasIntersectPermissions(["create_client"])); + + $this->assertFalse($this->user->hasIntersectPermissions(["createbank_transaction"])); + $this->assertTrue($this->user->hasIntersectPermissions(["create_bank_transaction"])); + $this->assertTrue($this->user->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])); + + } + public function testViewClientPermission() {