1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-09 20:52:56 +01:00

Working on multi-user support

This commit is contained in:
Hillel Coren 2014-07-23 18:02:56 +03:00
parent 4b177e160d
commit 051ba95b7b
20 changed files with 374 additions and 109 deletions

View File

@ -4,6 +4,6 @@ return array(
//'TAG_MANAGER_KEY' => '', //'TAG_MANAGER_KEY' => '',
//'ANALYTICS_KEY' => '', //'ANALYTICS_KEY' => '',
//'NINJA_PROD' => true, //'NINJA_DEV' => true,
); );

View File

@ -11,20 +11,53 @@
use ninja\repositories\AccountRepository; use ninja\repositories\AccountRepository;
use ninja\mailers\ContactMailer; use ninja\mailers\ContactMailer;
use ninja\mailers\UserMailer;
class UserController extends BaseController { class UserController extends BaseController {
protected $accountRepo; protected $accountRepo;
protected $contactMailer; protected $contactMailer;
protected $userMailer;
public function __construct(AccountRepository $accountRepo, ContactMailer $contactMailer) public function __construct(AccountRepository $accountRepo, ContactMailer $contactMailer, UserMailer $userMailer)
{ {
parent::__construct(); parent::__construct();
$this->accountRepo = $accountRepo; $this->accountRepo = $accountRepo;
$this->contactMailer = $contactMailer; $this->contactMailer = $contactMailer;
$this->userMailer = $userMailer;
} }
public function getDatatable()
{
$query = DB::table('users')
->where('users.account_id', '=', Auth::user()->account_id)
->where('users.deleted_at', '=', null)
->where('users.public_id', '>', 0)
->select('users.public_id', 'users.first_name', 'users.last_name', 'users.email', 'users.confirmed', 'users.public_id');
return Datatable::query($query)
->addColumn('first_name', function($model) { return link_to('users/' . $model->public_id . '/edit', $model->first_name . ' ' . $model->last_name); })
->addColumn('email', function($model) { return $model->email; })
->addColumn('confirmed', function($model) { return $model->confirmed ? trans('texts.active') : trans('texts.pending'); })
->addColumn('dropdown', function($model)
{
return '<div class="btn-group tr-action" style="visibility:hidden;">
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
'.trans('texts.select').' <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="' . URL::to('users/'.$model->public_id) . '/edit">'.uctrans('texts.edit_user').'</a></li>
<li class="divider"></li>
<li><a href="javascript:deleteUser(' . $model->public_id. ')">'.uctrans('texts.delete_user').'</a></li>
</ul>
</div>';
})
->orderColumns(['first_name', 'email', 'confirmed'])
->make();
}
public function setTheme() public function setTheme()
{ {
$user = User::find(Auth::user()->id); $user = User::find(Auth::user()->id);
@ -45,50 +78,146 @@ class UserController extends BaseController {
return Redirect::to('/dashboard'); return Redirect::to('/dashboard');
} }
public function edit($publicId)
{
$user = User::where('account_id', '=', Auth::user()->account_id)
->where('public_id', '=', $publicId)->firstOrFail();
$data = [
'showBreadcrumbs' => false,
'user' => $user,
'method' => 'PUT',
'url' => 'users/' . $publicId,
'title' => trans('texts.edit_user')
];
return View::make('users.edit', $data);
}
public function update($publicId)
{
return $this->save($publicId);
}
public function store()
{
return $this->save();
}
/** /**
* Displays the form for account creation * Displays the form for account creation
* *
*/ */
public function create() public function create()
{ {
return View::make(Config::get('confide::signup_form')); if (!Auth::user()->confirmed)
{
Session::flash('error', trans('texts.register_to_add_user'));
return Redirect::to('company/advanced_settings/user_management');
}
if (Utils::isNinja())
{
$count = User::where('account_id', '=', Auth::user()->account_id)->count();
if ($count >= MAX_NUM_USERS)
{
Session::flash('error', trans('texts.limit_users'));
return Redirect::to('company/advanced_settings/user_management');
}
}
$data = [
'showBreadcrumbs' => false,
'user' => null,
'method' => 'POST',
'url' => 'users',
'title' => trans('texts.add_user')
];
return View::make('users.edit', $data);
}
public function delete()
{
$userPublicId = Input::get('userPublicId');
$user = User::where('account_id', '=', Auth::user()->account_id)
->where('public_id', '=', $userPublicId)->firstOrFail();
$user->delete();
Session::flash('message', trans('texts.deleted_user'));
return Redirect::to('company/advanced_settings/user_management');
} }
/** /**
* Stores new account * Stores new account
* *
*/ */
public function store() public function save($userPublicId = false)
{ {
$user = new User; $rules = [
'first_name' => 'required',
'last_name' => 'required',
];
$user->username = Input::get( 'username' ); if ($userPublicId)
$user->email = Input::get( 'email' );
$user->password = Input::get( 'password' );
// The password confirmation will be removed from model
// before saving. This field will be used in Ardent's
// auto validation.
$user->password_confirmation = Input::get( 'password_confirmation' );
// Save if valid. Password field will be hashed before save
$user->save();
if ( $user->id )
{ {
// Redirect with success message, You may replace "Lang::get(..." for your custom message. $user = User::where('account_id', '=', Auth::user()->account_id)
return Redirect::action('UserController@login') ->where('public_id', '=', $userPublicId)->firstOrFail();
->with( 'notice', Lang::get('confide::confide.alerts.account_created') );
$rules['email'] = 'required|email|unique:users,email,' . $user->id . ',id';
} }
else else
{ {
// Get validation errors (see Ardent package) $rules['email'] = 'required|email|unique:users';
$error = $user->errors()->all(':message');
return Redirect::action('UserController@create')
->withInput(Input::except('password'))
->with( 'error', $error );
} }
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails())
{
return Redirect::to($userPublicId ? 'users/edit' : 'users/create')->withInput()->withErrors($validator);
}
if ($userPublicId)
{
$user->first_name = trim(Input::get('first_name'));
$user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email'));
$user->email = trim(Input::get('email'));
}
else
{
$lastUser = User::withTrashed()->where('account_id', '=', Auth::user()->account_id)
->orderBy('public_id', 'DESC')->first();
$user = new User;
$user->account_id = Auth::user()->account_id;
$user->first_name = trim(Input::get('first_name'));
$user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email'));
$user->email = trim(Input::get('email'));
$user->registered = true;
$user->password = str_random(RANDOM_KEY_LENGTH);
$user->password_confirmation = $user->password;
$user->public_id = $lastUser->public_id + 1;
}
$user->save();
if (!$user->confirmed)
{
$this->userMailer->sendConfirmation($user, Auth::user());
$message = trans('texts.sent_invite');
}
else
{
$message = trans('texts.updated_user');
}
Session::flash('message', $message);
return Redirect::to('company/advanced_settings/user_management');
} }
/** /**
@ -183,21 +312,34 @@ class UserController extends BaseController {
public function confirm( $code ) public function confirm( $code )
{ {
if ( Confide::confirm( $code ) ) if ( Confide::confirm( $code ) )
{ {
$notice_msg = trans('texts.confide.confirmation'); $notice_msg = trans('texts.confide.confirmation');
$user = User::where('confirmation_code', '=', $code)->get()->first();
$user->confirmation_code = '';
$user->save();
if (Session::has(REQUESTED_PRO_PLAN)) if ($user->public_id)
{ {
Session::forget(REQUESTED_PRO_PLAN); Auth::login($user);
if ($invoice = $this->accountRepo->enableProPlan()) return Redirect::to('user/reset');
}
else
{
if (Session::has(REQUESTED_PRO_PLAN))
{ {
$this->contactMailer->sendInvoice($invoice); Session::forget(REQUESTED_PRO_PLAN);
$notice_msg = trans('texts.pro_plan_success');
}
}
return Redirect::action('UserController@login')->with( 'message', $notice_msg ); if ($invoice = $this->accountRepo->enableProPlan())
{
$this->contactMailer->sendInvoice($invoice);
$notice_msg = trans('texts.pro_plan_success');
}
}
return Redirect::action('UserController@login')->with( 'message', $notice_msg );
}
} }
else else
{ {
@ -249,7 +391,7 @@ class UserController extends BaseController {
* Shows the change password form with the given token * Shows the change password form with the given token
* *
*/ */
public function reset_password( $token ) public function reset_password( $token = false )
{ {
return View::make(Config::get('confide::reset_password_form')) return View::make(Config::get('confide::reset_password_form'))
->with('token', $token); ->with('token', $token);
@ -260,26 +402,49 @@ class UserController extends BaseController {
* *
*/ */
public function do_reset_password() public function do_reset_password()
{ {
$input = array( if (Auth::check())
'token'=>Input::get( 'token' ),
'password'=>Input::get( 'password' ),
'password_confirmation'=>Input::get( 'password_confirmation' ),
);
// By passing an array with the token, password and confirmation
if( Confide::resetPassword( $input ) )
{ {
$notice_msg = trans('texts.confide.password_reset'); $rules = [
return Redirect::action('UserController@login') 'password' => 'required|between:4,11|confirmed',
->with( 'notice', $notice_msg ); 'password_confirmation' => 'between:4,11',
];
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails())
{
return Redirect::to('user/reset')->withInput()->withErrors($validator);
}
$user = Auth::user();
$user->password = Input::get('password');
$user->save();
Session::flash('message', trans('texts.confide.password_reset'));
return Redirect::to('/dashboard');
} }
else else
{ {
$error_msg = trans('texts.confide.wrong_password_reset'); $input = array(
return Redirect::action('UserController@reset_password', array('token'=>$input['token'])) 'token'=>Input::get( 'token' ),
->withInput() 'password'=>Input::get( 'password' ),
->with( 'error', $error_msg ); 'password_confirmation'=>Input::get( 'password_confirmation' ),
);
// By passing an array with the token, password and confirmation
if( Confide::resetPassword( $input ) )
{
$notice_msg = trans('texts.confide.password_reset');
return Redirect::action('UserController@login')
->with( 'notice', $notice_msg );
}
else
{
$error_msg = trans('texts.confide.wrong_password_reset');
return Redirect::action('UserController@reset_password', array('token'=>$input['token']))
->withInput()
->with( 'error', $error_msg );
}
} }
} }

View File

@ -400,11 +400,24 @@ return array(
'invoice_fields' => 'Invoice Fields', 'invoice_fields' => 'Invoice Fields',
'invoice_options' => 'Invoice Options', 'invoice_options' => 'Invoice Options',
'hide_quantity' => 'Hide quantity', 'hide_quantity' => 'Hide quantity',
'hide_quantity_help' => 'All line items will have a quantity of one', 'hide_quantity_help' => 'If your line items quantities are always 1, then you can declutter invoices by no longer displaying this field.',
'hide_paid_to_date' => 'Hide paid to date', 'hide_paid_to_date' => 'Hide paid to date',
'hide_paid_to_date_help' => 'Hide until a payment is made', 'hide_paid_to_date_help' => 'Only display the "Paid to Date" area on your invoices once a payment has been received.',
'charge_taxes' => 'Charge taxes', 'charge_taxes' => 'Charge taxes',
'user_management' => 'User Management',
'add_user' => 'Add User',
'send_invite' => 'Send invitation',
'sent_invite' => 'Successfully sent invitation',
'updated_user' => 'Successfully updated user',
'invitation_message' => 'You\'ve been invited by :invitor. ',
'register_to_add_user' => 'Please sign up to add a user',
'user_state' => 'State',
'edit_user' => 'Edit User',
'delete_user' => 'Delete User',
'active' => 'Active',
'pending' => 'Pending',
'deleted_user' => 'Successfully deleted user',
'limit_users' => 'Sorry, this will exceed the limit of ' . MAX_NUM_USERS . ' users',
); );

View File

@ -8,7 +8,7 @@ use Utils;
class UserMailer extends Mailer { class UserMailer extends Mailer {
public function sendConfirmation(User $user) public function sendConfirmation(User $user, User $invitor = null)
{ {
if (!$user->email) if (!$user->email)
{ {
@ -19,7 +19,8 @@ class UserMailer extends Mailer {
$subject = trans('texts.confirmation_subject'); $subject = trans('texts.confirmation_subject');
$data = [ $data = [
'user' => $user 'user' => $user,
'invitationMessage' => $invitor ? trans('texts.invitation_message', ['invitor' => $invitor->getDisplayName()]) : ''
]; ];
$this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); $this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);

View File

@ -261,8 +261,8 @@ class InvoiceRepository
$total *= (100 - $invoice->discount) / 100; $total *= (100 - $invoice->discount) / 100;
} }
$invoice->custom_value1 = $data['custom_value1']; $invoice->custom_value1 = round($data['custom_value1'], 2);
$invoice->custom_value2 = $data['custom_value2']; $invoice->custom_value2 = round($data['custom_value2'], 2);
$invoice->custom_taxes1 = $data['custom_taxes1'] ? true : false; $invoice->custom_taxes1 = $data['custom_taxes1'] ? true : false;
$invoice->custom_taxes2 = $data['custom_taxes2'] ? true : false; $invoice->custom_taxes2 = $data['custom_taxes2'] ? true : false;

View File

@ -57,7 +57,7 @@ Route::post('login', 'UserController@do_login');
Route::get('user/confirm/{code}', 'UserController@confirm'); Route::get('user/confirm/{code}', 'UserController@confirm');
Route::get('forgot_password', 'UserController@forgot_password'); Route::get('forgot_password', 'UserController@forgot_password');
Route::post('forgot_password', 'UserController@do_forgot_password'); Route::post('forgot_password', 'UserController@do_forgot_password');
Route::get('user/reset/{token}', 'UserController@reset_password'); Route::get('user/reset/{token?}', 'UserController@reset_password');
Route::post('user/reset', 'UserController@do_reset_password'); Route::post('user/reset', 'UserController@do_reset_password');
Route::get('logout', 'UserController@logout'); Route::get('logout', 'UserController@logout');
@ -67,18 +67,15 @@ Route::group(array('before' => 'auth'), function()
Route::get('dashboard', 'DashboardController@index'); Route::get('dashboard', 'DashboardController@index');
Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible'); Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
Route::get('force_inline_pdf', 'UserController@forcePDFJS'); Route::get('force_inline_pdf', 'UserController@forcePDFJS');
Route::get('api/users', array('as'=>'api.users', 'uses'=>'UserController@getDatatable'));
Route::resource('users', 'UserController');
Route::post('users/delete', 'UserController@delete');
Route::get('api/products', array('as'=>'api.products', 'uses'=>'ProductController@getDatatable')); Route::get('api/products', array('as'=>'api.products', 'uses'=>'ProductController@getDatatable'));
Route::resource('products', 'ProductController'); Route::resource('products', 'ProductController');
Route::get('products/{product_id}/archive', 'ProductController@archive'); Route::get('products/{product_id}/archive', 'ProductController@archive');
/*
Route::get('company/products/{product_id}/edit', 'ProductController@showProduct');
Route::get('company/products/{product_id}/archive', 'ProductController@archiveProduct');
Route::get('company/products/create', 'ProductController@createProduct');
Route::post('company/products/{product_id?}', 'AccountController@saveProduct');
*/
Route::get('company/advanced_settings/chart_builder', 'ReportController@report'); Route::get('company/advanced_settings/chart_builder', 'ReportController@report');
Route::post('company/advanced_settings/chart_builder', 'ReportController@report'); Route::post('company/advanced_settings/chart_builder', 'ReportController@report');
@ -163,14 +160,16 @@ define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
define('ACCOUNT_CUSTOM_FIELDS', 'custom_fields'); define('ACCOUNT_CUSTOM_FIELDS', 'custom_fields');
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design'); define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
define('ACCOUNT_CHART_BUILDER', 'chart_builder'); define('ACCOUNT_CHART_BUILDER', 'chart_builder');
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
define('DEFAULT_INVOICE_NUMBER', '0001'); define('DEFAULT_INVOICE_NUMBER', '0001');
define('RECENTLY_VIEWED_LIMIT', 8); define('RECENTLY_VIEWED_LIMIT', 8);
define('LOGGED_ERROR_LIMIT', 100); define('LOGGED_ERROR_LIMIT', 100);
define('RANDOM_KEY_LENGTH', 32); define('RANDOM_KEY_LENGTH', 32);
define('MAX_NUM_CLIENTS', 500); define('MAX_NUM_CLIENTS', 500);
define('MAX_NUM_CLIENTS_PRO', 5000); define('MAX_NUM_CLIENTS_PRO', 20000);
define('MAX_NUM_USERS', 5);
define('INVOICE_STATUS_DRAFT', 1); define('INVOICE_STATUS_DRAFT', 1);
define('INVOICE_STATUS_SENT', 2); define('INVOICE_STATUS_SENT', 2);

View File

@ -58,9 +58,15 @@ $monolog->pushHandler(new Monolog\Handler\SyslogHandler('intranet', 'user', Logg
App::error(function(Exception $exception, $code) App::error(function(Exception $exception, $code)
{ {
Utils::logError($exception . ' ' . $code); if (Utils::isNinjaProd())
{
return Response::view('error', ['hideHeader' => true, 'error' => "A {$code} error occurred."], $code); Utils::logError($exception . ' ' . $code);
return Response::view('error', ['hideHeader' => true, 'error' => "A {$code} error occurred."], $code);
}
else
{
return null;
}
}); });
/* /*

View File

@ -2,6 +2,7 @@
{{ HTML::nav_link('company/advanced_settings/custom_fields', 'custom_fields') }} {{ HTML::nav_link('company/advanced_settings/custom_fields', 'custom_fields') }}
{{ HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') }} {{ HTML::nav_link('company/advanced_settings/invoice_design', 'invoice_design') }}
{{ HTML::nav_link('company/advanced_settings/chart_builder', 'chart_builder') }} {{ HTML::nav_link('company/advanced_settings/chart_builder', 'chart_builder') }}
{{ HTML::nav_link('company/advanced_settings/user_management', 'user_management') }}
</ul> </ul>
<p>&nbsp;</p> <p>&nbsp;</p>

View File

@ -28,10 +28,10 @@
->setOptions('bFilter', false) ->setOptions('bFilter', false)
->setOptions('bAutoWidth', false) ->setOptions('bAutoWidth', false)
->setOptions('aoColumns', [[ "sWidth"=> "20%" ], [ "sWidth"=> "45%" ], ["sWidth"=> "20%"], ["sWidth"=> "15%" ]]) ->setOptions('aoColumns', [[ "sWidth"=> "20%" ], [ "sWidth"=> "45%" ], ["sWidth"=> "20%"], ["sWidth"=> "15%" ]])
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[3]]])
->render('datatable') }} ->render('datatable') }}
<script> <script>
window.onDatatableReady = function() { window.onDatatableReady = function() {
$('tbody tr').mouseover(function() { $('tbody tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').css('visibility','visible'); $(this).closest('tr').find('.tr-action').css('visibility','visible');
@ -42,8 +42,6 @@
} }
}); });
} }
</script> </script>

View File

@ -0,0 +1,52 @@
@extends('accounts.nav')
@section('content')
@parent
@include('accounts.nav_advanced')
{{ Button::success_link(URL::to('users/create'), trans("texts.add_user"), array('class' => 'pull-right'))->append_with_icon('plus-sign') }}
{{ Datatable::table()
->addColumn(
trans('texts.name'),
trans('texts.email'),
trans('texts.user_state'),
trans('texts.action'))
->setUrl(url('api/users/'))
->setOptions('sPaginationType', 'bootstrap')
->setOptions('bFilter', false)
->setOptions('bAutoWidth', false)
->setOptions('aoColumns', [[ "sWidth"=> "20%" ], [ "sWidth"=> "45%" ], ["sWidth"=> "20%"], ["sWidth"=> "15%" ]])
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[3]]])
->render('datatable') }}
{{ Former::open('users/delete')->addClass('user-form') }}
<div style="display:none">
{{ Former::text('userPublicId') }}
</div>
{{ Former::close() }}
<script>
window.onDatatableReady = function() {
$('tbody tr').mouseover(function() {
$(this).closest('tr').find('.tr-action').css('visibility','visible');
}).mouseout(function() {
$dropdown = $(this).closest('tr').find('.tr-action');
if (!$dropdown.hasClass('open')) {
$dropdown.css('visibility','hidden');
}
});
}
function deleteUser(id) {
if (!confirm('Are you sure?')) {
return;
}
$('#userPublicId').val(id);
$('form.user-form').submit();
}
</script>
@stop

View File

@ -9,7 +9,7 @@
{{ Former::open($url)->addClass('col-md-10 col-md-offset-1 warn-on-exit')->method($method)->rules(array( {{ Former::open($url)->addClass('col-md-10 col-md-offset-1 warn-on-exit')->method($method)->rules(array(
'client' => 'required', 'client' => 'required',
'amount' => 'required', 'amount' => 'required',
)); }} )); }}

View File

@ -1,6 +1,6 @@
<h1>{{ trans('texts.confirmation_header') }}</h1> <h1>{{ trans('texts.confirmation_header') }}</h1>
{{ trans('texts.confirmation_message') }}<p/> {{ $invitationMessage . trans('texts.confirmation_message') }}<p/>
<a href='{{{ URL::to("user/confirm/{$user->confirmation_code}") }}}'> <a href='{{{ URL::to("user/confirm/{$user->confirmation_code}") }}}'>
{{{ URL::to("user/confirm/{$user->confirmation_code}") }}} {{{ URL::to("user/confirm/{$user->confirmation_code}") }}}
</a><p/> </a><p/>

View File

@ -1,6 +1,6 @@
{{ trans('texts.confirmation_header') }} {{ trans('texts.confirmation_header') }}
{{ trans('texts.confirmation_message') }} {{ $invitationMessage . trans('texts.confirmation_message') }}
{{{ URL::to("user/confirm/{$user->confirmation_code}") }}} {{{ URL::to("user/confirm/{$user->confirmation_code}") }}}
{{ trans('texts.email_signature') }} {{ trans('texts.email_signature') }}

View File

@ -187,7 +187,7 @@
<td class="hide-border" colspan="3"/> <td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/> <td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label1 }}</td> <td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label1 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td> <td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr> </tr>
@endif @endif
@ -196,7 +196,7 @@
<td class="hide-border" colspan="3"/> <td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/> <td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label2 }}</td> <td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label2 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td> <td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr> </tr>
@endif @endif
@ -215,7 +215,7 @@
<td class="hide-border" colspan="3"/> <td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/> <td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label1 }}</td> <td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label1 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td> <td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value1, valueUpdate: 'afterkeydown'"/></td>
</tr> </tr>
@endif @endif
@ -224,7 +224,7 @@
<td class="hide-border" colspan="3"/> <td class="hide-border" colspan="3"/>
<td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/> <td style="display:none" class="hide-border" data-bind="visible: $root.invoice_item_taxes.show"/>
<td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label2 }}</td> <td colspan="{{ $account->hide_quantity ? 1 : 2 }}">{{ $account->custom_invoice_label2 }}</td>
<td style="text-align: right"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td> <td style="text-align: right;padding-right: 28px" colspan="2"><input class="form-control" data-bind="value: custom_value2, valueUpdate: 'afterkeydown'"/></td>
</tr> </tr>
@endif @endif
@ -561,7 +561,7 @@
}, 1); }, 1);
}); });
@if ($client || $invoice) @if ($client || $invoice || count($clients) == 0)
$('#invoice_number').focus(); $('#invoice_number').focus();
@else @else
$('.client_select input.form-control').focus(); $('.client_select input.form-control').focus();
@ -1016,9 +1016,7 @@
refreshPDF(); refreshPDF();
model.clientBackup = false; model.clientBackup = false;
$('#clientModal').modal('hide'); $('#clientModal').modal('hide');
$('#invoice_number').focus();
} }
self.clientLinkText = ko.computed(function() { self.clientLinkText = ko.computed(function() {

View File

@ -36,7 +36,7 @@
<div class="pro col-md-4"> <div class="pro col-md-4">
<div class="cell">Pro Plan<span class="glyphicon glyphicon-star"></div> <div class="cell">Pro Plan<span class="glyphicon glyphicon-star"></div>
<div class="cell"><div class="hide-desktop">Number of clients per account</div><span style="color: #2299c0; font-size: 16px;">5,000</span></div> <div class="cell"><div class="hide-desktop">Number of clients per account</div><span style="color: #2299c0; font-size: 16px;">Unlimited</span></div>
<div class="cell"><div class="hide-desktop">Unlimited client invoices</div><span class="glyphicon glyphicon-ok"></div> <div class="cell"><div class="hide-desktop">Unlimited client invoices</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Add your company logo</div><span class="glyphicon glyphicon-ok"></div> <div class="cell"><div class="hide-desktop">Add your company logo</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Live .PDF invoice creation</div><span class="glyphicon glyphicon-ok"></div> <div class="cell"><div class="hide-desktop">Live .PDF invoice creation</div><span class="glyphicon glyphicon-ok"></div>

View File

@ -0,0 +1,29 @@
@extends('accounts.nav')
@section('content')
@parent
{{ Former::open($url)->method($method)->addClass('col-md-8 col-md-offset-2 warn-on-exit')->rules(array(
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required|email',
)); }}
{{ Former::legend($title) }}
@if ($user)
{{ Former::populate($user) }}
@endif
{{ Former::text('first_name') }}
{{ Former::text('last_name') }}
{{ Former::text('email') }}
{{ Former::actions(
Button::lg_success_submit(trans($user && $user->confirmed ? 'texts.save' : 'texts.send_invite'))->append_with_icon($user && $user->confirmed ? 'floppy-disk' : 'send'),
Button::lg_default_link('company/advanced_settings/user_management', 'Cancel')->append_with_icon('remove-circle')
) }}
{{ Former::close() }}
@stop

View File

@ -57,8 +57,8 @@
'password_confirmation' => 'required', 'password_confirmation' => 'required',
)); }} )); }}
<h2 class="form-signin-heading">Passord Reset</h2> <h2 class="form-signin-heading">Set Passord</h2><p/>&nbsp;
<input type="hidden" name="token" value="{{{ $token }}}"> <input type="hidden" name="token" value="{{{ $token }}}">
<p> <p>
{{ Former::password('password') }} {{ Former::password('password') }}

View File

@ -1,6 +1,5 @@
<?php <?php
/* /*
require_once 'google/appengine/api/app_identity/AppIdentityService.php'; require_once 'google/appengine/api/app_identity/AppIdentityService.php';
use \google\appengine\api\app_identity\AppIdentityService; use \google\appengine\api\app_identity\AppIdentityService;
@ -126,4 +125,8 @@ require $framework.'/Illuminate/Foundation/start.php';
| |
*/ */
return $app; // http://stackoverflow.com/questions/20293116/override-http-headers-default-settings-x-frame-options
App::forgetMiddleware('Illuminate\Http\FrameGuard');
return $app;

View File

@ -47261,19 +47261,19 @@ function displaySubtotals(doc, layout, invoice, y, rightAlignTitleX)
{'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false} {'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false}
]; ];
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) }) data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
} }
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) }) data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
} }
data.push({'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false}); data.push({'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false});
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) }) data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
} }
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) }) data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
} }
@ -47433,10 +47433,10 @@ function calculateAmounts(invoice) {
} }
// custom fields with taxes // custom fields with taxes
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
total += roundToTwo(invoice.custom_value1); total += roundToTwo(invoice.custom_value1);
} }
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
total += roundToTwo(invoice.custom_value2); total += roundToTwo(invoice.custom_value2);
} }
@ -47453,10 +47453,10 @@ function calculateAmounts(invoice) {
} }
// custom fields w/o with taxes // custom fields w/o with taxes
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
total += roundToTwo(invoice.custom_value1); total += roundToTwo(invoice.custom_value1);
} }
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
total += roundToTwo(invoice.custom_value2); total += roundToTwo(invoice.custom_value2);
} }

