1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 08:21:34 +02:00

Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
David Bomba 2016-03-19 20:26:57 +11:00
commit b4e9dc3063
61 changed files with 1095 additions and 264 deletions

View File

@ -13,7 +13,7 @@ open-source software.
1. Redistributions of source code, in whole or part and with or without
modification requires the express permission of the author and must prominently
display "Powered by InvoiceNinja" or the Invoice Ninja logo in verifiable form
display "Powered by InvoiceNinja" and the Invoice Ninja logo in verifiable form
with hyperlink to said site.
2. Neither the name nor any trademark of the Author may be used to
endorse or promote products derived from this software without specific

View File

@ -137,8 +137,6 @@ class AccountController extends BaseController
if ($section == ACCOUNT_COMPANY_DETAILS) {
return self::showCompanyDetails();
} elseif ($section == ACCOUNT_USER_DETAILS) {
return self::showUserDetails();
} elseif ($section == ACCOUNT_LOCALIZATION) {
return self::showLocalization();
} elseif ($section == ACCOUNT_PAYMENTS) {
@ -232,7 +230,7 @@ class AccountController extends BaseController
return View::make('accounts.details', $data);
}
private function showUserDetails()
public function showUserDetails()
{
$oauthLoginUrls = [];
foreach (AuthService::$providers as $provider) {
@ -467,8 +465,6 @@ class AccountController extends BaseController
{
if ($section === ACCOUNT_COMPANY_DETAILS) {
return AccountController::saveDetails();
} elseif ($section === ACCOUNT_USER_DETAILS) {
return AccountController::saveUserDetails();
} elseif ($section === ACCOUNT_LOCALIZATION) {
return AccountController::saveLocalization();
} elseif ($section === ACCOUNT_NOTIFICATIONS) {
@ -764,7 +760,7 @@ class AccountController extends BaseController
}
$labels = [];
foreach (['item', 'description', 'unit_cost', 'quantity', 'line_total', 'terms'] as $field) {
foreach (['item', 'description', 'unit_cost', 'quantity', 'line_total', 'terms', 'balance_due', 'partial_due'] as $field) {
$labels[$field] = Input::get("labels_{$field}");
}
$account->invoice_labels = json_encode($labels);
@ -839,7 +835,7 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_COMPANY_DETAILS);
}
private function saveUserDetails()
public function saveUserDetails()
{
$user = Auth::user();
$rules = ['email' => 'email|required|unique:users,email,'.$user->id.',id'];

View File

@ -1,10 +1,14 @@
<?php namespace App\Http\Controllers;
use App\Http\Middleware\PermissionsRequired;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Auth;
class BaseController extends Controller
{
use DispatchesJobs;
protected $model = 'App\Models\EntityModel';
/**
* Setup the layout used by the controller.
@ -17,11 +21,40 @@ class BaseController extends Controller
$this->layout = View::make($this->layout);
}
}
/*
public function __construct()
{
$this->beforeFilter('csrf', array('on' => array('post', 'delete', 'put')));
protected function checkViewPermission($object, &$response = null){
if(!$object->canView()){
$response = response('Unauthorized.', 401);
return false;
}
return true;
}
protected function checkEditPermission($object, &$response = null){
if(!$object->canEdit()){
$response = response('Unauthorized.', 401);
return false;
}
return true;
}
protected function checkCreatePermission(&$response = null){
if(!call_user_func(array($this->model, 'canCreate'))){
$response = response('Unauthorized.', 401);
return false;
}
return true;
}
protected function checkUpdatePermission($input, &$response = null){
$creating = empty($input['public_id']) || $input['public_id'] == '-1';
if($creating){
return $this->checkCreatePermission($response);
}
else{
$object = call_user_func(array($this->model, 'scope'), $input['public_id'])->firstOrFail();
return $this->checkEditPermission($object, $response);
}
}
*/
}

View File

@ -20,6 +20,9 @@ use App\Models\Size;
use App\Models\PaymentTerm;
use App\Models\Industry;
use App\Models\Currency;
use App\Models\Payment;
use App\Models\Credit;
use App\Models\Expense;
use App\Models\Country;
use App\Models\Task;
use App\Ninja\Repositories\ClientRepository;
@ -32,6 +35,7 @@ class ClientController extends BaseController
{
protected $clientService;
protected $clientRepo;
protected $model = 'App\Models\Client';
public function __construct(ClientRepository $clientRepo, ClientService $clientService)
{
@ -77,7 +81,13 @@ class ClientController extends BaseController
*/
public function store(CreateClientRequest $request)
{
$client = $this->clientService->save($request->input());
$data = $request->input();
if(!$this->checkUpdatePermission($data, $response)){
return $response;
}
$client = $this->clientService->save($data);
Session::flash('message', trans('texts.created_client'));
@ -93,22 +103,36 @@ class ClientController extends BaseController
public function show($publicId)
{
$client = Client::withTrashed()->scope($publicId)->with('contacts', 'size', 'industry')->firstOrFail();
if(!$this->checkViewPermission($client, $response)){
return $response;
}
Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT);
$actionLinks = [
['label' => trans('texts.new_task'), 'url' => '/tasks/create/'.$client->public_id]
];
if (Utils::isPro()) {
array_push($actionLinks, ['label' => trans('texts.new_quote'), 'url' => '/quotes/create/'.$client->public_id]);
$actionLinks = [];
if(Task::canCreate()){
$actionLinks[] = ['label' => trans('texts.new_task'), 'url' => '/tasks/create/'.$client->public_id];
}
if (Utils::isPro() && Invoice::canCreate()) {
$actionLinks[] = ['label' => trans('texts.new_quote'), 'url' => '/quotes/create/'.$client->public_id];
}
if(!empty($actionLinks)){
$actionLinks[] = \DropdownButton::DIVIDER;
}
if(Payment::canCreate()){
$actionLinks[] = ['label' => trans('texts.enter_payment'), 'url' => '/payments/create/'.$client->public_id];
}
if(Credit::canCreate()){
$actionLinks[] = ['label' => trans('texts.enter_credit'), 'url' => '/credits/create/'.$client->public_id];
}
if(Expense::canCreate()){
$actionLinks[] = ['label' => trans('texts.enter_expense'), 'url' => '/expenses/create/0/'.$client->public_id];
}
array_push($actionLinks,
\DropdownButton::DIVIDER,
['label' => trans('texts.enter_payment'), 'url' => '/payments/create/'.$client->public_id],
['label' => trans('texts.enter_credit'), 'url' => '/credits/create/'.$client->public_id],
['label' => trans('texts.enter_expense'), 'url' => '/expenses/create/0/'.$client->public_id]
);
$data = array(
'actionLinks' => $actionLinks,
@ -132,6 +156,10 @@ class ClientController extends BaseController
*/
public function create()
{
if(!$this->checkCreatePermission($response)){
return $response;
}
if (Client::scope()->withTrashed()->count() > Auth::user()->getMaxNumClients()) {
return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumClients()." clients"]);
}
@ -157,6 +185,11 @@ class ClientController extends BaseController
public function edit($publicId)
{
$client = Client::scope($publicId)->with('contacts')->firstOrFail();
if(!$this->checkEditPermission($client, $response)){
return $response;
}
$data = [
'client' => $client,
'method' => 'PUT',
@ -199,7 +232,13 @@ class ClientController extends BaseController
*/
public function update(UpdateClientRequest $request)
{
$client = $this->clientService->save($request->input());
$data = $request->input();
if(!$this->checkUpdatePermission($data, $response)){
return $response;
}
$client = $this->clientService->save($data);
Session::flash('message', trans('texts.updated_client'));

View File

@ -17,10 +17,11 @@ class CreditController extends BaseController
{
protected $creditRepo;
protected $creditService;
protected $model = 'App\Models\Credit';
public function __construct(CreditRepository $creditRepo, CreditService $creditService)
{
//parent::__construct();
// parent::__construct();
$this->creditRepo = $creditRepo;
$this->creditService = $creditService;
@ -56,6 +57,10 @@ class CreditController extends BaseController
public function create($clientPublicId = 0)
{
if(!$this->checkCreatePermission($response)){
return $response;
}
$data = array(
'clientPublicId' => Input::old('client') ? Input::old('client') : $clientPublicId,
//'invoicePublicId' => Input::old('invoice') ? Input::old('invoice') : $invoicePublicId,
@ -72,6 +77,11 @@ class CreditController extends BaseController
public function edit($publicId)
{
$credit = Credit::scope($publicId)->firstOrFail();
if(!$this->checkEditPermission($credit, $response)){
return $response;
}
$credit->credit_date = Utils::fromSqlDate($credit->credit_date);
$data = array(

View File

@ -11,7 +11,9 @@ class DashboardController extends BaseController
{
public function index()
{
$view_all = !Auth::user()->hasPermission('view_all');
$user_id = Auth::user()->id;
// total_income, billed_clients, invoice_sent and active_clients
$select = DB::raw('COUNT(DISTINCT CASE WHEN invoices.id IS NOT NULL THEN clients.id ELSE null END) billed_clients,
SUM(CASE WHEN invoices.invoice_status_id >= '.INVOICE_STATUS_SENT.' THEN 1 ELSE 0 END) invoices_sent,
@ -24,8 +26,19 @@ class DashboardController extends BaseController
->where('clients.is_deleted', '=', false)
->where('invoices.is_deleted', '=', false)
->where('invoices.is_recurring', '=', false)
->where('invoices.is_quote', '=', false)
->groupBy('accounts.id')
->where('invoices.is_quote', '=', false);
if(!$view_all){
$metrics = $metrics->where(function($query) use($user_id){
$query->where('invoices.user_id', '=', $user_id);
$query->orwhere(function($query) use($user_id){
$query->where('invoices.user_id', '=', null);
$query->where('clients.user_id', '=', $user_id);
});
});
}
$metrics = $metrics->groupBy('accounts.id')
->first();
$select = DB::raw('SUM(clients.paid_to_date) as value, clients.currency_id as currency_id');
@ -33,8 +46,13 @@ class DashboardController extends BaseController
->select($select)
->leftJoin('clients', 'accounts.id', '=', 'clients.account_id')
->where('accounts.id', '=', Auth::user()->account_id)
->where('clients.is_deleted', '=', false)
->groupBy('accounts.id')
->where('clients.is_deleted', '=', false);
if(!$view_all){
$paidToDate = $paidToDate->where('clients.user_id', '=', $user_id);
}
$paidToDate = $paidToDate->groupBy('accounts.id')
->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END'))
->get();
@ -47,8 +65,13 @@ class DashboardController extends BaseController
->where('clients.is_deleted', '=', false)
->where('invoices.is_deleted', '=', false)
->where('invoices.is_quote', '=', false)
->where('invoices.is_recurring', '=', false)
->groupBy('accounts.id')
->where('invoices.is_recurring', '=', false);
if(!$view_all){
$averageInvoice = $averageInvoice->where('invoices.user_id', '=', $user_id);
}
$averageInvoice = $averageInvoice->groupBy('accounts.id')
->groupBy(DB::raw('CASE WHEN clients.currency_id IS NULL THEN CASE WHEN accounts.currency_id IS NULL THEN 1 ELSE accounts.currency_id END ELSE clients.currency_id END'))
->get();
@ -63,9 +86,14 @@ class DashboardController extends BaseController
->get();
$activities = Activity::where('activities.account_id', '=', Auth::user()->account_id)
->where('activities.activity_type_id', '>', 0);
if(!$view_all){
$activities = $activities->where('activities.user_id', '=', $user_id);
}
$activities = $activities->orderBy('activities.created_at', 'desc')
->with('client.contacts', 'user', 'invoice', 'payment', 'credit', 'account')
->where('activity_type_id', '>', 0)
->orderBy('created_at', 'desc')
->take(50)
->get();
@ -81,8 +109,13 @@ class DashboardController extends BaseController
->where('invoices.is_deleted', '=', false)
->where('invoices.deleted_at', '=', null)
->where('contacts.is_primary', '=', true)
->where('invoices.due_date', '<', date('Y-m-d'))
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'is_quote'])
->where('invoices.due_date', '<', date('Y-m-d'));
if(!$view_all){
$pastDue = $pastDue->where('invoices.user_id', '=', $user_id);
}
$pastDue = $pastDue->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'is_quote'])
->orderBy('invoices.due_date', 'asc')
->take(50)
->get();
@ -100,9 +133,14 @@ class DashboardController extends BaseController
->where('invoices.is_deleted', '=', false)
->where('contacts.is_primary', '=', true)
->where('invoices.due_date', '>=', date('Y-m-d'))
->orderBy('invoices.due_date', 'asc')
->take(50)
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'is_quote'])
->orderBy('invoices.due_date', 'asc');
if(!$view_all){
$upcoming = $upcoming->where('invoices.user_id', '=', $user_id);
}
$upcoming = $upcoming->take(50)
->select(['invoices.due_date', 'invoices.balance', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id', 'is_quote'])
->get();
$payments = DB::table('payments')
@ -114,8 +152,13 @@ class DashboardController extends BaseController
->where('invoices.is_deleted', '=', false)
->where('clients.is_deleted', '=', false)
->where('contacts.deleted_at', '=', null)
->where('contacts.is_primary', '=', true)
->select(['payments.payment_date', 'payments.amount', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id'])
->where('contacts.is_primary', '=', true);
if(!$view_all){
$payments = $payments->where('payments.user_id', '=', $user_id);
}
$payments = $payments->select(['payments.payment_date', 'payments.amount', 'invoices.public_id', 'invoices.invoice_number', 'clients.name as client_name', 'contacts.email', 'contacts.first_name', 'contacts.last_name', 'clients.currency_id', 'clients.public_id as client_public_id', 'clients.user_id as client_user_id'])
->orderBy('payments.payment_date', 'desc')
->take(50)
->get();

View File

@ -25,10 +25,11 @@ class ExpenseController extends BaseController
// Expenses
protected $expenseRepo;
protected $expenseService;
protected $model = 'App\Models\Expense';
public function __construct(ExpenseRepository $expenseRepo, ExpenseService $expenseService)
{
//parent::__construct();
// parent::__construct();
$this->expenseRepo = $expenseRepo;
$this->expenseService = $expenseService;
@ -44,7 +45,7 @@ class ExpenseController extends BaseController
return View::make('list', array(
'entityType' => ENTITY_EXPENSE,
'title' => trans('texts.expenses'),
'sortCol' => '1',
'sortCol' => '3',
'columns' => Utils::trans([
'checkbox',
'vendor',
@ -70,6 +71,10 @@ class ExpenseController extends BaseController
public function create($vendorPublicId = null, $clientPublicId = null)
{
if(!$this->checkCreatePermission($response)){
return $response;
}
if($vendorPublicId != 0) {
$vendor = Vendor::scope($vendorPublicId)->with('vendorcontacts')->firstOrFail();
} else {
@ -95,6 +100,11 @@ class ExpenseController extends BaseController
public function edit($publicId)
{
$expense = Expense::scope($publicId)->firstOrFail();
if(!$this->checkEditPermission($expense, $response)){
return $response;
}
$expense->expense_date = Utils::fromSqlDate($expense->expense_date);
$actions = [];

View File

@ -34,10 +34,11 @@ class InvoiceController extends BaseController
protected $clientRepo;
protected $invoiceService;
protected $recurringInvoiceService;
protected $model = 'App\Models\Invoice';
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, RecurringInvoiceService $recurringInvoiceService)
{
//parent::__construct();
// parent::__construct();
$this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo;
@ -51,6 +52,7 @@ class InvoiceController extends BaseController
$data = [
'title' => trans('texts.invoices'),
'entityType' => ENTITY_INVOICE,
'sortCol' => '3',
'columns' => Utils::trans([
'checkbox',
'invoice_number',
@ -90,6 +92,11 @@ class InvoiceController extends BaseController
->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')
->withTrashed()
->firstOrFail();
if(!$this->checkEditPermission($invoice, $response)){
return $response;
}
$entityType = $invoice->getEntityType();
$contactIds = DB::table('invitations')
@ -159,6 +166,10 @@ class InvoiceController extends BaseController
$lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null;
if(!Auth::user()->hasPermission('view_all')){
$clients = $clients->where('clients.user_id', '=', Auth::user()->id);
}
$data = array(
'clients' => $clients->get(),
'entityType' => $entityType,
@ -206,7 +217,11 @@ class InvoiceController extends BaseController
public function create($clientPublicId = 0, $isRecurring = false)
{
$account = Auth::user()->account;
if(!$this->checkCreatePermission($response)){
return $response;
}
$account = Auth::user()->account;
$entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE;
$clientId = null;
@ -217,8 +232,13 @@ class InvoiceController extends BaseController
$invoice = $account->createInvoice($entityType, $clientId);
$invoice->public_id = 0;
$clients = Client::scope()->with('contacts', 'country')->orderBy('name');
if(!Auth::user()->hasPermission('view_all')){
$clients = $clients->where('clients.user_id', '=', Auth::user()->id);
}
$data = [
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
'clients' => $clients->get(),
'entityType' => $invoice->getEntityType(),
'invoice' => $invoice,
'method' => 'POST',
@ -335,10 +355,16 @@ class InvoiceController extends BaseController
*/
public function store(SaveInvoiceWithClientRequest $request)
{
$data = $request->input();
if(!$this->checkUpdatePermission($data, $response)){
return $response;
}
$action = Input::get('action');
$entityType = Input::get('entityType');
$invoice = $this->invoiceService->save($request->input());
$invoice = $this->invoiceService->save($data, true);
$entityType = $invoice->getEntityType();
$message = trans("texts.created_{$entityType}");
@ -369,10 +395,16 @@ class InvoiceController extends BaseController
*/
public function update(SaveInvoiceWithClientRequest $request)
{
$data = $request->input();
if(!$this->checkUpdatePermission($data, $response)){
return $response;
}
$action = Input::get('action');
$entityType = Input::get('entityType');
$invoice = $this->invoiceService->save($request->input());
$invoice = $this->invoiceService->save($data, true);
$entityType = $invoice->getEntityType();
$message = trans("texts.updated_{$entityType}");
Session::flash('message', $message);

View File

@ -30,9 +30,11 @@ use App\Http\Requests\UpdatePaymentRequest;
class PaymentController extends BaseController
{
protected $model = 'App\Models\Payment';
public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService)
{
//parent::__construct();
// parent::__construct();
$this->paymentRepo = $paymentRepo;
$this->invoiceRepo = $invoiceRepo;
@ -46,6 +48,7 @@ class PaymentController extends BaseController
return View::make('list', array(
'entityType' => ENTITY_PAYMENT,
'title' => trans('texts.payments'),
'sortCol' => '6',
'columns' => Utils::trans([
'checkbox',
'invoice',
@ -66,6 +69,10 @@ class PaymentController extends BaseController
public function create($clientPublicId = 0, $invoicePublicId = 0)
{
if(!$this->checkCreatePermission($response)){
return $response;
}
$invoices = Invoice::scope()
->where('is_recurring', '=', false)
->where('is_quote', '=', false)
@ -92,6 +99,11 @@ class PaymentController extends BaseController
public function edit($publicId)
{
$payment = Payment::scope($publicId)->firstOrFail();
if(!$this->checkEditPermission($payment, $response)){
return $response;
}
$payment->payment_date = Utils::fromSqlDate($payment->payment_date);
$data = array(
@ -573,6 +585,11 @@ class PaymentController extends BaseController
public function store(CreatePaymentRequest $request)
{
$input = $request->input();
if(!$this->checkUpdatePermission($input, $response)){
return $response;
}
$input['invoice_id'] = Invoice::getPrivateId($input['invoice']);
$input['client_id'] = Client::getPrivateId($input['client']);
$payment = $this->paymentRepo->save($input);
@ -590,6 +607,11 @@ class PaymentController extends BaseController
public function update(UpdatePaymentRequest $request)
{
$input = $request->input();
if(!$this->checkUpdatePermission($input, $response)){
return $response;
}
$payment = $this->paymentRepo->save($input);
Session::flash('message', trans('texts.updated_payment'));

View File

@ -33,10 +33,11 @@ class QuoteController extends BaseController
protected $invoiceRepo;
protected $clientRepo;
protected $invoiceService;
protected $model = 'App\Models\Invoice';
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService)
{
//parent::__construct();
// parent::__construct();
$this->mailer = $mailer;
$this->invoiceRepo = $invoiceRepo;
@ -53,6 +54,7 @@ class QuoteController extends BaseController
$data = [
'title' => trans('texts.quotes'),
'entityType' => ENTITY_QUOTE,
'sortCol' => '3',
'columns' => Utils::trans([
'checkbox',
'quote_number',
@ -78,6 +80,10 @@ class QuoteController extends BaseController
public function create($clientPublicId = 0)
{
if(!$this->checkCreatePermission($response)){
return $response;
}
if (!Utils::isPro()) {
return Redirect::to('/invoices/create');
}

View File

@ -22,10 +22,11 @@ class TaskController extends BaseController
{
protected $taskRepo;
protected $taskService;
protected $model = 'App\Models\Task';
public function __construct(TaskRepository $taskRepo, InvoiceRepository $invoiceRepo, TaskService $taskService)
{
//parent::__construct();
// parent::__construct();
$this->taskRepo = $taskRepo;
$this->invoiceRepo = $invoiceRepo;
@ -84,6 +85,9 @@ class TaskController extends BaseController
*/
public function create($clientPublicId = 0)
{
if(!$this->checkCreatePermission($response)){
return $response;
}
$this->checkTimezone();
$data = [
@ -113,6 +117,10 @@ class TaskController extends BaseController
$task = Task::scope($publicId)->with('client', 'invoice')->withTrashed()->firstOrFail();
if(!$this->checkEditPermission($task, $response)){
return $response;
}
$actions = [];
if ($task->invoice) {
$actions[] = ['url' => URL::to("invoices/{$task->invoice->public_id}/edit"), 'label' => trans("texts.view_invoice")];
@ -175,6 +183,10 @@ class TaskController extends BaseController
private function save($publicId = null)
{
$action = Input::get('action');
if(!$this->checkUpdatePermission(array('public_id'=>$publicId)/* Hacky, but works */, $response)){
return $response;
}
if (in_array($action, ['archive', 'delete', 'restore'])) {
return self::bulk();

View File

@ -192,6 +192,8 @@ class UserController extends BaseController
$user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email'));
$user->email = trim(Input::get('email'));
$user->is_admin = boolval(Input::get('is_admin'));
$user->permissions = Input::get('permissions');
} else {
$lastUser = User::withTrashed()->where('account_id', '=', Auth::user()->account_id)
->orderBy('public_id', 'DESC')->first();
@ -202,10 +204,12 @@ class UserController extends BaseController
$user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email'));
$user->email = trim(Input::get('email'));
$user->is_admin = boolval(Input::get('is_admin'));
$user->registered = true;
$user->password = str_random(RANDOM_KEY_LENGTH);
$user->confirmation_code = str_random(RANDOM_KEY_LENGTH);
$user->public_id = $lastUser->public_id + 1;
$user->permissions = Input::get('permissions');
}
$user->save();

View File

@ -30,6 +30,7 @@ class VendorController extends BaseController
{
protected $vendorService;
protected $vendorRepo;
protected $model = 'App\Models\Vendor';
public function __construct(VendorRepository $vendorRepo, VendorService $vendorService)
{
@ -76,7 +77,13 @@ class VendorController extends BaseController
*/
public function store(CreateVendorRequest $request)
{
$vendor = $this->vendorService->save($request->input());
$data = $request->input();
if(!$this->checkUpdatePermission($data, $response)){
return $response;
}
$vendor = $this->vendorService->save($data);
Session::flash('message', trans('texts.created_vendor'));
@ -92,6 +99,11 @@ class VendorController extends BaseController
public function show($publicId)
{
$vendor = Vendor::withTrashed()->scope($publicId)->with('vendorcontacts', 'size', 'industry')->firstOrFail();
if(!$this->checkViewPermission($vendor, $response)){
return $response;
}
Utils::trackViewed($vendor->getDisplayName(), 'vendor');
$actionLinks = [
@ -119,6 +131,10 @@ class VendorController extends BaseController
*/
public function create()
{
if(!$this->checkCreatePermission($response)){
return $response;
}
if (Vendor::scope()->count() > Auth::user()->getMaxNumVendors()) {
return View::make('error', ['hideHeader' => true, 'error' => "Sorry, you've exceeded the limit of ".Auth::user()->getMaxNumVendors()." vendors"]);
}
@ -144,6 +160,11 @@ class VendorController extends BaseController
public function edit($publicId)
{
$vendor = Vendor::scope($publicId)->with('vendorcontacts')->firstOrFail();
if(!$this->checkEditPermission($vendor, $response)){
return $response;
}
$data = [
'vendor' => $vendor,
'method' => 'PUT',
@ -180,7 +201,13 @@ class VendorController extends BaseController
*/
public function update(UpdateVendorRequest $request)
{
$vendor = $this->vendorService->save($request->input());
$data = $request->input();
if(!$this->checkUpdatePermission($data, $response)){
return $response;
}
$vendor = $this->vendorService->save($data);
Session::flash('message', trans('texts.updated_vendor'));

View File

@ -28,6 +28,7 @@ class Kernel extends HttpKernel {
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'permissions.required' => 'App\Http\Middleware\PermissionsRequired',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'api' => 'App\Http\Middleware\ApiCheck',
];

View File

@ -0,0 +1,57 @@
<?php namespace App\Http\Middleware;
use Closure;
use Auth;
class PermissionsRequired {
/**
* @var array of controller => [action => permission]
*/
static protected $actions = [];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next, $guard = 'user')
{
// Get the current route.
$route = $request->route();
// Get the current route actions.
$actions = $route->getAction();
// Check if we have any permissions to check the user has.
if ($permissions = !empty($actions['permissions']) ? $actions['permissions'] : null)
{
if(!Auth::user($guard)->hasPermission($permissions, !empty($actions['permissions_require_all']))){
return response('Unauthorized.', 401);
}
}
// Check controller permissions
$action = explode('@', $request->route()->getActionName());
if(isset(static::$actions[$action[0]]) && isset(static::$actions[$action[0]][$action[1]])) {
$controller_permissions = static::$actions[$action[0]][$action[1]];
if(!Auth::user($guard)->hasPermission($controller_permissions)){
return response('Unauthorized.', 401);
}
}
return $next($request);
}
/**
* add a controller's action permission
*
* @param \App\Http\Controllers\Controller $controller
* @param array $permissions
*/
public static function addPermission(\App\Http\Controllers\Controller $controller, $permissions)
{
static::$actions[get_class($controller)] = $permissions;
}
}

View File

@ -104,62 +104,9 @@ Route::group(['middleware' => 'auth:user'], function() {
Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
Route::get('hide_message', 'HomeController@hideMessage');
Route::get('force_inline_pdf', 'UserController@forcePDFJS');
Route::get('api/users', array('as'=>'api.users', 'uses'=>'UserController@getDatatable'));
Route::resource('users', 'UserController');
Route::post('users/bulk', 'UserController@bulk');
Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation');
Route::get('start_trial', 'AccountController@startTrial');
Route::get('restore_user/{user_id}', 'UserController@restoreUser');
Route::post('users/change_password', 'UserController@changePassword');
Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
Route::get('/manage_companies', 'UserController@manageCompanies');
Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable'));
Route::resource('tokens', 'TokenController');
Route::post('tokens/bulk', 'TokenController@bulk');
Route::get('api/products', array('as'=>'api.products', 'uses'=>'ProductController@getDatatable'));
Route::resource('products', 'ProductController');
Route::post('products/bulk', 'ProductController@bulk');
Route::get('api/tax_rates', array('as'=>'api.tax_rates', 'uses'=>'TaxRateController@getDatatable'));
Route::resource('tax_rates', 'TaxRateController');
Route::post('tax_rates/bulk', 'TaxRateController@bulk');
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
Route::get('settings/data_visualizations', 'ReportController@d3');
Route::get('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
Route::post('settings/company_details', 'AccountController@updateDetails');
Route::get('settings/{section?}', 'AccountController@showSection');
Route::post('settings/{section?}', 'AccountController@doSection');
//Route::get('api/payment_terms', array('as'=>'api.payment_terms', 'uses'=>'PaymentTermController@getDatatable'));
//Route::resource('payment_terms', 'PaymentTermController');
//Route::post('payment_terms/bulk', 'PaymentTermController@bulk');
Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData'));
Route::post('user/setTheme', 'UserController@setTheme');
Route::post('remove_logo', 'AccountController@removeLogo');
Route::post('account/go_pro', 'AccountController@enableProPlan');
Route::post('/export', 'ExportController@doExport');
Route::post('/import', 'ImportController@doImport');
Route::post('/import_csv', 'ImportController@doImportCSV');
Route::resource('gateways', 'AccountGatewayController');
Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable'));
Route::post('account_gateways/bulk', 'AccountGatewayController@bulk');
Route::resource('bank_accounts', 'BankAccountController');
Route::get('api/bank_accounts', array('as'=>'api.bank_accounts', 'uses'=>'BankAccountController@getDatatable'));
Route::post('bank_accounts/bulk', 'BankAccountController@bulk');
Route::post('bank_accounts/validate', 'BankAccountController@validateAccount');
Route::post('bank_accounts/import_expenses/{bank_id}', 'BankAccountController@importExpenses');
Route::get('settings/user_details', 'AccountController@showUserDetails');
Route::post('settings/user_details', 'AccountController@saveUserDetails');
Route::resource('clients', 'ClientController');
Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable'));
@ -222,6 +169,67 @@ Route::group(['middleware' => 'auth:user'], function() {
Route::post('expenses/bulk', 'ExpenseController@bulk');
});
Route::group([
'middleware' => ['auth:user', 'permissions.required'],
'permissions' => 'admin',
], function() {
Route::get('api/users', array('as'=>'api.users', 'uses'=>'UserController@getDatatable'));
Route::resource('users', 'UserController');
Route::post('users/bulk', 'UserController@bulk');
Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation');
Route::get('start_trial', 'AccountController@startTrial');
Route::get('restore_user/{user_id}', 'UserController@restoreUser');
Route::post('users/change_password', 'UserController@changePassword');
Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
Route::get('/manage_companies', 'UserController@manageCompanies');
Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable'));
Route::resource('tokens', 'TokenController');
Route::post('tokens/bulk', 'TokenController@bulk');
Route::get('api/products', array('as'=>'api.products', 'uses'=>'ProductController@getDatatable'));
Route::resource('products', 'ProductController');
Route::post('products/bulk', 'ProductController@bulk');
Route::get('api/tax_rates', array('as'=>'api.tax_rates', 'uses'=>'TaxRateController@getDatatable'));
Route::resource('tax_rates', 'TaxRateController');
Route::post('tax_rates/bulk', 'TaxRateController@bulk');
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
Route::get('settings/data_visualizations', 'ReportController@d3');
Route::get('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
Route::post('settings/company_details', 'AccountController@updateDetails');
Route::get('settings/{section?}', 'AccountController@showSection');
Route::post('settings/{section?}', 'AccountController@doSection');
//Route::get('api/payment_terms', array('as'=>'api.payment_terms', 'uses'=>'PaymentTermController@getDatatable'));
//Route::resource('payment_terms', 'PaymentTermController');
//Route::post('payment_terms/bulk', 'PaymentTermController@bulk');
Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData'));
Route::post('user/setTheme', 'UserController@setTheme');
Route::post('remove_logo', 'AccountController@removeLogo');
Route::post('account/go_pro', 'AccountController@enableProPlan');
Route::post('/export', 'ExportController@doExport');
Route::post('/import', 'ImportController@doImport');
Route::post('/import_csv', 'ImportController@doImportCSV');
Route::resource('gateways', 'AccountGatewayController');
Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable'));
Route::post('account_gateways/bulk', 'AccountGatewayController@bulk');
Route::resource('bank_accounts', 'BankAccountController');
Route::get('api/bank_accounts', array('as'=>'api.bank_accounts', 'uses'=>'BankAccountController@getDatatable'));
Route::post('bank_accounts/bulk', 'BankAccountController@bulk');
Route::post('bank_accounts/validate', 'BankAccountController@validateAccount');
Route::post('bank_accounts/import_expenses/{bank_id}', 'BankAccountController@importExpenses');
});
// Route groups for API
Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function()
{
@ -500,6 +508,8 @@ if (!defined('CONTACT_EMAIL')) {
define('GATEWAY_PAYFAST', 13);
define('GATEWAY_PAYPAL_EXPRESS', 17);
define('GATEWAY_PAYPAL_PRO', 18);
define('GATEWAY_SAGE_PAY_DIRECT', 20);
define('GATEWAY_SAGE_PAY_SERVER', 21);
define('GATEWAY_STRIPE', 23);
define('GATEWAY_GOCARDLESS', 6);
define('GATEWAY_TWO_CHECKOUT', 27);
@ -604,6 +614,7 @@ if (!defined('CONTACT_EMAIL')) {
define('USER_STATE_PENDING', 'pending');
define('USER_STATE_DISABLED', 'disabled');
define('USER_STATE_ADMIN', 'admin');
define('USER_STATE_OWNER', 'owner');
define('API_SERIALIZER_ARRAY', 'array');
define('API_SERIALIZER_JSON', 'json');

View File

@ -118,6 +118,21 @@ class Utils
return Auth::check() && Auth::user()->isPro();
}
public static function isAdmin()
{
return Auth::check() && Auth::user()->is_admin;
}
public static function hasPermission($permission, $requireAll = false)
{
return Auth::check() && Auth::user()->hasPermission($permission, $requireAll);
}
public static function hasAllPermissions($permission)
{
return Auth::check() && Auth::user()->hasPermissions($permission);
}
public static function isTrial()
{
return Auth::check() && Auth::user()->isTrial();

View File

@ -683,7 +683,7 @@ class Account extends Eloquent
'subtotal',
'paid_to_date',
'balance_due',
'amount_due',
'partial_due',
'terms',
'your_invoice',
'quote',

View File

@ -113,4 +113,56 @@ class EntityModel extends Eloquent
$name = $parts[count($parts)-1];
return strtolower($name) . '_id';
}
public static function canCreate() {
return Auth::user()->hasPermission('create_all');
}
public function canEdit() {
return static::canEditItem($this);
}
public static function canEditItem($item) {
return Auth::user()->hasPermission('edit_all') || (isset($item->user_id) && Auth::user()->id == $item->user_id);
}
public static function canEditItemById($item_id) {
if(Auth::user()->hasPermission('edit_all')) {
return true;
}
return static::whereId($item_id)->first()->user_id == Auth::user()->id;
}
public static function canEditItemByOwner($user_id) {
if(Auth::user()->hasPermission('edit_all')) {
return true;
}
return Auth::user()->id == $user_id;
}
public function canView() {
return static::canViewItem($this);
}
public static function canViewItem($item) {
return Auth::user()->hasPermission('view_all') || (isset($item->user_id) && Auth::user()->id == $item->user_id);
}
public static function canViewItemById($item_id) {
if(Auth::user()->hasPermission('view_all')) {
return true;
}
return static::whereId($item_id)->first()->user_id == Auth::user()->id;
}
public static function canViewItemByOwner($user_id) {
if(Auth::user()->hasPermission('view_all')) {
return true;
}
return Auth::user()->id == $user_id;
}
}

View File

@ -75,6 +75,8 @@ class Gateway extends Eloquent
$link = 'https://bitpay.com/dashboard/signup';
} elseif ($this->id == GATEWAY_DWOLLA) {
$link = 'https://www.dwolla.com/register';
} elseif ($this->id == GATEWAY_SAGE_PAY_DIRECT || $this->id == GATEWAY_SAGE_PAY_SERVER) {
$link = 'https://applications.sagepay.com/apply/2C02C252-0F8A-1B84-E10D-CF933EFCAA99';
}
$key = 'texts.gateway_help_'.$this->id;

View File

@ -1,5 +1,6 @@
<?php namespace App\Models;
use Auth;
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends EntityModel
@ -21,4 +22,8 @@ class Product extends EntityModel
{
return $this->belongsTo('App\Models\TaxRate');
}
public function canEdit() {
return Auth::user()->hasPermission('admin');
}
}

View File

@ -16,4 +16,8 @@ class TaxRate extends EntityModel
{
return ENTITY_TAX_RATE;
}
public function canEdit() {
return Auth::user()->hasPermission('admin');
}
}

View File

@ -14,7 +14,12 @@ use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
public static $all_permissions = array(
'create_all' => 0b0001,
'view_all' => 0b0010,
'edit_all' => 0b0100,
);
use Authenticatable, CanResetPassword;
/**
@ -253,7 +258,69 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
&& $this->email != $this->getOriginal('email')
&& $this->getOriginal('confirmed');
}
/**
* Set the permissions attribute on the model.
*
* @param mixed $value
* @return $this
*/
protected function setPermissionsAttribute($value){
if(empty($value)) {
$this->attributes['permissions'] = 0;
} else {
$bitmask = 0;
foreach($value as $permission){
$bitmask = $bitmask | static::$all_permissions[$permission];
}
$this->attributes['permissions'] = $bitmask;
}
return $this;
}
/**
* Expands the value of the permissions attribute
*
* @param mixed $value
* @return mixed
*/
protected function getPermissionsAttribute($value){
$permissions = array();
foreach(static::$all_permissions as $permission => $bitmask){
if(($value & $bitmask) == $bitmask) {
$permissions[$permission] = $permission;
}
}
return $permissions;
}
/**
* Checks to see if the user has the required permission
*
* @param mixed $permission Either a single permission or an array of possible permissions
* @param boolean True to require all permissions, false to require only one
* @return boolean
*/
public function hasPermission($permission, $requireAll = false){
if ($this->is_admin) {
return true;
} else if(is_string($permission)){
return !empty($this->permissions[$permission]);
} else if(is_array($permission)) {
if($requireAll){
return count(array_diff($permission, $this->permissions)) == 0;
} else {
return count(array_intersect($permission, $this->permissions)) > 0;
}
}
return false;
}
}
User::updating(function ($user) {

View File

@ -19,7 +19,7 @@ class InvoicePresenter extends Presenter {
public function balanceDueLabel()
{
if ($this->entity->partial) {
return 'amount_due';
return 'partial_due';
} elseif ($this->entity->is_quote) {
return 'total';
} else {

View File

@ -46,7 +46,8 @@ class ClientRepository extends BaseRepository
'clients.work_phone',
'contacts.email',
'clients.deleted_at',
'clients.is_deleted'
'clients.is_deleted',
'clients.user_id'
);
if (!\Session::get('show_trash:client')) {

View File

@ -29,6 +29,7 @@ class CreditRepository extends BaseRepository
'credits.public_id',
'clients.name as client_name',
'clients.public_id as client_public_id',
'clients.user_id as client_user_id',
'credits.amount',
'credits.balance',
'credits.credit_date',
@ -37,7 +38,8 @@ class CreditRepository extends BaseRepository
'contacts.email',
'credits.private_notes',
'credits.deleted_at',
'credits.is_deleted'
'credits.is_deleted',
'credits.user_id'
);
if ($clientPublicId) {

View File

@ -40,7 +40,8 @@ class ExpenseRepository extends BaseRepository
'expenses.public_id',
'expenses.deleted_at',
'expenses.should_be_invoiced',
'expenses.created_at'
'expenses.created_at',
'expenses.user_id'
);
return $query;
@ -80,11 +81,15 @@ class ExpenseRepository extends BaseRepository
'expenses.vendor_id',
'expenses.expense_currency_id',
'expenses.invoice_currency_id',
'expenses.user_id',
'invoices.public_id as invoice_public_id',
'invoices.user_id as invoice_user_id',
'vendors.name as vendor_name',
'vendors.public_id as vendor_public_id',
'vendors.user_id as vendor_user_id',
'clients.name as client_name',
'clients.public_id as client_public_id',
'clients.user_id as client_user_id',
'contacts.first_name',
'contacts.email',
'contacts.last_name',

View File

@ -49,6 +49,7 @@ class InvoiceRepository extends BaseRepository
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
DB::raw('COALESCE(clients.country_id, accounts.country_id) country_id'),
'clients.public_id as client_public_id',
'clients.user_id as client_user_id',
'invoice_number',
'invoice_status_id',
'clients.name as client_name',
@ -65,7 +66,8 @@ class InvoiceRepository extends BaseRepository
'invoices.quote_invoice_id',
'invoices.deleted_at',
'invoices.is_deleted',
'invoices.partial'
'invoices.partial',
'invoices.user_id'
);
if (!\Session::get('show_trash:'.$entityType)) {
@ -189,7 +191,7 @@ class InvoiceRepository extends BaseRepository
->make();
}
public function save($data)
public function save($data, $checkSubPermissions = false)
{
$account = \Auth::user()->account;
$publicId = isset($data['public_id']) ? $data['public_id'] : false;
@ -405,29 +407,40 @@ class InvoiceRepository extends BaseRepository
$task = false;
if (isset($item['task_public_id']) && $item['task_public_id']) {
$task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail();
$task->invoice_id = $invoice->id;
$task->client_id = $invoice->client_id;
$task->save();
if(!$checkSubPermissions || $task->canEdit()){
$task->invoice_id = $invoice->id;
$task->client_id = $invoice->client_id;
$task->save();
}
}
$expense = false;
if (isset($item['expense_public_id']) && $item['expense_public_id']) {
$expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail();
$expense->invoice_id = $invoice->id;
$expense->client_id = $invoice->client_id;
$expense->save();
if(!$checkSubPermissions || $expense->canEdit()){
$expense->invoice_id = $invoice->id;
$expense->client_id = $invoice->client_id;
$expense->save();
}
}
if ($productKey = trim($item['product_key'])) {
if (\Auth::user()->account->update_products && ! strtotime($productKey)) {
$product = Product::findProductByKey($productKey);
if (!$product) {
$product = Product::createNew();
$product->product_key = trim($item['product_key']);
if(!$checkSubPermissions || Product::canCreate()){
$product = Product::createNew();
$product->product_key = trim($item['product_key']);
}
else{
$product = null;
}
}
if($product && (!$checkSubPermissions || $product->canEdit())){
$product->notes = ($task || $expense) ? '' : $item['notes'];
$product->cost = $expense ? 0 : $item['cost'];
$product->save();
}
$product->notes = ($task || $expense) ? '' : $item['notes'];
$product->cost = $expense ? 0 : $item['cost'];
$product->save();
}
}

View File

@ -36,9 +36,11 @@ class PaymentRepository extends BaseRepository
'payments.transaction_reference',
'clients.name as client_name',
'clients.public_id as client_public_id',
'clients.user_id as client_user_id',
'payments.amount',
'payments.payment_date',
'invoices.public_id as invoice_public_id',
'invoices.user_id as invoice_user_id',
'invoices.invoice_number',
'contacts.first_name',
'contacts.last_name',
@ -47,6 +49,7 @@ class PaymentRepository extends BaseRepository
'payments.account_gateway_id',
'payments.deleted_at',
'payments.is_deleted',
'payments.user_id',
'invoices.is_deleted as invoice_is_deleted',
'gateways.name as gateway_name'
);

View File

@ -27,6 +27,7 @@ class TaskRepository
'tasks.public_id',
'clients.name as client_name',
'clients.public_id as client_public_id',
'clients.user_id as client_user_id',
'contacts.first_name',
'contacts.email',
'contacts.last_name',
@ -36,9 +37,11 @@ class TaskRepository
'tasks.deleted_at',
'invoices.invoice_number',
'invoices.public_id as invoice_public_id',
'invoices.user_id as invoice_user_id',
'tasks.is_running',
'tasks.time_log',
'tasks.created_at'
'tasks.created_at',
'tasks.user_id'
);
if ($clientPublicId) {

View File

@ -22,7 +22,7 @@ class UserRepository extends BaseRepository
$query->where('users.deleted_at', '=', null);
}
$query->select('users.public_id', 'users.first_name', 'users.last_name', 'users.email', 'users.confirmed', 'users.public_id', 'users.deleted_at');
$query->select('users.public_id', 'users.first_name', 'users.last_name', 'users.email', 'users.confirmed', 'users.public_id', 'users.deleted_at', 'users.is_admin', 'users.permissions');
return $query;
}
@ -34,5 +34,4 @@ class UserRepository extends BaseRepository
return $user;
}
}

View File

@ -42,7 +42,8 @@ class VendorRepository extends BaseRepository
'vendors.city',
'vendor_contacts.email',
'vendors.deleted_at',
'vendors.is_deleted'
'vendors.is_deleted',
'vendors.user_id'
);
if (!\Session::get('show_trash:vendor')) {

View File

@ -8,6 +8,9 @@ use Form;
use URL;
use Request;
use Validator;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Vendor;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
@ -46,31 +49,38 @@ class AppServiceProvider extends ServiceProvider {
$class = ( Request::is($types) || Request::is('*'.$type.'*')) && !Request::is('*settings*') ? ' active' : '';
$str = '<li class="dropdown '.$class.'">
<a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a>
<ul class="dropdown-menu" id="menu1">
<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
<a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a>';
$items = [];
if(Auth::user()->hasPermission('create_all')){
$items[] = '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
}
if ($type == ENTITY_INVOICE) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('recurring_invoices').'">'.trans("texts.recurring_invoices").'</a></li>
<li><a href="'.URL::to('recurring_invoices/create').'">'.trans("texts.new_recurring_invoice").'</a></li>';
if(!empty($items))$items[] = '<li class="divider"></li>';
$items[] = '<li><a href="'.URL::to('recurring_invoices').'">'.trans("texts.recurring_invoices").'</a></li>';
if(Invoice::canCreate())$items[] = '<li><a href="'.URL::to('recurring_invoices/create').'">'.trans("texts.new_recurring_invoice").'</a></li>';
if (Auth::user()->isPro()) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></li>
<li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>';
$items[] = '<li class="divider"></li>';
$items[] = '<li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></li>';
if(Invoice::canCreate())$items[] = '<li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>';
}
} else if ($type == ENTITY_CLIENT) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('credits').'">'.trans("texts.credits").'</a></li>
<li><a href="'.URL::to('credits/create').'">'.trans("texts.new_credit").'</a></li>';
if(!empty($items))$items[] = '<li class="divider"></li>';
$items[] = '<li><a href="'.URL::to('credits').'">'.trans("texts.credits").'</a></li>';
if(Credit::canCreate())$items[] = '<li><a href="'.URL::to('credits/create').'">'.trans("texts.new_credit").'</a></li>';
} else if ($type == ENTITY_EXPENSE) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('vendors').'">'.trans("texts.vendors").'</a></li>
<li><a href="'.URL::to('vendors/create').'">'.trans("texts.new_vendor").'</a></li>';
if(!empty($items))$items[] = '<li class="divider"></li>';
$items[] = '<li><a href="'.URL::to('vendors').'">'.trans("texts.vendors").'</a></li>';
if(Vendor::canCreate())$items[] = '<li><a href="'.URL::to('vendors/create').'">'.trans("texts.new_vendor").'</a></li>';
}
if(!empty($items)){
$str.= '<ul class="dropdown-menu" id="menu1">'.implode($items).'</ul>';
}
$str .= '</ul>
</li>';
$str .= '</li>';
return $str;
});

View File

@ -14,14 +14,16 @@ class BaseService
public function bulk($ids, $action)
{
if ( ! $ids) {
if ( ! $ids ) {
return 0;
}
$entities = $this->getRepo()->findByPublicIdsWithTrashed($ids);
foreach ($entities as $entity) {
$this->getRepo()->$action($entity);
if($entity->canEdit()){
$this->getRepo()->$action($entity);
}
}
return count($entities);

View File

@ -4,6 +4,12 @@ use Utils;
use URL;
use Auth;
use App\Services\BaseService;
use App\Models\Client;
use App\Models\Invoice;
use App\Models\Credit;
use App\Models\Expense;
use App\Models\Payment;
use App\Models\Task;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\NinjaRepository;
@ -37,6 +43,10 @@ class ClientService extends BaseService
{
$query = $this->clientRepo->find($search);
if(!Utils::hasPermission('view_all')){
$query->where('clients.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_CLIENT, $query);
}
@ -89,19 +99,33 @@ class ClientService extends BaseService
trans('texts.edit_client'),
function ($model) {
return URL::to("clients/{$model->public_id}/edit");
},
function ($model) {
return Client::canEditItem($model);
}
],
[
'--divider--', function(){return false;},
function ($model) {
return Client::canEditItem($model) && (Task::canCreate() || Invoice::canCreate());
}
],
[],
[
trans('texts.new_task'),
function ($model) {
return URL::to("tasks/create/{$model->public_id}");
},
function ($model) {
return Task::canCreate();
}
],
[
trans('texts.new_invoice'),
function ($model) {
return URL::to("invoices/create/{$model->public_id}");
},
function ($model) {
return Invoice::canCreate();
}
],
[
@ -110,26 +134,40 @@ class ClientService extends BaseService
return URL::to("quotes/create/{$model->public_id}");
},
function ($model) {
return Auth::user()->isPro();
return Auth::user()->isPro() && Invoice::canCreate();
}
],
[
'--divider--', function(){return false;},
function ($model) {
return (Task::canCreate() || Invoice::canCreate()) && (Payment::canCreate() || Credit::canCreate() || Expense::canCreate());
}
],
[],
[
trans('texts.enter_payment'),
function ($model) {
return URL::to("payments/create/{$model->public_id}");
},
function ($model) {
return Payment::canCreate();
}
],
[
trans('texts.enter_credit'),
function ($model) {
return URL::to("credits/create/{$model->public_id}");
},
function ($model) {
return Credit::canCreate();
}
],
[
trans('texts.enter_expense'),
function ($model) {
return URL::to("expenses/create/0/{$model->public_id}");
},
function ($model) {
return Expense::canCreate();
}
]
];

View File

@ -2,7 +2,10 @@
use Utils;
use URL;
use Auth;
use App\Services\BaseService;
use App\Models\Client;
use App\Models\Payment;
use App\Ninja\Repositories\CreditRepository;
@ -30,6 +33,10 @@ class CreditService extends BaseService
public function getDatatable($clientPublicId, $search)
{
$query = $this->creditRepo->find($clientPublicId, $search);
if(!Utils::hasPermission('view_all')){
$query->where('credits.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_CREDIT, $query, !$clientPublicId);
}
@ -40,6 +47,10 @@ class CreditService extends BaseService
[
'client_name',
function ($model) {
if(!Client::canViewItemByOwner($model->client_user_id)){
return Utils::getClientDisplayName($model);
}
return $model->client_public_id ? link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml() : '';
},
! $hideClient
@ -78,6 +89,9 @@ class CreditService extends BaseService
trans('texts.apply_credit'),
function ($model) {
return URL::to("payments/create/{$model->client_public_id}") . '?paymentTypeId=1';
},
function ($model) {
return Payment::canCreate();
}
]
];

View File

@ -3,6 +3,7 @@
use HtmlString;
use Utils;
use Datatable;
use Auth;
class DatatableService
{
@ -13,7 +14,9 @@ class DatatableService
if ($actions && $showCheckbox) {
$table->addColumn('checkbox', function ($model) {
return '<input type="checkbox" name="ids[]" value="' . $model->public_id
$can_edit = Auth::user()->hasPermission('edit_all') || (isset($model->user_id) && Auth::user()->id == $model->user_id);
return !$can_edit?'':'<input type="checkbox" name="ids[]" value="' . $model->public_id
. '" ' . Utils::getEntityRowClass($model) . '>';
});
}
@ -45,6 +48,8 @@ class DatatableService
$hasAction = false;
$str = '<center style="min-width:100px">';
$can_edit = Auth::user()->hasPermission('edit_all') || (isset($model->user_id) && Auth::user()->id == $model->user_id);
if (property_exists($model, 'is_deleted') && $model->is_deleted) {
$str .= '<button type="button" class="btn btn-sm btn-danger tr-status">'.trans('texts.deleted').'</button>';
} elseif ($model->deleted_at && $model->deleted_at !== '0000-00-00') {
@ -70,9 +75,15 @@ class DatatableService
}
list($value, $url, $visible) = $action;
if ($visible($model)) {
$str .= "<li><a href=\"{$url($model)}\">{$value}</a></li>";
$lastIsDivider = false;
$hasAction = true;
if($value == '--divider--'){
$str .= "<li class=\"divider\"></li>";
$lastIsDivider = true;
}
else {
$str .= "<li><a href=\"{$url($model)}\">{$value}</a></li>";
$hasAction = true;
$lastIsDivider = false;
}
}
} elseif ( ! $lastIsDivider) {
$str .= "<li class=\"divider\"></li>";
@ -84,20 +95,20 @@ class DatatableService
return '';
}
if ( ! $lastIsDivider) {
if ( $can_edit && ! $lastIsDivider) {
$str .= "<li class=\"divider\"></li>";
}
if ($entityType != ENTITY_USER || $model->public_id) {
if (($entityType != ENTITY_USER || $model->public_id) && $can_edit) {
$str .= "<li><a href=\"javascript:archiveEntity({$model->public_id})\">"
. trans("texts.archive_{$entityType}") . "</a></li>";
}
} else {
} else if($can_edit) {
$str .= "<li><a href=\"javascript:restoreEntity({$model->public_id})\">"
. trans("texts.restore_{$entityType}") . "</a></li>";
}
if (property_exists($model, 'is_deleted') && !$model->is_deleted) {
if (property_exists($model, 'is_deleted') && !$model->is_deleted && $can_edit) {
$str .= "<li><a href=\"javascript:deleteEntity({$model->public_id})\">"
. trans("texts.delete_{$entityType}") . "</a></li>";
}

View File

@ -1,10 +1,13 @@
<?php namespace App\Services;
use Auth;
use DB;
use Utils;
use URL;
use App\Services\BaseService;
use App\Ninja\Repositories\ExpenseRepository;
use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Client;
use App\Models\Vendor;
@ -42,6 +45,10 @@ class ExpenseService extends BaseService
{
$query = $this->expenseRepo->find($search);
if(!Utils::hasPermission('view_all')){
$query->where('expenses.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_EXPENSE, $query);
}
@ -63,6 +70,10 @@ class ExpenseService extends BaseService
function ($model)
{
if ($model->vendor_public_id) {
if(!Vendor::canViewItemByOwner($model->vendor_user_id)){
return $model->vendor_name;
}
return link_to("vendors/{$model->vendor_public_id}", $model->vendor_name)->toHtml();
} else {
return '';
@ -74,6 +85,10 @@ class ExpenseService extends BaseService
function ($model)
{
if ($model->client_public_id) {
if(!Client::canViewItemByOwner($model->client_user_id)){
return Utils::getClientDisplayName($model);
}
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml();
} else {
return '';
@ -83,6 +98,10 @@ class ExpenseService extends BaseService
[
'expense_date',
function ($model) {
if(!Expense::canEditItemByOwner($model->user_id)){
return Utils::fromSqlDate($model->expense_date);
}
return link_to("expenses/{$model->public_id}/edit", Utils::fromSqlDate($model->expense_date))->toHtml();
}
],
@ -151,6 +170,9 @@ class ExpenseService extends BaseService
trans('texts.edit_expense'),
function ($model) {
return URL::to("expenses/{$model->public_id}/edit") ;
},
function ($model) {
return Expense::canEditItem($model);
}
],
[
@ -159,7 +181,7 @@ class ExpenseService extends BaseService
return URL::to("/invoices/{$model->invoice_public_id}/edit");
},
function ($model) {
return $model->invoice_public_id;
return $model->invoice_public_id && Invoice::canEditItemByOwner($model->invoice_user_id);
}
],
[
@ -168,7 +190,7 @@ class ExpenseService extends BaseService
return "javascript:invoiceEntity({$model->public_id})";
},
function ($model) {
return ! $model->invoice_id && (!$model->deleted_at || $model->deleted_at == '0000-00-00');
return ! $model->invoice_id && (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Invoice::canCreate();
}
],
];

View File

@ -8,6 +8,9 @@ use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\ClientRepository;
use App\Events\QuoteInvitationWasApproved;
use App\Models\Invitation;
use App\Models\Invoice;
use App\Models\Client;
use App\Models\Payment;
class InvoiceService extends BaseService
{
@ -27,14 +30,26 @@ class InvoiceService extends BaseService
return $this->invoiceRepo;
}
public function save($data)
public function save($data, $checkSubPermissions = false)
{
if (isset($data['client'])) {
$client = $this->clientRepo->save($data['client']);
$data['client_id'] = $client->id;
$can_save_client = !$checkSubPermissions;
if(!$can_save_client){
if(empty($data['client']['public_id']) || $data['client']['public_id']=='-1'){
$can_save_client = Client::canCreate();
}
else{
$can_save_client = Client::wherePublicId($data['client']['public_id'])->first()->canEdit();
}
}
if($can_save_client){
$client = $this->clientRepo->save($data['client']);
$data['client_id'] = $client->id;
}
}
$invoice = $this->invoiceRepo->save($data);
$invoice = $this->invoiceRepo->save($data, $checkSubPermissions);
$client = $invoice->client;
$client->load('contacts');
@ -109,6 +124,10 @@ class InvoiceService extends BaseService
$query = $this->invoiceRepo->getInvoices($accountId, $clientPublicId, $entityType, $search)
->where('invoices.is_quote', '=', $entityType == ENTITY_QUOTE ? true : false);
if(!Utils::hasPermission('view_all')){
$query->where('invoices.user_id', '=', Auth::user()->id);
}
return $this->createDatatable($entityType, $query, !$clientPublicId);
}
@ -118,12 +137,19 @@ class InvoiceService extends BaseService
[
'invoice_number',
function ($model) use ($entityType) {
if(!Invoice::canEditItem($model)){
return $model->invoice_number;
}
return link_to("{$entityType}s/{$model->public_id}/edit", $model->invoice_number, ['class' => Utils::getEntityRowClass($model)])->toHtml();
}
],
[
'client_name',
function ($model) {
if(!Client::canViewItemByOwner($model->client_user_id)){
return Utils::getClientDisplayName($model);
}
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml();
},
! $hideClient
@ -174,12 +200,18 @@ class InvoiceService extends BaseService
trans("texts.edit_{$entityType}"),
function ($model) use ($entityType) {
return URL::to("{$entityType}s/{$model->public_id}/edit");
},
function ($model) {
return Invoice::canEditItem($model);
}
],
[
trans("texts.clone_{$entityType}"),
function ($model) use ($entityType) {
return URL::to("{$entityType}s/{$model->public_id}/clone");
},
function ($model) {
return Invoice::canCreate();
}
],
[
@ -188,14 +220,19 @@ class InvoiceService extends BaseService
return URL::to("{$entityType}s/{$entityType}_history/{$model->public_id}");
}
],
[],
[
'--divider--', function(){return false;},
function ($model) {
return Invoice::canEditItem($model) || Payment::canCreate();
}
],
[
trans("texts.mark_sent"),
function ($model) {
return "javascript:markEntity({$model->public_id})";
},
function ($model) {
return $model->invoice_status_id < INVOICE_STATUS_SENT;
return $model->invoice_status_id < INVOICE_STATUS_SENT && Invoice::canEditItem($model);
}
],
[
@ -204,7 +241,7 @@ class InvoiceService extends BaseService
return URL::to("payments/create/{$model->client_public_id}/{$model->public_id}");
},
function ($model) use ($entityType) {
return $entityType == ENTITY_INVOICE && $model->balance > 0;
return $entityType == ENTITY_INVOICE && $model->balance > 0 && Payment::canCreate();
}
],
[
@ -213,7 +250,7 @@ class InvoiceService extends BaseService
return URL::to("quotes/{$model->quote_id}/edit");
},
function ($model) use ($entityType) {
return $entityType == ENTITY_INVOICE && $model->quote_id;
return $entityType == ENTITY_INVOICE && $model->quote_id && Invoice::canEditItem($model);
}
],
[
@ -222,7 +259,7 @@ class InvoiceService extends BaseService
return URL::to("invoices/{$model->quote_invoice_id}/edit");
},
function ($model) use ($entityType) {
return $entityType == ENTITY_QUOTE && $model->quote_invoice_id;
return $entityType == ENTITY_QUOTE && $model->quote_invoice_id && Invoice::canEditItem($model);
}
],
[
@ -231,7 +268,7 @@ class InvoiceService extends BaseService
return "javascript:convertEntity({$model->public_id})";
},
function ($model) use ($entityType) {
return $entityType == ENTITY_QUOTE && ! $model->quote_invoice_id;
return $entityType == ENTITY_QUOTE && ! $model->quote_invoice_id && Invoice::canEditItem($model);
}
]
];

View File

@ -1,6 +1,7 @@
<?php namespace App\Services;
use Utils;
use Auth;
use URL;
use DateTime;
use Event;
@ -10,6 +11,8 @@ use CreditCard;
use App\Models\Payment;
use App\Models\Account;
use App\Models\Country;
use App\Models\Client;
use App\Models\Invoice;
use App\Models\AccountGatewayToken;
use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\AccountRepository;
@ -286,6 +289,10 @@ class PaymentService extends BaseService
{
$query = $this->paymentRepo->find($clientPublicId, $search);
if(!Utils::hasPermission('view_all')){
$query->where('payments.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_PAYMENT, $query, !$clientPublicId);
}
@ -295,12 +302,20 @@ class PaymentService extends BaseService
[
'invoice_number',
function ($model) {
if(!Invoice::canEditItemByOwner($model->invoice_user_id)){
return $model->invoice_number;
}
return link_to("invoices/{$model->invoice_public_id}/edit", $model->invoice_number, ['class' => Utils::getEntityRowClass($model)])->toHtml();
}
],
[
'client_name',
function ($model) {
if(!Client::canViewItemByOwner($model->client_user_id)){
return Utils::getClientDisplayName($model);
}
return $model->client_public_id ? link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml() : '';
},
! $hideClient
@ -339,6 +354,9 @@ class PaymentService extends BaseService
trans('texts.edit_payment'),
function ($model) {
return URL::to("payments/{$model->public_id}/edit");
},
function ($model) {
return Payment::canEditItem($model);
}
]
];

View File

@ -1,7 +1,9 @@
<?php namespace App\Services;
use URL;
use Auth;
use Utils;
use App\Models\Invoice;
use App\Ninja\Repositories\InvoiceRepository;
class RecurringInvoiceService extends BaseService
@ -19,6 +21,10 @@ class RecurringInvoiceService extends BaseService
{
$query = $this->invoiceRepo->getRecurringInvoices($accountId, $clientPublicId, $search);
if(!Utils::hasPermission('view_all')){
$query->where('invoices.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_RECURRING_INVOICE, $query, !$clientPublicId);
}
@ -66,6 +72,9 @@ class RecurringInvoiceService extends BaseService
trans('texts.edit_invoice'),
function ($model) {
return URL::to("invoices/{$model->public_id}/edit");
},
function ($model) {
return Invoice::canEditItem($model);
}
]
];

View File

@ -1,8 +1,11 @@
<?php namespace App\Services;
use Auth;
use URL;
use Utils;
use App\Models\Task;
use App\Models\Invoice;
use App\Models\Client;
use App\Ninja\Repositories\TaskRepository;
use App\Services\BaseService;
@ -33,6 +36,10 @@ class TaskService extends BaseService
{
$query = $this->taskRepo->find($clientPublicId, $search);
if(!Utils::hasPermission('view_all')){
$query->where('tasks.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_TASK, $query, !$clientPublicId);
}
@ -42,6 +49,10 @@ class TaskService extends BaseService
[
'client_name',
function ($model) {
if(!Client::canViewItemByOwner($model->client_user_id)){
return Utils::getClientDisplayName($model);
}
return $model->client_public_id ? link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml() : '';
},
! $hideClient
@ -82,7 +93,7 @@ class TaskService extends BaseService
return URL::to('tasks/'.$model->public_id.'/edit');
},
function ($model) {
return !$model->deleted_at || $model->deleted_at == '0000-00-00';
return (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Task::canEditItem($model);
}
],
[
@ -91,7 +102,7 @@ class TaskService extends BaseService
return URL::to("/invoices/{$model->invoice_public_id}/edit");
},
function ($model) {
return $model->invoice_number;
return $model->invoice_number && Invoice::canEditItemByOwner($model->invoice_user_id);
}
],
[
@ -100,7 +111,7 @@ class TaskService extends BaseService
return "javascript:stopTask({$model->public_id})";
},
function ($model) {
return $model->is_running;
return $model->is_running && Task::canEditItem($model);
}
],
[
@ -109,7 +120,7 @@ class TaskService extends BaseService
return "javascript:invoiceEntity({$model->public_id})";
},
function ($model) {
return ! $model->invoice_number && (!$model->deleted_at || $model->deleted_at == '0000-00-00');
return ! $model->invoice_number && (!$model->deleted_at || $model->deleted_at == '0000-00-00') && Invoice::canCreate();
}
]
];

View File

@ -53,11 +53,15 @@ class UserService extends BaseService
'confirmed',
function ($model) {
if (!$model->public_id) {
return self::getStatusLabel(USER_STATE_ADMIN);
return self::getStatusLabel(USER_STATE_OWNER);
} elseif ($model->deleted_at) {
return self::getStatusLabel(USER_STATE_DISABLED);
} elseif ($model->confirmed) {
return self::getStatusLabel(USER_STATE_ACTIVE);
if($model->is_admin){
return self::getStatusLabel(USER_STATE_ADMIN);
} else {
return self::getStatusLabel(USER_STATE_ACTIVE);
}
} else {
return self::getStatusLabel(USER_STATE_PENDING);
}
@ -96,17 +100,20 @@ class UserService extends BaseService
$class = 'default';
switch ($state) {
case USER_STATE_PENDING:
$class = 'info';
$class = 'default';
break;
case USER_STATE_ACTIVE:
$class = 'primary';
$class = 'info';
break;
case USER_STATE_DISABLED:
$class = 'warning';
break;
case USER_STATE_ADMIN:
case USER_STATE_OWNER:
$class = 'success';
break;
case USER_STATE_ADMIN:
$class = 'primary';
break;
}
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
}

View File

@ -3,6 +3,8 @@
use Utils;
use URL;
use Auth;
use App\Models\Vendor;
use App\Models\Expense;
use App\Services\BaseService;
use App\Ninja\Repositories\VendorRepository;
use App\Ninja\Repositories\NinjaRepository;
@ -36,6 +38,10 @@ class VendorService extends BaseService
public function getDatatable($search)
{
$query = $this->vendorRepo->find($search);
if(!Utils::hasPermission('view_all')){
$query->where('vendors.user_id', '=', Auth::user()->id);
}
return $this->createDatatable(ENTITY_VENDOR, $query);
}
@ -83,13 +89,25 @@ class VendorService extends BaseService
trans('texts.edit_vendor'),
function ($model) {
return URL::to("vendors/{$model->public_id}/edit");
},
function ($model) {
return Vendor::canEditItem($model);
}
],
[],
[
'--divider--', function(){return false;},
function ($model) {
return Vendor::canEditItem($model) && Expense::canCreate();
}
],
[
trans('texts.enter_expense'),
function ($model) {
return URL::to("expenses/create/{$model->public_id}");
},
function ($model) {
return Expense::canCreate();
}
]
];

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddUserPermissions extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function($table) {
$table->boolean('is_admin')->default(true);
$table->unsignedInteger('permissions')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function($table) {
$table->dropColumn('is_admin');
$table->dropColumn('permissions');
});
}
}

View File

@ -54,6 +54,7 @@ class CurrenciesSeeder extends Seeder
['name' => 'Croatian Kuna', 'code' => 'HKR', 'symbol' => 'kn', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => ','],
['name' => 'Saudi Riyal', 'code' => 'SAR', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Japanese Yen', 'code' => 'JPY', 'symbol' => '¥', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['name' => 'Maldivian Rufiyaa', 'code' => 'MVR', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
];
foreach ($currencies as $currency) {

View File

@ -30487,6 +30487,7 @@ function calculateAmounts(invoice) {
for (var i=0; i<invoice.invoice_items.length; i++) {
var item = invoice.invoice_items[i];
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
lineTotal = roundToTwo(lineTotal);
if (lineTotal) {
total += lineTotal;
}
@ -31182,7 +31183,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
var value = getDescendantProp(invoice, field);
if (match.indexOf('?') < 0 || value) {
if (invoice.partial && field == 'balance_due') {
field = 'amount_due';
field = 'partial_due';
} else if (invoice.is_quote) {
field = field.replace('invoice', 'quote');
}
@ -31439,12 +31440,22 @@ NINJA.subtotals = function(invoice, hideBalance)
data.push([{text:invoiceLabels.paid_to_date}, {text:formatMoneyInvoice(paid, invoice)}]);
}
if (!hideBalance) {
var isPartial = NINJA.parseFloat(invoice.partial);
var isPartial = NINJA.parseFloat(invoice.partial);
if (!hideBalance || isPartial) {
data.push([
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style:['balanceDue']}
{ text: invoiceLabels.balance_due, style: [isPartial ? '' : 'balanceDueLabel'] },
{ text: formatMoneyInvoice(invoice.total_amount, invoice), style: [isPartial ? '' : 'balanceDue'] }
]);
}
if (!hideBalance) {
if (isPartial) {
data.push([
{ text: invoiceLabels.partial_due, style: ['balanceDueLabel'] },
{ text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['balanceDue'] }
]);
}
}
return NINJA.prepareDataPairs(data, 'subtotals');
@ -31453,7 +31464,7 @@ NINJA.subtotals = function(invoice, hideBalance)
NINJA.subtotalsBalance = function(invoice) {
var isPartial = NINJA.parseFloat(invoice.partial);
return [[
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style:['balanceDue']}
]];
}
@ -31531,18 +31542,18 @@ NINJA.invoiceDetails = function(invoice) {
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
data.push([
{text: invoiceLabels.total},
{text: invoiceLabels.balance_due},
{text: formatMoneyInvoice(invoice.amount, invoice)}
]);
} else if (isPartial) {
data.push([
{text: invoiceLabels.total},
{text: invoiceLabels.balance_due},
{text: formatMoneyInvoice(invoice.total_amount, invoice)}
]);
}
data.push([
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']}
])

View File

@ -190,7 +190,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
var value = getDescendantProp(invoice, field);
if (match.indexOf('?') < 0 || value) {
if (invoice.partial && field == 'balance_due') {
field = 'amount_due';
field = 'partial_due';
} else if (invoice.is_quote) {
field = field.replace('invoice', 'quote');
}
@ -447,12 +447,22 @@ NINJA.subtotals = function(invoice, hideBalance)
data.push([{text:invoiceLabels.paid_to_date}, {text:formatMoneyInvoice(paid, invoice)}]);
}
if (!hideBalance) {
var isPartial = NINJA.parseFloat(invoice.partial);
var isPartial = NINJA.parseFloat(invoice.partial);
if (!hideBalance || isPartial) {
data.push([
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style:['balanceDue']}
{ text: invoiceLabels.balance_due, style: [isPartial ? '' : 'balanceDueLabel'] },
{ text: formatMoneyInvoice(invoice.total_amount, invoice), style: [isPartial ? '' : 'balanceDue'] }
]);
}
if (!hideBalance) {
if (isPartial) {
data.push([
{ text: invoiceLabels.partial_due, style: ['balanceDueLabel'] },
{ text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['balanceDue'] }
]);
}
}
return NINJA.prepareDataPairs(data, 'subtotals');
@ -461,7 +471,7 @@ NINJA.subtotals = function(invoice, hideBalance)
NINJA.subtotalsBalance = function(invoice) {
var isPartial = NINJA.parseFloat(invoice.partial);
return [[
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style:['balanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style:['balanceDue']}
]];
}
@ -539,18 +549,18 @@ NINJA.invoiceDetails = function(invoice) {
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
data.push([
{text: invoiceLabels.total},
{text: invoiceLabels.balance_due},
{text: formatMoneyInvoice(invoice.amount, invoice)}
]);
} else if (isPartial) {
data.push([
{text: invoiceLabels.total},
{text: invoiceLabels.balance_due},
{text: formatMoneyInvoice(invoice.total_amount, invoice)}
]);
}
data.push([
{text: isPartial ? invoiceLabels.amount_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']}
])

View File

@ -595,6 +595,7 @@ function calculateAmounts(invoice) {
for (var i=0; i<invoice.invoice_items.length; i++) {
var item = invoice.invoice_items[i];
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
lineTotal = roundToTwo(lineTotal);
if (lineTotal) {
total += lineTotal;
}

View File

@ -1065,6 +1065,17 @@ $LANG = array(
'cost' => 'Cost',
'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.',
// User Permissions
'owner' => 'Owner',
'administrator' => 'Administrator',
'administrator_help' => 'Allow user to manage users, change settings, and view and modify all data',
'user_create_all' => 'Create clients, invoices, etc.',
'user_view_all' => 'View all clients, invoices, etc.',
'user_edit_all' => 'Edit all clients, invoices, etc.',
'gateway_help_20' => ':link to sign up for Sage Pay.',
'gateway_help_21' => ':link to sign up for Sage Pay.',
'partial_due' => 'Partial Due',
);
return $LANG;

View File

@ -52,6 +52,15 @@
@foreach ($gateways as $gateway)
<div id="gateway_{{ $gateway->id }}_div" class='gateway-fields' style="display: none">
@if ($gateway->getHelp())
<div class="form-group">
<label class="control-label col-lg-4 col-sm-4"></label>
<div class="col-lg-8 col-sm-8 help-block">
{!! $gateway->getHelp() !!}
</div>
</div>
@endif
@foreach ($gateway->fields as $field => $details)
@if ($details && !$accountGateway)
@ -73,15 +82,6 @@
@endforeach
@if ($gateway->getHelp())
<div class="form-group">
<label class="control-label col-lg-4 col-sm-4"></label>
<div class="col-lg-8 col-sm-8 help-block">
{!! $gateway->getHelp() !!}
</div>
</div>
@endif
@if ($gateway->id == GATEWAY_STRIPE)
{!! Former::text('publishable_key') !!}

View File

@ -60,7 +60,16 @@
@else
NINJA.headerFont = NINJA.bodyFont = 'Roboto';
@endif
var fields = ['item', 'description', 'unit_cost', 'quantity', 'line_total', 'terms'];
var fields = [
'item',
'description',
'unit_cost',
'quantity',
'line_total',
'terms',
'balance_due',
'partial_due'
];
invoiceLabels.old = {};
for (var i=0; i<fields.length; i++) {
var field = fields[i];
@ -181,11 +190,13 @@
{!! Former::text('labels_item')->label(trans('texts.item')) !!}
{!! Former::text('labels_description')->label(trans('texts.description')) !!}
{!! Former::text('labels_unit_cost')->label(trans('texts.unit_cost')) !!}
{!! Former::text('labels_quantity')->label(trans('texts.quantity')) !!}
</div>
<div class="col-md-6">
{!! Former::text('labels_quantity')->label(trans('texts.quantity')) !!}
{!! Former::text('labels_line_total')->label(trans('texts.line_total')) !!}
{!! Former::text('labels_terms')->label(trans('texts.terms')) !!}
{!! Former::text('labels_balance_due')->label(trans('texts.balance_due')) !!}
{!! Former::text('labels_partial_due')->label(trans('texts.partial_due')) !!}
</div>
</div>

View File

@ -21,7 +21,9 @@
{{ Former::populateField('referral_code', true) }}
@endif
@include('accounts.nav', ['selected' => ACCOUNT_USER_DETAILS])
@if (Utils::isAdmin())
@include('accounts.nav', ['selected' => ACCOUNT_USER_DETAILS])
@endif
<div class="row">
<div class="col-md-12">

View File

@ -43,8 +43,11 @@
@endif
@if ($client->trashed())
{!! Button::primary(trans('texts.restore_client'))->withAttributes(['onclick' => 'onRestoreClick()']) !!}
@if ($client->canEdit())
{!! Button::primary(trans('texts.restore_client'))->withAttributes(['onclick' => 'onRestoreClick()']) !!}
@endif
@else
@if ($client->canEdit())
{!! DropdownButton::normal(trans('texts.edit_client'))
->withAttributes(['class'=>'normalDropDown'])
->withContents([
@ -52,10 +55,12 @@
['label' => trans('texts.delete_client'), 'url' => "javascript:onDeleteClick()"],
]
)->split() !!}
{!! DropdownButton::primary(trans('texts.new_invoice'))
->withAttributes(['class'=>'primaryDropDown'])
->withContents($actionLinks)->split() !!}
@endif
@if (\App\Models\Invoice::canCreate())
{!! DropdownButton::primary(trans('texts.new_invoice'))
->withAttributes(['class'=>'primaryDropDown'])
->withContents($actionLinks)->split() !!}
@endif
@endif
{!! Former::close() !!}

View File

@ -116,7 +116,11 @@
@foreach ($payments as $payment)
<tr>
<td>{!! \App\Models\Invoice::calcLink($payment) !!}</td>
<td>{!! link_to('/clients/'.$payment->client_public_id, trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email)) !!}</td>
@if (\App\Models\Client::canViewItemByOwner($payment->client_user_id))
<td>{!! link_to('/clients/'.$payment->client_public_id, trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email)) !!}</td>
@else
<td>{{ trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email) }}</td>
@endif
<td>{{ Utils::fromSqlDate($payment->payment_date) }}</td>
<td>{{ Utils::formatMoney($payment->amount, $payment->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}</td>
</tr>
@ -149,7 +153,11 @@
@if (!$invoice->is_quote)
<tr>
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
<td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
@if (\App\Models\Client::canViewItemByOwner($payment->client_user_id))
<td>{!! link_to('/clients/'.$payment->client_public_id, trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email)) !!}</td>
@else
<td>{{ trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email) }}</td>
@endif
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}</td>
</tr>
@ -160,7 +168,7 @@
</div>
</div>
</div>
<div class="col-md-6">
<div class="col-md-6">
<div class="panel panel-default dashboard" style="height:320px">
<div class="panel-heading" style="background-color:#e37329 !important">
<h3 class="panel-title in-bold-white">
@ -180,7 +188,11 @@
@if (!$invoice->is_quote)
<tr>
<td>{!! \App\Models\Invoice::calcLink($invoice) !!}</td>
<td>{!! link_to('/clients/'.$invoice->client_public_id, trim($invoice->client_name) ?: (trim($invoice->first_name . ' ' . $invoice->last_name) ?: $invoice->email)) !!}</td>
@if (\App\Models\Client::canViewItemByOwner($payment->client_user_id))
<td>{!! link_to('/clients/'.$payment->client_public_id, trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email)) !!}</td>
@else
<td>{{ trim($payment->client_name) ?: (trim($payment->first_name . ' ' . $payment->last_name) ?: $payment->email) }}</td>
@endif
<td>{{ Utils::fromSqlDate($invoice->due_date) }}</td>
<td>{{ Utils::formatMoney($invoice->balance, $invoice->currency_id ?: ($account->currency_id ?: DEFAULT_CURRENCY)) }}</td>
</tr>

View File

@ -401,6 +401,7 @@
<span class="icon-bar"></span>
</button>
<a href="{{ URL::to(NINJA_WEB_URL) }}" class='navbar-brand' target="_blank">
{{-- Per our license, please do not remove or modify this link. --}}
<img src="{{ asset('images/invoiceninja-logo.png') }}" style="height:20px;width:auto;padding-right:10px"/>
</a>
</div>
@ -472,11 +473,13 @@
'selected' => true,
])
@endif
<li class="divider"></li>
@if (count(session(SESSION_USER_ACCOUNTS)) > 1)
<li>{!! link_to('/manage_companies', trans('texts.manage_companies')) !!}</li>
@elseif (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5)
<li>{!! link_to('/login?new_company=true', trans('texts.add_company')) !!}</li>
<li class="divider"></li>
@if (Utils::isAdmin())
@if (count(session(SESSION_USER_ACCOUNTS)) > 1)
<li>{!! link_to('/manage_companies', trans('texts.manage_companies')) !!}</li>
@elseif (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5)
<li>{!! link_to('/login?new_company=true', trans('texts.add_company')) !!}</li>
@endif
@endif
<li>{!! link_to('#', trans('texts.logout'), array('onclick'=>'logout()')) !!}</li>
</ul>
@ -486,15 +489,21 @@
<ul class="nav navbar-nav navbar-right navbar-settings">
<li class="dropdown">
<a href="{{ URL::to('/settings') }}" class="dropdown-toggle">
<span class="glyphicon glyphicon-cog" title="{{ trans('texts.settings') }}"/>
</a>
<ul class="dropdown-menu">
@foreach (\App\Models\Account::$basicSettings as $setting)
<li>{!! link_to('settings/' . $setting, uctrans("texts.{$setting}")) !!}</li>
@endforeach
<li><a href="{{ url('settings/' . ACCOUNT_INVOICE_SETTINGS) }}">{!! uctrans('texts.advanced_settings') . Utils::getProLabel(ACCOUNT_ADVANCED_SETTINGS) !!}</a></li>
</ul>
@if (Utils::isAdmin())
<a href="{{ URL::to('/settings') }}" class="dropdown-toggle">
<span class="glyphicon glyphicon-cog" title="{{ trans('texts.settings') }}"/>
</a>
<ul class="dropdown-menu">
@foreach (\App\Models\Account::$basicSettings as $setting)
<li>{!! link_to('settings/' . $setting, uctrans("texts.{$setting}")) !!}</li>
@endforeach
<li><a href="{{ url('settings/' . ACCOUNT_INVOICE_SETTINGS) }}">{!! uctrans('texts.advanced_settings') . Utils::getProLabel(ACCOUNT_ADVANCED_SETTINGS) !!}</a></li>
</ul>
@else
<a href="{{ URL::to('/settings/user_details') }}" class="dropdown-toggle">
<span class="glyphicon glyphicon-user" title="{{ trans('texts.settings') }}"/>
</a>
@endif
</li>
</ul>

View File

@ -63,14 +63,19 @@
<label for="client" class="control-label col-lg-4 col-sm-4">{{ trans('texts.client') }}</label>
<div class="col-lg-8 col-sm-8">
<h4><div data-bind="text: getClientDisplayName(ko.toJS(client()))"></div></h4>
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ trans('texts.edit_client') }}</a> |
{!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!}
@if($invoice->client->canView())
@if ($invoice->client->canEdit())
<a id="editClientLink" class="pointer" data-bind="click: $root.showClientForm">{{ trans('texts.edit_client') }}</a> |
@endif
{!! link_to('/clients/'.$invoice->client->public_id, trans('texts.view_client'), ['target' => '_blank']) !!}
@endif
</div>
</div>
<div style="display:none">
@endif
{!! Former::select('client')->addOption('', '')->data_bind("dropdown: client")->addClass('client-input')->addGroupClass('client_select closer-row') !!}
{!! Former::select('client')->addOption('', '')->data_bind("dropdown: client")->addClass('client-input')->addGroupClass('client_select closer-row') !!}
<div class="form-group" style="margin-bottom: 8px">
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
@ -380,11 +385,18 @@
</tr>
@endif
<tr style="font-size:1.05em">
<tr data-bind="style: { 'font-weight': partial() ? 'normal' : 'bold', 'font-size': partial() ? '1em' : '1.05em' }">
<td class="hide-border" colspan="3"/>
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td class="hide-border" colspan="{{ $account->hide_quantity ? 1 : 2 }}"><b>{{ trans($entityType == ENTITY_INVOICE ? 'texts.balance_due' : 'texts.total') }}</b></td>
<td class="hide-border" style="text-align: right"><span data-bind="text: totals.total"></span></td>
<td class="hide-border" data-bind="css: {'hide-border': !partial()}" colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $entityType == ENTITY_INVOICE ? $invoiceLabels['balance_due'] : trans('texts.total') }}</td>
<td class="hide-border" data-bind="css: {'hide-border': !partial()}" style="text-align: right"><span data-bind="text: totals.total"></span></td>
</tr>
<tr style="font-size:1.05em; display:none; font-weight:bold" data-bind="visible: partial">
<td class="hide-border" colspan="3"/>
<td class="hide-border" style="display:none" data-bind="visible: $root.invoice_item_taxes.show"/>
<td class="hide-border" colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $invoiceLabels['partial_due'] }}</td>
<td class="hide-border" style="text-align: right"><span data-bind="text: totals.partial"></span></td>
</tr>
</tfoot>

View File

@ -512,7 +512,11 @@ function InvoiceModel(data) {
});
self.totals.total = ko.computed(function() {
return self.formatMoney(self.partial() ? self.partial() : self.totals.rawTotal());
return self.formatMoney(self.totals.rawTotal());
});
self.totals.partial = ko.computed(function() {
return self.formatMoney(self.partial());
});
self.onDragged = function(item) {

View File

@ -9,12 +9,14 @@
{!! Former::text('public_id') !!}
</div>
@if ($entityType == ENTITY_TASK)
{!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!}
@endif
@if ($entityType == ENTITY_EXPENSE)
{!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!}
@endif
@if (\App\Models\Invoice::canCreate())
@if ($entityType == ENTITY_TASK)
{!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!}
@endif
@if ($entityType == ENTITY_EXPENSE)
{!! Button::primary(trans('texts.invoice'))->withAttributes(['class'=>'invoice', 'onclick' =>'submitForm("invoice")'])->appendIcon(Icon::create('check')) !!}
@endif
@endif
{!! DropdownButton::normal(trans('texts.archive'))->withContents([
['label' => trans('texts.archive_'.$entityType), 'url' => 'javascript:submitForm("archive")'],
@ -38,7 +40,9 @@
{!! Button::normal(trans('texts.credits'))->asLinkTo(URL::to('/credits'))->appendIcon(Icon::create('list')) !!}
@endif
{!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!}
@if (Auth::user()->hasPermission('create_all'))
{!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!}
@endif
</div>

View File

@ -1,8 +1,12 @@
<li style="margin-top: 4px; margin-bottom: 4px; min-width: 220px; cursor: pointer">
@if (isset($user_id) && $user_id != Auth::user()->id)
<a href="{{ URL::to("/switch_account/{$user_id}") }}">
@else
<a href="{{ URL::to("/settings/company_details") }}">
@if (Utils::isAdmin())
@if (isset($user_id) && $user_id != Auth::user()->id)
<a href="{{ URL::to("/switch_account/{$user_id}") }}">
@else
<a href="{{ URL::to("/settings/company_details") }}">
@endif
@else
<a href="{{ URL::to("/settings/user_details") }}">
@endif
@if (file_exists($logo_path))
@ -23,7 +27,6 @@
@if (isset($selected) && $selected)
</b>
@endif
</a>
</li>

View File

@ -23,7 +23,26 @@
{!! Former::text('first_name') !!}
{!! Former::text('last_name') !!}
{!! Former::text('email') !!}
{!! Former::checkbox('is_admin')
->label('&nbsp;')
->text(trans('texts.administrator'))
->help(trans('texts.administrator_help')) !!}
{!! Former::checkbox('permissions[create_all]')
->value('create_all')
->label('&nbsp;')
->id('permissions_create_all')
->text(trans('texts.user_create_all')) !!}
{!! Former::checkbox('permissions[view_all]')
->value('view_all')
->label('&nbsp;')
->id('permissions_view_all')
->text(trans('texts.user_view_all')) !!}
{!! Former::checkbox('permissions[edit_all]')
->value('edit_all')
->label('&nbsp;')
->id('permissions_edit_all')
->text(trans('texts.user_edit_all')) !!}
</div>
</div>
@ -38,4 +57,15 @@
@section('onReady')
$('#first_name').focus();
$('#is_admin, #permissions_view_all').change(fixCheckboxes);
function fixCheckboxes(){
var adminChecked = $('#is_admin').is(':checked');
var viewChecked = $('#permissions_view_all').is(':checked');
$('#permissions_view_all').prop('disabled', adminChecked);
$('#permissions_create_all').prop('disabled', adminChecked);
$('#permissions_edit_all').prop('disabled', adminChecked || !viewChecked);
if(!viewChecked)$('#permissions_edit_all').prop('checked',false)
}
fixCheckboxes();
@stop