From 986487b4c95914eb468e921693e206a173e44342 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 11 Jul 2016 20:08:43 +0300 Subject: [PATCH] Working on new hosted pricing --- .travis.yml | 2 +- app/Console/Commands/SendRenewalInvoices.php | 31 ++++--- app/Http/Controllers/AccountController.php | 33 +++---- app/Http/Controllers/QuoteController.php | 4 - app/Http/Controllers/UserController.php | 34 +++---- app/Http/routes.php | 15 ++-- app/Libraries/Utils.php | 40 +++++++++ app/Models/Account.php | 27 +++--- app/Models/User.php | 17 ++++ .../PaymentDrivers/BasePaymentDriver.php | 8 ++ app/Ninja/Repositories/AccountRepository.php | 18 ++-- app/Policies/ExpensePolicy.php | 18 +++- app/Policies/InvoicePolicy.php | 2 +- app/Policies/QuotePolicy.php | 21 +++++ app/Policies/TaskPolicy.php | 18 +++- app/Policies/VendorPolicy.php | 18 +++- app/Providers/AppServiceProvider.php | 12 +-- resources/lang/en/texts.php | 3 +- .../views/accounts/bank_account.blade.php | 62 ++++++------- resources/views/accounts/banks.blade.php | 18 ++-- resources/views/accounts/management.blade.php | 68 ++++++++++---- .../views/accounts/user_management.blade.php | 12 +-- resources/views/expenses/edit.blade.php | 20 +++-- resources/views/header.blade.php | 5 ++ resources/views/invoices/knockout.blade.php | 1 + resources/views/master.blade.php | 5 -- resources/views/tasks/edit.blade.php | 90 ++++++++++--------- resources/views/vendors/edit.blade.php | 42 --------- 28 files changed, 390 insertions(+), 254 deletions(-) create mode 100644 app/Policies/QuotePolicy.php diff --git a/.travis.yml b/.travis.yml index 682706edc640..21fb9cb965dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ script: - 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 OnlinePaymentCest.php - php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php - php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php diff --git a/app/Console/Commands/SendRenewalInvoices.php b/app/Console/Commands/SendRenewalInvoices.php index bb34849e7cdf..1c46f50680c5 100644 --- a/app/Console/Commands/SendRenewalInvoices.php +++ b/app/Console/Commands/SendRenewalInvoices.php @@ -1,5 +1,6 @@ mailer = $mailer; $this->accountRepo = $repo; } - + public function fire() { $this->info(date('Y-m-d').' Running SendRenewalInvoices...'); @@ -58,28 +59,36 @@ class SendRenewalInvoices extends Command if (!count($company->accounts)) { continue; } - + $account = $company->accounts->sortBy('id')->first(); - $plan = $company->plan; - $term = $company->plan_term; - + $plan = []; + $plan['plan'] = $company->plan; + $plan['term'] = $company->plan_term; + $plan['num_users'] = $company->num_users; + $plan['price'] = min($company->plan_price, Utils::getPlanPrice($plan)); + if ($company->pending_plan) { - $plan = $company->pending_plan; - $term = $company->pending_term; + $plan['plan'] = $company->pending_plan; + $plan['term'] = $company->pending_term; + $plan['num_users'] = $company->pending_num_users; + $plan['price'] = min($company->pending_plan_price, Utils::getPlanPrice($plan)); } - - if ($plan == PLAN_FREE || !$plan || !$term ){ + + if ($plan['plan'] == PLAN_FREE || !$plan['plan'] || !$plan['term'] || !$plan['price']){ continue; } - + $client = $this->accountRepo->getNinjaClient($account); - $invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, $term); + $invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, 0, false); // set the due date to 10 days from now $invoice = $invitation->invoice; $invoice->due_date = date('Y-m-d', strtotime('+ 10 days')); $invoice->save(); + $term = $plan['term']; + $plan = $plan['plan']; + if ($term == PLAN_TERM_YEARLY) { $this->mailer->sendInvoice($invoice); $this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}"); diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index d2417048c4e9..158ea60b0297 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -154,20 +154,6 @@ class AccountController extends BaseController return Redirect::to($redirectTo)->with('sign_up', Input::get('sign_up')); } - /** - * @return bool|mixed - */ - public function enableProPlan() - { - if (Auth::user()->isPro() && ! Auth::user()->isTrial()) { - return false; - } - - $invitation = $this->accountRepo->enablePlan(); - - return $invitation->invitation_key; - } - /** * @return \Illuminate\Http\RedirectResponse */ @@ -201,7 +187,8 @@ class AccountController extends BaseController 'plan' => $plan, 'term' => $term ]; - } elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY) { + } elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY + || $planDetails['num_users'] != Input::get('num_users')) { $new_plan = [ 'plan' => $plan, 'term' => $term, @@ -260,8 +247,7 @@ class AccountController extends BaseController $days_total = $planDetails['paid']->diff($planDetails['expires'])->days; $percent_used = $days_used / $days_total; - $old_plan_price = Account::$plan_prices[$planDetails['plan']][$planDetails['term']]; - $credit = $old_plan_price * (1 - $percent_used); + $credit = $planDetails['plan_price'] * (1 - $percent_used); } } else { $new_plan = [ @@ -271,15 +257,23 @@ class AccountController extends BaseController } if (!empty($pending_change) && empty($new_plan)) { + $pending_change['num_users'] = Input::get('num_users'); $account->company->pending_plan = $pending_change['plan']; $account->company->pending_term = $pending_change['term']; + $account->company->pending_num_users = $pending_change['num_users']; + $account->company->pending_plan_price = Utils::getPlanPrice($pending_change); $account->company->save(); Session::flash('message', trans('texts.updated_plan')); } if (!empty($new_plan) && $new_plan['plan'] != PLAN_FREE) { - $invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly)); + $new_plan['num_users'] = 1; + if ($new_plan['plan'] == PLAN_ENTERPRISE) { + $new_plan['num_users'] = Input::get('num_users'); + } + $new_plan['price'] = Utils::getPlanPrice($new_plan); + $invitation = $this->accountRepo->enablePlan($new_plan, $credit, !empty($pending_monthly)); return Redirect::to('view/'.$invitation->invitation_key); } @@ -483,7 +477,8 @@ class AccountController extends BaseController private function showBankAccounts() { return View::make('accounts.banks', [ - 'title' => trans('texts.bank_accounts') + 'title' => trans('texts.bank_accounts'), + 'advanced' => ! Auth::user()->hasFeature(FEATURE_EXPENSES), ]); } diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 996febdc5f39..a9719798a1b2 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -41,10 +41,6 @@ class QuoteController extends BaseController public function index() { - if (!Utils::hasFeature(FEATURE_QUOTES)) { - return Redirect::to('/invoices/create'); - } - $data = [ 'title' => trans('texts.quotes'), 'entityType' => ENTITY_QUOTE, diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 44f77778bb3a..b8d5e15f54e0 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -93,21 +93,19 @@ class UserController extends BaseController */ public function create() { - if (!Auth::user()->registered) { + if ( ! Auth::user()->registered) { Session::flash('error', trans('texts.register_to_add_user')); return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT); } - if (!Auth::user()->confirmed) { + + if ( ! Auth::user()->confirmed) { Session::flash('error', trans('texts.confirmation_required')); return Redirect::to('settings/' . ACCOUNT_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', ['limit' => MAX_NUM_USERS])); - return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT); - } + if (Utils::isNinja() && ! Auth::user()->caddAddUsers()) { + Session::flash('error', trans('texts.max_users_reached')); + return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT); } $data = [ @@ -132,6 +130,11 @@ class UserController extends BaseController if ($action === 'archive') { $user->delete(); } else { + if ( ! Auth::user()->caddAddUsers()) { + return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT) + ->with('error', trans('texts.max_users_reached')); + } + $user->restore(); } @@ -140,19 +143,6 @@ class UserController extends BaseController return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT); } - public function restoreUser($userPublicId) - { - $user = User::where('account_id', '=', Auth::user()->account_id) - ->where('public_id', '=', $userPublicId) - ->withTrashed()->firstOrFail(); - - $user->restore(); - - Session::flash('message', trans('texts.restored_user')); - - return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT); - } - /** * Stores new account * @@ -257,7 +247,7 @@ class UserController extends BaseController $token = Password::getRepository()->create($user); return Redirect::to("/password/reset/{$token}"); - } else { + } else { if (Auth::check()) { if (Session::has(REQUESTED_PRO_PLAN)) { Session::forget(REQUESTED_PRO_PLAN); diff --git a/app/Http/routes.php b/app/Http/routes.php index bed171dc537c..1d38fb6f0a89 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -214,7 +214,6 @@ Route::group([ Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); Route::get('start_trial/{plan}', 'AccountController@startTrial') ->where(['plan'=>'pro']); - Route::get('restore_user/{user_id}', 'UserController@restoreUser'); Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount'); Route::get('/manage_companies', 'UserController@manageCompanies'); @@ -245,7 +244,6 @@ Route::group([ Route::post('user/setTheme', 'UserController@setTheme'); Route::post('remove_logo', 'AccountController@removeLogo'); - Route::post('account/go_pro', 'AccountController@enableProPlan'); Route::post('/export', 'ExportController@doExport'); Route::post('/import', 'ImportController@doImport'); @@ -466,7 +464,7 @@ if (!defined('CONTACT_EMAIL')) { define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35); define('ACTIVITY_TYPE_DELETE_EXPENSE', 36); define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37); - + // tasks define('ACTIVITY_TYPE_CREATE_TASK', 42); define('ACTIVITY_TYPE_UPDATE_TASK', 43); @@ -638,10 +636,10 @@ if (!defined('CONTACT_EMAIL')) { define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74'); define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); - define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 5)); - define('PLAN_PRICE_PRO_YEARLY', env('PLAN_PRICE_PRO_YEARLY', 50)); - define('PLAN_PRICE_ENTERPRISE_MONTHLY', env('PLAN_PRICE_ENTERPRISE_MONTHLY', 10)); - define('PLAN_PRICE_ENTERPRISE_YEARLY', env('PLAN_PRICE_ENTERPRISE_YEARLY', 100)); + define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 12)); + define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 18)); + define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 26)); + define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 38)); define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20)); define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10)); @@ -751,6 +749,8 @@ if (!defined('CONTACT_EMAIL')) { define('FEATURE_PDF_ATTACHMENT', 'pdf_attachment'); define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs'); define('FEATURE_QUOTES', 'quotes'); + define('FEATURE_TASKS', 'tasks'); + define('FEATURE_EXPENSES', 'expenses'); define('FEATURE_REPORTS', 'reports'); define('FEATURE_API', 'api'); define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password'); @@ -771,6 +771,7 @@ if (!defined('CONTACT_EMAIL')) { // Pro users who started paying on or before this date will be able to manage users define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-06-04'); + define('EXTRAS_GRANDFATHER_COMPANY_ID', 0); // WePay define('WEPAY_PRODUCTION', 'production'); diff --git a/app/Libraries/Utils.php b/app/Libraries/Utils.php index 498196ae8b46..3447e81f538d 100644 --- a/app/Libraries/Utils.php +++ b/app/Libraries/Utils.php @@ -215,6 +215,46 @@ class Utils } } + public static function getPlanPrice($plan) + { + $term = $plan['term']; + $numUsers = $plan['num_users']; + $plan = $plan['plan']; + + if ($plan == PLAN_FREE) { + $price = 0; + } elseif ($plan == PLAN_PRO) { + $price = PLAN_PRICE_PRO_MONTHLY; + } elseif ($plan == PLAN_ENTERPRISE) { + if ($numUsers <= 2) { + $price = PLAN_PRICE_ENTERPRISE_MONTHLY_2; + } elseif ($numUsers <= 5) { + $price = PLAN_PRICE_ENTERPRISE_MONTHLY_5; + } elseif ($numUsers <= 10) { + $price = PLAN_PRICE_ENTERPRISE_MONTHLY_10; + } else { + static::fatalError('Invalid number of users: ' . $numUsers); + } + } + + if ($term == PLAN_TERM_YEARLY) { + $price = $price * 10; + } + + return $price; + } + + public static function getMinNumUsers($max) + { + if ($max <= 2) { + return 1; + } elseif ($max <= 5) { + return 3; + } else { + return 6; + } + } + public static function basePath() { return substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1); diff --git a/app/Models/Account.php b/app/Models/Account.php index d940b108094e..56c6829f243b 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -20,20 +20,6 @@ class Account extends Eloquent use PresentableTrait; use SoftDeletes; - /** - * @var array - */ - public static $plan_prices = [ - PLAN_PRO => [ - PLAN_TERM_MONTHLY => PLAN_PRICE_PRO_MONTHLY, - PLAN_TERM_YEARLY => PLAN_PRICE_PRO_YEARLY, - ], - PLAN_ENTERPRISE => [ - PLAN_TERM_MONTHLY => PLAN_PRICE_ENTERPRISE_MONTHLY, - PLAN_TERM_YEARLY => PLAN_PRICE_ENTERPRISE_YEARLY, - ], - ]; - /** * @var string */ @@ -90,7 +76,6 @@ class Account extends Eloquent ACCOUNT_USER_DETAILS, ACCOUNT_LOCALIZATION, ACCOUNT_PAYMENTS, - ACCOUNT_BANKS, ACCOUNT_TAX_RATES, ACCOUNT_PRODUCTS, ACCOUNT_NOTIFICATIONS, @@ -106,6 +91,7 @@ class Account extends Eloquent ACCOUNT_INVOICE_DESIGN, ACCOUNT_EMAIL_SETTINGS, ACCOUNT_TEMPLATES_AND_REMINDERS, + ACCOUNT_BANKS, ACCOUNT_CLIENT_PORTAL, ACCOUNT_CHARTS_AND_REPORTS, ACCOUNT_DATA_VISUALIZATIONS, @@ -1189,6 +1175,10 @@ class Account extends Eloquent case FEATURE_CUSTOM_URL: return $selfHost || !empty($planDetails); + case FEATURE_TASKS: + case FEATURE_EXPENSES: + return $selfHost || !empty($planDetails) || $planDetails['company_id'] < EXTRAS_GRANDFATHER_COMPANY_ID; + // Pro; No trial allowed, unless they're trialing enterprise with an active pro plan case FEATURE_MORE_CLIENTS: return $selfHost || !empty($planDetails) && (!$planDetails['trial'] || !empty($this->getPlanDetails(false, false))); @@ -1272,6 +1262,7 @@ class Account extends Eloquent } $plan = $this->company->plan; + $price = $this->company->plan_price; $trial_plan = $this->company->trial_plan; if(!$plan && (!$trial_plan || !$include_trial)) { @@ -1335,6 +1326,9 @@ class Account extends Eloquent if ($use_plan) { return [ + 'company_id' => $this->company->id, + 'num_users' => $this->company->num_users, + 'plan_price' => $price, 'trial' => false, 'plan' => $plan, 'started' => DateTime::createFromFormat('Y-m-d', $this->company->plan_started), @@ -1345,6 +1339,9 @@ class Account extends Eloquent ]; } else { return [ + 'company_id' => $this->company->id, + 'num_users' => 1, + 'plan_price' => 0, 'trial' => true, 'plan' => $trial_plan, 'started' => $trial_started, diff --git a/app/Models/User.php b/app/Models/User.php index 68a3d1470f6f..e95459f384b6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -430,6 +430,23 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac public function filterId() { return $this->hasPermission('view_all') ? false : $this->id; } + + + public function caddAddUsers() { + if ( ! $this->hasFeature(FEATURE_USERS)) { + return false; + } + + $account = $this->account; + $company = $account->company; + + $numUsers = 1; + foreach ($company->accounts as $account) { + $numUsers += $account->users->count() - 1; + } + + return $numUsers < $company->num_users; + } } User::updating(function ($user) { diff --git a/app/Ninja/PaymentDrivers/BasePaymentDriver.php b/app/Ninja/PaymentDrivers/BasePaymentDriver.php index b809f5985c81..17ed76d3fc7c 100644 --- a/app/Ninja/PaymentDrivers/BasePaymentDriver.php +++ b/app/Ninja/PaymentDrivers/BasePaymentDriver.php @@ -576,6 +576,12 @@ class BasePaymentDriver if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) { $plan = strtolower($matches[1]); $term = strtolower($matches[2]); + if ($plan == PLAN_ENTERPRISE) { + preg_match('/###[\d] [\w]* (\d*)/', $invoice_item->notes, $matches); + $numUsers = $matches[1]; + } else { + $numUsers = 1; + } } elseif ($invoice_item->product_key == 'Pending Monthly') { $pending_monthly = true; } @@ -607,6 +613,8 @@ class BasePaymentDriver $account->company->payment_id = $payment->id; $account->company->plan = $plan; $account->company->plan_term = $term; + $account->company->plan_price = $payment->amount; + $account->company->num_users = $numUsers; $account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid) ->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d'); diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 8f7000b540d5..1c6f7f30c35f 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -228,23 +228,26 @@ class AccountRepository return $data; } - public function enablePlan($plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false) + public function enablePlan($plan, $credit = 0, $pending_monthly = false) { $account = Auth::user()->account; $client = $this->getNinjaClient($account); - $invitation = $this->createNinjaInvoice($client, $account, $plan, $term, $credit, $pending_monthly); + $invitation = $this->createNinjaInvoice($client, $account, $plan, $credit, $pending_monthly); return $invitation; } - public function createNinjaInvoice($client, $clientAccount, $plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false) + public function createNinjaInvoice($client, $clientAccount, $plan, $credit = 0, $pending_monthly = false) { + $term = $plan['term']; + $plan_cost = $plan['price']; + $num_users = $plan['num_users']; + $plan = $plan['plan']; + if ($credit < 0) { $credit = 0; } - $plan_cost = Account::$plan_prices[$plan][$term]; - $account = $this->getNinjaAccount(); $lastInvoice = Invoice::withTrashed()->whereAccountId($account->id)->orderBy('public_id', 'DESC')->first(); $publicId = $lastInvoice ? ($lastInvoice->public_id + 1) : 1; @@ -272,6 +275,11 @@ class AccountRepository $item->cost = $plan_cost; $item->notes = trans("texts.{$plan}_plan_{$term}_description"); + if ($plan == PLAN_ENTERPRISE) { + $min = Utils::getMinNumUsers($num_users); + $item->notes .= "\n\n###" . trans('texts.min_to_max_users', ['min' => $min, 'max' => $num_users]); + } + // Don't change this without updating the regex in PaymentService->createPayment() $item->product_key = 'Plan - '.ucfirst($plan).' ('.ucfirst($term).')'; $invoice->invoice_items()->save($item); diff --git a/app/Policies/ExpensePolicy.php b/app/Policies/ExpensePolicy.php index 4fdac4d627aa..d281d2f407c2 100644 --- a/app/Policies/ExpensePolicy.php +++ b/app/Policies/ExpensePolicy.php @@ -2,4 +2,20 @@ namespace App\Policies; -class ExpensePolicy extends EntityPolicy {} \ No newline at end of file +use App\Models\User; + +class ExpensePolicy extends EntityPolicy +{ + /** + * @param User $user + * @return bool + */ + public static function create(User $user) { + if ( ! parent::create($user)) { + return false; + } + + return $user->hasFeature(FEATURE_EXPENSES); + } + +} diff --git a/app/Policies/InvoicePolicy.php b/app/Policies/InvoicePolicy.php index a51a099a6814..aa72b4c0533c 100644 --- a/app/Policies/InvoicePolicy.php +++ b/app/Policies/InvoicePolicy.php @@ -2,4 +2,4 @@ namespace App\Policies; -class InvoicePolicy extends EntityPolicy {} \ No newline at end of file +class InvoicePolicy extends EntityPolicy {} diff --git a/app/Policies/QuotePolicy.php b/app/Policies/QuotePolicy.php new file mode 100644 index 000000000000..8d5f60ea7fbc --- /dev/null +++ b/app/Policies/QuotePolicy.php @@ -0,0 +1,21 @@ +hasFeature(FEATURE_QUOTES); + } + +} diff --git a/app/Policies/TaskPolicy.php b/app/Policies/TaskPolicy.php index b1fbe2902974..6e7391e031dc 100644 --- a/app/Policies/TaskPolicy.php +++ b/app/Policies/TaskPolicy.php @@ -2,4 +2,20 @@ namespace App\Policies; -class TaskPolicy extends EntityPolicy {} \ No newline at end of file +use App\Models\User; + +class TaskPolicy extends EntityPolicy +{ + /** + * @param User $user + * @return bool + */ + public static function create(User $user) { + if ( ! parent::create($user)) { + return false; + } + + return $user->hasFeature(FEATURE_TASKS); + } + +} diff --git a/app/Policies/VendorPolicy.php b/app/Policies/VendorPolicy.php index 681cdcb50240..8c388193251d 100644 --- a/app/Policies/VendorPolicy.php +++ b/app/Policies/VendorPolicy.php @@ -2,4 +2,20 @@ namespace App\Policies; -class VendorPolicy extends EntityPolicy {} \ No newline at end of file +use App\Models\User; + +class VendorPolicy extends EntityPolicy +{ + /** + * @param User $user + * @return bool + */ + public static function create(User $user) { + if ( ! parent::create($user)) { + return false; + } + + return $user->hasFeature(FEATURE_EXPENSES); + } + +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f7089370d4fd..9ce79f8554d2 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -60,17 +60,17 @@ class AppServiceProvider extends ServiceProvider $items = []; - if($user->can('create', $type))$items[] = '
  • '.trans("texts.new_$type").'
  • '; + if ($user->can('create', $type)) { + $items[] = '
  • '.trans("texts.new_$type").'
  • '; + } if ($type == ENTITY_INVOICE) { if(!empty($items))$items[] = '
  • '; $items[] = '
  • '.trans('texts.recurring_invoices').'
  • '; if($user->can('create', ENTITY_INVOICE))$items[] = '
  • '.trans('texts.new_recurring_invoice').'
  • '; - if ($user->hasFeature(FEATURE_QUOTES)) { - $items[] = '
  • '; - $items[] = '
  • '.trans('texts.quotes').'
  • '; - if($user->can('create', ENTITY_INVOICE))$items[] = '
  • '.trans('texts.new_quote').'
  • '; - } + $items[] = '
  • '; + $items[] = '
  • '.trans('texts.quotes').'
  • '; + if($user->can('create', ENTITY_QUOTE))$items[] = '
  • '.trans('texts.new_quote').'
  • '; } else if ($type == ENTITY_CLIENT) { if(!empty($items))$items[] = '
  • '; $items[] = '
  • '.trans('texts.credits').'
  • '; diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 87d92df4f55b..4038ee96a24e 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -2026,7 +2026,8 @@ $LANG = array( 'restore_expense_category' => 'Restore expense category', 'restored_expense_category' => 'Successfully restored expense category', 'apply_taxes' => 'Apply taxes', - + 'min_to_max_users' => ':min to :max users', + 'max_users_reached' => 'The maximum number of users has been reached.' ); diff --git a/resources/views/accounts/bank_account.blade.php b/resources/views/accounts/bank_account.blade.php index 2df373cb0e3a..8291b95373c1 100644 --- a/resources/views/accounts/bank_account.blade.php +++ b/resources/views/accounts/bank_account.blade.php @@ -161,39 +161,41 @@

     

    - {!! Former::actions( - count(Cache::get('banks')) > 0 ? - Button::normal(trans('texts.cancel')) + @if (Auth::user()->hasFeature(FEATURE_EXPENSES)) + {!! Former::actions( + count(Cache::get('banks')) > 0 ? + Button::normal(trans('texts.cancel')) + ->withAttributes([ + 'data-bind' => 'visible: !importResults()', + ]) + ->large() + ->asLinkTo(URL::to('/settings/bank_accounts')) + ->appendIcon(Icon::create('remove-circle')) : false, + Button::success(trans('texts.validate')) ->withAttributes([ - 'data-bind' => 'visible: !importResults()', + 'data-bind' => 'css: {disabled: disableValidate}, visible: page() == "login"', + 'onclick' => 'validate()' ]) ->large() - ->asLinkTo(URL::to('/settings/bank_accounts')) - ->appendIcon(Icon::create('remove-circle')) : false, - Button::success(trans('texts.validate')) - ->withAttributes([ - 'data-bind' => 'css: {disabled: disableValidate}, visible: page() == "login"', - 'onclick' => 'validate()' - ]) - ->large() - ->appendIcon(Icon::create('lock')), - Button::success(trans('texts.save')) - ->withAttributes([ - 'data-bind' => 'css: {disabled: disableSave}, visible: page() == "setup"', - 'style' => 'display:none', - 'onclick' => 'save()' - ]) - ->large() - ->appendIcon(Icon::create('floppy-disk')) , - Button::success(trans('texts.import')) - ->withAttributes([ - 'data-bind' => 'css: {disabled: disableSaveExpenses}, visible: page() == "import"', - 'style' => 'display:none', - 'onclick' => 'saveExpenses()' - ]) - ->large() - ->appendIcon(Icon::create('floppy-disk'))) !!} - + ->appendIcon(Icon::create('lock')), + Button::success(trans('texts.save')) + ->withAttributes([ + 'data-bind' => 'css: {disabled: disableSave}, visible: page() == "setup"', + 'style' => 'display:none', + 'onclick' => 'save()' + ]) + ->large() + ->appendIcon(Icon::create('floppy-disk')) , + Button::success(trans('texts.import')) + ->withAttributes([ + 'data-bind' => 'css: {disabled: disableSaveExpenses}, visible: page() == "import"', + 'style' => 'display:none', + 'onclick' => 'saveExpenses()' + ]) + ->large() + ->appendIcon(Icon::create('floppy-disk'))) !!} + @endif + {!! Former::close() !!} diff --git a/resources/views/vendors/edit.blade.php b/resources/views/vendors/edit.blade.php index 82a619281513..0343d8a0eae9 100644 --- a/resources/views/vendors/edit.blade.php +++ b/resources/views/vendors/edit.blade.php @@ -113,48 +113,6 @@ ->fromQuery($currencies, 'name', 'id') !!} {!! Former::textarea('private_notes')->rows(6) !!} - - @if (Auth::user()->account->isNinjaAccount()) - @if (isset($planDetails)) - {!! Former::populateField('plan', $planDetails['plan']) !!} - {!! Former::populateField('plan_term', $planDetails['term']) !!} - @if (!empty($planDetails['paid'])) - {!! Former::populateField('plan_paid', $planDetails['paid']->format('Y-m-d')) !!} - @endif - @if (!empty($planDetails['expires'])) - {!! Former::populateField('plan_expires', $planDetails['expires']->format('Y-m-d')) !!} - @endif - @if (!empty($planDetails['started'])) - {!! Former::populateField('plan_started', $planDetails['started']->format('Y-m-d')) !!} - @endif - @endif - {!! Former::select('plan') - ->addOption(trans('texts.plan_free'), PLAN_FREE) - ->addOption(trans('texts.plan_pro'), PLAN_PRO) - ->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE)!!} - {!! Former::select('plan_term') - ->addOption() - ->addOption(trans('texts.plan_term_yearly'), PLAN_TERM_YEARLY) - ->addOption(trans('texts.plan_term_monthly'), PLAN_TERM_MONTHLY)!!} - {!! Former::text('plan_started') - ->data_date_format('yyyy-mm-dd') - ->addGroupClass('plan_start_date') - ->append('') !!} - {!! Former::text('plan_paid') - ->data_date_format('yyyy-mm-dd') - ->addGroupClass('plan_paid_date') - ->append('') !!} - {!! Former::text('plan_expires') - ->data_date_format('yyyy-mm-dd') - ->addGroupClass('plan_expire_date') - ->append('') !!} - - @endif -