View File

@ -1336,19 +1336,19 @@ function displaySubtotals(doc, layout, invoice, y, rightAlignTitleX)
{'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false} {'discount': invoice.discount_amount > 0 ? formatMoney(invoice.discount_amount, invoice.client.currency_id) : false}
]; ];
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) }) data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
} }
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) }) data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
} }
data.push({'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false}); data.push({'tax': invoice.tax_amount > 0 ? formatMoney(invoice.tax_amount, invoice.client.currency_id) : false});
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) }) data.push({'custom_invoice_label1': formatMoney(invoice.custom_value1, invoice.client.currency_id) })
} }
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) }) data.push({'custom_invoice_label2': formatMoney(invoice.custom_value2, invoice.client.currency_id) })
} }
@ -1508,10 +1508,10 @@ function calculateAmounts(invoice) {
} }
// custom fields with taxes // custom fields with taxes
if (NINJA.parseFloat(invoice.custom_value1) && NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 == '1') {
total += roundToTwo(invoice.custom_value1); total += roundToTwo(invoice.custom_value1);
} }
if (NINJA.parseFloat(invoice.custom_value2) && NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 == '1') {
total += roundToTwo(invoice.custom_value2); total += roundToTwo(invoice.custom_value2);
} }
@ -1528,10 +1528,10 @@ function calculateAmounts(invoice) {
} }
// custom fields w/o with taxes // custom fields w/o with taxes
if (NINJA.parseFloat(invoice.custom_value1) && !NINJA.parseFloat(invoice.custom_taxes1)) { if (NINJA.parseFloat(invoice.custom_value1) && invoice.custom_taxes1 != '1') {
total += roundToTwo(invoice.custom_value1); total += roundToTwo(invoice.custom_value1);
} }
if (NINJA.parseFloat(invoice.custom_value2) && !NINJA.parseFloat(invoice.custom_taxes2)) { if (NINJA.parseFloat(invoice.custom_value2) && invoice.custom_taxes2 != '1') {
total += roundToTwo(invoice.custom_value2); total += roundToTwo(invoice.custom_value2);
} }