Working on filtering responses based on user permissions

This commit is contained in:
David Bomba 2021-01-29 23:05:03 +11:00
parent e213203ed9
commit af472b21c1
5 changed files with 178 additions and 198 deletions

View File

@ -4,12 +4,18 @@
![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop)
![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable)
[![codecov](https://codecov.io/gh/invoiceninja/invoiceninja/branch/v2/graph/badge.svg)](https://codecov.io/gh/invoiceninja/invoiceninja)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/d39acb4bf0f74a0698dc77f382769ba5)](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
# Invoice Ninja version 5 is in Beta!
# Invoice Ninja version 5.1 RC2!
We will be using the lessons learnt in Invoice Ninja 4.0 to build a bigger better platform to work from. If you would like to contribute to the project we will gladly accept contributions for code, user guides, bug tracking and feedback! Please consider the following guidelines prior to submitting a pull request:
Invoice Ninja version 5.1 has now reached Release Candidate 2!
What does this mean exactly? We consider this version _almost_ stable. There may be some remaining small issues which we would love to get feedback on. We would really appreciate the community booting up this version and attempting the migration from their Invoice Ninja V4 application and inspect the migrated data.
We'd also like feedback on any issues that you can see, and help us nail down the few remaining issues before Version 5 graduates to Stable Gold Release.
Please note we do not consider this version ready for production use, please stick with your V4 installation for your production clients!
## Quick Start
@ -17,13 +23,10 @@ Currently the client portal and API are of alpha quality, to get started:
```bash
git clone https://github.com/invoiceninja/invoiceninja.git
git checkout v2
git checkout v5-stable
cp .env.example .env
cp .env.dusk.example .env.dusk.local
php artisan key:generate
composer update
npm i
npm run production
```
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
@ -33,7 +36,7 @@ Run if you want to load sample data, remember to configure .env
php artisan migrate:fresh --seed && php artisan db:seed && php artisan ninja:create-test-data
```
To Run the web server
To run the web server
```
php artisan serve
```

View File

@ -235,8 +235,8 @@ class CompanySettings extends BaseSettings
public $font_size = 7; //@implemented
public $primary_font = 'Roboto';
public $secondary_font = 'Roboto';
public $primary_color = '#4caf50';
public $secondary_color = '#2196f3';
public $primary_color = '#142cb5';
public $secondary_color = '#7081e0';
public $hide_paid_to_date = false; //@TODO where?
public $embed_documents = false; //@TODO where?

View File

@ -184,7 +184,9 @@ class BaseController extends Controller
protected function refreshResponse($query)
{
if (auth()->user()->getCompany()->is_large)
$user = auth()->user();
if ($user->getCompany()->is_large)
$this->manager->parseIncludes($this->mini_load);
else
$this->manager->parseIncludes($this->first_load);
@ -200,74 +202,145 @@ class BaseController extends Controller
$transformer = new $this->entity_transformer($this->serializer);
$updated_at = request()->has('updated_at') ? request()->input('updated_at') : 0;
// if (auth()->user()->getCompany()->is_large && ! request()->has('updated_at')) {
// return response()->json(['message' => ctrans('texts.large_account_update_parameter'), 'errors' =>[]], 401);
// }
$updated_at = date('Y-m-d H:i:s', $updated_at);
$query->with(
[
'company' => function ($query) use ($updated_at) {
'company' => function ($query) use ($updated_at, $user) {
$query->whereNotNull('updated_at')->with('documents');
},
'company.clients' => function ($query) use ($updated_at) {
'company.clients' => function ($query) use ($updated_at, $user) {
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents');
if(!$user->hasPermission('view_client'))
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
},
'company.company_gateways' => function ($query) {
'company.company_gateways' => function ($query) use ($user) {
$query->whereNotNull('updated_at');
if(!$user->isAdmin())
$query->where('company_gateways.user_id', $user->id);
},
'company.credits'=> function ($query) use ($updated_at) {
'company.credits'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_credit'))
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
},
'company.designs'=> function ($query) use ($updated_at) {
'company.designs'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('company');
if(!$user->isAdmin())
$query->where('designs.user_id', $user->id);
},
'company.documents'=> function ($query) use ($updated_at) {
'company.documents'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
},
'company.expenses'=> function ($query) use ($updated_at) {
'company.expenses'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('documents');
if(!$user->hasPermission('view_expense'))
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
},
'company.groups' => function ($query) use ($updated_at) {
'company.groups' => function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if(!$user->isAdmin())
$query->where('group_settings.user_id', $user->id);
},
'company.invoices'=> function ($query) use ($updated_at) {
'company.invoices'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_invoice'))
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
},
'company.payments'=> function ($query) use ($updated_at) {
'company.payments'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('paymentables', 'documents');
if(!$user->hasPermission('view_payment'))
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
},
'company.payment_terms'=> function ($query) use ($updated_at) {
'company.payment_terms'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if(!$user->isAdmin())
$query->where('payment_terms.user_id', $user->id);
},
'company.products' => function ($query) use ($updated_at) {
'company.products' => function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('documents');
if(!$user->hasPermission('view_product'))
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
},
'company.projects'=> function ($query) use ($updated_at) {
'company.projects'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('documents');
if(!$user->hasPermission('view_project'))
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
},
'company.quotes'=> function ($query) use ($updated_at) {
'company.quotes'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_quote'))
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
},
'company.recurring_invoices'=> function ($query) use ($updated_at) {
'company.recurring_invoices'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
if(!$user->hasPermission('view_recurring_invoice'))
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
},
'company.tasks'=> function ($query) use ($updated_at) {
'company.tasks'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('documents');
if(!$user->hasPermission('view_task'))
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
},
'company.tax_rates' => function ($query) use ($updated_at) {
'company.tax_rates' => function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if(!$user->isAdmin())
$query->where('tax_rates.user_id', $user->id);
},
'company.vendors'=> function ($query) use ($updated_at) {
'company.vendors'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
if(!$user->hasPermission('view_vendor'))
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
},
'company.expense_categories'=> function ($query) use ($updated_at) {
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if(!$user->isAdmin())
$query->where('expense_categories.user_id', $user->id);
},
'company.task_statuses'=> function ($query) use ($updated_at) {
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if(!$user->isAdmin())
$query->where('task_statuses.user_id', $user->id);
},
'company.activities'=> function ($query) use($user) {
if(!$user->isAdmin())
$query->where('activities.user_id', $user->id);
}
]
);

View File

@ -202,15 +202,22 @@ class User extends Authenticatable implements MustVerifyEmail
$this->id = auth()->user()->id;
}
if (request()->header('X-API-TOKEN')) {
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
->where('company_tokens.token', request()->header('X-API-TOKEN'))
->withTrashed();
} else {
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
->where('company_user.user_id', $this->id)
->withTrashed();
}
return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'user_id', 'id', 'user_id')
->withTrashed();
// if (request()->header('X-API-TOKEN')) {
// nlog("with an API token");
// nlog(request()->header('X-API-TOKEN'));
// return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
// ->where('company_tokens.token', request()->header('X-API-TOKEN'))
// ->withTrashed();
// } else {
// return $this->hasOneThrough(CompanyUser::class, CompanyToken::class, 'user_id', 'company_id', 'id', 'company_id')
// ->where('company_user.user_id', $this->id)
// ->withTrashed();
// }
}
/**

View File

@ -33,74 +33,12 @@ trait GeneratesCounter
//todo in the form validation, we need to ensure that if a prefix and pattern is set we throw a validation error,
//only one type is allow else this will cause confusion to the end user
/**
* Gets the next invoice number.
*
* @param Client $client The client
*
* @param Invoice|null $invoice
* @return string The next invoice number.
*/
public function getNextInvoiceNumber(Client $client, ?Invoice $invoice) :string
{
//Reset counters if enabled
$this->resetCounters($client);
//todo handle if we have specific client patterns in the future
$pattern = $client->getSetting('invoice_number_pattern');
//Determine if we are using client_counters
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
if (property_exists($client->settings, 'invoice_number_counter')) {
$counter = $client->settings->invoice_number_counter;
} else {
$counter = 1;
}
$counter_entity = $client;
} elseif (strpos($pattern, 'groupCounter') || strpos($pattern, 'group_counter')) {
$counter = $client->group_settings->invoice_number_counter;
$counter_entity = $client->group_settings;
} else {
$counter = $client->company->settings->invoice_number_counter;
$counter_entity = $client->company;
}
//Return a valid counter
$pattern = $client->getSetting('invoice_number_pattern');
$padding = $client->getSetting('counter_padding');
$prefix = '';
if ($invoice && $invoice->recurring_id) {
$prefix = $client->getSetting('recurring_number_prefix');
}
$invoice_number = $this->checkEntityNumber(Invoice::class, $client, $counter, $padding, $pattern, $prefix);
$this->incrementCounter($counter_entity, 'invoice_number_counter');
return $invoice_number;
}
/**
* Gets the next credit number.
*
* @param Client $client The client
*
* @return string The next credit number.
*/
public function getNextCreditNumber(Client $client) :string
{
return $this->getNextEntityNumber(Credit::class, $client);
}
public function getNextQuoteNumber(Client $client)
{
return $this->getNextEntityNumber(Quote::class, $client);
}
private function getNextEntityNumber($entity, Client $client)
{
//Reset counters if enabled
$prefix = '';
$this->resetCounters($client);
$is_client_counter = false;
@ -108,9 +46,6 @@ trait GeneratesCounter
$counter_string = $this->getEntityCounter($entity, $client);
$pattern = $this->getNumberPattern($entity, $client);
nlog("counter string = {$counter_string}");
nlog("pattern = {$pattern}");
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
if (property_exists($client->settings, $counter_string)) {
@ -135,14 +70,15 @@ nlog("pattern = {$pattern}");
$counter_entity = $client->company;
}
nlog($counter_entity->toArray());
//If it is a quote - we need to
$pattern = $this->getNumberPattern($entity, $client);
$padding = $client->getSetting('counter_padding');
$entity_number = $this->checkEntityNumber($entity, $client, $counter, $padding, $pattern);
if($entity instanceof Invoice && $entity && $entity->recurring_id)
$prefix = $client->getSetting('recurring_number_prefix');
$entity_number = $this->checkEntityNumber($entity, $client, $counter, $padding, $pattern, $prefix);
$this->incrementCounter($counter_entity, $counter_string);
@ -210,97 +146,58 @@ nlog($counter_entity->toArray());
}
}
public function getNextRecurringInvoiceNumber(Client $client)
/**
* Gets the next invoice number.
*
* @param Client $client The client
*
* @param Invoice|null $invoice
* @return string The next invoice number.
*/
public function getNextInvoiceNumber(Client $client, ?Invoice $invoice) :string
{
//Reset counters if enabled
$this->resetCounters($client);
$is_client_counter = false;
//todo handle if we have specific client patterns in the future
$pattern = $client->company->settings->recurring_invoice_number_pattern;
if (strpos($pattern, 'clientCounter') || strpos($pattern, 'client_counter')) {
if (property_exists($client->settings, 'recurring_invoice_number_counter')) {
$counter = $client->settings->recurring_invoice_number_counter;
} else {
$counter = 1;
}
$counter_entity = $client;
} elseif (strpos($pattern, 'groupCounter') || strpos($pattern, 'group_counter')) {
if (property_exists($client->group_settings, 'recurring_invoice_number_counter')) {
$counter = $client->group_settings->recurring_invoice_number_counter;
} else {
$counter = 1;
}
$counter_entity = $client->group_settings;
} else {
$counter = $client->company->settings->recurring_invoice_number_counter;
$counter_entity = $client->company;
}
$padding = $client->getSetting('counter_padding');
$invoice_number = $this->checkEntityNumber(RecurringInvoice::class, $client, $counter, $padding, $pattern);
//increment the correct invoice_number Counter (company vs client)
if ($is_client_counter) {
$this->incrementCounter($client, 'recurring_invoice_number_counter');
} else {
$this->incrementCounter($client->company, 'recurring_invoice_number_counter');
}
return $invoice_number;
return $this->getNextEntityNumber(Invoice::class, $client);
}
/**
* Payment Number Generator.
* @param Client $client
* @return string The payment number
* Gets the next credit number.
*
* @param Client $client The client
*
* @return string The next credit number.
*/
public function getNextCreditNumber(Client $client) :string
{
return $this->getNextEntityNumber(Credit::class, $client);
}
/**
* Gets the next quote number.
*
* @param Client $client The client
*
* @return string The next credit number.
*/
public function getNextQuoteNumber(Client $client)
{
return $this->getNextEntityNumber(Quote::class, $client);
}
public function getNextRecurringInvoiceNumber(Client $client)
{
return $this->getNextEntityNumber(RecurringInvoice::class, $client);
}
/**
* Gets the next Payment number.
*
* @param Client $client The client
*
* @return string The next payment number.
*/
public function getNextPaymentNumber(Client $client) :string
{
//Reset counters if enabled
$this->resetCounters($client);
$is_client_counter = false;
//todo handle if we have specific client patterns in the future
$pattern = $client->company->settings->payment_number_pattern;
//Determine if we are using client_counters
if (strpos($pattern, 'client_counter') === false) {
$counter = $client->company->settings->payment_number_counter;
} else {
if (property_exists($client->settings, 'payment_number_counter')) {
$counter = $client->settings->payment_number_counter;
} else {
$counter = 1;
}
$is_client_counter = true;
}
//Return a valid counter
$pattern = '';
$padding = $client->getSetting('counter_padding');
$payment_number = $this->checkEntityNumber(Payment::class, $client, $counter, $padding, $pattern);
//increment the correct invoice_number Counter (company vs client)
if ($is_client_counter) {
$this->incrementCounter($client, 'payment_number_counter');
} else {
$this->incrementCounter($client->company, 'payment_number_counter');
}
return (string) $payment_number;
return $this->getNextEntityNumber(Payment::class, $client);
}
/**