mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 16:14:28 -04:00
Merge branch 'release-2.5.1'
Conflicts: app/Ninja/Mailers/ContactMailer.php
This commit is contained in:
commit
a61f5cc98a
22
.env.example
22
.env.example
@ -20,11 +20,27 @@ MAIL_FROM_ADDRESS
|
||||
MAIL_FROM_NAME
|
||||
MAIL_PASSWORD
|
||||
|
||||
#POSTMARK_API_TOKEN=
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
LOG=single
|
||||
REQUIRE_HTTPS=false
|
||||
API_SECRET=password
|
||||
|
||||
GOOGLE_CLIENT_ID
|
||||
GOOGLE_CLIENT_SECRET
|
||||
GOOGLE_OAUTH_REDIRECT=http://ninja.dev/auth/google
|
||||
#TRUSTED_PROXIES=
|
||||
|
||||
#SESSION_DRIVER=
|
||||
#SESSION_DOMAIN=
|
||||
#SESSION_ENCRYPT=
|
||||
#SESSION_SECURE=
|
||||
|
||||
#CACHE_DRIVER=
|
||||
#CACHE_HOST=
|
||||
#CACHE_PORT1=
|
||||
#CACHE_PORT2=
|
||||
|
||||
#GOOGLE_CLIENT_ID=
|
||||
#GOOGLE_CLIENT_SECRET=
|
||||
#GOOGLE_OAUTH_REDIRECT=http://ninja.dev/auth/google
|
||||
|
||||
#GOOGLE_MAPS_API_KEY=
|
25
.travis.yml
25
.travis.yml
@ -3,7 +3,7 @@ language: php
|
||||
sudo: true
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.5.9
|
||||
# - 5.6
|
||||
# - 7.0
|
||||
# - hhvm
|
||||
@ -34,7 +34,6 @@ install:
|
||||
# these providers require referencing git commit's which cause Travis to fail
|
||||
- sed -i '/mollie/d' composer.json
|
||||
- sed -i '/2checkout/d' composer.json
|
||||
- sed -i '/omnipay-neteller/d' composer.json
|
||||
- travis_retry composer install --prefer-dist;
|
||||
|
||||
before_script:
|
||||
@ -65,16 +64,18 @@ before_script:
|
||||
|
||||
script:
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance AllPagesCept.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance APICest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance CheckBalanceCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance ClientCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance CreditCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceDesignCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run acceptance OnlinePaymentCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
|
||||
#- php ./vendor/codeception/codeception/codecept run --debug acceptance TaxRatesCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance APICest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance CheckBalanceCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance ClientCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance ExpenseCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance CreditCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance QuoteCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceDesignCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run acceptance OnlinePaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaxRatesCest.php
|
||||
|
||||
#- sed -i 's/NINJA_DEV=true/NINJA_PROD=true/g' .env
|
||||
#- php ./vendor/codeception/codeception/codecept run acceptance GoProCest.php
|
||||
|
@ -95,19 +95,19 @@ module.exports = function(grunt) {
|
||||
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.no.min.js',
|
||||
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.es.min.js',
|
||||
'public/vendor/bootstrap-datepicker/dist/locales/bootstrap-datepicker.sv.min.js',
|
||||
'public/vendor/typeahead.js/dist/typeahead.min.js',
|
||||
'public/vendor/typeahead.js/dist/typeahead.jquery.min.js',
|
||||
'public/vendor/accounting/accounting.min.js',
|
||||
'public/vendor/spectrum/spectrum.js',
|
||||
'public/vendor/jspdf/dist/jspdf.min.js',
|
||||
'public/vendor/moment/min/moment.min.js',
|
||||
'public/vendor/moment-timezone/builds/moment-timezone-with-data.min.js',
|
||||
'public/vendor/stacktrace-js/dist/stacktrace-with-polyfills.min.js',
|
||||
'public/vendor/fuse.js/src/fuse.min.js',
|
||||
//'public/vendor/moment-duration-format/lib/moment-duration-format.js',
|
||||
//'public/vendor/handsontable/dist/jquery.handsontable.full.min.js',
|
||||
//'public/vendor/pdfmake/build/pdfmake.min.js',
|
||||
//'public/vendor/pdfmake/build/vfs_fonts.js',
|
||||
//'public/js/vfs_fonts.js',
|
||||
'public/js/lightbox.min.js',
|
||||
'public/js/bootstrap-combobox.js',
|
||||
'public/js/script.js',
|
||||
'public/js/pdf.pdfmake.js',
|
||||
@ -140,7 +140,6 @@ module.exports = function(grunt) {
|
||||
'public/vendor/spectrum/spectrum.css',
|
||||
'public/css/bootstrap-combobox.css',
|
||||
'public/css/typeahead.js-bootstrap.css',
|
||||
'public/css/lightbox.css',
|
||||
//'public/vendor/handsontable/dist/jquery.handsontable.full.css',
|
||||
'public/css/style.css',
|
||||
],
|
||||
|
2
LICENSE
2
LICENSE
@ -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
|
||||
|
63
app/Console/Commands/ChargeRenewalInvoices.php
Normal file
63
app/Console/Commands/ChargeRenewalInvoices.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Services\PaymentService;
|
||||
use App\Models\Invoice;
|
||||
|
||||
class ChargeRenewalInvoices extends Command
|
||||
{
|
||||
protected $name = 'ninja:charge-renewals';
|
||||
protected $description = 'Charge renewal invoices';
|
||||
|
||||
protected $mailer;
|
||||
protected $accountRepo;
|
||||
protected $paymentService;
|
||||
|
||||
public function __construct(Mailer $mailer, AccountRepository $repo, PaymentService $paymentService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->accountRepo = $repo;
|
||||
$this->paymentService = $paymentService;
|
||||
}
|
||||
|
||||
public function fire()
|
||||
{
|
||||
$this->info(date('Y-m-d').' ChargeRenewalInvoices...');
|
||||
|
||||
$account = $this->accountRepo->getNinjaAccount();
|
||||
$invoices = Invoice::whereAccountId($account->id)
|
||||
->whereDueDate(date('Y-m-d'))
|
||||
->with('client')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$this->info(count($invoices).' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
$this->info("Charging invoice {$invoice->invoice_number}");
|
||||
$this->paymentService->autoBillInvoice($invoice);
|
||||
}
|
||||
|
||||
$this->info('Done');
|
||||
}
|
||||
|
||||
protected function getArguments()
|
||||
{
|
||||
return array(
|
||||
//array('example', InputArgument::REQUIRED, 'An example argument.'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return array(
|
||||
//array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null),
|
||||
);
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ class SendRenewalInvoices extends Command
|
||||
}
|
||||
|
||||
$client = $this->accountRepo->getNinjaClient($account);
|
||||
$invitation = $this->accountRepo->createNinjaInvoice($client);
|
||||
$invitation = $this->accountRepo->createNinjaInvoice($client, $account);
|
||||
|
||||
// set the due date to 10 days from now
|
||||
$invoice = $invitation->invoice;
|
||||
|
@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel
|
||||
'App\Console\Commands\ResetData',
|
||||
'App\Console\Commands\CheckData',
|
||||
'App\Console\Commands\SendRenewalInvoices',
|
||||
'App\Console\Commands\ChargeRenewalInvoices',
|
||||
'App\Console\Commands\SendReminders',
|
||||
'App\Console\Commands\TestOFX',
|
||||
'App\Console\Commands\GenerateResources',
|
||||
|
@ -47,13 +47,16 @@ class Handler extends ExceptionHandler {
|
||||
if ($e instanceof ModelNotFoundException) {
|
||||
return Redirect::to('/');
|
||||
} elseif ($e instanceof \Illuminate\Session\TokenMismatchException) {
|
||||
// https://gist.github.com/jrmadsen67/bd0f9ad0ef1ed6bb594e
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput($request->except('password', '_token'))
|
||||
->with([
|
||||
'warning' => trans('texts.token_expired')
|
||||
]);
|
||||
// prevent loop since the page auto-submits
|
||||
if ($request->path() != 'get_started') {
|
||||
// https://gist.github.com/jrmadsen67/bd0f9ad0ef1ed6bb594e
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput($request->except('password', '_token'))
|
||||
->with([
|
||||
'warning' => trans('texts.token_expired')
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// In production, except for maintenance mode, we'll show a custom error screen
|
||||
|
@ -19,6 +19,8 @@ use App\Ninja\Transformers\UserAccountTransformer;
|
||||
use App\Http\Controllers\BaseAPIController;
|
||||
use Swagger\Annotations as SWG;
|
||||
|
||||
use App\Events\UserSignedUp;
|
||||
use App\Http\Requests\RegisterRequest;
|
||||
use App\Http\Requests\UpdateAccountRequest;
|
||||
|
||||
class AccountApiController extends BaseAPIController
|
||||
@ -32,13 +34,20 @@ class AccountApiController extends BaseAPIController
|
||||
$this->accountRepo = $accountRepo;
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request)
|
||||
{
|
||||
|
||||
$account = $this->accountRepo->create($request->first_name, $request->last_name, $request->email, $request->password);
|
||||
$user = $account->users()->first();
|
||||
|
||||
Auth::login($user, true);
|
||||
event(new UserSignedUp());
|
||||
|
||||
return $this->processLogin($request);
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
if ( ! env(API_SECRET) || $request->api_secret !== env(API_SECRET)) {
|
||||
sleep(ERROR_DELAY);
|
||||
return $this->errorResponse(['message'=>'Invalid secret'],401);
|
||||
}
|
||||
|
||||
if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
|
||||
return $this->processLogin($request);
|
||||
} else {
|
||||
@ -65,24 +74,7 @@ class AccountApiController extends BaseAPIController
|
||||
$account = Auth::user()->account;
|
||||
$updatedAt = $request->updated_at ? date('Y-m-d H:i:s', $request->updated_at) : false;
|
||||
|
||||
$map = [
|
||||
'users' => [],
|
||||
'clients' => ['contacts'],
|
||||
'invoices' => ['invoice_items', 'user', 'client', 'payments'],
|
||||
'products' => [],
|
||||
'tax_rates' => [],
|
||||
'expenses' => ['client', 'invoice', 'vendor'],
|
||||
'payments' => ['invoice'],
|
||||
];
|
||||
|
||||
foreach ($map as $key => $values) {
|
||||
$account->load([$key => function($query) use ($values, $updatedAt) {
|
||||
$query->withTrashed()->with($values);
|
||||
if ($updatedAt) {
|
||||
$query->where('updated_at', '>=', $updatedAt);
|
||||
}
|
||||
}]);
|
||||
}
|
||||
$account->loadAllData($updatedAt);
|
||||
|
||||
$transformer = new AccountTransformer(null, $request->serializer);
|
||||
$account = $this->createItem($account, $transformer, 'account');
|
||||
@ -117,4 +109,82 @@ class AccountApiController extends BaseAPIController
|
||||
|
||||
return $this->response($account);
|
||||
}
|
||||
|
||||
public function addDeviceToken(Request $request)
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
|
||||
//scan if this user has a token already registered (tokens can change, so we need to use the users email as key)
|
||||
$devices = json_decode($account->devices,TRUE);
|
||||
|
||||
|
||||
for($x=0; $x<count($devices); $x++)
|
||||
{
|
||||
if ($devices[$x]['email'] == Auth::user()->username) {
|
||||
$devices[$x]['token'] = $request->token; //update
|
||||
$account->devices = json_encode($devices);
|
||||
$account->save();
|
||||
$devices[$x]['account_key'] = $account->account_key;
|
||||
|
||||
return $this->response($devices[$x]);
|
||||
}
|
||||
}
|
||||
|
||||
//User does not have a device, create new record
|
||||
|
||||
$newDevice = [
|
||||
'token' => $request->token,
|
||||
'email' => $request->email,
|
||||
'device' => $request->device,
|
||||
'account_key' => $account->account_key,
|
||||
'notify_sent' => TRUE,
|
||||
'notify_viewed' => TRUE,
|
||||
'notify_approved' => TRUE,
|
||||
'notify_paid' => TRUE,
|
||||
];
|
||||
|
||||
$devices[] = $newDevice;
|
||||
$account->devices = json_encode($devices);
|
||||
$account->save();
|
||||
|
||||
return $this->response($newDevice);
|
||||
|
||||
}
|
||||
|
||||
public function updatePushNotifications(Request $request)
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
|
||||
$devices = json_decode($account->devices, TRUE);
|
||||
|
||||
if(count($devices) < 1)
|
||||
return $this->errorResponse(['message'=>'No registered devices.'], 400);
|
||||
|
||||
for($x=0; $x<count($devices); $x++)
|
||||
{
|
||||
if($devices[$x]['email'] == Auth::user()->username)
|
||||
{
|
||||
|
||||
$newDevice = [
|
||||
'token' => $devices[$x]['token'],
|
||||
'email' => $devices[$x]['email'],
|
||||
'device' => $devices[$x]['device'],
|
||||
'account_key' => $account->account_key,
|
||||
'notify_sent' => $request->notify_sent,
|
||||
'notify_viewed' => $request->notify_viewed,
|
||||
'notify_approved' => $request->notify_approved,
|
||||
'notify_paid' => $request->notify_paid,
|
||||
];
|
||||
|
||||
//unset($devices[$x]);
|
||||
|
||||
$devices[$x] = $newDevice;
|
||||
$account->devices = json_encode($devices);
|
||||
$account->save();
|
||||
|
||||
return $this->response($newDevice);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use Response;
|
||||
use Request;
|
||||
use App\Models\Affiliate;
|
||||
use App\Models\License;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\User;
|
||||
use App\Models\Account;
|
||||
use App\Models\Gateway;
|
||||
@ -25,7 +26,7 @@ use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Ninja\Repositories\ReferralRepository;
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use App\Events\UserLoggedIn;
|
||||
use App\Events\UserSignedUp;
|
||||
use App\Events\UserSettingsChanged;
|
||||
use App\Services\AuthService;
|
||||
|
||||
@ -40,7 +41,7 @@ class AccountController extends BaseController
|
||||
|
||||
public function __construct(AccountRepository $accountRepo, UserMailer $userMailer, ContactMailer $contactMailer, ReferralRepository $referralRepository)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->accountRepo = $accountRepo;
|
||||
$this->userMailer = $userMailer;
|
||||
@ -99,7 +100,7 @@ class AccountController extends BaseController
|
||||
}
|
||||
|
||||
Auth::login($user, true);
|
||||
event(new UserLoggedIn());
|
||||
event(new UserSignedUp());
|
||||
|
||||
$redirectTo = Input::get('redirect_to') ?: 'invoices/create';
|
||||
|
||||
@ -122,7 +123,8 @@ class AccountController extends BaseController
|
||||
|
||||
public function getSearchData()
|
||||
{
|
||||
$data = $this->accountRepo->getSearchData();
|
||||
$account = Auth::user()->account;
|
||||
$data = $this->accountRepo->getSearchData($account);
|
||||
|
||||
return Response::json($data);
|
||||
}
|
||||
@ -135,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) {
|
||||
@ -150,7 +150,7 @@ class AccountController extends BaseController
|
||||
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
return self::showInvoiceDesign($section);
|
||||
} elseif ($section == ACCOUNT_CLIENT_PORTAL) {
|
||||
return self::showClientViewStyling();
|
||||
return self::showClientPortal();
|
||||
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
return self::showTemplates();
|
||||
} elseif ($section === ACCOUNT_PRODUCTS) {
|
||||
@ -230,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) {
|
||||
@ -393,12 +393,27 @@ class AccountController extends BaseController
|
||||
|
||||
if ($section == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
$data['customDesign'] = ($account->custom_design && !$design) ? $account->custom_design : $design;
|
||||
|
||||
// sample invoice to help determine variables
|
||||
$invoice = Invoice::scope()
|
||||
->with('client', 'account')
|
||||
->where('is_quote', '=', false)
|
||||
->where('is_recurring', '=', false)
|
||||
->first();
|
||||
|
||||
if ($invoice) {
|
||||
$invoice->hidePrivateFields();
|
||||
unset($invoice->account);
|
||||
unset($invoice->invoice_items);
|
||||
unset($invoice->client->contacts);
|
||||
$data['sampleInvoice'] = $invoice;
|
||||
}
|
||||
}
|
||||
|
||||
return View::make("accounts.{$section}", $data);
|
||||
}
|
||||
|
||||
private function showClientViewStyling()
|
||||
private function showClientPortal()
|
||||
{
|
||||
$account = Auth::user()->account->load('country');
|
||||
$css = $account->client_view_css ? $account->client_view_css : '';
|
||||
@ -414,8 +429,11 @@ class AccountController extends BaseController
|
||||
|
||||
$data = [
|
||||
'client_view_css' => $css,
|
||||
'enable_portal_password' => $account->enable_portal_password,
|
||||
'send_portal_password' => $account->send_portal_password,
|
||||
'title' => trans("texts.client_portal"),
|
||||
'section' => ACCOUNT_CLIENT_PORTAL,
|
||||
'account' => $account,
|
||||
];
|
||||
|
||||
return View::make("accounts.client_portal", $data);
|
||||
@ -447,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) {
|
||||
@ -528,6 +544,11 @@ class AccountController extends BaseController
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$account->client_view_css = $sanitized_css;
|
||||
|
||||
$account->enable_client_portal = !!Input::get('enable_client_portal');
|
||||
$account->enable_portal_password = !!Input::get('enable_portal_password');
|
||||
$account->send_portal_password = !!Input::get('send_portal_password');
|
||||
|
||||
$account->save();
|
||||
|
||||
Session::flash('message', trans('texts.updated_settings'));
|
||||
@ -668,6 +689,8 @@ class AccountController extends BaseController
|
||||
$account->custom_invoice_taxes2 = Input::get('custom_invoice_taxes2') ? true : false;
|
||||
$account->custom_invoice_text_label1 = trim(Input::get('custom_invoice_text_label1'));
|
||||
$account->custom_invoice_text_label2 = trim(Input::get('custom_invoice_text_label2'));
|
||||
$account->custom_invoice_item_label1 = trim(Input::get('custom_invoice_item_label1'));
|
||||
$account->custom_invoice_item_label2 = trim(Input::get('custom_invoice_item_label2'));
|
||||
|
||||
$account->invoice_number_counter = Input::get('invoice_number_counter');
|
||||
$account->quote_number_prefix = Input::get('quote_number_prefix');
|
||||
@ -676,6 +699,7 @@ class AccountController extends BaseController
|
||||
$account->invoice_footer = Input::get('invoice_footer');
|
||||
$account->quote_terms = Input::get('quote_terms');
|
||||
$account->auto_convert_quote = Input::get('auto_convert_quote');
|
||||
$account->recurring_invoice_number_prefix = Input::get('recurring_invoice_number_prefix');
|
||||
|
||||
if (Input::has('recurring_hour')) {
|
||||
$account->recurring_hour = Input::get('recurring_hour');
|
||||
@ -736,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);
|
||||
@ -811,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'];
|
||||
|
@ -24,7 +24,7 @@ class AccountGatewayController extends BaseController
|
||||
|
||||
public function __construct(AccountGatewayService $accountGatewayService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->accountGatewayService = $accountGatewayService;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class ActivityController extends BaseController
|
||||
|
||||
public function __construct(ActivityService $activityService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->activityService = $activityService;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class AppController extends BaseController
|
||||
|
||||
public function __construct(AccountRepository $accountRepo, Mailer $mailer, EmailService $emailService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->accountRepo = $accountRepo;
|
||||
$this->mailer = $mailer;
|
||||
@ -231,7 +231,8 @@ class AppController extends BaseController
|
||||
}
|
||||
Artisan::call('optimize', array('--force' => true));
|
||||
} catch (Exception $e) {
|
||||
Response::make($e->getMessage(), 500);
|
||||
Utils::logError($e);
|
||||
return Response::make($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +244,7 @@ class AppController extends BaseController
|
||||
if (!Utils::isNinjaProd()) {
|
||||
try {
|
||||
set_time_limit(60 * 5);
|
||||
Artisan::call('optimize', array('--force' => true));
|
||||
Cache::flush();
|
||||
Session::flush();
|
||||
Artisan::call('migrate', array('--force' => true));
|
||||
@ -250,15 +252,19 @@ class AppController extends BaseController
|
||||
'PaymentLibraries',
|
||||
'Fonts',
|
||||
'Banks',
|
||||
'InvoiceStatus'
|
||||
'InvoiceStatus',
|
||||
'Currencies',
|
||||
'DateFormats',
|
||||
'InvoiceDesigns',
|
||||
'PaymentTerms',
|
||||
] as $seeder) {
|
||||
Artisan::call('db:seed', array('--force' => true, '--class' => "{$seeder}Seeder"));
|
||||
}
|
||||
Artisan::call('optimize', array('--force' => true));
|
||||
Event::fire(new UserSettingsChanged());
|
||||
Session::flash('message', trans('texts.processed_updates'));
|
||||
} catch (Exception $e) {
|
||||
Response::make($e->getMessage(), 500);
|
||||
Utils::logError($e);
|
||||
return Response::make($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,7 +294,7 @@ class AppController extends BaseController
|
||||
}
|
||||
|
||||
if (Utils::getResllerType() == RESELLER_REVENUE_SHARE) {
|
||||
$payments = DB::table('accounts')
|
||||
$data = DB::table('accounts')
|
||||
->leftJoin('payments', 'payments.account_id', '=', 'accounts.id')
|
||||
->leftJoin('clients', 'clients.id', '=', 'payments.client_id')
|
||||
->where('accounts.account_key', '=', NINJA_ACCOUNT_KEY)
|
||||
@ -300,15 +306,9 @@ class AppController extends BaseController
|
||||
'payments.amount'
|
||||
]);
|
||||
} else {
|
||||
$payments = DB::table('accounts')
|
||||
->leftJoin('payments', 'payments.account_id', '=', 'accounts.id')
|
||||
->leftJoin('clients', 'clients.id', '=', 'payments.client_id')
|
||||
->where('accounts.account_key', '=', NINJA_ACCOUNT_KEY)
|
||||
->where('payments.is_deleted', '=', false)
|
||||
->groupBy('clients.id')
|
||||
->count();
|
||||
$data = DB::table('users')->count();
|
||||
}
|
||||
|
||||
return json_encode($payments);
|
||||
return json_encode($data);
|
||||
}
|
||||
}
|
@ -10,8 +10,6 @@ use App\Events\UserLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Services\AuthService;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Contracts\Auth\Registrar;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
|
||||
|
||||
class AuthController extends Controller {
|
||||
@ -29,7 +27,6 @@ class AuthController extends Controller {
|
||||
|
||||
use AuthenticatesAndRegistersUsers;
|
||||
|
||||
protected $loginPath = '/login';
|
||||
protected $redirectTo = '/dashboard';
|
||||
protected $authService;
|
||||
protected $accountRepo;
|
||||
@ -41,16 +38,38 @@ class AuthController extends Controller {
|
||||
* @param \Illuminate\Contracts\Auth\Registrar $registrar
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Guard $auth, Registrar $registrar, AccountRepository $repo, AuthService $authService)
|
||||
public function __construct(AccountRepository $repo, AuthService $authService)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->registrar = $registrar;
|
||||
$this->accountRepo = $repo;
|
||||
$this->authService = $authService;
|
||||
|
||||
//$this->middleware('guest', ['except' => 'getLogout']);
|
||||
}
|
||||
|
||||
public function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
* @return User
|
||||
*/
|
||||
public function create(array $data)
|
||||
{
|
||||
return User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => bcrypt($data['password']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function authLogin($provider, Request $request)
|
||||
{
|
||||
return $this->authService->execute($provider, $request->has('code'));
|
||||
|
@ -1,8 +1,6 @@
|
||||
<?php namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Contracts\Auth\PasswordBroker;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
class PasswordController extends Controller {
|
||||
@ -29,11 +27,8 @@ class PasswordController extends Controller {
|
||||
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Guard $auth, PasswordBroker $passwords)
|
||||
public function __construct()
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->passwords = $passwords;
|
||||
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ class BankAccountController extends BaseController
|
||||
|
||||
public function __construct(BankAccountService $bankAccountService, BankAccountRepository $bankAccountRepo)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->bankAccountService = $bankAccountService;
|
||||
$this->bankAccountRepo = $bankAccountRepo;
|
||||
|
@ -1,10 +1,14 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesCommands;
|
||||
use App\Http\Middleware\PermissionsRequired;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Auth;
|
||||
|
||||
class BaseController extends Controller
|
||||
{
|
||||
use DispatchesCommands;
|
||||
use DispatchesJobs;
|
||||
|
||||
protected $model = 'App\Models\EntityModel';
|
||||
|
||||
/**
|
||||
* Setup the layout used by the controller.
|
||||
@ -18,8 +22,39 @@ class BaseController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
79
app/Http/Controllers/ClientAuth/AuthController.php
Normal file
79
app/Http/Controllers/ClientAuth/AuthController.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php namespace App\Http\Controllers\ClientAuth;
|
||||
|
||||
use Auth;
|
||||
use Event;
|
||||
use Utils;
|
||||
use Session;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Events\UserLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Services\AuthService;
|
||||
use App\Models\Invitation;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
|
||||
class AuthController extends Controller {
|
||||
|
||||
protected $guard = 'client';
|
||||
protected $redirectTo = '/client/dashboard';
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
public function showLoginForm()
|
||||
{
|
||||
$data = array(
|
||||
);
|
||||
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$data['hideLogo'] = $account->isWhiteLabel();
|
||||
$data['clientViewCSS'] = $account->clientViewCSS();
|
||||
$data['clientFontUrl'] = $account->getFontsUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return view('clientauth.login')->with($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the needed authorization credentials from the request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
protected function getCredentials(Request $request)
|
||||
{
|
||||
$credentials = $request->only('password');
|
||||
$credentials['id'] = null;
|
||||
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$credentials['id'] = $invitation->contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the user login request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return void
|
||||
*/
|
||||
protected function validateLogin(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'password' => 'required',
|
||||
]);
|
||||
}
|
||||
}
|
197
app/Http/Controllers/ClientAuth/PasswordController.php
Normal file
197
app/Http/Controllers/ClientAuth/PasswordController.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?php namespace App\Http\Controllers\ClientAuth;
|
||||
|
||||
use Config;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use App\Models\Invitation;
|
||||
|
||||
|
||||
class PasswordController extends Controller {
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
protected $redirectTo = '/client/dashboard';
|
||||
|
||||
/**
|
||||
* Create a new password controller instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Guard $auth
|
||||
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
Config::set("auth.defaults.passwords","client");
|
||||
}
|
||||
|
||||
public function showLinkRequestForm()
|
||||
{
|
||||
$data = array();
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$data['hideLogo'] = $account->isWhiteLabel();
|
||||
$data['clientViewCSS'] = $account->clientViewCSS();
|
||||
$data['clientFontUrl'] = $account->getFontsUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return view('clientauth.password')->with($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
$broker = $this->getBroker();
|
||||
|
||||
$contact_id = null;
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$contact_id = $invitation->contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
$response = Password::broker($broker)->sendResetLink(array('id'=>$contact_id), function (Message $message) {
|
||||
$message->subject($this->getEmailSubject());
|
||||
});
|
||||
|
||||
switch ($response) {
|
||||
case Password::RESET_LINK_SENT:
|
||||
return $this->getSendResetLinkEmailSuccessResponse($response);
|
||||
|
||||
case Password::INVALID_USER:
|
||||
default:
|
||||
return $this->getSendResetLinkEmailFailureResponse($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the password reset view for the given token.
|
||||
*
|
||||
* If no token is present, display the link request form.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string|null $invitation_key
|
||||
* @param string|null $token
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function showResetForm(Request $request, $invitation_key = null, $token = null)
|
||||
{
|
||||
if (is_null($token)) {
|
||||
return $this->getEmail();
|
||||
}
|
||||
|
||||
$data = compact('token', 'invitation_key');
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$data['hideLogo'] = $account->isWhiteLabel();
|
||||
$data['clientViewCSS'] = $account->clientViewCSS();
|
||||
$data['clientFontUrl'] = $account->getFontsUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return view('clientauth.reset')->with($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Display the password reset view for the given token.
|
||||
*
|
||||
* If no token is present, display the link request form.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string|null $invitation_key
|
||||
* @param string|null $token
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getReset(Request $request, $invitation_key = null, $token = null)
|
||||
{
|
||||
return $this->showResetForm($request, $invitation_key, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the given user's password.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function reset(Request $request)
|
||||
{
|
||||
$this->validate($request, $this->getResetValidationRules());
|
||||
|
||||
$credentials = $request->only(
|
||||
'password', 'password_confirmation', 'token'
|
||||
);
|
||||
|
||||
$credentials['id'] = null;
|
||||
|
||||
$invitation_key = $request->input('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$credentials['id'] = $invitation->contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
$broker = $this->getBroker();
|
||||
|
||||
$response = Password::broker($broker)->reset($credentials, function ($user, $password) {
|
||||
$this->resetPassword($user, $password);
|
||||
});
|
||||
|
||||
switch ($response) {
|
||||
case Password::PASSWORD_RESET:
|
||||
return $this->getResetSuccessResponse($response);
|
||||
|
||||
default:
|
||||
return $this->getResetFailureResponse($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password reset validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getResetValidationRules()
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
];
|
||||
}
|
||||
}
|
@ -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,10 +35,11 @@ class ClientController extends BaseController
|
||||
{
|
||||
protected $clientService;
|
||||
protected $clientRepo;
|
||||
protected $model = 'App\Models\Client';
|
||||
|
||||
public function __construct(ClientRepository $clientRepo, ClientService $clientService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->clientRepo = $clientRepo;
|
||||
$this->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();
|
||||
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]);
|
||||
if(!$this->checkViewPermission($client, $response)){
|
||||
return $response;
|
||||
}
|
||||
|
||||
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]
|
||||
);
|
||||
Utils::trackViewed($client->getDisplayName(), ENTITY_CLIENT);
|
||||
|
||||
$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];
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'actionLinks' => $actionLinks,
|
||||
@ -132,7 +156,11 @@ class ClientController extends BaseController
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
if (Client::scope()->count() > Auth::user()->getMaxNumClients()) {
|
||||
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'));
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesCommands;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
|
||||
abstract class Controller extends BaseController {
|
||||
|
||||
use DispatchesCommands, ValidatesRequests;
|
||||
use DispatchesJobs, ValidatesRequests;
|
||||
|
||||
}
|
||||
|
@ -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(
|
||||
|
179
app/Http/Controllers/DashboardApiController.php
Normal file
179
app/Http/Controllers/DashboardApiController.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use DB;
|
||||
use View;
|
||||
use App\Models\Activity;
|
||||
|
||||
class DashboardApiController extends BaseAPIController
|
||||
{
|
||||
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,
|
||||
COUNT(DISTINCT clients.id) active_clients');
|
||||
$metrics = DB::table('accounts')
|
||||
->select($select)
|
||||
->leftJoin('clients', 'accounts.id', '=', 'clients.account_id')
|
||||
->leftJoin('invoices', 'clients.id', '=', 'invoices.client_id')
|
||||
->where('accounts.id', '=', Auth::user()->account_id)
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
->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');
|
||||
$paidToDate = DB::table('accounts')
|
||||
->select($select)
|
||||
->leftJoin('clients', 'accounts.id', '=', 'clients.account_id')
|
||||
->where('accounts.id', '=', Auth::user()->account_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();
|
||||
|
||||
$select = DB::raw('AVG(invoices.amount) as invoice_avg, clients.currency_id as currency_id');
|
||||
$averageInvoice = DB::table('accounts')
|
||||
->select($select)
|
||||
->leftJoin('clients', 'accounts.id', '=', 'clients.account_id')
|
||||
->leftJoin('invoices', 'clients.id', '=', 'invoices.client_id')
|
||||
->where('accounts.id', '=', Auth::user()->account_id)
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('invoices.is_quote', '=', false)
|
||||
->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();
|
||||
|
||||
$select = DB::raw('SUM(clients.balance) as value, clients.currency_id as currency_id');
|
||||
$balances = DB::table('accounts')
|
||||
->select($select)
|
||||
->leftJoin('clients', 'accounts.id', '=', 'clients.account_id')
|
||||
->where('accounts.id', '=', Auth::user()->account_id)
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->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();
|
||||
|
||||
$pastDue = DB::table('invoices')
|
||||
->leftJoin('clients', 'clients.id', '=', 'invoices.client_id')
|
||||
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('invoices.account_id', '=', Auth::user()->account_id)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
//->where('invoices.is_quote', '=', false)
|
||||
->where('invoices.balance', '>', 0)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->where('contacts.is_primary', '=', true)
|
||||
->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();
|
||||
|
||||
$upcoming = DB::table('invoices')
|
||||
->leftJoin('clients', 'clients.id', '=', 'invoices.client_id')
|
||||
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('invoices.account_id', '=', Auth::user()->account_id)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
//->where('invoices.is_quote', '=', false)
|
||||
->where('invoices.balance', '>', 0)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('contacts.is_primary', '=', true)
|
||||
->where('invoices.due_date', '>=', date('Y-m-d'))
|
||||
->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')
|
||||
->leftJoin('clients', 'clients.id', '=', 'payments.client_id')
|
||||
->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id')
|
||||
->where('payments.account_id', '=', Auth::user()->account_id)
|
||||
->where('payments.is_deleted', '=', false)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->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();
|
||||
|
||||
$hasQuotes = false;
|
||||
foreach ([$upcoming, $pastDue] as $data) {
|
||||
foreach ($data as $invoice) {
|
||||
if ($invoice->is_quote) {
|
||||
$hasQuotes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'id' => 1,
|
||||
'paidToDate' => $paidToDate[0]->value,
|
||||
'paidToDateCurrency' => $paidToDate[0]->currency_id,
|
||||
'balances' => $balances[0]->value,
|
||||
'balancesCurrency' => $balances[0]->currency_id,
|
||||
'averageInvoice' => $averageInvoice[0]->invoice_avg,
|
||||
'averageInvoiceCurrency' => $averageInvoice[0]->currency_id,
|
||||
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
|
||||
'activeClients' => $metrics ? $metrics->active_clients : 0,
|
||||
];
|
||||
|
||||
|
||||
|
||||
return $this->response($data);
|
||||
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ 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,
|
||||
@ -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();
|
||||
|
@ -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 = [];
|
||||
|
@ -89,6 +89,8 @@ class ExportController extends BaseController
|
||||
if ($key === 'quotes') {
|
||||
$key = 'invoices';
|
||||
$data['entityType'] = ENTITY_QUOTE;
|
||||
} elseif ($key === 'recurringInvoices') {
|
||||
$key = 'recurring_invoices';
|
||||
}
|
||||
$sheet->loadView("export.{$key}", $data);
|
||||
});
|
||||
@ -109,8 +111,7 @@ class ExportController extends BaseController
|
||||
if ($request->input(ENTITY_CLIENT)) {
|
||||
$data['clients'] = Client::scope()
|
||||
->with('user', 'contacts', 'country')
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->withArchived()
|
||||
->get();
|
||||
|
||||
$data['contacts'] = Contact::scope()
|
||||
@ -126,33 +127,36 @@ class ExportController extends BaseController
|
||||
if ($request->input(ENTITY_TASK)) {
|
||||
$data['tasks'] = Task::scope()
|
||||
->with('user', 'client.contacts')
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->withArchived()
|
||||
->get();
|
||||
}
|
||||
|
||||
if ($request->input(ENTITY_INVOICE)) {
|
||||
$data['invoices'] = Invoice::scope()
|
||||
->with('user', 'client.contacts', 'invoice_status')
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->withArchived()
|
||||
->where('is_quote', '=', false)
|
||||
->where('is_recurring', '=', false)
|
||||
->get();
|
||||
|
||||
$data['quotes'] = Invoice::scope()
|
||||
->with('user', 'client.contacts', 'invoice_status')
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->withArchived()
|
||||
->where('is_quote', '=', true)
|
||||
->where('is_recurring', '=', false)
|
||||
->get();
|
||||
|
||||
$data['recurringInvoices'] = Invoice::scope()
|
||||
->with('user', 'client.contacts', 'invoice_status', 'frequency')
|
||||
->withArchived()
|
||||
->where('is_quote', '=', false)
|
||||
->where('is_recurring', '=', true)
|
||||
->get();
|
||||
}
|
||||
|
||||
if ($request->input(ENTITY_PAYMENT)) {
|
||||
$data['payments'] = Payment::scope()
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->withArchived()
|
||||
->with('user', 'client.contacts', 'payment_type', 'invoice', 'account_gateway.gateway')
|
||||
->get();
|
||||
}
|
||||
@ -161,14 +165,14 @@ class ExportController extends BaseController
|
||||
if ($request->input(ENTITY_VENDOR)) {
|
||||
$data['clients'] = Vendor::scope()
|
||||
->with('user', 'vendorcontacts', 'country')
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->withArchived()
|
||||
->get();
|
||||
|
||||
$data['vendor_contacts'] = VendorContact::scope()
|
||||
->with('user', 'vendor.contacts')
|
||||
->withTrashed()
|
||||
->get();
|
||||
|
||||
/*
|
||||
$data['expenses'] = Credit::scope()
|
||||
->with('user', 'client.contacts')
|
||||
|
@ -17,7 +17,7 @@ class HomeController extends BaseController
|
||||
|
||||
public function __construct(Mailer $mailer)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class ImportController extends BaseController
|
||||
{
|
||||
public function __construct(ImportService $importService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->importService = $importService;
|
||||
}
|
||||
|
@ -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',
|
||||
@ -87,9 +89,14 @@ class InvoiceController extends BaseController
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$invoice = Invoice::scope($publicId)
|
||||
->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')
|
||||
->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items', 'payments')
|
||||
->withTrashed()
|
||||
->firstOrFail();
|
||||
|
||||
if(!$this->checkEditPermission($invoice, $response)){
|
||||
return $response;
|
||||
}
|
||||
|
||||
$entityType = $invoice->getEntityType();
|
||||
|
||||
$contactIds = DB::table('invitations')
|
||||
@ -99,6 +106,8 @@ class InvoiceController extends BaseController
|
||||
->where('invitations.deleted_at', '=', null)
|
||||
->select('contacts.public_id')->lists('public_id');
|
||||
|
||||
$clients = Client::scope()->withTrashed()->with('contacts', 'country');
|
||||
|
||||
if ($clone) {
|
||||
$invoice->id = $invoice->public_id = null;
|
||||
$invoice->invoice_number = $account->getNextInvoiceNumber($invoice);
|
||||
@ -111,6 +120,7 @@ class InvoiceController extends BaseController
|
||||
Utils::trackViewed($invoice->getDisplayName().' - '.$invoice->client->getDisplayName(), $invoice->getEntityType());
|
||||
$method = 'PUT';
|
||||
$url = "{$entityType}s/{$publicId}";
|
||||
$clients->whereId($invoice->client_id);
|
||||
}
|
||||
|
||||
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||
@ -145,6 +155,14 @@ class InvoiceController extends BaseController
|
||||
if (!$invoice->is_recurring && $invoice->balance > 0) {
|
||||
$actions[] = ['url' => 'javascript:onPaymentClick()', 'label' => trans('texts.enter_payment')];
|
||||
}
|
||||
|
||||
foreach ($invoice->payments as $payment) {
|
||||
$label = trans("texts.view_payment");
|
||||
if (count($invoice->payments) > 1) {
|
||||
$label .= ' - ' . $account->formatMoney($payment->amount, $invoice->client);
|
||||
}
|
||||
$actions[] = ['url' => $payment->present()->url, 'label' => $label];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($actions) > 3) {
|
||||
@ -156,8 +174,12 @@ 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' => Client::scope()->withTrashed()->with('contacts', 'country')->whereId($invoice->client_id)->get(),
|
||||
'clients' => $clients->get(),
|
||||
'entityType' => $entityType,
|
||||
'showBreadcrumbs' => $clone,
|
||||
'invoice' => $invoice,
|
||||
@ -203,7 +225,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;
|
||||
|
||||
@ -214,8 +240,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',
|
||||
@ -332,10 +363,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}");
|
||||
|
||||
@ -366,10 +403,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);
|
||||
|
@ -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'));
|
||||
@ -620,6 +642,6 @@ class PaymentController extends BaseController
|
||||
$message .= $error ?: trans('texts.payment_error');
|
||||
|
||||
Session::flash('error', $message);
|
||||
Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message));
|
||||
Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class PaymentTermController extends BaseController
|
||||
|
||||
public function __construct(PaymentTermService $paymentTermService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->paymentTermService = $paymentTermService;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class ProductController extends BaseController
|
||||
|
||||
public function __construct(ProductService $productService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->productService = $productService;
|
||||
}
|
||||
|
@ -34,10 +34,7 @@ class PublicClientController extends BaseController
|
||||
public function view($invitationKey)
|
||||
{
|
||||
if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
|
||||
return response()->view('error', [
|
||||
'error' => trans('texts.invoice_not_found'),
|
||||
'hideHeader' => true,
|
||||
]);
|
||||
return $this->returnError();
|
||||
}
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
@ -53,7 +50,8 @@ class PublicClientController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
if (!Input::has('phantomjs') && !Session::has($invitationKey) && (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||
if (!Input::has('phantomjs') && !Input::has('silent') && !Session::has($invitationKey)
|
||||
&& (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||
if ($invoice->is_quote) {
|
||||
event(new QuoteInvitationWasViewed($invoice, $invitation));
|
||||
} else {
|
||||
@ -104,7 +102,9 @@ class PublicClientController extends BaseController
|
||||
// Checkout.com requires first getting a payment token
|
||||
$checkoutComToken = false;
|
||||
$checkoutComKey = false;
|
||||
$checkoutComDebug = false;
|
||||
if ($accountGateway = $account->getGatewayConfig(GATEWAY_CHECKOUT_COM)) {
|
||||
$checkoutComDebug = $accountGateway->getConfigField('testMode');
|
||||
if ($checkoutComToken = $this->paymentService->getCheckoutComToken($invitation)) {
|
||||
$checkoutComKey = $accountGateway->getConfigField('publicApiKey');
|
||||
$invitation->transaction_reference = $checkoutComToken;
|
||||
@ -118,6 +118,7 @@ class PublicClientController extends BaseController
|
||||
'showBreadcrumbs' => false,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'hideHeader' => $account->isNinjaAccount(),
|
||||
'hideDashboard' => !$account->enable_client_portal,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'clientFontUrl' => $account->getFontsUrl(),
|
||||
'invoice' => $invoice->hidePrivateFields(),
|
||||
@ -128,6 +129,7 @@ class PublicClientController extends BaseController
|
||||
'paymentURL' => $paymentURL,
|
||||
'checkoutComToken' => $checkoutComToken,
|
||||
'checkoutComKey' => $checkoutComKey,
|
||||
'checkoutComDebug' => $checkoutComDebug,
|
||||
'phantomjs' => Input::has('phantomjs'),
|
||||
);
|
||||
|
||||
@ -188,11 +190,16 @@ class PublicClientController extends BaseController
|
||||
if (!$invitation = $this->getInvitation()) {
|
||||
return $this->returnError();
|
||||
}
|
||||
|
||||
$account = $invitation->account;
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$color = $account->primary_color ? $account->primary_color : '#0b4d78';
|
||||
|
||||
if (!$account->enable_client_portal) {
|
||||
return $this->returnError();
|
||||
}
|
||||
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'account' => $account,
|
||||
@ -244,6 +251,7 @@ class PublicClientController extends BaseController
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'hideDashboard' => !$account->enable_client_portal,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'clientFontUrl' => $account->getFontsUrl(),
|
||||
'title' => trans('texts.invoices'),
|
||||
@ -275,6 +283,7 @@ class PublicClientController extends BaseController
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'hideDashboard' => !$account->enable_client_portal,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'clientFontUrl' => $account->getFontsUrl(),
|
||||
'entityType' => ENTITY_PAYMENT,
|
||||
@ -293,7 +302,7 @@ class PublicClientController extends BaseController
|
||||
$payments = $this->paymentRepo->findForContact($invitation->contact->id, Input::get('sSearch'));
|
||||
|
||||
return Datatable::query($payments)
|
||||
->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number) : $model->invoice_number; })
|
||||
->addColumn('invoice_number', function ($model) { return $model->invitation_key ? link_to('/view/'.$model->invitation_key, $model->invoice_number) : $model->invoice_number; })->toHtml()
|
||||
->addColumn('transaction_reference', function ($model) { return $model->transaction_reference ? $model->transaction_reference : '<i>Manual entry</i>'; })
|
||||
->addColumn('payment_type', function ($model) { return $model->payment_type ? $model->payment_type : ($model->account_gateway_id ? '<i>Online payment</i>' : ''); })
|
||||
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); })
|
||||
@ -312,6 +321,7 @@ class PublicClientController extends BaseController
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'hideDashboard' => !$account->enable_client_portal,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'clientFontUrl' => $account->getFontsUrl(),
|
||||
'title' => trans('texts.quotes'),
|
||||
@ -332,13 +342,11 @@ class PublicClientController extends BaseController
|
||||
return $this->invoiceRepo->getClientDatatable($invitation->contact_id, ENTITY_QUOTE, Input::get('sSearch'));
|
||||
}
|
||||
|
||||
private function returnError()
|
||||
private function returnError($error = false)
|
||||
{
|
||||
return response()->view('error', [
|
||||
'error' => trans('texts.invoice_not_found'),
|
||||
'error' => $error ?: trans('texts.invoice_not_found'),
|
||||
'hideHeader' => true,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'clientFontUrl' => $account->getFontsUrl(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ class RecurringInvoiceController extends BaseController
|
||||
|
||||
public function __construct(InvoiceRepository $invoiceRepo)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ use DatePeriod;
|
||||
use Session;
|
||||
use View;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Expense;
|
||||
|
||||
class ReportController extends BaseController
|
||||
{
|
||||
@ -47,6 +50,7 @@ class ReportController extends BaseController
|
||||
$groupBy = Input::get('group_by');
|
||||
$chartType = Input::get('chart_type');
|
||||
$reportType = Input::get('report_type');
|
||||
$dateField = Input::get('date_field');
|
||||
$startDate = Utils::toSqlDate(Input::get('start_date'), false);
|
||||
$endDate = Utils::toSqlDate(Input::get('end_date'), false);
|
||||
$enableReport = Input::get('enable_report') ? true : false;
|
||||
@ -55,6 +59,7 @@ class ReportController extends BaseController
|
||||
$groupBy = 'MONTH';
|
||||
$chartType = 'Bar';
|
||||
$reportType = ENTITY_INVOICE;
|
||||
$dateField = FILTER_INVOICE_DATE;
|
||||
$startDate = Utils::today(false)->modify('-3 month');
|
||||
$endDate = Utils::today(false);
|
||||
$enableReport = true;
|
||||
@ -76,6 +81,8 @@ class ReportController extends BaseController
|
||||
ENTITY_CLIENT => trans('texts.client'),
|
||||
ENTITY_INVOICE => trans('texts.invoice'),
|
||||
ENTITY_PAYMENT => trans('texts.payment'),
|
||||
ENTITY_EXPENSE => trans('texts.expenses'),
|
||||
ENTITY_TAX_RATE => trans('texts.taxes'),
|
||||
];
|
||||
|
||||
$params = [
|
||||
@ -94,10 +101,11 @@ class ReportController extends BaseController
|
||||
|
||||
if (Auth::user()->account->isPro()) {
|
||||
if ($enableReport) {
|
||||
$params = array_merge($params, self::generateReport($reportType, $groupBy, $startDate, $endDate));
|
||||
$isExport = $action == 'export';
|
||||
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));
|
||||
|
||||
if ($action == 'export') {
|
||||
self::export($params['exportData'], $params['reportTotals']);
|
||||
if ($isExport) {
|
||||
self::export($params['displayData'], $params['columns'], $params['reportTotals']);
|
||||
}
|
||||
}
|
||||
if ($enableChart) {
|
||||
@ -106,11 +114,7 @@ class ReportController extends BaseController
|
||||
} else {
|
||||
$params['columns'] = [];
|
||||
$params['displayData'] = [];
|
||||
$params['reportTotals'] = [
|
||||
'amount' => [],
|
||||
'balance' => [],
|
||||
'paid' => [],
|
||||
];
|
||||
$params['reportTotals'] = [];
|
||||
$params['labels'] = [];
|
||||
$params['datasets'] = [];
|
||||
$params['scaleStepWidth'] = 100;
|
||||
@ -212,165 +216,320 @@ class ReportController extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
private function generateReport($reportType, $groupBy, $startDate, $endDate)
|
||||
private function generateReport($reportType, $startDate, $endDate, $dateField, $isExport)
|
||||
{
|
||||
if ($reportType == ENTITY_CLIENT) {
|
||||
$columns = ['client', 'amount', 'paid', 'balance'];
|
||||
return $this->generateClientReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_INVOICE) {
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'paid', 'balance'];
|
||||
} else {
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
||||
return $this->generateInvoiceReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_PAYMENT) {
|
||||
return $this->generatePaymentReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_TAX_RATE) {
|
||||
return $this->generateTaxRateReport($startDate, $endDate, $dateField, $isExport);
|
||||
} elseif ($reportType == ENTITY_EXPENSE) {
|
||||
return $this->generateExpenseReport($startDate, $endDate, $isExport);
|
||||
}
|
||||
}
|
||||
|
||||
$query = DB::table('invoices')
|
||||
->join('accounts', 'accounts.id', '=', 'invoices.account_id')
|
||||
->join('clients', 'clients.id', '=', 'invoices.client_id')
|
||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('invoices.account_id', '=', Auth::user()->account_id)
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->where('contacts.deleted_at', '=', null)
|
||||
->where('invoices.invoice_date', '>=', $startDate->format('Y-m-d'))
|
||||
->where('invoices.invoice_date', '<=', $endDate->format('Y-m-d'))
|
||||
->where('invoices.is_quote', '=', false)
|
||||
->where('invoices.is_recurring', '=', false)
|
||||
->where('contacts.is_primary', '=', true);
|
||||
private function generateTaxRateReport($startDate, $endDate, $dateField, $isExport)
|
||||
{
|
||||
$columns = ['tax_name', 'tax_rate', 'amount', 'paid'];
|
||||
|
||||
$select = [
|
||||
DB::raw('COALESCE(clients.currency_id, accounts.currency_id) currency_id'),
|
||||
'accounts.country_id',
|
||||
'contacts.first_name',
|
||||
'contacts.last_name',
|
||||
'contacts.email',
|
||||
'clients.name as client_name',
|
||||
'clients.public_id as client_public_id',
|
||||
'invoices.public_id as invoice_public_id'
|
||||
];
|
||||
|
||||
if ($reportType == ENTITY_CLIENT) {
|
||||
$query->groupBy('clients.id');
|
||||
array_push($select, DB::raw('sum(invoices.amount) amount'), DB::raw('sum(invoices.balance) balance'), DB::raw('sum(invoices.amount - invoices.balance) paid'));
|
||||
} else {
|
||||
$query->orderBy('invoices.id');
|
||||
array_push($select, 'invoices.invoice_number', 'invoices.amount', 'invoices.balance', 'invoices.invoice_date');
|
||||
if ($reportType == ENTITY_INVOICE) {
|
||||
array_push($select, DB::raw('(invoices.amount - invoices.balance) paid'));
|
||||
} else {
|
||||
$query->join('payments', 'payments.invoice_id', '=', 'invoices.id')
|
||||
->leftJoin('payment_types', 'payment_types.id', '=', 'payments.payment_type_id')
|
||||
->leftJoin('account_gateways', 'account_gateways.id', '=', 'payments.account_gateway_id')
|
||||
->leftJoin('gateways', 'gateways.id', '=', 'account_gateways.gateway_id');
|
||||
array_push($select, 'payments.payment_date', 'payments.amount as paid', 'payment_types.name as payment_type', 'gateways.name as gateway');
|
||||
}
|
||||
}
|
||||
|
||||
$query->select($select);
|
||||
$data = $query->get();
|
||||
|
||||
$lastInvoiceId = null;
|
||||
$sameAsLast = false;
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$exportData = [];
|
||||
$reportTotals = [
|
||||
'amount' => [],
|
||||
'balance' => [],
|
||||
'paid' => [],
|
||||
];
|
||||
$clients = Client::scope()
|
||||
->withArchived()
|
||||
->with('contacts')
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate, $dateField) {
|
||||
$query->withArchived();
|
||||
if ($dateField == FILTER_PAYMENT_DATE) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->whereHas('payments', function($query) use ($startDate, $endDate) {
|
||||
$query->where('payment_date', '>=', $startDate)
|
||||
->where('payment_date', '<=', $endDate)
|
||||
->withArchived();
|
||||
})
|
||||
->with(['payments' => function($query) use ($startDate, $endDate) {
|
||||
$query->where('payment_date', '>=', $startDate)
|
||||
->where('payment_date', '<=', $endDate)
|
||||
->withArchived()
|
||||
->with('payment_type', 'account_gateway.gateway');
|
||||
}, 'invoice_items']);
|
||||
}
|
||||
}]);
|
||||
|
||||
foreach ($data as $record) {
|
||||
$sameAsLast = ($lastInvoiceId == $record->invoice_public_id);
|
||||
$lastInvoiceId = $record->invoice_public_id;
|
||||
foreach ($clients->get() as $client) {
|
||||
$currencyId = $client->currency_id ?: Auth::user()->account->getCurrencyId();
|
||||
$amount = 0;
|
||||
$paid = 0;
|
||||
$taxTotals = [];
|
||||
|
||||
$displayRow = [];
|
||||
if ($sameAsLast) {
|
||||
array_push($displayRow, '', '', '', '');
|
||||
} else {
|
||||
array_push($displayRow, link_to('/clients/'.$record->client_public_id, Utils::getClientDisplayName($record)));
|
||||
if ($reportType != ENTITY_CLIENT) {
|
||||
array_push($displayRow,
|
||||
link_to('/invoices/'.$record->invoice_public_id, $record->invoice_number),
|
||||
Utils::fromSqlDate($record->invoice_date, true)
|
||||
);
|
||||
foreach ($client->invoices as $invoice) {
|
||||
foreach ($invoice->getTaxes(true) as $key => $tax) {
|
||||
if ( ! isset($taxTotals[$currencyId])) {
|
||||
$taxTotals[$currencyId] = [];
|
||||
}
|
||||
if (isset($taxTotals[$currencyId][$key])) {
|
||||
$taxTotals[$currencyId][$key]['amount'] += $tax['amount'];
|
||||
$taxTotals[$currencyId][$key]['paid'] += $tax['paid'];
|
||||
} else {
|
||||
$taxTotals[$currencyId][$key] = $tax;
|
||||
}
|
||||
}
|
||||
array_push($displayRow, Utils::formatMoney($record->amount, $record->currency_id, $record->country_id));
|
||||
}
|
||||
if ($reportType != ENTITY_PAYMENT) {
|
||||
array_push($displayRow, Utils::formatMoney($record->paid, $record->currency_id, $record->country_id));
|
||||
}
|
||||
if ($reportType == ENTITY_PAYMENT) {
|
||||
array_push($displayRow,
|
||||
Utils::fromSqlDate($record->payment_date, true),
|
||||
Utils::formatMoney($record->paid, $record->currency_id, $record->country_id),
|
||||
$record->gateway ?: $record->payment_type
|
||||
);
|
||||
} else {
|
||||
array_push($displayRow, Utils::formatMoney($record->balance, $record->currency_id, $record->country_id));
|
||||
|
||||
$amount += $invoice->amount;
|
||||
$paid += $invoice->getAmountPaid();
|
||||
}
|
||||
|
||||
// export data
|
||||
$exportRow = [];
|
||||
if ($sameAsLast) {
|
||||
$exportRow[trans('texts.client')] = ' ';
|
||||
$exportRow[trans('texts.invoice_number')] = ' ';
|
||||
$exportRow[trans('texts.invoice_date')] = ' ';
|
||||
$exportRow[trans('texts.amount')] = ' ';
|
||||
} else {
|
||||
$exportRow[trans('texts.client')] = Utils::getClientDisplayName($record);
|
||||
if ($reportType != ENTITY_CLIENT) {
|
||||
$exportRow[trans('texts.invoice_number')] = $record->invoice_number;
|
||||
$exportRow[trans('texts.invoice_date')] = Utils::fromSqlDate($record->invoice_date, true);
|
||||
foreach ($taxTotals as $currencyId => $taxes) {
|
||||
foreach ($taxes as $tax) {
|
||||
$displayData[] = [
|
||||
$tax['name'],
|
||||
$tax['rate'] . '%',
|
||||
$account->formatMoney($tax['amount'], $client),
|
||||
$account->formatMoney($tax['paid'], $client)
|
||||
];
|
||||
}
|
||||
$exportRow[trans('texts.amount')] = Utils::formatMoney($record->amount, $record->currency_id, $record->country_id);
|
||||
}
|
||||
if ($reportType != ENTITY_PAYMENT) {
|
||||
$exportRow[trans('texts.paid')] = Utils::formatMoney($record->paid, $record->currency_id, $record->country_id);
|
||||
}
|
||||
if ($reportType == ENTITY_PAYMENT) {
|
||||
$exportRow[trans('texts.payment_date')] = Utils::fromSqlDate($record->payment_date, true);
|
||||
$exportRow[trans('texts.payment_amount')] = Utils::formatMoney($record->paid, $record->currency_id, $record->country_id);
|
||||
$exportRow[trans('texts.method')] = $record->gateway ?: $record->payment_type;
|
||||
} else {
|
||||
$exportRow[trans('texts.balance')] = Utils::formatMoney($record->balance, $record->currency_id, $record->country_id);
|
||||
}
|
||||
|
||||
$displayData[] = $displayRow;
|
||||
$exportData[] = $exportRow;
|
||||
|
||||
$accountCurrencyId = Auth::user()->account->currency_id;
|
||||
$currencyId = $record->currency_id ? $record->currency_id : ($accountCurrencyId ? $accountCurrencyId : DEFAULT_CURRENCY);
|
||||
if (!isset($reportTotals['amount'][$currencyId])) {
|
||||
$reportTotals['amount'][$currencyId] = 0;
|
||||
$reportTotals['balance'][$currencyId] = 0;
|
||||
$reportTotals['paid'][$currencyId] = 0;
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $tax['amount']);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $tax['paid']);
|
||||
}
|
||||
if (!$sameAsLast) {
|
||||
$reportTotals['amount'][$currencyId] += $record->amount;
|
||||
$reportTotals['balance'][$currencyId] += $record->balance;
|
||||
}
|
||||
$reportTotals['paid'][$currencyId] += $record->paid;
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
'exportData' => $exportData
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
private function generatePaymentReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$payments = Payment::scope()
|
||||
->withTrashed()
|
||||
->where('is_deleted', '=', false)
|
||||
->whereHas('client', function($query) {
|
||||
$query->where('is_deleted', '=', false);
|
||||
})
|
||||
->whereHas('invoice', function($query) {
|
||||
$query->where('is_deleted', '=', false);
|
||||
})
|
||||
->with('client.contacts', 'invoice', 'payment_type', 'account_gateway.gateway')
|
||||
->where('payment_date', '>=', $startDate)
|
||||
->where('payment_date', '<=', $endDate);
|
||||
|
||||
foreach ($payments->get() as $payment) {
|
||||
$invoice = $payment->invoice;
|
||||
$client = $payment->client;
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
$account->formatMoney($invoice->amount, $client),
|
||||
$payment->present()->payment_date,
|
||||
$account->formatMoney($payment->amount, $client),
|
||||
$payment->present()->method,
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment->amount);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
private function export($data, $totals)
|
||||
private function generateInvoiceReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$clients = Client::scope()
|
||||
->withTrashed()
|
||||
->with('contacts')
|
||||
->where('is_deleted', '=', false)
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->where('is_deleted', '=', false)
|
||||
->where('is_quote', '=', false)
|
||||
->where('is_recurring', '=', false)
|
||||
->with(['payments' => function($query) {
|
||||
$query->withTrashed()
|
||||
->with('payment_type', 'account_gateway.gateway')
|
||||
->where('is_deleted', '=', false);
|
||||
}, 'invoice_items'])
|
||||
->withTrashed();
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
foreach ($client->invoices as $invoice) {
|
||||
|
||||
$payments = count($invoice->payments) ? $invoice->payments : [false];
|
||||
foreach ($payments as $payment) {
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
$account->formatMoney($invoice->amount, $client),
|
||||
$payment ? $payment->present()->payment_date : '',
|
||||
$payment ? $account->formatMoney($payment->amount, $client) : '',
|
||||
$payment ? $payment->present()->method : '',
|
||||
];
|
||||
if ($payment) {
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment->amount);
|
||||
}
|
||||
}
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
private function generateClientReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'amount', 'paid', 'balance'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$clients = Client::scope()
|
||||
->withArchived()
|
||||
->with('contacts')
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->where('is_quote', '=', false)
|
||||
->where('is_recurring', '=', false)
|
||||
->withArchived();
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
$amount = 0;
|
||||
$paid = 0;
|
||||
|
||||
foreach ($client->invoices as $invoice) {
|
||||
$amount += $invoice->amount;
|
||||
$paid += $invoice->getAmountPaid();
|
||||
}
|
||||
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$account->formatMoney($amount, $client),
|
||||
$account->formatMoney($paid, $client),
|
||||
$account->formatMoney($amount - $paid, $client)
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $paid);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $amount - $paid);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
private function generateExpenseReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['vendor', 'client', 'date', 'expense_amount', 'invoiced_amount'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$expenses = Expense::scope()
|
||||
->withTrashed()
|
||||
->with('client.contacts', 'vendor')
|
||||
->where('expense_date', '>=', $startDate)
|
||||
->where('expense_date', '<=', $endDate);
|
||||
|
||||
|
||||
foreach ($expenses->get() as $expense) {
|
||||
$amount = $expense->amount;
|
||||
$invoiced = $expense->present()->invoiced_amount;
|
||||
|
||||
$displayData[] = [
|
||||
$expense->vendor ? ($isExport ? $expense->vendor->name : $expense->vendor->present()->link) : '',
|
||||
$expense->client ? ($isExport ? $expense->client->getDisplayName() : $expense->client->present()->link) : '',
|
||||
$expense->present()->expense_date,
|
||||
Utils::formatMoney($amount, $expense->currency_id),
|
||||
Utils::formatMoney($invoiced, $expense->invoice_currency_id),
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->expense_currency_id, 'amount', $amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->invoice_currency_id, 'amount', 0);
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->invoice_currency_id, 'invoiced', $invoiced);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->expense_currency_id, 'invoiced', 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
private function addToTotals($data, $currencyId, $field, $value) {
|
||||
$currencyId = $currencyId ?: Auth::user()->account->getCurrencyId();
|
||||
|
||||
if (!isset($data[$currencyId][$field])) {
|
||||
$data[$currencyId][$field] = 0;
|
||||
}
|
||||
|
||||
$data[$currencyId][$field] += $value;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function export($data, $columns, $totals)
|
||||
{
|
||||
$output = fopen('php://output', 'w') or Utils::fatalError();
|
||||
header('Content-Type:application/csv');
|
||||
header('Content-Disposition:attachment;filename=ninja-report.csv');
|
||||
|
||||
Utils::exportData($output, $data);
|
||||
Utils::exportData($output, $data, Utils::trans($columns));
|
||||
|
||||
foreach (['amount', 'paid', 'balance'] as $type) {
|
||||
$csv = trans("texts.{$type}").',';
|
||||
foreach ($totals[$type] as $currencyId => $amount) {
|
||||
$csv .= Utils::formatMoney($amount, $currencyId).',';
|
||||
fwrite($output, trans('texts.totals'));
|
||||
foreach ($totals as $currencyId => $fields) {
|
||||
foreach ($fields as $key => $value) {
|
||||
fwrite($output, ',' . trans("texts.{$key}"));
|
||||
}
|
||||
fwrite($output, "\n");
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($totals as $currencyId => $fields) {
|
||||
$csv = Utils::getFromCache($currencyId, 'currencies')->name . ',';
|
||||
foreach ($fields as $key => $value) {
|
||||
$csv .= '"' . Utils::formatMoney($value, $currencyId).'",';
|
||||
}
|
||||
fwrite($output, $csv."\n");
|
||||
}
|
||||
|
@ -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")];
|
||||
@ -176,6 +184,10 @@ class TaskController extends BaseController
|
||||
{
|
||||
$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();
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ class TaxRateController extends BaseController
|
||||
|
||||
public function __construct(TaxRateService $taxRateService, TaxRateRepository $taxRateRepo)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->taxRateService = $taxRateService;
|
||||
$this->taxRateRepo = $taxRateRepo;
|
||||
|
@ -20,7 +20,7 @@ class TokenController extends BaseController
|
||||
|
||||
public function __construct(TokenService $tokenService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->tokenService = $tokenService;
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ use Request;
|
||||
use Redirect;
|
||||
use Session;
|
||||
use URL;
|
||||
use Password;
|
||||
use Utils;
|
||||
use Validator;
|
||||
use Illuminate\Auth\Passwords\TokenRepositoryInterface;
|
||||
use App\Models\User;
|
||||
use App\Http\Requests;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
@ -30,7 +30,7 @@ class UserController extends BaseController
|
||||
|
||||
public function __construct(AccountRepository $accountRepo, ContactMailer $contactMailer, UserMailer $userMailer, UserService $userService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->accountRepo = $accountRepo;
|
||||
$this->contactMailer = $contactMailer;
|
||||
@ -77,7 +77,6 @@ class UserController extends BaseController
|
||||
'user' => $user,
|
||||
'method' => 'PUT',
|
||||
'url' => 'users/'.$publicId,
|
||||
'title' => trans('texts.edit_user'),
|
||||
];
|
||||
|
||||
return View::make('users.edit', $data);
|
||||
@ -120,7 +119,6 @@ class UserController extends BaseController
|
||||
'user' => null,
|
||||
'method' => 'POST',
|
||||
'url' => 'users',
|
||||
'title' => trans('texts.add_user'),
|
||||
];
|
||||
|
||||
return View::make('users.edit', $data);
|
||||
@ -192,6 +190,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 +202,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();
|
||||
@ -240,7 +242,7 @@ class UserController extends BaseController
|
||||
*
|
||||
* @param string $code
|
||||
*/
|
||||
public function confirm($code, TokenRepositoryInterface $tokenRepo)
|
||||
public function confirm($code)
|
||||
{
|
||||
$user = User::where('confirmation_code', '=', $code)->get()->first();
|
||||
|
||||
@ -253,7 +255,7 @@ class UserController extends BaseController
|
||||
|
||||
if ($user->public_id) {
|
||||
//Auth::login($user);
|
||||
$token = $tokenRepo->create($user);
|
||||
$token = Password::getRepository()->create($user);
|
||||
|
||||
return Redirect::to("/password/reset/{$token}");
|
||||
} else {
|
||||
|
@ -30,10 +30,11 @@ class VendorController extends BaseController
|
||||
{
|
||||
protected $vendorService;
|
||||
protected $vendorRepo;
|
||||
protected $model = 'App\Models\Vendor';
|
||||
|
||||
public function __construct(VendorRepository $vendorRepo, VendorService $vendorService)
|
||||
{
|
||||
parent::__construct();
|
||||
//parent::__construct();
|
||||
|
||||
$this->vendorRepo = $vendorRepo;
|
||||
$this->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'));
|
||||
|
||||
|
@ -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',
|
||||
];
|
||||
|
@ -21,11 +21,15 @@ class ApiCheck {
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$loggingIn = $request->is('api/v1/login');
|
||||
$loggingIn = $request->is('api/v1/login') || $request->is('api/v1/register');
|
||||
$headers = Utils::getApiHeaders();
|
||||
|
||||
if ($loggingIn) {
|
||||
// do nothing
|
||||
// check API secret
|
||||
if ( ! $request->api_secret || ! env(API_SECRET) || ! hash_equals($request->api_secret, env(API_SECRET))) {
|
||||
sleep(ERROR_DELAY);
|
||||
return Response::json('Invalid secret', 403, $headers);
|
||||
}
|
||||
} else {
|
||||
// check for a valid token
|
||||
$token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']);
|
||||
@ -34,7 +38,7 @@ class ApiCheck {
|
||||
Auth::loginUsingId($token->user_id);
|
||||
Session::set('token_id', $token->id);
|
||||
} else {
|
||||
sleep(3);
|
||||
sleep(ERROR_DELAY);
|
||||
return Response::json('Invalid token', 403, $headers);
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,13 @@
|
||||
<?php namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Auth;
|
||||
use Session;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Account;
|
||||
|
||||
class Authenticate {
|
||||
|
||||
/**
|
||||
* The Guard implementation.
|
||||
*
|
||||
* @var Guard
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Create a new filter instance.
|
||||
*
|
||||
* @param Guard $auth
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Guard $auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
@ -30,9 +15,46 @@ class Authenticate {
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
public function handle($request, Closure $next, $guard = 'user')
|
||||
{
|
||||
if ($this->auth->guest())
|
||||
$authenticated = Auth::guard($guard)->check();
|
||||
|
||||
if($guard == 'client' && !empty($request->invitation_key)){
|
||||
$old_key = session('invitation_key');
|
||||
if($old_key && $old_key != $request->invitation_key){
|
||||
if($this->getInvitationContactId($old_key) != $this->getInvitationContactId($request->invitation_key)){
|
||||
// This is a different client; reauthenticate
|
||||
$authenticated = false;
|
||||
Auth::guard($guard)->logout();
|
||||
}
|
||||
}
|
||||
Session::put('invitation_key', $request->invitation_key);
|
||||
}
|
||||
|
||||
if($guard=='client'){
|
||||
$invitation_key = session('invitation_key');
|
||||
$account_id = $this->getInvitationAccountId($invitation_key);
|
||||
|
||||
if(Auth::guard('user')->check() && Auth::user('user')->account_id === $account_id){
|
||||
// This is an admin; let them pretend to be a client
|
||||
$authenticated = true;
|
||||
}
|
||||
|
||||
// Does this account require portal passwords?
|
||||
$account = Account::whereId($account_id)->first();
|
||||
if(!$account->enable_portal_password || !$account->isPro()){
|
||||
$authenticated = true;
|
||||
}
|
||||
|
||||
if(!$authenticated){
|
||||
$contact = Contact::whereId($this->getInvitationContactId($invitation_key))->first();
|
||||
if($contact && !$contact->password){
|
||||
$authenticated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$authenticated)
|
||||
{
|
||||
if ($request->ajax())
|
||||
{
|
||||
@ -40,11 +62,30 @@ class Authenticate {
|
||||
}
|
||||
else
|
||||
{
|
||||
return redirect()->guest('/login');
|
||||
return redirect()->guest($guard=='client'?'/client/login':'/login');
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
protected function getInvitation($key){
|
||||
$invitation = Invitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
return $invitation;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
protected function getInvitationContactId($key){
|
||||
$invitation = $this->getInvitation($key);
|
||||
|
||||
return $invitation?$invitation->contact_id:null;
|
||||
}
|
||||
|
||||
protected function getInvitationAccountId($key){
|
||||
$invitation = $this->getInvitation($key);
|
||||
|
||||
return $invitation?$invitation->account_id:null;
|
||||
}
|
||||
}
|
||||
|
57
app/Http/Middleware/PermissionsRequired.php
Normal file
57
app/Http/Middleware/PermissionsRequired.php
Normal 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;
|
||||
}
|
||||
}
|
@ -48,6 +48,9 @@ class StartupCheck
|
||||
$file = storage_path() . '/version.txt';
|
||||
$version = @file_get_contents($file);
|
||||
if ($version != NINJA_VERSION) {
|
||||
if (version_compare(phpversion(), '5.5.9', '<')) {
|
||||
dd('Please update PHP to >= 5.5.9');
|
||||
}
|
||||
$handle = fopen($file, 'w');
|
||||
fwrite($handle, NINJA_VERSION);
|
||||
fclose($handle);
|
||||
|
@ -24,7 +24,7 @@ class CreateExpenseRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'amount' => 'positive',
|
||||
'amount' => 'numeric',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
67
app/Http/Requests/RegisterRequest.php
Normal file
67
app/Http/Requests/RegisterRequest.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php namespace app\Http\Requests;
|
||||
|
||||
use Auth;
|
||||
use App\Http\Requests\Request;
|
||||
use Illuminate\Http\Request as InputRequest;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Factory;
|
||||
use App\Libraries\Utils;
|
||||
use Response;
|
||||
|
||||
class RegisterRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function __construct(InputRequest $req)
|
||||
{
|
||||
$this->req = $req;
|
||||
}
|
||||
|
||||
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
|
||||
$rules = [
|
||||
'email' => 'required|unique:users',
|
||||
'first_name' => 'required',
|
||||
'last_name' => 'required',
|
||||
'password' => 'required',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function response(array $errors)
|
||||
{
|
||||
/* If the user is not validating from a mobile app - pass through parent::response */
|
||||
if(!isset($this->req->api_secret))
|
||||
return parent::response($errors);
|
||||
|
||||
/* If the user is validating from a mobile app - pass through first error string and return error */
|
||||
foreach($errors as $error) {
|
||||
foreach ($error as $key => $value) {
|
||||
|
||||
$message['error'] = ['message'=>$value];
|
||||
$message = json_encode($message, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders();
|
||||
|
||||
return Response::make($message, 400, $headers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ class UpdateExpenseRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'amount' => 'positive',
|
||||
'amount' => 'numeric',
|
||||
'expense_date' => 'required',
|
||||
];
|
||||
}
|
||||
|
@ -35,17 +35,20 @@ Route::get('/keep_alive', 'HomeController@keepAlive');
|
||||
Route::post('/get_started', 'AccountController@getStarted');
|
||||
|
||||
// Client visible pages
|
||||
Route::get('view/{invitation_key}', 'PublicClientController@view');
|
||||
Route::get('download/{invitation_key}', 'PublicClientController@download');
|
||||
Route::get('view', 'HomeController@viewLogo');
|
||||
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||
Route::get('complete', 'PaymentController@offsite_payment');
|
||||
Route::get('client/quotes', 'PublicClientController@quoteIndex');
|
||||
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
|
||||
Route::get('client/payments', 'PublicClientController@paymentIndex');
|
||||
Route::get('client/dashboard', 'PublicClientController@dashboard');
|
||||
Route::group(['middleware' => 'auth:client'], function() {
|
||||
Route::get('view/{invitation_key}', 'PublicClientController@view');
|
||||
Route::get('download/{invitation_key}', 'PublicClientController@download');
|
||||
Route::get('view', 'HomeController@viewLogo');
|
||||
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||
Route::get('complete', 'PaymentController@offsite_payment');
|
||||
Route::get('client/quotes', 'PublicClientController@quoteIndex');
|
||||
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
|
||||
Route::get('client/payments', 'PublicClientController@paymentIndex');
|
||||
Route::get('client/dashboard', 'PublicClientController@dashboard');
|
||||
});
|
||||
|
||||
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable'));
|
||||
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'PublicClientController@invoiceDatatable'));
|
||||
Route::get('api/client.payments', array('as'=>'api.client.payments', 'uses'=>'PublicClientController@paymentDatatable'));
|
||||
@ -65,16 +68,25 @@ Route::post('/hook/email_bounced', 'AppController@emailBounced');
|
||||
Route::post('/hook/email_opened', 'AppController@emailOpened');
|
||||
|
||||
// Laravel auth routes
|
||||
get('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@getRegister'));
|
||||
post('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@postRegister'));
|
||||
get('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@getLoginWrapper'));
|
||||
post('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@postLoginWrapper'));
|
||||
get('/logout', array('as' => 'logout', 'uses' => 'Auth\AuthController@getLogoutWrapper'));
|
||||
get('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getEmail'));
|
||||
post('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postEmail'));
|
||||
get('/password/reset/{token}', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getReset'));
|
||||
post('/password/reset', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postReset'));
|
||||
get('/user/confirm/{code}', 'UserController@confirm');
|
||||
Route::get('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@getRegister'));
|
||||
Route::post('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@postRegister'));
|
||||
Route::get('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@getLoginWrapper'));
|
||||
Route::post('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@postLoginWrapper'));
|
||||
Route::get('/logout', array('as' => 'logout', 'uses' => 'Auth\AuthController@getLogoutWrapper'));
|
||||
Route::get('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getEmail'));
|
||||
Route::post('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postEmail'));
|
||||
Route::get('/password/reset/{token}', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getReset'));
|
||||
Route::post('/password/reset', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postReset'));
|
||||
Route::get('/user/confirm/{code}', 'UserController@confirm');
|
||||
|
||||
// Client auth
|
||||
Route::get('/client/login', array('as' => 'login', 'uses' => 'ClientAuth\AuthController@getLogin'));
|
||||
Route::post('/client/login', array('as' => 'login', 'uses' => 'ClientAuth\AuthController@postLogin'));
|
||||
Route::get('/client/logout', array('as' => 'logout', 'uses' => 'ClientAuth\AuthController@getLogout'));
|
||||
Route::get('/client/forgot', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@getEmail'));
|
||||
Route::post('/client/forgot', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@postEmail'));
|
||||
Route::get('/client/password/reset/{invitation_key}/{token}', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@getReset'));
|
||||
Route::post('/client/password/reset', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@postReset'));
|
||||
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
@ -87,12 +99,80 @@ if (Utils::isReseller()) {
|
||||
Route::post('/reseller_stats', 'AppController@stats');
|
||||
}
|
||||
|
||||
Route::group(['middleware' => 'auth'], function() {
|
||||
Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::get('dashboard', 'DashboardController@index');
|
||||
Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
|
||||
Route::get('hide_message', 'HomeController@hideMessage');
|
||||
Route::get('force_inline_pdf', 'UserController@forcePDFJS');
|
||||
|
||||
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'));
|
||||
Route::get('api/activities/{client_id?}', array('as'=>'api.activities', 'uses'=>'ActivityController@getDatatable'));
|
||||
Route::post('clients/bulk', 'ClientController@bulk');
|
||||
|
||||
Route::resource('tasks', 'TaskController');
|
||||
Route::get('api/tasks/{client_id?}', array('as'=>'api.tasks', 'uses'=>'TaskController@getDatatable'));
|
||||
Route::get('tasks/create/{client_id?}', 'TaskController@create');
|
||||
Route::post('tasks/bulk', 'TaskController@bulk');
|
||||
|
||||
Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable'));
|
||||
|
||||
Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
||||
Route::get('quotes/quote_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
||||
|
||||
Route::resource('invoices', 'InvoiceController');
|
||||
Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
|
||||
Route::get('invoices/create/{client_id?}', 'InvoiceController@create');
|
||||
Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring');
|
||||
Route::get('recurring_invoices', 'RecurringInvoiceController@index');
|
||||
Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice');
|
||||
Route::post('invoices/bulk', 'InvoiceController@bulk');
|
||||
Route::post('recurring_invoices/bulk', 'InvoiceController@bulk');
|
||||
|
||||
Route::get('quotes/create/{client_id?}', 'QuoteController@create');
|
||||
Route::get('quotes/{public_id}/clone', 'InvoiceController@cloneInvoice');
|
||||
Route::get('quotes/{public_id}/edit', 'InvoiceController@edit');
|
||||
Route::put('quotes/{public_id}', 'InvoiceController@update');
|
||||
Route::get('quotes/{public_id}', 'InvoiceController@edit');
|
||||
Route::post('quotes', 'InvoiceController@store');
|
||||
Route::get('quotes', 'QuoteController@index');
|
||||
Route::get('api/quotes/{client_id?}', array('as'=>'api.quotes', 'uses'=>'QuoteController@getDatatable'));
|
||||
Route::post('quotes/bulk', 'QuoteController@bulk');
|
||||
|
||||
Route::resource('payments', 'PaymentController');
|
||||
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');
|
||||
Route::get('api/payments/{client_id?}', array('as'=>'api.payments', 'uses'=>'PaymentController@getDatatable'));
|
||||
Route::post('payments/bulk', 'PaymentController@bulk');
|
||||
|
||||
Route::resource('credits', 'CreditController');
|
||||
Route::get('credits/create/{client_id?}/{invoice_id?}', 'CreditController@create');
|
||||
Route::get('api/credits/{client_id?}', array('as'=>'api.credits', 'uses'=>'CreditController@getDatatable'));
|
||||
Route::post('credits/bulk', 'CreditController@bulk');
|
||||
|
||||
Route::get('/resend_confirmation', 'AccountController@resendConfirmation');
|
||||
Route::post('/update_setup', 'AppController@updateSetup');
|
||||
|
||||
|
||||
// vendor
|
||||
Route::resource('vendors', 'VendorController');
|
||||
Route::get('api/vendor', array('as'=>'api.vendors', 'uses'=>'VendorController@getDatatable'));
|
||||
Route::post('vendors/bulk', 'VendorController@bulk');
|
||||
|
||||
// Expense
|
||||
Route::resource('expenses', 'ExpenseController');
|
||||
Route::get('expenses/create/{vendor_id?}/{client_id?}', 'ExpenseController@create');
|
||||
Route::get('api/expense', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable'));
|
||||
Route::get('api/expenseVendor/{id}', array('as'=>'api.expense', 'uses'=>'ExpenseController@getDatatableVendor'));
|
||||
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');
|
||||
@ -148,66 +228,6 @@ Route::group(['middleware' => 'auth'], function() {
|
||||
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::resource('clients', 'ClientController');
|
||||
Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable'));
|
||||
Route::get('api/activities/{client_id?}', array('as'=>'api.activities', 'uses'=>'ActivityController@getDatatable'));
|
||||
Route::post('clients/bulk', 'ClientController@bulk');
|
||||
|
||||
Route::resource('tasks', 'TaskController');
|
||||
Route::get('api/tasks/{client_id?}', array('as'=>'api.tasks', 'uses'=>'TaskController@getDatatable'));
|
||||
Route::get('tasks/create/{client_id?}', 'TaskController@create');
|
||||
Route::post('tasks/bulk', 'TaskController@bulk');
|
||||
|
||||
Route::get('api/recurring_invoices/{client_id?}', array('as'=>'api.recurring_invoices', 'uses'=>'InvoiceController@getRecurringDatatable'));
|
||||
|
||||
Route::get('invoices/invoice_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
||||
Route::get('quotes/quote_history/{invoice_id}', 'InvoiceController@invoiceHistory');
|
||||
|
||||
Route::resource('invoices', 'InvoiceController');
|
||||
Route::get('api/invoices/{client_id?}', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable'));
|
||||
Route::get('invoices/create/{client_id?}', 'InvoiceController@create');
|
||||
Route::get('recurring_invoices/create/{client_id?}', 'InvoiceController@createRecurring');
|
||||
Route::get('recurring_invoices', 'RecurringInvoiceController@index');
|
||||
Route::get('invoices/{public_id}/clone', 'InvoiceController@cloneInvoice');
|
||||
Route::post('invoices/bulk', 'InvoiceController@bulk');
|
||||
Route::post('recurring_invoices/bulk', 'InvoiceController@bulk');
|
||||
|
||||
Route::get('quotes/create/{client_id?}', 'QuoteController@create');
|
||||
Route::get('quotes/{public_id}/clone', 'InvoiceController@cloneInvoice');
|
||||
Route::get('quotes/{public_id}/edit', 'InvoiceController@edit');
|
||||
Route::put('quotes/{public_id}', 'InvoiceController@update');
|
||||
Route::get('quotes/{public_id}', 'InvoiceController@edit');
|
||||
Route::post('quotes', 'InvoiceController@store');
|
||||
Route::get('quotes', 'QuoteController@index');
|
||||
Route::get('api/quotes/{client_id?}', array('as'=>'api.quotes', 'uses'=>'QuoteController@getDatatable'));
|
||||
Route::post('quotes/bulk', 'QuoteController@bulk');
|
||||
|
||||
Route::resource('payments', 'PaymentController');
|
||||
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');
|
||||
Route::get('api/payments/{client_id?}', array('as'=>'api.payments', 'uses'=>'PaymentController@getDatatable'));
|
||||
Route::post('payments/bulk', 'PaymentController@bulk');
|
||||
|
||||
Route::resource('credits', 'CreditController');
|
||||
Route::get('credits/create/{client_id?}/{invoice_id?}', 'CreditController@create');
|
||||
Route::get('api/credits/{client_id?}', array('as'=>'api.credits', 'uses'=>'CreditController@getDatatable'));
|
||||
Route::post('credits/bulk', 'CreditController@bulk');
|
||||
|
||||
get('/resend_confirmation', 'AccountController@resendConfirmation');
|
||||
post('/update_setup', 'AppController@updateSetup');
|
||||
|
||||
|
||||
// vendor
|
||||
Route::resource('vendors', 'VendorController');
|
||||
Route::get('api/vendor', array('as'=>'api.vendors', 'uses'=>'VendorController@getDatatable'));
|
||||
Route::post('vendors/bulk', 'VendorController@bulk');
|
||||
|
||||
// Expense
|
||||
Route::resource('expenses', 'ExpenseController');
|
||||
Route::get('expenses/create/{vendor_id?}/{client_id?}', 'ExpenseController@create');
|
||||
Route::get('api/expense', array('as'=>'api.expenses', 'uses'=>'ExpenseController@getDatatable'));
|
||||
Route::get('api/expenseVendor/{id}', array('as'=>'api.expense', 'uses'=>'ExpenseController@getDatatableVendor'));
|
||||
Route::post('expenses/bulk', 'ExpenseController@bulk');
|
||||
});
|
||||
|
||||
// Route groups for API
|
||||
@ -215,6 +235,7 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function()
|
||||
{
|
||||
Route::get('ping', 'ClientApiController@ping');
|
||||
Route::post('login', 'AccountApiController@login');
|
||||
Route::post('register', 'AccountApiController@register');
|
||||
Route::get('static', 'AccountApiController@getStaticData');
|
||||
Route::get('accounts', 'AccountApiController@show');
|
||||
Route::put('accounts', 'AccountApiController@update');
|
||||
@ -234,6 +255,9 @@ Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function()
|
||||
Route::resource('tax_rates', 'TaxRateApiController');
|
||||
Route::resource('users', 'UserApiController');
|
||||
Route::resource('expenses','ExpenseApiController');
|
||||
Route::post('add_token', 'AccountApiController@addDeviceToken');
|
||||
Route::post('update_notifications', 'AccountApiController@updatePushNotifications');
|
||||
Route::get('dashboard', 'DashboardApiController@index');
|
||||
|
||||
// Vendor
|
||||
Route::resource('vendors', 'VendorApiController');
|
||||
@ -485,6 +509,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);
|
||||
@ -509,7 +535,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
|
||||
define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
|
||||
define('NINJA_APP_URL', 'https://app.invoiceninja.com');
|
||||
define('NINJA_VERSION', '2.5.0.4');
|
||||
define('NINJA_VERSION', '2.5.1');
|
||||
define('NINJA_DATE', '2000-01-01');
|
||||
|
||||
define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja');
|
||||
@ -527,6 +553,8 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup');
|
||||
define('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all');
|
||||
|
||||
define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=');
|
||||
|
||||
define('COUNT_FREE_DESIGNS', 4);
|
||||
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
|
||||
define('PRODUCT_ONE_CLICK_INSTALL', 1);
|
||||
@ -549,6 +577,9 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('TEST_PASSWORD', 'password');
|
||||
define('API_SECRET', 'API_SECRET');
|
||||
|
||||
define('IOS_PRODUCTION_PUSH','ninjaIOS');
|
||||
define('IOS_DEV_PUSH','devNinjaIOS');
|
||||
|
||||
define('TOKEN_BILLING_DISABLED', 1);
|
||||
define('TOKEN_BILLING_OPT_IN', 2);
|
||||
define('TOKEN_BILLING_OPT_OUT', 3);
|
||||
@ -572,6 +603,9 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('REMINDER_FIELD_DUE_DATE', 1);
|
||||
define('REMINDER_FIELD_INVOICE_DATE', 2);
|
||||
|
||||
define('FILTER_INVOICE_DATE', 'invoice_date');
|
||||
define('FILTER_PAYMENT_DATE', 'payment_date');
|
||||
|
||||
define('SOCIAL_GOOGLE', 'Google');
|
||||
define('SOCIAL_FACEBOOK', 'Facebook');
|
||||
define('SOCIAL_GITHUB', 'GitHub');
|
||||
@ -581,6 +615,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');
|
||||
@ -668,8 +703,9 @@ if (Utils::isNinjaDev()) {
|
||||
*/
|
||||
|
||||
/*
|
||||
if (Utils::isNinjaDev() && Auth::check() && Auth::user()->id === 1)
|
||||
if (Utils::isNinjaDev())
|
||||
{
|
||||
Auth::loginUsingId(1);
|
||||
//ini_set('memory_limit','1024M');
|
||||
//Auth::loginUsingId(1);
|
||||
}
|
||||
*/
|
@ -104,8 +104,8 @@ class Login
|
||||
"<ORG>".$this->bank->org."\n".
|
||||
"<FID>".$this->bank->fid."\n".
|
||||
"</FI>\n".
|
||||
"<APPID>QMOFX\n".
|
||||
"<APPVER>1900\n".
|
||||
"<APPID>QWIN\n".
|
||||
"<APPVER>2500\n".
|
||||
"</SONRQ>\n".
|
||||
"</SIGNONMSGSRQV1>\n".
|
||||
"<SIGNUPMSGSRQV1>\n".
|
||||
@ -171,8 +171,8 @@ class Account
|
||||
"<ORG>".$this->login->bank->org."\n".
|
||||
"<FID>".$this->login->bank->fid."\n".
|
||||
"</FI>\n".
|
||||
"<APPID>QMOFX\n".
|
||||
"<APPVER>1900\n".
|
||||
"<APPID>QWIN\n".
|
||||
"<APPVER>2500\n".
|
||||
"</SONRQ>\n".
|
||||
"</SIGNONMSGSRQV1>\n";
|
||||
if ($this->type == 'BANK') {
|
||||
|
@ -72,7 +72,7 @@ class Utils
|
||||
|
||||
public static function requireHTTPS()
|
||||
{
|
||||
if (Request::root() === 'http://ninja.dev:8000') {
|
||||
if (Request::root() === 'http://ninja.dev' || Request::root() === 'http://ninja.dev:8000') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
@ -128,6 +143,13 @@ class Utils
|
||||
return App::getLocale() == 'en';
|
||||
}
|
||||
|
||||
public static function getLocaleRegion()
|
||||
{
|
||||
$parts = explode('_', App::getLocale());
|
||||
|
||||
return count($parts) ? $parts[0] : 'en';
|
||||
}
|
||||
|
||||
public static function getUserType()
|
||||
{
|
||||
if (Utils::isNinja()) {
|
||||
@ -225,7 +247,7 @@ class Utils
|
||||
return "***{$class}*** [{$code}] : {$exception->getFile()} [Line {$exception->getLine()}] => {$exception->getMessage()}";
|
||||
}
|
||||
|
||||
public static function logError($error, $context = 'PHP')
|
||||
public static function logError($error, $context = 'PHP', $info = false)
|
||||
{
|
||||
if ($error instanceof Exception) {
|
||||
$error = self::getErrorString($error);
|
||||
@ -249,7 +271,11 @@ class Utils
|
||||
'count' => Session::get('error_count', 0),
|
||||
];
|
||||
|
||||
Log::error($error."\n", $data);
|
||||
if ($info) {
|
||||
Log::info($error."\n", $data);
|
||||
} else {
|
||||
Log::error($error."\n", $data);
|
||||
}
|
||||
|
||||
/*
|
||||
Mail::queue('emails.error', ['message'=>$error.' '.json_encode($data)], function($message)
|
||||
@ -598,8 +624,8 @@ class Utils
|
||||
|
||||
private static function getMonth($offset)
|
||||
{
|
||||
$months = [ "January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December", ];
|
||||
$months = [ "january", "february", "march", "april", "may", "june",
|
||||
"july", "august", "september", "october", "november", "december", ];
|
||||
|
||||
$month = intval(date('n')) - 1;
|
||||
|
||||
@ -610,7 +636,7 @@ class Utils
|
||||
$month += 12;
|
||||
}
|
||||
|
||||
return $months[$month];
|
||||
return trans('texts.' . $months[$month]);
|
||||
}
|
||||
|
||||
private static function getQuarter($offset)
|
||||
@ -774,9 +800,11 @@ class Utils
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function exportData($output, $data)
|
||||
public static function exportData($output, $data, $headers = false)
|
||||
{
|
||||
if (count($data) > 0) {
|
||||
if ($headers) {
|
||||
fputcsv($output, $headers);
|
||||
} elseif (count($data) > 0) {
|
||||
fputcsv($output, array_keys($data[0]));
|
||||
}
|
||||
|
||||
|
@ -9,16 +9,20 @@ use App\Events\InvoiceInvitationWasViewed;
|
||||
use App\Events\QuoteInvitationWasViewed;
|
||||
use App\Events\QuoteInvitationWasApproved;
|
||||
use App\Events\PaymentWasCreated;
|
||||
use App\Ninja\Notifications;
|
||||
use App\Services\PushService;
|
||||
|
||||
class NotificationListener
|
||||
{
|
||||
protected $userMailer;
|
||||
protected $contactMailer;
|
||||
protected $pushService;
|
||||
|
||||
public function __construct(UserMailer $userMailer, ContactMailer $contactMailer)
|
||||
public function __construct(UserMailer $userMailer, ContactMailer $contactMailer, PushService $pushService)
|
||||
{
|
||||
$this->userMailer = $userMailer;
|
||||
$this->contactMailer = $contactMailer;
|
||||
$this->pushService = $pushService;
|
||||
}
|
||||
|
||||
private function sendEmails($invoice, $type, $payment = null)
|
||||
@ -35,26 +39,31 @@ class NotificationListener
|
||||
public function emailedInvoice(InvoiceWasEmailed $event)
|
||||
{
|
||||
$this->sendEmails($event->invoice, 'sent');
|
||||
$this->pushService->sendNotification($event->invoice, 'sent');
|
||||
}
|
||||
|
||||
public function emailedQuote(QuoteWasEmailed $event)
|
||||
{
|
||||
$this->sendEmails($event->quote, 'sent');
|
||||
$this->pushService->sendNotification($event->quote, 'sent');
|
||||
}
|
||||
|
||||
public function viewedInvoice(InvoiceInvitationWasViewed $event)
|
||||
{
|
||||
$this->sendEmails($event->invoice, 'viewed');
|
||||
$this->pushService->sendNotification($event->invoice, 'viewed');
|
||||
}
|
||||
|
||||
public function viewedQuote(QuoteInvitationWasViewed $event)
|
||||
{
|
||||
$this->sendEmails($event->quote, 'viewed');
|
||||
$this->pushService->sendNotification($event->quote, 'viewed');
|
||||
}
|
||||
|
||||
public function approvedQuote(QuoteInvitationWasApproved $event)
|
||||
{
|
||||
$this->sendEmails($event->quote, 'approved');
|
||||
$this->pushService->sendNotification($event->quote, 'approved');
|
||||
}
|
||||
|
||||
public function createdPayment(PaymentWasCreated $event)
|
||||
@ -66,6 +75,8 @@ class NotificationListener
|
||||
|
||||
$this->contactMailer->sendPaymentConfirmation($event->payment);
|
||||
$this->sendEmails($event->payment->invoice, 'paid', $event->payment);
|
||||
|
||||
$this->pushService->sendNotification($event->payment->invoice, 'paid');
|
||||
}
|
||||
|
||||
}
|
@ -24,66 +24,46 @@ class SubscriptionListener
|
||||
{
|
||||
public function createdClient(ClientWasCreated $event)
|
||||
{
|
||||
if ( ! Auth::check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transformer = new ClientTransformer(Auth::user()->account);
|
||||
$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_CLIENT, $event->client, $transformer);
|
||||
$transformer = new ClientTransformer($event->client->account);
|
||||
$this->checkSubscriptions(EVENT_CREATE_CLIENT, $event->client, $transformer);
|
||||
}
|
||||
|
||||
public function createdQuote(QuoteWasCreated $event)
|
||||
{
|
||||
if ( ! Auth::check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transformer = new InvoiceTransformer(Auth::user()->account);
|
||||
$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_QUOTE, $event->quote, $transformer, ENTITY_CLIENT);
|
||||
$transformer = new InvoiceTransformer($event->quote->account);
|
||||
$this->checkSubscriptions(EVENT_CREATE_QUOTE, $event->quote, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
public function createdPayment(PaymentWasCreated $event)
|
||||
{
|
||||
if ( ! Auth::check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transformer = new PaymentTransformer(Auth::user()->account);
|
||||
$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_PAYMENT, $event->payment, $transformer, [ENTITY_CLIENT, ENTITY_INVOICE]);
|
||||
}
|
||||
|
||||
public function createdCredit(CreditWasCreated $event)
|
||||
{
|
||||
if ( ! Auth::check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_CREDIT, $event->credit);
|
||||
$transformer = new PaymentTransformer($event->payment->account);
|
||||
$this->checkSubscriptions(EVENT_CREATE_PAYMENT, $event->payment, $transformer, [ENTITY_CLIENT, ENTITY_INVOICE]);
|
||||
}
|
||||
|
||||
public function createdInvoice(InvoiceWasCreated $event)
|
||||
{
|
||||
if ( ! Auth::check()) {
|
||||
return;
|
||||
}
|
||||
$transformer = new InvoiceTransformer($event->invoice->account);
|
||||
$this->checkSubscriptions(EVENT_CREATE_INVOICE, $event->invoice, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
public function createdCredit(CreditWasCreated $event)
|
||||
{
|
||||
|
||||
$transformer = new InvoiceTransformer(Auth::user()->account);
|
||||
$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_INVOICE, $event->invoice, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
public function createdVendor(VendorWasCreated $event)
|
||||
{
|
||||
//$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_VENDOR, $event->vendor);
|
||||
|
||||
}
|
||||
|
||||
public function createdExpense(ExpenseWasCreated $event)
|
||||
{
|
||||
//$this->checkSubscriptions(ACTIVITY_TYPE_CREATE_EXPENSE, $event->expense);
|
||||
|
||||
}
|
||||
|
||||
private function checkSubscriptions($activityTypeId, $entity, $transformer, $include = '')
|
||||
private function checkSubscriptions($eventId, $entity, $transformer, $include = '')
|
||||
{
|
||||
$subscription = $entity->account->getSubscription($activityTypeId);
|
||||
$subscription = $entity->account->getSubscription($eventId);
|
||||
|
||||
if ($subscription) {
|
||||
$manager = new Manager();
|
||||
@ -93,6 +73,11 @@ class SubscriptionListener
|
||||
$resource = new Item($entity, $transformer, $entity->getEntityType());
|
||||
$data = $manager->createData($resource)->toArray();
|
||||
|
||||
// For legacy Zapier support
|
||||
if (isset($data['client_id'])) {
|
||||
$data['client_name'] = $entity->client->getDisplayName();
|
||||
}
|
||||
|
||||
Utils::notifyZapier($subscription, $data);
|
||||
}
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ class Account extends Eloquent
|
||||
return $height;
|
||||
}
|
||||
|
||||
public function createInvoice($entityType, $clientId = null)
|
||||
public function createInvoice($entityType = ENTITY_INVOICE, $clientId = null)
|
||||
{
|
||||
$invoice = Invoice::createNew();
|
||||
|
||||
@ -614,6 +614,28 @@ class Account extends Eloquent
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function loadAllData($updatedAt = null)
|
||||
{
|
||||
$map = [
|
||||
'users' => [],
|
||||
'clients' => ['contacts'],
|
||||
'invoices' => ['invoice_items', 'user', 'client', 'payments'],
|
||||
'products' => [],
|
||||
'tax_rates' => [],
|
||||
'expenses' => ['client', 'invoice', 'vendor'],
|
||||
'payments' => ['invoice'],
|
||||
];
|
||||
|
||||
foreach ($map as $key => $values) {
|
||||
$this->load([$key => function($query) use ($values, $updatedAt) {
|
||||
$query->withTrashed()->with($values);
|
||||
if ($updatedAt) {
|
||||
$query->where('updated_at', '>=', $updatedAt);
|
||||
}
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
public function loadLocalizationSettings($client = false)
|
||||
{
|
||||
$this->load('timezone', 'date_format', 'datetime_format', 'language');
|
||||
@ -661,7 +683,7 @@ class Account extends Eloquent
|
||||
'subtotal',
|
||||
'paid_to_date',
|
||||
'balance_due',
|
||||
'amount_due',
|
||||
'partial_due',
|
||||
'terms',
|
||||
'your_invoice',
|
||||
'quote',
|
||||
@ -1001,7 +1023,7 @@ class Account extends Eloquent
|
||||
return true;
|
||||
}
|
||||
|
||||
public function showCustomField($field, $entity)
|
||||
public function showCustomField($field, $entity = false)
|
||||
{
|
||||
if ($this->isPro()) {
|
||||
return $this->$field ? true : false;
|
||||
@ -1050,7 +1072,13 @@ class Account extends Eloquent
|
||||
|
||||
public function hasLargeFont()
|
||||
{
|
||||
return stripos($this->getBodyFontName(), 'chinese') || stripos($this->getHeaderFontName(), 'chinese');
|
||||
foreach (['chinese', 'japanese'] as $language) {
|
||||
if (stripos($this->getBodyFontName(), $language) || stripos($this->getHeaderFontName(), $language)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFontsUrl($protocol = ''){
|
||||
|
@ -155,6 +155,14 @@ class Client extends EntityModel
|
||||
$contact->send_invoice = true;
|
||||
}
|
||||
|
||||
if (!Utils::isPro() || $this->account->enable_portal_password){
|
||||
if(!empty($data['password']) && $data['password']!='-%unchanged%-'){
|
||||
$contact->password = bcrypt($data['password']);
|
||||
} else if(empty($data['password'])){
|
||||
$contact->password = null;
|
||||
}
|
||||
}
|
||||
|
||||
$contact->fill($data);
|
||||
$contact->is_primary = $isPrimary;
|
||||
|
||||
@ -272,6 +280,11 @@ class Client extends EntityModel
|
||||
return $token ? "https://dashboard.stripe.com/customers/{$token}" : false;
|
||||
}
|
||||
|
||||
public function getAmount()
|
||||
{
|
||||
return $this->balance + $this->paid_to_date;
|
||||
}
|
||||
|
||||
public function getCurrencyId()
|
||||
{
|
||||
if ($this->currency_id) {
|
||||
|
@ -3,10 +3,14 @@
|
||||
use HTML;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||
|
||||
class Contact extends EntityModel
|
||||
class Contact extends EntityModel implements AuthenticatableContract, CanResetPasswordContract
|
||||
{
|
||||
use SoftDeletes;
|
||||
use SoftDeletes, Authenticatable, CanResetPassword;
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
protected $fillable = [
|
||||
|
@ -42,7 +42,7 @@ class EntityModel extends Eloquent
|
||||
{
|
||||
$className = get_called_class();
|
||||
|
||||
return $className::scope($publicId)->withTrashed()->pluck('id');
|
||||
return $className::scope($publicId)->withTrashed()->value('id');
|
||||
}
|
||||
|
||||
public function getActivityKey()
|
||||
@ -81,6 +81,11 @@ class EntityModel extends Eloquent
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function scopeWithArchived($query)
|
||||
{
|
||||
return $query->withTrashed()->where('is_deleted', '=', false);
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->public_id;
|
||||
@ -108,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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -129,13 +129,21 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAmountPaid()
|
||||
public function getAmountPaid($calculate = false)
|
||||
{
|
||||
if ($this->is_quote || $this->is_recurring) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($this->amount - $this->balance);
|
||||
if ($calculate) {
|
||||
$amount = 0;
|
||||
foreach ($this->payments as $payment) {
|
||||
$amount += $payment->amount;
|
||||
}
|
||||
return $amount;
|
||||
} else {
|
||||
return ($this->amount - $this->balance);
|
||||
}
|
||||
}
|
||||
|
||||
public function trashed()
|
||||
@ -192,6 +200,11 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return $this->hasMany('App\Models\Invoice', 'recurring_invoice_id');
|
||||
}
|
||||
|
||||
public function frequency()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Frequency');
|
||||
}
|
||||
|
||||
public function invitations()
|
||||
{
|
||||
return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id');
|
||||
@ -442,12 +455,16 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
'show_item_taxes',
|
||||
'custom_invoice_text_label1',
|
||||
'custom_invoice_text_label2',
|
||||
'custom_invoice_item_label1',
|
||||
'custom_invoice_item_label2',
|
||||
]);
|
||||
|
||||
foreach ($this->invoice_items as $invoiceItem) {
|
||||
$invoiceItem->setVisible([
|
||||
'product_key',
|
||||
'notes',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
'cost',
|
||||
'qty',
|
||||
'tax_name',
|
||||
@ -752,6 +769,98 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
|
||||
return Utils::decodePDF($pdfString);
|
||||
}
|
||||
|
||||
public function getItemTaxable($invoiceItem, $invoiceTotal)
|
||||
{
|
||||
$total = $invoiceItem->qty * $invoiceItem->cost;
|
||||
|
||||
if ($this->discount > 0) {
|
||||
if ($this->is_amount_discount) {
|
||||
$total -= $invoiceTotal ? ($total / $invoiceTotal * $this->discount) : 0;
|
||||
} else {
|
||||
$total *= (100 - $this->discount) / 100;
|
||||
$total = round($total, 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
public function getTaxable()
|
||||
{
|
||||
$total = 0;
|
||||
|
||||
foreach ($this->invoice_items as $invoiceItem) {
|
||||
$total += $invoiceItem->qty * $invoiceItem->cost;
|
||||
}
|
||||
|
||||
if ($this->discount > 0) {
|
||||
if ($this->is_amount_discount) {
|
||||
$total -= $this->discount;
|
||||
} else {
|
||||
$total *= (100 - $this->discount) / 100;
|
||||
$total = round($total, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->custom_value1 && $this->custom_taxes1) {
|
||||
$total += $this->custom_value1;
|
||||
}
|
||||
|
||||
if ($this->custom_value2 && $this->custom_taxes2) {
|
||||
$total += $this->custom_value2;
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
public function getTaxes($calculatePaid = false)
|
||||
{
|
||||
$taxes = [];
|
||||
$taxable = $this->getTaxable();
|
||||
|
||||
if ($this->tax_rate && $this->tax_name) {
|
||||
$taxAmount = $taxable * ($this->tax_rate / 100);
|
||||
$taxAmount = round($taxAmount, 2);
|
||||
|
||||
if ($taxAmount) {
|
||||
$taxes[$this->tax_name.$this->tax_rate] = [
|
||||
'name' => $this->tax_name,
|
||||
'rate' => $this->tax_rate,
|
||||
'amount' => $taxAmount,
|
||||
'paid' => round($this->getAmountPaid($calculatePaid) / $this->amount * $taxAmount, 2)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->invoice_items as $invoiceItem) {
|
||||
if ( ! $invoiceItem->tax_rate || ! $invoiceItem->tax_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$taxAmount = $this->getItemTaxable($invoiceItem, $taxable);
|
||||
$taxAmount = $taxable * ($invoiceItem->tax_rate / 100);
|
||||
$taxAmount = round($taxAmount, 2);
|
||||
|
||||
if ($taxAmount) {
|
||||
$key = $invoiceItem->tax_name.$invoiceItem->tax_rate;
|
||||
|
||||
if ( ! isset($taxes[$key])) {
|
||||
$taxes[$key] = [
|
||||
'amount' => 0,
|
||||
'paid' => 0
|
||||
];
|
||||
}
|
||||
|
||||
$taxes[$key]['amount'] += $taxAmount;
|
||||
$taxes[$key]['paid'] += $this->amount && $taxAmount ? round($this->getAmountPaid($calculatePaid) / $this->amount * $taxAmount, 2) : 0;
|
||||
$taxes[$key]['name'] = $invoiceItem->tax_name;
|
||||
$taxes[$key]['rate'] = $invoiceItem->tax_rate;
|
||||
}
|
||||
}
|
||||
|
||||
return $taxes;
|
||||
}
|
||||
}
|
||||
|
||||
Invoice::creating(function ($invoice) {
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -16,4 +16,8 @@ class TaxRate extends EntityModel
|
||||
{
|
||||
return ENTITY_TAX_RATE;
|
||||
}
|
||||
|
||||
public function canEdit() {
|
||||
return Auth::user()->hasPermission('admin');
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ 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;
|
||||
|
||||
@ -254,6 +259,68 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
&& $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) {
|
||||
|
@ -5,6 +5,7 @@ use DB;
|
||||
use Carbon;
|
||||
use App\Events\VendorWasCreated;
|
||||
use App\Events\VendorWasUpdated;
|
||||
use App\Events\VendorWasDeleted;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php namespace App\Ninja\Mailers;
|
||||
|
||||
use HTML;
|
||||
use Form;
|
||||
use Utils;
|
||||
use Event;
|
||||
use URL;
|
||||
@ -27,6 +27,7 @@ class ContactMailer extends Mailer
|
||||
'firstName',
|
||||
'invoice',
|
||||
'quote',
|
||||
'password',
|
||||
'viewLink',
|
||||
'viewButton',
|
||||
'paymentLink',
|
||||
@ -110,6 +111,13 @@ class ContactMailer extends Mailer
|
||||
'amount' => $invoice->getRequestedAmount()
|
||||
];
|
||||
|
||||
if (empty($invitation->contact->password) && $account->isPro() && $account->enable_portal_password && $account->send_portal_password) {
|
||||
// The contact needs a password
|
||||
$variables['password'] = $password = $this->generatePassword();
|
||||
$invitation->contact->password = bcrypt($password);
|
||||
$invitation->contact->save();
|
||||
}
|
||||
|
||||
$data = [
|
||||
'body' => $this->processVariables($body, $variables),
|
||||
'link' => $invitation->getLink(),
|
||||
@ -144,6 +152,28 @@ class ContactMailer extends Mailer
|
||||
}
|
||||
}
|
||||
|
||||
protected function generatePassword($length = 9)
|
||||
{
|
||||
$sets = array(
|
||||
'abcdefghjkmnpqrstuvwxyz',
|
||||
'ABCDEFGHJKMNPQRSTUVWXYZ',
|
||||
'23456789',
|
||||
);
|
||||
$all = '';
|
||||
$password = '';
|
||||
foreach($sets as $set)
|
||||
{
|
||||
$password .= $set[array_rand(str_split($set))];
|
||||
$all .= $set;
|
||||
}
|
||||
$all = str_split($all);
|
||||
for($i = 0; $i < $length - count($sets); $i++)
|
||||
$password .= $all[array_rand($all)];
|
||||
$password = str_shuffle($password);
|
||||
|
||||
return $password;
|
||||
}
|
||||
|
||||
public function sendPaymentConfirmation(Payment $payment)
|
||||
{
|
||||
$account = $payment->account;
|
||||
@ -232,6 +262,7 @@ class ContactMailer extends Mailer
|
||||
$client = $data['client'];
|
||||
$invitation = $data['invitation'];
|
||||
$invoice = $invitation->invoice;
|
||||
$passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
|
||||
|
||||
$variables = [
|
||||
'$footer' => $account->getEmailFooter(),
|
||||
@ -245,10 +276,11 @@ class ContactMailer extends Mailer
|
||||
'$invoice' => $invoice->invoice_number,
|
||||
'$quote' => $invoice->invoice_number,
|
||||
'$link' => $invitation->getLink(),
|
||||
'$viewLink' => $invitation->getLink(),
|
||||
'$viewButton' => HTML::emailViewButton($invitation->getLink(), $invoice->getEntityType()),
|
||||
'$paymentLink' => $invitation->getLink('payment'),
|
||||
'$paymentButton' => HTML::emailPaymentButton($invitation->getLink('payment')),
|
||||
'$password' => $passwordHTML,
|
||||
'$viewLink' => $invitation->getLink().'$password',
|
||||
'$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password',
|
||||
'$paymentLink' => $invitation->getLink('payment').'$password',
|
||||
'$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password',
|
||||
'$customClient1' => $account->custom_client_label1,
|
||||
'$customClient2' => $account->custom_client_label2,
|
||||
'$customInvoice1' => $account->custom_invoice_text_label1,
|
||||
@ -260,10 +292,21 @@ class ContactMailer extends Mailer
|
||||
$camelType = Gateway::getPaymentTypeName($type);
|
||||
$type = Utils::toSnakeCase($camelType);
|
||||
$variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}";
|
||||
$variables["\${$camelType}Button"] = HTML::emailPaymentButton($invitation->getLink('payment') . "/{$type}");
|
||||
$variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}");
|
||||
}
|
||||
|
||||
$includesPasswordPlaceholder = strpos($template, '$password') !== false;
|
||||
|
||||
$str = str_replace(array_keys($variables), array_values($variables), $template);
|
||||
|
||||
if(!$includesPasswordPlaceholder && $passwordHTML){
|
||||
$pos = strrpos($str, '$password');
|
||||
if($pos !== false)
|
||||
{
|
||||
$str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */);
|
||||
}
|
||||
}
|
||||
$str = str_replace('$password', '', $str);
|
||||
$str = autolink($str, 100);
|
||||
|
||||
return $str;
|
||||
|
@ -81,7 +81,7 @@ class Mailer
|
||||
$emailError = $exception->getMessage();
|
||||
}
|
||||
|
||||
Utils::logError("Email Error: $emailError");
|
||||
//Utils::logError("Email Error: $emailError");
|
||||
|
||||
if (isset($data['invitation'])) {
|
||||
$invitation = $data['invitation'];
|
||||
|
96
app/Ninja/Notifications/PushFactory.php
Normal file
96
app/Ninja/Notifications/PushFactory.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ninja\Notifications;
|
||||
|
||||
use Davibennun\LaravelPushNotification\Facades\PushNotification;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class PushFactory
|
||||
* @package App\Ninja\Notifications
|
||||
*/
|
||||
|
||||
class PushFactory
|
||||
{
|
||||
/**
|
||||
* PushFactory constructor.
|
||||
*
|
||||
* @param $this->certificate - Development or production.
|
||||
*
|
||||
* Static variables defined in routes.php
|
||||
*
|
||||
* IOS_PRODUCTION_PUSH
|
||||
* IOS_DEV_PUSH
|
||||
*/
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->certificate = IOS_DEV_PUSH;
|
||||
}
|
||||
|
||||
/**
|
||||
* customMessage function
|
||||
*
|
||||
* Send a message with a nested custom payload to perform additional trickery within application
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param $token
|
||||
* @param $message
|
||||
* @param $messageArray
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customMessage($token, $message, $messageArray)
|
||||
{
|
||||
$customMessage = PushNotification::Message($message, $messageArray);
|
||||
|
||||
$this->message($token, $customMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* message function
|
||||
*
|
||||
* Send a plain text only message to a single device.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param $token - device token
|
||||
* @param $message - user specific message
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
|
||||
public function message($token, $message)
|
||||
{
|
||||
PushNotification::app($this->certificate)
|
||||
->to($token)
|
||||
->send($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* getFeedback function
|
||||
*
|
||||
* Returns an array of expired/invalid tokens to be removed from iOS PUSH notifications.
|
||||
*
|
||||
* We need to run this once ~ 24hrs
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param string $token - A valid token (can be any valid token)
|
||||
* @param string $message - Nil value for message
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFeedback($token, $message = '')
|
||||
{
|
||||
|
||||
$feedback = PushNotification::app($this->certificate)
|
||||
->to($token)
|
||||
->send($message);
|
||||
|
||||
return $feedback->getFeedback();
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Ninja\Presenters;
|
||||
|
||||
use URL;
|
||||
use Utils;
|
||||
use Laracasts\Presenter\Presenter;
|
||||
|
||||
@ -26,6 +27,15 @@ class ClientPresenter extends Presenter {
|
||||
}
|
||||
|
||||
return "<span class=\"label label-{$class}\">{$text}</span>";
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return URL::to('/clients/' . $this->entity->public_id);
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/clients/' . $this->entity->public_id, $this->entity->getDisplayName());
|
||||
}
|
||||
}
|
@ -20,4 +20,14 @@ class ExpensePresenter extends Presenter {
|
||||
{
|
||||
return round($this->entity->amount * $this->entity->exchange_rate, 2);
|
||||
}
|
||||
|
||||
public function invoiced_amount()
|
||||
{
|
||||
return $this->entity->invoice_id ? $this->converted_amount() : 0;
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/expenses/' . $this->entity->public_id, $this->entity->name);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Ninja\Presenters;
|
||||
|
||||
use URL;
|
||||
use Utils;
|
||||
use Laracasts\Presenter\Presenter;
|
||||
|
||||
@ -18,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 {
|
||||
@ -40,9 +41,15 @@ class InvoicePresenter extends Presenter {
|
||||
|
||||
public function status()
|
||||
{
|
||||
$status = $this->entity->invoice_status ? $this->entity->invoice_status->name : 'draft';
|
||||
$status = strtolower($status);
|
||||
return trans("texts.status_{$status}");
|
||||
if ($this->entity->is_deleted) {
|
||||
return trans('texts.deleted');
|
||||
} elseif ($this->entity->trashed()) {
|
||||
return trans('texts.archived');
|
||||
} else {
|
||||
$status = $this->entity->invoice_status ? $this->entity->invoice_status->name : 'draft';
|
||||
$status = strtolower($status);
|
||||
return trans("texts.status_{$status}");
|
||||
}
|
||||
}
|
||||
|
||||
public function invoice_date()
|
||||
@ -55,4 +62,24 @@ class InvoicePresenter extends Presenter {
|
||||
return Utils::fromSqlDate($this->entity->due_date);
|
||||
}
|
||||
|
||||
public function frequency()
|
||||
{
|
||||
return $this->entity->frequency ? $this->entity->frequency->name : '';
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return URL::to('/invoices/' . $this->entity->public_id);
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/invoices/' . $this->entity->public_id, $this->entity->invoice_number);
|
||||
}
|
||||
|
||||
public function email()
|
||||
{
|
||||
$client = $this->entity->client;
|
||||
return count($client->contacts) ? $client->contacts[0]->email : '';
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Ninja\Presenters;
|
||||
|
||||
use URL;
|
||||
use Utils;
|
||||
use Laracasts\Presenter\Presenter;
|
||||
|
||||
@ -24,4 +25,14 @@ class PaymentPresenter extends Presenter {
|
||||
}
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
return URL::to('/payments/' . $this->entity->public_id . '/edit');
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/payments/' . $this->entity->public_id . '/edit', $this->entity->getDisplayName());
|
||||
}
|
||||
|
||||
}
|
@ -9,4 +9,9 @@ class VendorPresenter extends Presenter {
|
||||
{
|
||||
return $this->entity->country ? $this->entity->country->name : '';
|
||||
}
|
||||
|
||||
public function link()
|
||||
{
|
||||
return link_to('/vendors/' . $this->entity->public_id, $this->entity->name);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ use Request;
|
||||
use Session;
|
||||
use Utils;
|
||||
use DB;
|
||||
use URL;
|
||||
use stdClass;
|
||||
use Validator;
|
||||
use Schema;
|
||||
@ -58,7 +59,7 @@ class AccountRepository
|
||||
}
|
||||
|
||||
$user->confirmed = !Utils::isNinja();
|
||||
$user->registered = !Utils::isNinja() && $user->email;
|
||||
$user->registered = !Utils::isNinja() || $email;
|
||||
|
||||
if (!$user->confirmed) {
|
||||
$user->confirmation_code = str_random(RANDOM_KEY_LENGTH);
|
||||
@ -69,49 +70,135 @@ class AccountRepository
|
||||
return $account;
|
||||
}
|
||||
|
||||
public function getSearchData()
|
||||
public function getSearchData($account)
|
||||
{
|
||||
$clients = \DB::table('clients')
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('clients.account_id', '=', \Auth::user()->account_id)
|
||||
->whereRaw("clients.name <> ''")
|
||||
->select(\DB::raw("'clients' as type, '" . trans('texts.clients') . "' as trans_type, clients.public_id, clients.name, '' as token"));
|
||||
$data = $this->getAccountSearchData($account);
|
||||
|
||||
$contacts = \DB::table('clients')
|
||||
->join('contacts', 'contacts.client_id', '=', 'clients.id')
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('clients.account_id', '=', \Auth::user()->account_id)
|
||||
->whereRaw("CONCAT(contacts.first_name, contacts.last_name, contacts.email) <> ''")
|
||||
->select(\DB::raw("'clients' as type, '" . trans('texts.contacts') . "' as trans_type, clients.public_id, CONCAT(contacts.first_name, ' ', contacts.last_name, ' ', contacts.email) as name, '' as token"));
|
||||
$data['navigation'] = $this->getNavigationSearchData();
|
||||
|
||||
$invoices = \DB::table('clients')
|
||||
->join('invoices', 'invoices.client_id', '=', 'clients.id')
|
||||
->where('clients.account_id', '=', \Auth::user()->account_id)
|
||||
->where('clients.deleted_at', '=', null)
|
||||
->where('invoices.deleted_at', '=', null)
|
||||
->select(\DB::raw("'invoices' as type, '" . trans('texts.invoices') . "' as trans_type, invoices.public_id, CONCAT(invoices.invoice_number, ': ', clients.name) as name, invoices.invoice_number as token"));
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
private function getAccountSearchData($account)
|
||||
{
|
||||
$data = [
|
||||
'clients' => [],
|
||||
'contacts' => [],
|
||||
'invoices' => [],
|
||||
'quotes' => [],
|
||||
];
|
||||
|
||||
foreach ($clients->union($contacts)->union($invoices)->get() as $row) {
|
||||
$type = $row->trans_type;
|
||||
// include custom client fields in search
|
||||
if ($account->custom_client_label1) {
|
||||
$data[$account->custom_client_label1] = [];
|
||||
}
|
||||
if ($account->custom_client_label2) {
|
||||
$data[$account->custom_client_label2] = [];
|
||||
}
|
||||
|
||||
if (!isset($data[$type])) {
|
||||
$data[$type] = [];
|
||||
$clients = Client::scope()
|
||||
->with('contacts', 'invoices')
|
||||
->get();
|
||||
|
||||
foreach ($clients as $client) {
|
||||
if ($client->name) {
|
||||
$data['clients'][] = [
|
||||
'value' => $client->name,
|
||||
'tokens' => $client->name,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
|
||||
$tokens = explode(' ', $row->name);
|
||||
$tokens[] = $type;
|
||||
|
||||
if ($type == 'Invoices') {
|
||||
$tokens[] = intVal($row->token).'';
|
||||
if ($client->custom_value1) {
|
||||
$data[$account->custom_client_label1][] = [
|
||||
'value' => "{$client->custom_value1}: " . $client->getDisplayName(),
|
||||
'tokens' => $client->custom_value1,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
if ($client->custom_value2) {
|
||||
$data[$account->custom_client_label2][] = [
|
||||
'value' => "{$client->custom_value2}: " . $client->getDisplayName(),
|
||||
'tokens' => $client->custom_value2,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
|
||||
$data[$type][] = [
|
||||
'value' => $row->name,
|
||||
'public_id' => $row->public_id,
|
||||
'tokens' => $tokens,
|
||||
'entity_type' => $row->type,
|
||||
foreach ($client->contacts as $contact) {
|
||||
if ($contact->getFullName()) {
|
||||
$data['contacts'][] = [
|
||||
'value' => $contact->getDisplayName(),
|
||||
'tokens' => $contact->getDisplayName(),
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
if ($contact->email) {
|
||||
$data['contacts'][] = [
|
||||
'value' => $contact->email,
|
||||
'tokens' => $contact->email,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($client->invoices as $invoice) {
|
||||
$entityType = $invoice->getEntityType();
|
||||
$data["{$entityType}s"][] = [
|
||||
'value' => $invoice->getDisplayName() . ': ' . $client->getDisplayName(),
|
||||
'tokens' => $invoice->getDisplayName() . ': ' . $client->getDisplayName(),
|
||||
'url' => $invoice->present()->url,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getNavigationSearchData()
|
||||
{
|
||||
$entityTypes = [
|
||||
ENTITY_INVOICE,
|
||||
ENTITY_CLIENT,
|
||||
ENTITY_QUOTE,
|
||||
ENTITY_TASK,
|
||||
ENTITY_EXPENSE,
|
||||
ENTITY_RECURRING_INVOICE,
|
||||
ENTITY_PAYMENT,
|
||||
ENTITY_CREDIT
|
||||
];
|
||||
|
||||
foreach ($entityTypes as $entityType) {
|
||||
$features[] = [
|
||||
"new_{$entityType}",
|
||||
"/{$entityType}s/create",
|
||||
];
|
||||
$features[] = [
|
||||
"list_{$entityType}s",
|
||||
"/{$entityType}s",
|
||||
];
|
||||
}
|
||||
|
||||
$features[] = ['dashboard', '/dashboard'];
|
||||
$features[] = ['customize_design', '/settings/customize_design'];
|
||||
$features[] = ['new_tax_rate', '/tax_rates/create'];
|
||||
$features[] = ['new_product', '/products/create'];
|
||||
$features[] = ['new_user', '/users/create'];
|
||||
$features[] = ['custom_fields', '/settings/invoice_settings'];
|
||||
|
||||
$settings = array_merge(Account::$basicSettings, Account::$advancedSettings);
|
||||
|
||||
foreach ($settings as $setting) {
|
||||
$features[] = [
|
||||
$setting,
|
||||
"/settings/{$setting}",
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($features as $feature) {
|
||||
$data[] = [
|
||||
'value' => trans('texts.' . $feature[0]),
|
||||
'tokens' => trans('texts.' . $feature[0]),
|
||||
'url' => URL::to($feature[1])
|
||||
];
|
||||
}
|
||||
|
||||
@ -127,8 +214,10 @@ class AccountRepository
|
||||
$account = Auth::user()->account;
|
||||
$client = $this->getNinjaClient($account);
|
||||
$invitation = $this->createNinjaInvoice($client, $account);
|
||||
|
||||
return $invitation;
|
||||
}
|
||||
|
||||
public function createNinjaInvoice($client, $clientAccount)
|
||||
{
|
||||
$account = $this->getNinjaAccount();
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php namespace App\Ninja\Repositories;
|
||||
|
||||
use DB;
|
||||
use Cache;
|
||||
use App\Ninja\Repositories\BaseRepository;
|
||||
use App\Models\Client;
|
||||
use App\Models\Contact;
|
||||
@ -45,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')) {
|
||||
@ -74,6 +76,17 @@ class ClientRepository extends BaseRepository
|
||||
$client = Client::scope($publicId)->with('contacts')->firstOrFail();
|
||||
}
|
||||
|
||||
// convert currency code to id
|
||||
if (isset($data['currency_code'])) {
|
||||
$currencyCode = strtolower($data['currency_code']);
|
||||
$currency = Cache::get('currencies')->filter(function($item) use ($currencyCode) {
|
||||
return strtolower($item->code) == $currencyCode;
|
||||
})->first();
|
||||
if ($currency) {
|
||||
$data['currency_id'] = $currency->id;
|
||||
}
|
||||
}
|
||||
|
||||
$client->fill($data);
|
||||
$client->save();
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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',
|
||||
|
@ -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)) {
|
||||
@ -170,7 +172,7 @@ class InvoiceRepository extends BaseRepository
|
||||
);
|
||||
|
||||
$table = \Datatable::query($query)
|
||||
->addColumn('invoice_number', function ($model) use ($entityType) { return link_to('/view/'.$model->invitation_key, $model->invoice_number); })
|
||||
->addColumn('invoice_number', function ($model) use ($entityType) { return link_to('/view/'.$model->invitation_key, $model->invoice_number)->toHtml(); })
|
||||
->addColumn('invoice_date', function ($model) { return Utils::fromSqlDate($model->invoice_date); })
|
||||
->addColumn('amount', function ($model) { return Utils::formatMoney($model->amount, $model->currency_id, $model->country_id); });
|
||||
|
||||
@ -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;
|
||||
@ -398,36 +400,47 @@ class InvoiceRepository extends BaseRepository
|
||||
|
||||
foreach ($data['invoice_items'] as $item) {
|
||||
$item = (array) $item;
|
||||
if (!$item['cost'] && !$item['product_key'] && !$item['notes']) {
|
||||
if (empty($item['cost']) && empty($item['product_key']) && empty($item['notes']) && empty($item['custom_value1']) && empty($item['custom_value2'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,6 +452,13 @@ class InvoiceRepository extends BaseRepository
|
||||
$invoiceItem->qty = Utils::parseFloat($item['qty']);
|
||||
$invoiceItem->tax_rate = 0;
|
||||
|
||||
if (isset($item['custom_value1'])) {
|
||||
$invoiceItem->custom_value1 = $item['custom_value1'];
|
||||
}
|
||||
if (isset($item['custom_value2'])) {
|
||||
$invoiceItem->custom_value2 = $item['custom_value2'];
|
||||
}
|
||||
|
||||
if (isset($item['tax_rate']) && isset($item['tax_name']) && $item['tax_name']) {
|
||||
$invoiceItem['tax_rate'] = Utils::parseFloat($item['tax_rate']);
|
||||
$invoiceItem['tax_name'] = trim($item['tax_name']);
|
||||
@ -603,7 +623,7 @@ class InvoiceRepository extends BaseRepository
|
||||
$invoice = Invoice::createNew($recurInvoice);
|
||||
$invoice->client_id = $recurInvoice->client_id;
|
||||
$invoice->recurring_invoice_id = $recurInvoice->id;
|
||||
$invoice->invoice_number = 'R'.$recurInvoice->account->getNextInvoiceNumber($recurInvoice);
|
||||
$invoice->invoice_number = $recurInvoice->account->recurring_invoice_number_prefix . $recurInvoice->account->getNextInvoiceNumber($recurInvoice);
|
||||
$invoice->amount = $recurInvoice->amount;
|
||||
$invoice->balance = $recurInvoice->amount;
|
||||
$invoice->invoice_date = date_create()->format('Y-m-d');
|
||||
|
@ -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'
|
||||
);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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')) {
|
||||
|
@ -21,7 +21,11 @@ class UserTransformer extends EntityTransformer
|
||||
'registered' => (bool) $user->registered,
|
||||
'confirmed' => (bool) $user->confirmed,
|
||||
'oauth_user_id' => $user->oauth_user_id,
|
||||
'oauth_provider_id' => $user->oauth_provider_id
|
||||
'oauth_provider_id' => $user->oauth_provider_id,
|
||||
'notify_sent' => (bool) $user->notify_sent,
|
||||
'notify_viewed' => (bool) $user->notify_viewed,
|
||||
'notify_paid' => (bool) $user->notify_paid,
|
||||
'notify_approved' => (bool) $user->notify_approved,
|
||||
];
|
||||
}
|
||||
}
|
@ -4,9 +4,13 @@ use Session;
|
||||
use Auth;
|
||||
use Utils;
|
||||
use HTML;
|
||||
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 {
|
||||
@ -18,62 +22,74 @@ class AppServiceProvider extends ServiceProvider {
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
HTML::macro('nav_link', function($url, $text, $url2 = '', $extra = '') {
|
||||
Form::macro('image_data', function($imagePath) {
|
||||
return 'data:image/jpeg;base64,' . base64_encode(file_get_contents($imagePath));
|
||||
});
|
||||
|
||||
Form::macro('nav_link', function($url, $text, $url2 = '', $extra = '') {
|
||||
$capitalize = config('former.capitalize_translations');
|
||||
$class = ( Request::is($url) || Request::is($url.'/*') || Request::is($url2.'/*') ) ? ' class="active"' : '';
|
||||
$title = ucwords(trans("texts.$text")) . Utils::getProLabel($text);
|
||||
if ($capitalize) {
|
||||
$title = ucwords(trans("texts.$text")) . Utils::getProLabel($text);
|
||||
} else {
|
||||
$title = trans("texts.$text") . Utils::getProLabel($text);
|
||||
}
|
||||
return '<li'.$class.'><a href="'.URL::to($url).'" '.$extra.'>'.$title.'</a></li>';
|
||||
});
|
||||
|
||||
HTML::macro('tab_link', function($url, $text, $active = false) {
|
||||
Form::macro('tab_link', function($url, $text, $active = false) {
|
||||
$class = $active ? ' class="active"' : '';
|
||||
return '<li'.$class.'><a href="'.URL::to($url).'" data-toggle="tab">'.$text.'</a></li>';
|
||||
});
|
||||
|
||||
HTML::macro('menu_link', function($type) {
|
||||
Form::macro('menu_link', function($type) {
|
||||
$types = $type.'s';
|
||||
$Type = ucfirst($type);
|
||||
$Types = ucfirst($types);
|
||||
$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>';
|
||||
}
|
||||
|
||||
$str .= '</ul>
|
||||
</li>';
|
||||
if(!empty($items)){
|
||||
$str.= '<ul class="dropdown-menu" id="menu1">'.implode($items).'</ul>';
|
||||
}
|
||||
|
||||
$str .= '</li>';
|
||||
|
||||
return $str;
|
||||
});
|
||||
|
||||
HTML::macro('image_data', function($imagePath) {
|
||||
return 'data:image/jpeg;base64,' . base64_encode(file_get_contents($imagePath));
|
||||
});
|
||||
|
||||
HTML::macro('flatButton', function($label, $color) {
|
||||
Form::macro('flatButton', function($label, $color) {
|
||||
return '<input type="button" value="' . trans("texts.{$label}") . '" style="background-color:' . $color . ';border:0 none;border-radius:5px;padding:12px 40px;margin:0 6px;cursor:hand;display:inline-block;font-size:14px;color:#fff;text-transform:none;font-weight:bold;"/>';
|
||||
});
|
||||
|
||||
HTML::macro('emailViewButton', function($link = '#', $entityType = ENTITY_INVOICE) {
|
||||
Form::macro('emailViewButton', function($link = '#', $entityType = ENTITY_INVOICE) {
|
||||
return view('partials.email_button')
|
||||
->with([
|
||||
'link' => $link,
|
||||
@ -83,7 +99,7 @@ class AppServiceProvider extends ServiceProvider {
|
||||
->render();
|
||||
});
|
||||
|
||||
HTML::macro('emailPaymentButton', function($link = '#') {
|
||||
Form::macro('emailPaymentButton', function($link = '#') {
|
||||
return view('partials.email_button')
|
||||
->with([
|
||||
'link' => $link,
|
||||
@ -93,7 +109,7 @@ class AppServiceProvider extends ServiceProvider {
|
||||
->render();
|
||||
});
|
||||
|
||||
HTML::macro('breadcrumbs', function($status = false) {
|
||||
Form::macro('breadcrumbs', function($status = false) {
|
||||
$str = '<ol class="breadcrumb">';
|
||||
|
||||
// Get the breadcrumbs by exploding the current path.
|
||||
@ -223,11 +239,6 @@ class AppServiceProvider extends ServiceProvider {
|
||||
'Illuminate\Contracts\Auth\Registrar',
|
||||
'App\Services\Registrar'
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
'App\Ninja\Import\DataImporterServiceInterface',
|
||||
'App\Ninja\Import\FreshBooks\FreshBooksDataImporterService'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class AccountGatewayService extends BaseService
|
||||
[
|
||||
'name',
|
||||
function ($model) {
|
||||
return link_to("gateways/{$model->public_id}/edit", $model->name);
|
||||
return link_to("gateways/{$model->public_id}/edit", $model->name)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
|
@ -38,11 +38,11 @@ class ActivityService extends BaseService
|
||||
'activity_type_id',
|
||||
function ($model) {
|
||||
$data = [
|
||||
'client' => link_to('/clients/' . $model->client_public_id, Utils::getClientDisplayName($model)),
|
||||
'client' => link_to('/clients/' . $model->client_public_id, Utils::getClientDisplayName($model))->toHtml(),
|
||||
'user' => $model->is_system ? '<i>' . trans('texts.system') . '</i>' : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email),
|
||||
'invoice' => $model->invoice ? link_to('/invoices/' . $model->invoice_public_id, $model->is_recurring ? trans('texts.recurring_invoice') : $model->invoice) : null,
|
||||
'quote' => $model->invoice ? link_to('/quotes/' . $model->invoice_public_id, $model->invoice) : null,
|
||||
'contact' => $model->contact_id ? link_to('/clients/' . $model->client_public_id, Utils::getClientDisplayName($model)) : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email),
|
||||
'invoice' => $model->invoice ? link_to('/invoices/' . $model->invoice_public_id, $model->is_recurring ? trans('texts.recurring_invoice') : $model->invoice)->toHtml() : null,
|
||||
'quote' => $model->invoice ? link_to('/quotes/' . $model->invoice_public_id, $model->invoice)->toHtml() : null,
|
||||
'contact' => $model->contact_id ? link_to('/clients/' . $model->client_public_id, Utils::getClientDisplayName($model))->toHtml() : Utils::getPersonDisplayName($model->user_first_name, $model->user_last_name, $model->user_email),
|
||||
'payment' => $model->payment ?: '',
|
||||
'credit' => Utils::formatMoney($model->credit, $model->currency_id, $model->country_id)
|
||||
];
|
||||
|
@ -4,7 +4,6 @@ use stdClass;
|
||||
use Utils;
|
||||
use URL;
|
||||
use Hash;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\BankSubaccount;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\Expense;
|
||||
@ -37,7 +36,7 @@ class BankAccountService extends BaseService
|
||||
|
||||
public function loadBankAccounts($bankId, $username, $password, $includeTransactions = true)
|
||||
{
|
||||
if ( ! $bankId || ! $username || ! $password) {
|
||||
if (! $bankId || ! $username || ! $password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -47,12 +46,13 @@ class BankAccountService extends BaseService
|
||||
->withTrashed()
|
||||
->get(['transaction_id'])
|
||||
->toArray();
|
||||
$expenses = array_flip(array_map(function($val) {
|
||||
$expenses = array_flip(array_map(function ($val) {
|
||||
return $val['transaction_id'];
|
||||
}, $expenses));
|
||||
|
||||
$vendorMap = $this->createVendorMap();
|
||||
$bankAccounts = BankSubaccount::scope()
|
||||
->whereHas('bank_account', function($query) use ($bankId) {
|
||||
->whereHas('bank_account', function ($query) use ($bankId) {
|
||||
$query->where('bank_id', '=', $bankId);
|
||||
})
|
||||
->get();
|
||||
@ -70,7 +70,7 @@ class BankAccountService extends BaseService
|
||||
$login->setup();
|
||||
foreach ($login->accounts as $account) {
|
||||
$account->setup($includeTransactions);
|
||||
if ($account = $this->parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions)) {
|
||||
if ($account = $this->parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions, $vendorMap)) {
|
||||
$data[] = $account;
|
||||
}
|
||||
}
|
||||
@ -83,9 +83,9 @@ class BankAccountService extends BaseService
|
||||
}
|
||||
}
|
||||
|
||||
private function parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions)
|
||||
private function parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions, $vendorMap)
|
||||
{
|
||||
$obj = new stdClass;
|
||||
$obj = new stdClass();
|
||||
$obj->account_name = '';
|
||||
|
||||
// look up bank account name
|
||||
@ -106,7 +106,7 @@ class BankAccountService extends BaseService
|
||||
$obj->balance = Utils::formatMoney($account->ledgerBalance, CURRENCY_DOLLAR);
|
||||
|
||||
if ($includeTransactions) {
|
||||
$ofxParser = new \OfxParser\Parser;
|
||||
$ofxParser = new \OfxParser\Parser();
|
||||
$ofx = $ofxParser->loadFromString($account->response);
|
||||
|
||||
$obj->start_date = $ofx->BankAccount->Statement->startDate;
|
||||
@ -121,7 +121,13 @@ class BankAccountService extends BaseService
|
||||
if ($transaction->amount >= 0) {
|
||||
continue;
|
||||
}
|
||||
$transaction->vendor = $this->prepareValue(substr($transaction->name, 0, 20));
|
||||
|
||||
// if vendor has already been imported use current name
|
||||
$vendorName = trim(substr($transaction->name, 0, 20));
|
||||
$key = strtolower($vendorName);
|
||||
$vendor = isset($vendorMap[$key]) ? $vendorMap[$key] : null;
|
||||
|
||||
$transaction->vendor = $vendor ? $vendor->name : $this->prepareValue($vendorName);
|
||||
$transaction->info = $this->prepareValue(substr($transaction->name, 20));
|
||||
$transaction->memo = $this->prepareValue($transaction->memo);
|
||||
$transaction->date = \Auth::user()->account->formatDate($transaction->date);
|
||||
@ -133,15 +139,13 @@ class BankAccountService extends BaseService
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private function prepareValue($value) {
|
||||
private function prepareValue($value)
|
||||
{
|
||||
return ucwords(strtolower(trim($value)));
|
||||
}
|
||||
|
||||
public function importExpenses($bankId, $input) {
|
||||
$countVendors = 0;
|
||||
$countExpenses = 0;
|
||||
|
||||
// create a vendor map
|
||||
private function createVendorMap()
|
||||
{
|
||||
$vendorMap = [];
|
||||
$vendors = Vendor::scope()
|
||||
->withTrashed()
|
||||
@ -151,6 +155,15 @@ class BankAccountService extends BaseService
|
||||
$vendorMap[strtolower($vendor->transaction_name)] = $vendor;
|
||||
}
|
||||
|
||||
return $vendorMap;
|
||||
}
|
||||
|
||||
public function importExpenses($bankId, $input)
|
||||
{
|
||||
$vendorMap = $this->createVendorMap();
|
||||
$countVendors = 0;
|
||||
$countExpenses = 0;
|
||||
|
||||
foreach ($input as $transaction) {
|
||||
$vendorName = $transaction['vendor'];
|
||||
$key = strtolower($vendorName);
|
||||
@ -165,7 +178,7 @@ class BankAccountService extends BaseService
|
||||
$field => $info,
|
||||
'name' => $vendorName,
|
||||
'transaction_name' => $transaction['vendor_orig'],
|
||||
'vendorcontact' => []
|
||||
'vendorcontact' => [],
|
||||
]);
|
||||
$vendorMap[$key] = $vendor;
|
||||
$vendorMap[$transaction['vendor_orig']] = $vendor;
|
||||
@ -191,7 +204,8 @@ class BankAccountService extends BaseService
|
||||
]);
|
||||
}
|
||||
|
||||
private function determineInfoField($value) {
|
||||
private function determineInfoField($value)
|
||||
{
|
||||
if (preg_match("/^[0-9\-\(\)\.]+$/", $value)) {
|
||||
return 'work_phone';
|
||||
} elseif (strpos($value, '.') !== false) {
|
||||
@ -214,8 +228,8 @@ class BankAccountService extends BaseService
|
||||
[
|
||||
'bank_name',
|
||||
function ($model) {
|
||||
return link_to("bank_accounts/{$model->public_id}/edit", $model->bank_name);
|
||||
}
|
||||
return link_to("bank_accounts/{$model->public_id}/edit", $model->bank_name)->toHtml();
|
||||
},
|
||||
],
|
||||
[
|
||||
'bank_library_id',
|
||||
@ -233,9 +247,8 @@ class BankAccountService extends BaseService
|
||||
uctrans('texts.edit_bank_account'),
|
||||
function ($model) {
|
||||
return URL::to("bank_accounts/{$model->public_id}/edit");
|
||||
}
|
||||
},
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
<?php namespace App\Services;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesCommands;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use App\Services\DatatableService;
|
||||
|
||||
class BaseService
|
||||
{
|
||||
use DispatchesCommands;
|
||||
use DispatchesJobs;
|
||||
|
||||
protected function getRepo()
|
||||
{
|
||||
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
@ -46,19 +56,19 @@ class ClientService extends BaseService
|
||||
[
|
||||
'name',
|
||||
function ($model) {
|
||||
return link_to("clients/{$model->public_id}", $model->name ?: '');
|
||||
return link_to("clients/{$model->public_id}", $model->name ?: '')->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
'first_name',
|
||||
function ($model) {
|
||||
return link_to("clients/{$model->public_id}", $model->first_name.' '.$model->last_name);
|
||||
return link_to("clients/{$model->public_id}", $model->first_name.' '.$model->last_name)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
'email',
|
||||
function ($model) {
|
||||
return link_to("clients/{$model->public_id}", $model->email ?: '');
|
||||
return link_to("clients/{$model->public_id}", $model->email ?: '')->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -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();
|
||||
}
|
||||
]
|
||||
];
|
||||
|
@ -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;
|
||||
|
||||
|
||||
@ -31,6 +34,10 @@ class CreditService extends BaseService
|
||||
{
|
||||
$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,7 +47,11 @@ class CreditService extends BaseService
|
||||
[
|
||||
'client_name',
|
||||
function ($model) {
|
||||
return $model->client_public_id ? link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($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();
|
||||
}
|
||||
]
|
||||
];
|
||||
|
@ -1,7 +1,9 @@
|
||||
<?php namespace App\Services;
|
||||
|
||||
use HtmlString;
|
||||
use Utils;
|
||||
use Datatable;
|
||||
use Auth;
|
||||
|
||||
class DatatableService
|
||||
{
|
||||
@ -12,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) . '>';
|
||||
});
|
||||
}
|
||||
@ -44,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') {
|
||||
@ -69,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>";
|
||||
@ -83,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>";
|
||||
}
|
||||
|
@ -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,7 +70,11 @@ class ExpenseService extends BaseService
|
||||
function ($model)
|
||||
{
|
||||
if ($model->vendor_public_id) {
|
||||
return link_to("vendors/{$model->vendor_public_id}", $model->vendor_name);
|
||||
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,7 +85,11 @@ class ExpenseService extends BaseService
|
||||
function ($model)
|
||||
{
|
||||
if ($model->client_public_id) {
|
||||
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model));
|
||||
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,7 +98,11 @@ class ExpenseService extends BaseService
|
||||
[
|
||||
'expense_date',
|
||||
function ($model) {
|
||||
return link_to("expenses/{$model->public_id}/edit", Utils::fromSqlDate($model->expense_date));
|
||||
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();
|
||||
}
|
||||
],
|
||||
];
|
||||
|
@ -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');
|
||||
@ -79,7 +94,8 @@ class InvoiceService extends BaseService
|
||||
|
||||
public function approveQuote($quote, $invitation = null)
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$account = $quote->account;
|
||||
|
||||
if (!$quote->is_quote || $quote->quote_invoice_id) {
|
||||
return null;
|
||||
}
|
||||
@ -108,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);
|
||||
}
|
||||
|
||||
@ -117,13 +137,20 @@ class InvoiceService extends BaseService
|
||||
[
|
||||
'invoice_number',
|
||||
function ($model) use ($entityType) {
|
||||
return link_to("{$entityType}s/{$model->public_id}/edit", $model->invoice_number, ['class' => Utils::getEntityRowClass($model)]);
|
||||
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) {
|
||||
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($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
|
||||
],
|
||||
@ -159,8 +186,8 @@ class InvoiceService extends BaseService
|
||||
],
|
||||
[
|
||||
'invoice_status_name',
|
||||
function ($model) {
|
||||
return $model->quote_invoice_id ? link_to("invoices/{$model->quote_invoice_id}/edit", trans('texts.converted')) : self::getStatusLabel($model);
|
||||
function ($model) use ($entityType) {
|
||||
return $model->quote_invoice_id ? link_to("invoices/{$model->quote_invoice_id}/edit", trans('texts.converted'))->toHtml() : self::getStatusLabel($entityType, $model);
|
||||
}
|
||||
]
|
||||
];
|
||||
@ -173,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();
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -187,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);
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -203,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();
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -212,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);
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -221,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);
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -230,18 +268,19 @@ 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);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
private function getStatusLabel($model)
|
||||
private function getStatusLabel($entityType, $model)
|
||||
{
|
||||
// check if invoice is overdue
|
||||
if (Utils::parseFloat($model->balance) && $model->due_date && $model->due_date != '0000-00-00') {
|
||||
if (\DateTime::createFromFormat('Y-m-d', $model->due_date) < new \DateTime("now")) {
|
||||
return "<h4><div class=\"label label-danger\">".trans('texts.overdue')."</div></h4>";
|
||||
$label = $entityType == ENTITY_INVOICE ? trans('texts.overdue') : trans('texts.expired');
|
||||
return "<h4><div class=\"label label-danger\">" . $label . "</div></h4>";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,13 +302,21 @@ class PaymentService extends BaseService
|
||||
[
|
||||
'invoice_number',
|
||||
function ($model) {
|
||||
return link_to("invoices/{$model->invoice_public_id}/edit", $model->invoice_number, ['class' => Utils::getEntityRowClass($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) {
|
||||
return $model->client_public_id ? link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($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);
|
||||
}
|
||||
]
|
||||
];
|
||||
|
@ -34,7 +34,7 @@ class PaymentTermService extends BaseService
|
||||
[
|
||||
'name',
|
||||
function ($model) {
|
||||
return link_to("payment_terms/{$model->public_id}/edit", $model->name);
|
||||
return link_to("payment_terms/{$model->public_id}/edit", $model->name)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
|
@ -44,7 +44,7 @@ class ProductService extends BaseService
|
||||
[
|
||||
'product_key',
|
||||
function ($model) {
|
||||
return link_to('products/'.$model->public_id.'/edit', $model->product_key);
|
||||
return link_to('products/'.$model->public_id.'/edit', $model->product_key)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
|
171
app/Services/PushService.php
Normal file
171
app/Services/PushService.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Ninja\Notifications\PushFactory;
|
||||
/**
|
||||
* Class PushService
|
||||
* @package App\Ninja\Notifications
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* $account->devices Definition
|
||||
*
|
||||
* @param string token (push notification device token)
|
||||
* @param string email (user email address - required for use as key)
|
||||
* @param string device (ios, gcm etc etc)
|
||||
* @param bool notify_sent
|
||||
* @param bool notify_paid
|
||||
* @param bool notify_approved
|
||||
* @param bool notify_viewed
|
||||
*/
|
||||
|
||||
class PushService
|
||||
{
|
||||
protected $pushFactory;
|
||||
|
||||
/**
|
||||
* @param PushFactory $pushFactory
|
||||
*/
|
||||
public function __construct(PushFactory $pushFactory)
|
||||
{
|
||||
$this->pushFactory = $pushFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice - Invoice object
|
||||
* @param $type - Type of notification, ie. Quote APPROVED, Invoice PAID, Invoice/Quote SENT, Invoice/Quote VIEWED
|
||||
*/
|
||||
|
||||
public function sendNotification($invoice, $type)
|
||||
{
|
||||
//check user has registered for push notifications
|
||||
if(!$this->checkDeviceExists($invoice->account))
|
||||
return;
|
||||
|
||||
//Harvest an array of devices that are registered for this notification type
|
||||
$devices = json_decode($invoice->account->devices, TRUE);
|
||||
|
||||
foreach($devices as $device)
|
||||
{
|
||||
if(($device["notify_{$type}"] == TRUE) && ($device['device'] == 'ios'))
|
||||
$this->pushMessage($invoice, $device['token'], $type);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pushMessage function
|
||||
*
|
||||
* method to dispatch iOS notifications
|
||||
*
|
||||
* @param $invoice
|
||||
* @param $token
|
||||
* @param $type
|
||||
*/
|
||||
private function pushMessage($invoice, $token, $type)
|
||||
{
|
||||
$this->pushFactory->message($token, $this->messageType($invoice, $type));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checkDeviceExists function
|
||||
*
|
||||
* Returns a boolean if this account has devices registered for PUSH notifications
|
||||
*
|
||||
* @param $account
|
||||
* @return bool
|
||||
*/
|
||||
private function checkDeviceExists($account)
|
||||
{
|
||||
$devices = json_decode($account->devices, TRUE);
|
||||
|
||||
if(count($devices) >= 1)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* messageType function
|
||||
*
|
||||
* method which formats an appropriate message depending on message type
|
||||
*
|
||||
* @param $invoice
|
||||
* @param $type
|
||||
* @return string
|
||||
*/
|
||||
private function messageType($invoice, $type)
|
||||
{
|
||||
switch($type)
|
||||
{
|
||||
case 'sent':
|
||||
return $this->entitySentMessage($invoice);
|
||||
break;
|
||||
|
||||
case 'paid':
|
||||
return $this->invoicePaidMessage($invoice);
|
||||
break;
|
||||
|
||||
case 'approved':
|
||||
return $this->quoteApprovedMessage($invoice);
|
||||
break;
|
||||
|
||||
case 'viewed':
|
||||
return $this->entityViewedMessage($invoice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @return string
|
||||
*/
|
||||
private function entitySentMessage($invoice)
|
||||
{
|
||||
if($invoice->is_quote)
|
||||
return trans("texts.notification_quote_sent_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||
else
|
||||
return trans("texts.notification_invoice_sent_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @return string
|
||||
*/
|
||||
private function invoicePaidMessage($invoice)
|
||||
{
|
||||
return trans("texts.notification_invoice_paid_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @return string
|
||||
*/
|
||||
private function quoteApprovedMessage($invoice)
|
||||
{
|
||||
return trans("texts.notification_quote_approved_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @return string
|
||||
*/
|
||||
private function entityViewedMessage($invoice)
|
||||
{
|
||||
if($invoice->is_quote)
|
||||
return trans("texts.notification_quote_viewed_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||
else
|
||||
return trans("texts.notification_invoice_viewed_subject", ['invoice' => $invoice->invoice_number, 'client' => $invoice->client->name]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
@ -28,13 +34,13 @@ class RecurringInvoiceService extends BaseService
|
||||
[
|
||||
'frequency',
|
||||
function ($model) {
|
||||
return link_to("invoices/{$model->public_id}", $model->frequency);
|
||||
return link_to("invoices/{$model->public_id}", $model->frequency)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
'client_name',
|
||||
function ($model) {
|
||||
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model));
|
||||
return link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($model))->toHtml();
|
||||
},
|
||||
! $hideClient
|
||||
],
|
||||
@ -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);
|
||||
}
|
||||
]
|
||||
];
|
||||
|
@ -1,39 +0,0 @@
|
||||
<?php namespace App\Services;
|
||||
|
||||
use App\Model\User;
|
||||
use Validator;
|
||||
use Illuminate\Contracts\Auth\Registrar as RegistrarContract;
|
||||
|
||||
class Registrar implements RegistrarContract {
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
public function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
* @return User
|
||||
*/
|
||||
public function create(array $data)
|
||||
{
|
||||
return User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => bcrypt($data['password']),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -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,14 +49,18 @@ class TaskService extends BaseService
|
||||
[
|
||||
'client_name',
|
||||
function ($model) {
|
||||
return $model->client_public_id ? link_to("clients/{$model->client_public_id}", Utils::getClientDisplayName($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
|
||||
],
|
||||
[
|
||||
'created_at',
|
||||
function ($model) {
|
||||
return link_to("tasks/{$model->public_id}/edit", Task::calcStartTime($model));
|
||||
return link_to("tasks/{$model->public_id}/edit", Task::calcStartTime($model))->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -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();
|
||||
}
|
||||
]
|
||||
];
|
||||
|
@ -41,7 +41,7 @@ class TaxRateService extends BaseService
|
||||
[
|
||||
'name',
|
||||
function ($model) {
|
||||
return link_to("tax_rates/{$model->public_id}/edit", $model->name);
|
||||
return link_to("tax_rates/{$model->public_id}/edit", $model->name)->toHtml();
|
||||
}
|
||||
],
|
||||
[
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user