Finalize multi-plan support

* Allow admins to change plan
* Check features instead of plans
* Support linking/unlinking accounts
* Support creating/deleting accounts
This commit is contained in:
Joshua Dwire 2016-04-18 22:35:18 -04:00
parent 454e9bc861
commit 22f65e8108
63 changed files with 472 additions and 238 deletions

View File

@ -36,7 +36,7 @@ class SendReminders extends Command
$this->info(count($accounts).' accounts found'); $this->info(count($accounts).' accounts found');
foreach ($accounts as $account) { foreach ($accounts as $account) {
if (!$account->isPro()) { if (!$account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
continue; continue;
} }

View File

@ -521,7 +521,7 @@ class AccountController extends BaseController
$invoice->client = $client; $invoice->client = $client;
$invoice->invoice_items = [$invoiceItem]; $invoice->invoice_items = [$invoiceItem];
//$invoice->documents = $account->isPro() ? [$document] : []; //$invoice->documents = $account->hasFeature(FEATURE_DOCUMENTS) ? [$document] : [];
$invoice->documents = []; $invoice->documents = [];
$data['account'] = $account; $data['account'] = $account;
@ -643,7 +643,7 @@ class AccountController extends BaseController
private function saveCustomizeDesign() private function saveCustomizeDesign()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) {
$account = Auth::user()->account; $account = Auth::user()->account;
$account->custom_design = Input::get('custom_design'); $account->custom_design = Input::get('custom_design');
$account->invoice_design_id = CUSTOM_DESIGN; $account->invoice_design_id = CUSTOM_DESIGN;
@ -658,7 +658,7 @@ class AccountController extends BaseController
private function saveClientPortal() private function saveClientPortal()
{ {
// Only allowed for pro Invoice Ninja users or white labeled self-hosted users // Only allowed for pro Invoice Ninja users or white labeled self-hosted users
if ((Utils::isNinja() && Auth::user()->account->isPro()) || Auth::user()->account->isWhiteLabel()) { if (Auth::user()->account->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) {
$input_css = Input::get('client_view_css'); $input_css = Input::get('client_view_css');
if (Utils::isNinja()) { if (Utils::isNinja()) {
// Allow referencing the body element // Allow referencing the body element
@ -709,7 +709,7 @@ class AccountController extends BaseController
private function saveEmailTemplates() private function saveEmailTemplates()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
$account = Auth::user()->account; $account = Auth::user()->account;
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) { foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, REMINDER1, REMINDER2, REMINDER3] as $type) {
@ -771,7 +771,7 @@ class AccountController extends BaseController
private function saveEmailSettings() private function saveEmailSettings()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_CUSTOM_EMAILS)) {
$rules = []; $rules = [];
$user = Auth::user(); $user = Auth::user();
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH)); $iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
@ -814,7 +814,7 @@ class AccountController extends BaseController
private function saveInvoiceSettings() private function saveInvoiceSettings()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_INVOICE_SETTINGS)) {
$rules = [ $rules = [
'invoice_number_pattern' => 'has_counter', 'invoice_number_pattern' => 'has_counter',
'quote_number_pattern' => 'has_counter', 'quote_number_pattern' => 'has_counter',
@ -894,7 +894,7 @@ class AccountController extends BaseController
private function saveInvoiceDesign() private function saveInvoiceDesign()
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) {
$account = Auth::user()->account; $account = Auth::user()->account;
$account->hide_quantity = Input::get('hide_quantity') ? true : false; $account->hide_quantity = Input::get('hide_quantity') ? true : false;
$account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false; $account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false;
@ -1188,6 +1188,9 @@ class AccountController extends BaseController
\Log::info("Canceled Account: {$account->name} - {$user->email}"); \Log::info("Canceled Account: {$account->name} - {$user->email}");
$this->accountRepo->unlinkAccount($account); $this->accountRepo->unlinkAccount($account);
if ($account->company->accounts->count() == 1) {
$account->company->forceDelete();
}
$account->forceDelete(); $account->forceDelete();
Auth::logout(); Auth::logout();

View File

@ -133,6 +133,9 @@ class AuthController extends Controller {
if (Auth::check() && !Auth::user()->registered) { if (Auth::check() && !Auth::user()->registered) {
$account = Auth::user()->account; $account = Auth::user()->account;
$this->accountRepo->unlinkAccount($account); $this->accountRepo->unlinkAccount($account);
if ($account->company->accounts->count() == 1) {
$account->company->forceDelete();
}
$account->forceDelete(); $account->forceDelete();
} }

View File

@ -33,7 +33,7 @@ class AuthController extends Controller {
$client = $invoice->client; $client = $invoice->client;
$account = $client->account; $account = $client->account;
$data['hideLogo'] = $account->isWhiteLabel(); $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL);
$data['clientViewCSS'] = $account->clientViewCSS(); $data['clientViewCSS'] = $account->clientViewCSS();
$data['clientFontUrl'] = $account->getFontsUrl(); $data['clientFontUrl'] = $account->getFontsUrl();
} }

View File

@ -50,7 +50,7 @@ class PasswordController extends Controller {
$client = $invoice->client; $client = $invoice->client;
$account = $client->account; $account = $client->account;
$data['hideLogo'] = $account->isWhiteLabel(); $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL);
$data['clientViewCSS'] = $account->clientViewCSS(); $data['clientViewCSS'] = $account->clientViewCSS();
$data['clientFontUrl'] = $account->getFontsUrl(); $data['clientFontUrl'] = $account->getFontsUrl();
} }
@ -117,7 +117,7 @@ class PasswordController extends Controller {
$client = $invoice->client; $client = $invoice->client;
$account = $client->account; $account = $client->account;
$data['hideLogo'] = $account->isWhiteLabel(); $data['hideLogo'] = $account->hasFeature(FEATURE_WHITE_LABEL);
$data['clientViewCSS'] = $account->clientViewCSS(); $data['clientViewCSS'] = $account->clientViewCSS();
$data['clientFontUrl'] = $account->getFontsUrl(); $data['clientFontUrl'] = $account->getFontsUrl();
} }

View File

@ -114,7 +114,7 @@ class ClientController extends BaseController
if(Task::canCreate()){ if(Task::canCreate()){
$actionLinks[] = ['label' => trans('texts.new_task'), 'url' => URL::to('/tasks/create/'.$client->public_id)]; $actionLinks[] = ['label' => trans('texts.new_task'), 'url' => URL::to('/tasks/create/'.$client->public_id)];
} }
if (Utils::isPro() && Invoice::canCreate()) { if (Utils::hasFeature(FEATURE_QUOTES) && Invoice::canCreate()) {
$actionLinks[] = ['label' => trans('texts.new_quote'), 'url' => URL::to('/quotes/create/'.$client->public_id)]; $actionLinks[] = ['label' => trans('texts.new_quote'), 'url' => URL::to('/quotes/create/'.$client->public_id)];
} }
@ -201,7 +201,7 @@ class ClientController extends BaseController
if (Auth::user()->account->isNinjaAccount()) { if (Auth::user()->account->isNinjaAccount()) {
if ($account = Account::whereId($client->public_id)->first()) { if ($account = Account::whereId($client->public_id)->first()) {
$data['proPlanPaid'] = $account['pro_plan_paid']; $data['planDetails'] = $account->getPlanDetails(false, false);
} }
} }

View File

@ -114,7 +114,7 @@ class DocumentController extends BaseController
public function postUpload() public function postUpload()
{ {
if (!Utils::isPro()) { if (!Utils::hasFeature(FEATURE_DOCUMENTS)) {
return; return;
} }

View File

@ -132,7 +132,11 @@ class InvoiceController extends BaseController
$invoice->start_date = Utils::fromSqlDate($invoice->start_date); $invoice->start_date = Utils::fromSqlDate($invoice->start_date);
$invoice->end_date = Utils::fromSqlDate($invoice->end_date); $invoice->end_date = Utils::fromSqlDate($invoice->end_date);
$invoice->last_sent_date = Utils::fromSqlDate($invoice->last_sent_date); $invoice->last_sent_date = Utils::fromSqlDate($invoice->last_sent_date);
$invoice->is_pro = Auth::user()->isPro(); $invoice->features = [
'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN),
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
];
$actions = [ $actions = [
['url' => 'javascript:onCloneClick()', 'label' => trans("texts.clone_{$entityType}")], ['url' => 'javascript:onCloneClick()', 'label' => trans("texts.clone_{$entityType}")],
@ -573,7 +577,11 @@ class InvoiceController extends BaseController
$invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country'); $invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country');
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
$invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date);
$invoice->is_pro = Auth::user()->isPro(); $invoice->features = [
'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN),
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
];
$invoice->is_quote = intval($invoice->is_quote); $invoice->is_quote = intval($invoice->is_quote);
$activityTypeId = $invoice->is_quote ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE; $activityTypeId = $invoice->is_quote ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE;
@ -591,7 +599,11 @@ class InvoiceController extends BaseController
$backup = json_decode($activity->json_backup); $backup = json_decode($activity->json_backup);
$backup->invoice_date = Utils::fromSqlDate($backup->invoice_date); $backup->invoice_date = Utils::fromSqlDate($backup->invoice_date);
$backup->due_date = Utils::fromSqlDate($backup->due_date); $backup->due_date = Utils::fromSqlDate($backup->due_date);
$backup->is_pro = Auth::user()->isPro(); $invoice->features = [
'customize_invoice_design' => Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN),
'remove_created_by' => Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY),
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
];
$backup->is_quote = isset($backup->is_quote) && intval($backup->is_quote); $backup->is_quote = isset($backup->is_quote) && intval($backup->is_quote);
$backup->account = $invoice->account->toArray(); $backup->account = $invoice->account->toArray();

View File

@ -191,7 +191,7 @@ class PaymentController extends BaseController
'currencyId' => $client->getCurrencyId(), 'currencyId' => $client->getCurrencyId(),
'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'), 'currencyCode' => $client->currency ? $client->currency->code : ($account->currency ? $account->currency->code : 'USD'),
'account' => $client->account, 'account' => $client->account,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'hideHeader' => $account->isNinjaAccount(), 'hideHeader' => $account->isNinjaAccount(),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),

View File

@ -72,7 +72,11 @@ class PublicClientController extends BaseController
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
$invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date);
$invoice->is_pro = $account->isPro(); $invoice->features = [
'customize_invoice_design' => $account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN),
'remove_created_by' => $account->hasFeature(FEATURE_REMOVE_CREATED_BY),
'invoice_settings' => $account->hasFeature(FEATURE_INVOICE_SETTINGS),
];
$invoice->invoice_fonts = $account->getFontsData(); $invoice->invoice_fonts = $account->getFontsData();
if ($invoice->invoice_design_id == CUSTOM_DESIGN) { if ($invoice->invoice_design_id == CUSTOM_DESIGN) {
@ -122,10 +126,10 @@ class PublicClientController extends BaseController
'account' => $account, 'account' => $account,
'showApprove' => $showApprove, 'showApprove' => $showApprove,
'showBreadcrumbs' => false, 'showBreadcrumbs' => false,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'hideHeader' => $account->isNinjaAccount() || !$account->enable_client_portal, 'hideHeader' => $account->isNinjaAccount() || !$account->enable_client_portal,
'hideDashboard' => !$account->enable_client_portal_dashboard, 'hideDashboard' => !$account->enable_client_portal_dashboard,
'showDocuments' => $account->isPro(), 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),
'invoice' => $invoice->hidePrivateFields(), 'invoice' => $invoice->hidePrivateFields(),
@ -140,7 +144,7 @@ class PublicClientController extends BaseController
'phantomjs' => Input::has('phantomjs'), 'phantomjs' => Input::has('phantomjs'),
); );
if($account->isPro() && $this->canCreateZip()){ if($account->hasFeature(FEATURE_DOCUMENTS) && $this->canCreateZip()){
$zipDocs = $this->getInvoiceZipDocuments($invoice, $size); $zipDocs = $this->getInvoiceZipDocuments($invoice, $size);
if(count($zipDocs) > 1){ if(count($zipDocs) > 1){
@ -220,8 +224,8 @@ class PublicClientController extends BaseController
'color' => $color, 'color' => $color,
'account' => $account, 'account' => $account,
'client' => $client, 'client' => $client,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'showDocuments' => $account->isPro(), 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),
]; ];
@ -273,9 +277,9 @@ class PublicClientController extends BaseController
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'hideDashboard' => !$account->enable_client_portal_dashboard, 'hideDashboard' => !$account->enable_client_portal_dashboard,
'showDocuments' => $account->isPro(), 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.invoices'), 'title' => trans('texts.invoices'),
@ -310,9 +314,9 @@ class PublicClientController extends BaseController
$color = $account->primary_color ? $account->primary_color : '#0b4d78'; $color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'hideDashboard' => !$account->enable_client_portal_dashboard, 'hideDashboard' => !$account->enable_client_portal_dashboard,
'showDocuments' => $account->isPro(), 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),
'entityType' => ENTITY_PAYMENT, 'entityType' => ENTITY_PAYMENT,
@ -354,9 +358,9 @@ class PublicClientController extends BaseController
$color = $account->primary_color ? $account->primary_color : '#0b4d78'; $color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'hideDashboard' => !$account->enable_client_portal_dashboard, 'hideDashboard' => !$account->enable_client_portal_dashboard,
'showDocuments' => $account->isPro(), 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.quotes'), 'title' => trans('texts.quotes'),
@ -392,9 +396,9 @@ class PublicClientController extends BaseController
$color = $account->primary_color ? $account->primary_color : '#0b4d78'; $color = $account->primary_color ? $account->primary_color : '#0b4d78';
$data = [ $data = [
'color' => $color, 'color' => $color,
'hideLogo' => $account->isWhiteLabel(), 'hideLogo' => $account->hasFeature(FEATURE_WHITE_LABEL),
'hideDashboard' => !$account->enable_client_portal_dashboard, 'hideDashboard' => !$account->enable_client_portal_dashboard,
'showDocuments' => $account->isPro(), 'showDocuments' => $account->hasFeature(FEATURE_DOCUMENTS),
'clientViewCSS' => $account->clientViewCSS(), 'clientViewCSS' => $account->clientViewCSS(),
'clientFontUrl' => $account->getFontsUrl(), 'clientFontUrl' => $account->getFontsUrl(),
'title' => trans('texts.documents'), 'title' => trans('texts.documents'),

View File

@ -47,7 +47,7 @@ class QuoteController extends BaseController
public function index() public function index()
{ {
if (!Utils::isPro()) { if (!Utils::hasFeature(FEATURE_QUOTES)) {
return Redirect::to('/invoices/create'); return Redirect::to('/invoices/create');
} }
@ -84,7 +84,7 @@ class QuoteController extends BaseController
return $response; return $response;
} }
if (!Utils::isPro()) { if (!Utils::hasFeature(FEATURE_QUOTES)) {
return Redirect::to('/invoices/create'); return Redirect::to('/invoices/create');
} }

View File

@ -21,7 +21,7 @@ class ReportController extends BaseController
$message = ''; $message = '';
$fileName = storage_path().'/dataviz_sample.txt'; $fileName = storage_path().'/dataviz_sample.txt';
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
$account = Account::where('id', '=', Auth::user()->account->id) $account = Account::where('id', '=', Auth::user()->account->id)
->with(['clients.invoices.invoice_items', 'clients.contacts']) ->with(['clients.invoices.invoice_items', 'clients.contacts'])
->first(); ->first();
@ -99,7 +99,7 @@ class ReportController extends BaseController
'title' => trans('texts.charts_and_reports'), 'title' => trans('texts.charts_and_reports'),
]; ];
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
if ($enableReport) { if ($enableReport) {
$isExport = $action == 'export'; $isExport = $action == 'export';
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport)); $params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));

View File

@ -93,7 +93,7 @@ class TokenController extends BaseController
*/ */
public function save($tokenPublicId = false) public function save($tokenPublicId = false)
{ {
if (Auth::user()->account->isPro()) { if (Auth::user()->account->hasFeature(FEATURE_API)) {
$rules = [ $rules = [
'name' => 'required', 'name' => 'required',
]; ];

View File

@ -164,7 +164,7 @@ class UserController extends BaseController
*/ */
public function save($userPublicId = false) public function save($userPublicId = false)
{ {
if (Auth::user()->isPro() && ! Auth::user()->isTrial()) { if (Auth::user()->hasFeature(FEATURE_USERS)) {
$rules = [ $rules = [
'first_name' => 'required', 'first_name' => 'required',
'last_name' => 'required', 'last_name' => 'required',
@ -190,8 +190,10 @@ class UserController extends BaseController
$user->last_name = trim(Input::get('last_name')); $user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email')); $user->username = trim(Input::get('email'));
$user->email = trim(Input::get('email')); $user->email = trim(Input::get('email'));
$user->is_admin = boolval(Input::get('is_admin')); if (Auth::user()->hasFeature(FEATURE_USER_PERMISSIONS)) {
$user->permissions = Input::get('permissions'); $user->is_admin = boolval(Input::get('is_admin'));
$user->permissions = Input::get('permissions');
}
} else { } else {
$lastUser = User::withTrashed()->where('account_id', '=', Auth::user()->account_id) $lastUser = User::withTrashed()->where('account_id', '=', Auth::user()->account_id)
->orderBy('public_id', 'DESC')->first(); ->orderBy('public_id', 'DESC')->first();
@ -202,12 +204,14 @@ class UserController extends BaseController
$user->last_name = trim(Input::get('last_name')); $user->last_name = trim(Input::get('last_name'));
$user->username = trim(Input::get('email')); $user->username = trim(Input::get('email'));
$user->email = trim(Input::get('email')); $user->email = trim(Input::get('email'));
$user->is_admin = boolval(Input::get('is_admin'));
$user->registered = true; $user->registered = true;
$user->password = str_random(RANDOM_KEY_LENGTH); $user->password = str_random(RANDOM_KEY_LENGTH);
$user->confirmation_code = str_random(RANDOM_KEY_LENGTH); $user->confirmation_code = str_random(RANDOM_KEY_LENGTH);
$user->public_id = $lastUser->public_id + 1; $user->public_id = $lastUser->public_id + 1;
$user->permissions = Input::get('permissions'); if (Auth::user()->hasFeature(FEATURE_USER_PERMISSIONS)) {
$user->is_admin = boolval(Input::get('is_admin'));
$user->permissions = Input::get('permissions');
}
} }
$user->save(); $user->save();
@ -286,6 +290,9 @@ class UserController extends BaseController
if (!Auth::user()->registered) { if (!Auth::user()->registered) {
$account = Auth::user()->account; $account = Auth::user()->account;
$this->accountRepo->unlinkAccount($account); $this->accountRepo->unlinkAccount($account);
if ($account->company->accounts->count() == 1) {
$account->company->forceDelete();
}
$account->forceDelete(); $account->forceDelete();
} }
} }

View File

@ -175,8 +175,8 @@ class VendorController extends BaseController
$data = array_merge($data, self::getViewModel()); $data = array_merge($data, self::getViewModel());
if (Auth::user()->account->isNinjaAccount()) { if (Auth::user()->account->isNinjaAccount()) {
if ($account = Account::whereId($vendor->public_id)->first()) { if ($account = Account::whereId($client->public_id)->first()) {
$data['proPlanPaid'] = $account['pro_plan_paid']; $data['planDetails'] = $account->getPlanDetails(false, false);
} }
} }

View File

@ -47,7 +47,7 @@ class ApiCheck {
return $next($request); return $next($request);
} }
if (!Utils::isPro() && !$loggingIn) { if (!Utils::hasFeature(FEATURE_API) && !$loggingIn) {
return Response::json('API requires pro plan', 403, $headers); return Response::json('API requires pro plan', 403, $headers);
} else { } else {
$key = Auth::check() ? Auth::user()->account->id : $request->getClientIp(); $key = Auth::check() ? Auth::user()->account->id : $request->getClientIp();

View File

@ -42,7 +42,7 @@ class Authenticate {
// Does this account require portal passwords? // Does this account require portal passwords?
$account = Account::whereId($account_id)->first(); $account = Account::whereId($account_id)->first();
if($account && (!$account->enable_portal_password || !$account->isPro())){ if($account && (!$account->enable_portal_password || !$account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD))){
$authenticated = true; $authenticated = true;
} }

View File

@ -141,9 +141,10 @@ class StartupCheck
} }
} elseif ($productId == PRODUCT_WHITE_LABEL) { } elseif ($productId == PRODUCT_WHITE_LABEL) {
if ($data == 'valid') { if ($data == 'valid') {
$account = Auth::user()->account; $company = Auth::user()->account->company;
$account->pro_plan_paid = date_create()->format('Y-m-d'); $company->plan_paid = date_create()->format('Y-m-d');
$account->save(); $company->plan = PLAN_WHITE_LABEL;
$company->save();
Session::flash('message', trans('texts.bought_white_label')); Session::flash('message', trans('texts.bought_white_label'));
} }

View File

@ -555,7 +555,6 @@ if (!defined('CONTACT_EMAIL')) {
define('NINJA_WEB_URL', 'https://www.invoiceninja.com'); define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
define('NINJA_APP_URL', 'https://app.invoiceninja.com'); define('NINJA_APP_URL', 'https://app.invoiceninja.com');
define('NINJA_VERSION', '2.5.1.3'); define('NINJA_VERSION', '2.5.1.3');
define('NINJA_DATE', '2000-01-01');
define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja');
define('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'); define('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja');
@ -656,9 +655,41 @@ if (!defined('CONTACT_EMAIL')) {
define('PLAN_FREE', 'free'); define('PLAN_FREE', 'free');
define('PLAN_PRO', 'pro'); define('PLAN_PRO', 'pro');
define('PLAN_ENTERPRISE', 'enterprise'); define('PLAN_ENTERPRISE', 'enterprise');
define('PLAN_WHITE_LABEL', 'white_label');
define('PLAN_TERM_MONTHLY', 'month'); define('PLAN_TERM_MONTHLY', 'month');
define('PLAN_TERM_YEARLY', 'year'); define('PLAN_TERM_YEARLY', 'year');
// Pro
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
define('FEATURE_DIFFERENT_DESIGNS', 'different_designs');
define('FEATURE_EMAIL_TEMPLATES_REMINDERS', 'email_templates_reminders');
define('FEATURE_INVOICE_SETTINGS', 'invoice_settings');
define('FEATURE_CUSTOM_EMAILS', 'custom_emails');
define('FEATURE_PDF_ATTACHMENT', 'pdf_attachment');
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
define('FEATURE_QUOTES', 'quotes');
define('FEATURE_REPORTS', 'reports');
define('FEATURE_API', 'api');
define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password');
define('FEATURE_CUSTOM_URL', 'custom_url');
define('FEATURE_MORE_CLIENTS', 'more_clients'); // No trial allowed
// Whitelabel
define('FEATURE_CLIENT_PORTAL_CSS', 'client_portal_css');
define('FEATURE_WHITE_LABEL', 'feature_white_label');
// Enterprise
define('FEATURE_DOCUMENTS', 'documents');
// No Trial allowed
define('FEATURE_USERS', 'users');// Grandfathered for old Pro users
define('FEATURE_USER_PERMISSIONS', 'user_permissions');
// Pro users who started paying on or before this date will be able to manage users
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-05-15');
$creditCards = [ $creditCards = [
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'], 1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'], 2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],

View File

@ -118,6 +118,11 @@ class Utils
return Auth::check() && Auth::user()->isPro(); return Auth::check() && Auth::user()->isPro();
} }
public static function hasFeature($feature)
{
return Auth::check() && Auth::user()->hasFeature($feature);
}
public static function isAdmin() public static function isAdmin()
{ {
return Auth::check() && Auth::user()->is_admin; return Auth::check() && Auth::user()->is_admin;

View File

@ -14,10 +14,11 @@ class InvoiceListener
{ {
public function createdInvoice(InvoiceWasCreated $event) public function createdInvoice(InvoiceWasCreated $event)
{ {
if (Utils::isPro()) { if (Utils::hasFeature(FEATURE_DIFFERENT_DESIGNS)) {
return; return;
} }
// Make sure the account has the same design set as the invoice does
if (Auth::check()) { if (Auth::check()) {
$invoice = $event->invoice; $invoice = $event->invoice;
$account = Auth::user()->account; $account = Auth::user()->account;

View File

@ -533,7 +533,7 @@ class Account extends Eloquent
public function getNumberPrefix($isQuote) public function getNumberPrefix($isQuote)
{ {
if ( ! $this->isPro()) { if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
return ''; return '';
} }
@ -542,7 +542,7 @@ class Account extends Eloquent
public function hasNumberPattern($isQuote) public function hasNumberPattern($isQuote)
{ {
if ( ! $this->isPro()) { if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
return false; return false;
} }
@ -662,7 +662,7 @@ class Account extends Eloquent
$default = $this->invoice_number_counter; $default = $this->invoice_number_counter;
$actual = Utils::parseInt($invoice->invoice_number); $actual = Utils::parseInt($invoice->invoice_number);
if ( ! $this->isPro() && $default != $actual) { if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $default != $actual) {
$this->invoice_number_counter = $actual + 1; $this->invoice_number_counter = $actual + 1;
} else { } else {
$this->invoice_number_counter += 1; $this->invoice_number_counter += 1;
@ -794,6 +794,63 @@ class Account extends Eloquent
$this->company->save(); $this->company->save();
} }
public function hasFeature($feature)
{
$planDetails = $this->getPlanDetails();
$selfHost = !Utils::isNinjaProd();
switch ($feature) {
// Pro
case FEATURE_CUSTOMIZE_INVOICE_DESIGN:
case FEATURE_REMOVE_CREATED_BY:
case FEATURE_DIFFERENT_DESIGNS:
case FEATURE_EMAIL_TEMPLATES_REMINDERS:
case FEATURE_INVOICE_SETTINGS:
case FEATURE_CUSTOM_EMAILS:
case FEATURE_PDF_ATTACHMENT:
case FEATURE_MORE_INVOICE_DESIGNS:
case FEATURE_QUOTES:
case FEATURE_REPORTS:
case FEATURE_API:
case FEATURE_CLIENT_PORTAL_PASSWORD:
case FEATURE_CUSTOM_URL:
return $selfHost || !empty($planDetails);
// 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)));
// White Label
case FEATURE_WHITE_LABEL:
if ($this->isNinjaAccount() || (!$selfHost && $planDetails && !$plan_details['expires'])) {
return false;
}
// Fallthrough
case FEATURE_CLIENT_PORTAL_CSS:
return !empty($planDetails);// A plan is required even for self-hosted users
// Enterprise
case FEATURE_DOCUMENTS:
return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE;
// Enterprise; No Trial allowed; grandfathered for old pro users
case FEATURE_USERS:// Grandfathered for old Pro users
if($planDetails && $planDetails['trial']) {
// Do they have a non-trial plan?
$planDetails = $this->getPlanDetails(false, false);
}
return $selfHost || !empty($planDetails) && ($planDetails['plan'] == PLAN_ENTERPRISE || $planDetails['started'] <= date_create(PRO_USERS_GRANDFATHER_DEADLINE));
// Enterprise; No Trial allowed
case FEATURE_USER_PERMISSIONS:
return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && !$planDetails['trial'];
default:
return false;
}
}
public function isPro(&$plan_details = null) public function isPro(&$plan_details = null)
{ {
if (!Utils::isNinjaProd()) { if (!Utils::isNinjaProd()) {
@ -850,7 +907,7 @@ class Account extends Eloquent
$plan_active = false; $plan_active = false;
if ($plan) { if ($plan) {
if ($this->company->plan_expires == null && $this->company->plan_paid == NINJA_DATE) { if ($this->company->plan_expires == null) {
$plan_active = true; $plan_active = true;
$plan_expires = false; $plan_expires = false;
} else { } else {
@ -861,7 +918,7 @@ class Account extends Eloquent
} }
} }
if (!$include_inactive && !$plan_active && !$trial_plan) { if (!$include_inactive && !$plan_active && !$trial_active) {
return null; return null;
} }
@ -956,7 +1013,7 @@ class Account extends Eloquent
$today = new DateTime('now'); $today = new DateTime('now');
$interval = $today->diff($planDetails['expires']); $interval = $today->diff($planDetails['expires']);
return $interval ? 14 - $interval->d : 0; return $interval ? $interval->d : 0;
} }
public function getRenewalDate() public function getRenewalDate()
@ -973,20 +1030,6 @@ class Account extends Eloquent
return $date->format('Y-m-d'); return $date->format('Y-m-d');
} }
public function isWhiteLabel()
{
if ($this->isNinjaAccount()) {
return false;
}
if (Utils::isNinjaProd()) {
return self::isPro($plan_details) && $plan_details['expires'];
} else {
$plan_details = $this->getPlanDetails();
return $plan_details;
}
}
public function getLogoSize() public function getLogoSize()
{ {
if(!$this->hasLogo()){ if(!$this->hasLogo()){
@ -1063,7 +1106,7 @@ class Account extends Eloquent
public function getEmailSubject($entityType) public function getEmailSubject($entityType)
{ {
if ($this->isPro()) { if ($this->hasFeature(FEATURE_CUSTOM_EMAILS)) {
$field = "email_subject_{$entityType}"; $field = "email_subject_{$entityType}";
$value = $this->$field; $value = $this->$field;
@ -1083,7 +1126,7 @@ class Account extends Eloquent
$template = "<div>\$client,</div><br>"; $template = "<div>\$client,</div><br>";
if ($this->isPro() && $this->email_design_id != EMAIL_DESIGN_PLAIN) { if ($this->hasFeature(FEATURE_CUSTOM_EMAILS) && $this->email_design_id != EMAIL_DESIGN_PLAIN) {
$template .= "<div>" . trans("texts.{$entityType}_message_button", ['amount' => '$amount']) . "</div><br>" . $template .= "<div>" . trans("texts.{$entityType}_message_button", ['amount' => '$amount']) . "</div><br>" .
"<div style=\"text-align: center;\">\$viewButton</div><br>"; "<div style=\"text-align: center;\">\$viewButton</div><br>";
} else { } else {
@ -1102,7 +1145,7 @@ class Account extends Eloquent
{ {
$template = false; $template = false;
if ($this->isPro()) { if ($this->hasFeature(FEATURE_CUSTOM_EMAILS)) {
$field = "email_template_{$entityType}"; $field = "email_template_{$entityType}";
$template = $this->$field; $template = $this->$field;
} }
@ -1198,7 +1241,7 @@ class Account extends Eloquent
public function showCustomField($field, $entity = false) public function showCustomField($field, $entity = false)
{ {
if ($this->isPro()) { if ($this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
return $this->$field ? true : false; return $this->$field ? true : false;
} }
@ -1214,18 +1257,18 @@ class Account extends Eloquent
public function attatchPDF() public function attatchPDF()
{ {
return $this->isPro() && $this->pdf_email_attachment; return $this->hasFeaure(FEATURE_PDF_ATTACHMENT) && $this->pdf_email_attachment;
} }
public function getEmailDesignId() public function getEmailDesignId()
{ {
return $this->isPro() ? $this->email_design_id : EMAIL_DESIGN_PLAIN; return $this->hasFeature(FEATURE_CUSTOM_EMAILS) ? $this->email_design_id : EMAIL_DESIGN_PLAIN;
} }
public function clientViewCSS(){ public function clientViewCSS(){
$css = null; $css = '';
if ($this->isPro()) { if ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN)) {
$bodyFont = $this->getBodyFontCss(); $bodyFont = $this->getBodyFontCss();
$headerFont = $this->getHeaderFontCss(); $headerFont = $this->getHeaderFontCss();
@ -1233,11 +1276,10 @@ class Account extends Eloquent
if ($headerFont != $bodyFont) { if ($headerFont != $bodyFont) {
$css .= 'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{'.$headerFont.'}'; $css .= 'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{'.$headerFont.'}';
} }
}
if ((Utils::isNinja() && $this->isPro()) || $this->isWhiteLabel()) { if ($this->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) {
// For self-hosted users, a white-label license is required for custom CSS // For self-hosted users, a white-label license is required for custom CSS
$css .= $this->client_view_css; $css .= $this->client_view_css;
}
} }
return $css; return $css;
@ -1270,11 +1312,11 @@ class Account extends Eloquent
} }
public function getHeaderFontId() { public function getHeaderFontId() {
return ($this->isPro() && $this->header_font_id) ? $this->header_font_id : DEFAULT_HEADER_FONT; return ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) && $this->header_font_id) ? $this->header_font_id : DEFAULT_HEADER_FONT;
} }
public function getBodyFontId() { public function getBodyFontId() {
return ($this->isPro() && $this->body_font_id) ? $this->body_font_id : DEFAULT_BODY_FONT; return ($this->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) && $this->body_font_id) ? $this->body_font_id : DEFAULT_BODY_FONT;
} }
public function getHeaderFontName(){ public function getHeaderFontName(){

View File

@ -155,7 +155,7 @@ class Client extends EntityModel
$contact->send_invoice = true; $contact->send_invoice = true;
} }
if (!Utils::isPro() || $this->account->enable_portal_password){ if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $this->account->enable_portal_password){
if(!empty($data['password']) && $data['password']!='-%unchanged%-'){ if(!empty($data['password']) && $data['password']!='-%unchanged%-'){
$contact->password = bcrypt($data['password']); $contact->password = bcrypt($data['password']);
} else if(empty($data['password'])){ } else if(empty($data['password'])){

View File

@ -40,7 +40,7 @@ class Invitation extends EntityModel
$url = SITE_URL; $url = SITE_URL;
$iframe_url = $this->account->iframe_url; $iframe_url = $this->account->iframe_url;
if ($this->account->isPro()) { if ($this->account->hasFeature(FEATURE_CUSTOM_URL)) {
if ($iframe_url && !$forceOnsite) { if ($iframe_url && !$forceOnsite) {
return "{$iframe_url}?{$this->invitation_key}"; return "{$iframe_url}?{$this->invitation_key}";
} elseif ($this->account->subdomain) { } elseif ($this->account->subdomain) {

View File

@ -409,7 +409,7 @@ class Invoice extends EntityModel implements BalanceAffecting
'invoice_design', 'invoice_design',
'invoice_design_id', 'invoice_design_id',
'invoice_fonts', 'invoice_fonts',
'is_pro', 'features',
'is_quote', 'is_quote',
'custom_value1', 'custom_value1',
'custom_value2', 'custom_value2',

View File

@ -112,9 +112,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->account->isPro(); return $this->account->isPro();
} }
public function hasFeature($feature)
{
return $this->account->hasFeature($feature);
}
public function isPaidPro() public function isPaidPro()
{ {
return $this->isPro() && ! $this->isTrial(); return $this->isPro($accountDetails) && !$accountDetails['trial'];
} }
public function isTrial() public function isTrial()
@ -129,7 +134,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function maxInvoiceDesignId() public function maxInvoiceDesignId()
{ {
return $this->isPro() ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST); return $this->hasFeature(FEATURE_MORE_INVOICE_DESIGNS) ? 11 : (Utils::isNinja() ? COUNT_FREE_DESIGNS : COUNT_FREE_DESIGNS_SELF_HOST);
} }
public function getDisplayName() public function getDisplayName()
@ -173,7 +178,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function getMaxNumClients() public function getMaxNumClients()
{ {
if ($this->isPro() && ! $this->isTrial()) { if ($this->hasFeature(FEATURE_MORE_CLIENTS)) {
return MAX_NUM_CLIENTS_PRO; return MAX_NUM_CLIENTS_PRO;
} }
@ -186,7 +191,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
public function getMaxNumVendors() public function getMaxNumVendors()
{ {
if ($this->isPro() && ! $this->isTrial()) { if ($this->hasFeature(FEATURE_MORE_CLIENTS)) {
return MAX_NUM_VENDORS_PRO; return MAX_NUM_VENDORS_PRO;
} }

View File

@ -136,7 +136,7 @@ class ContactMailer extends Mailer
'amount' => $invoice->getRequestedAmount() 'amount' => $invoice->getRequestedAmount()
]; ];
if (empty($invitation->contact->password) && $account->isPro() && $account->enable_portal_password && $account->send_portal_password) { if (empty($invitation->contact->password) && $account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password && $account->send_portal_password) {
// The contact needs a password // The contact needs a password
$variables['password'] = $password = $this->generatePassword(); $variables['password'] = $password = $this->generatePassword();
$invitation->contact->password = bcrypt($password); $invitation->contact->password = bcrypt($password);
@ -291,7 +291,7 @@ class ContactMailer extends Mailer
$passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false; $passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
$documentsHTML = ''; $documentsHTML = '';
if($account->isPro() && $invoice->hasDocuments()){ if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){
$documentsHTML .= trans('texts.email_documents_header').'<ul>'; $documentsHTML .= trans('texts.email_documents_header').'<ul>';
foreach($invoice->documents as $document){ foreach($invoice->documents as $document){
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>'; $documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';

View File

@ -17,6 +17,7 @@ use App\Models\Client;
use App\Models\Language; use App\Models\Language;
use App\Models\Contact; use App\Models\Contact;
use App\Models\Account; use App\Models\Account;
use App\Models\Company;
use App\Models\User; use App\Models\User;
use App\Models\UserAccount; use App\Models\UserAccount;
use App\Models\AccountToken; use App\Models\AccountToken;
@ -25,9 +26,13 @@ class AccountRepository
{ {
public function create($firstName = '', $lastName = '', $email = '', $password = '') public function create($firstName = '', $lastName = '', $email = '', $password = '')
{ {
$company = new Company();
$company->save();
$account = new Account(); $account = new Account();
$account->ip = Request::getClientIp(); $account->ip = Request::getClientIp();
$account->account_key = str_random(RANDOM_KEY_LENGTH); $account->account_key = str_random(RANDOM_KEY_LENGTH);
$account->company_id = $company->id;
// Track referal code // Track referal code
if ($referralCode = Session::get(SESSION_REFERRAL_CODE)) { if ($referralCode = Session::get(SESSION_REFERRAL_CODE)) {
@ -493,10 +498,10 @@ class AccountRepository
$item = new stdClass(); $item = new stdClass();
$item->id = $record->id; $item->id = $record->id;
$item->user_id = $user->id; $item->user_id = $user->id;
$item->public_id = $user->public_id;
$item->user_name = $user->getDisplayName(); $item->user_name = $user->getDisplayName();
$item->account_id = $user->account->id; $item->account_id = $user->account->id;
$item->account_name = $user->account->getDisplayName(); $item->account_name = $user->account->getDisplayName();
$item->pro_plan_paid = $user->account->pro_plan_paid;
$item->logo_url = $user->account->hasLogo() ? $user->account->getLogoUrl() : null; $item->logo_url = $user->account->hasLogo() ? $user->account->getLogoUrl() : null;
$data[] = $item; $data[] = $item;
} }
@ -509,43 +514,6 @@ class AccountRepository
return self::prepareUsersData($record); return self::prepareUsersData($record);
} }
public function syncAccounts($userId, $proPlanPaid) {
$users = self::loadAccounts($userId);
self::syncUserAccounts($users, $proPlanPaid);
}
public function syncUserAccounts($users, $proPlanPaid = false) {
if (!$users) {
return;
}
if (!$proPlanPaid) {
foreach ($users as $user) {
if ($user->pro_plan_paid && $user->pro_plan_paid != '0000-00-00') {
$proPlanPaid = $user->pro_plan_paid;
break;
}
}
}
if (!$proPlanPaid) {
return;
}
$accountIds = [];
foreach ($users as $user) {
if ($user->pro_plan_paid != $proPlanPaid) {
$accountIds[] = $user->account_id;
}
}
if (count($accountIds)) {
DB::table('accounts')
->whereIn('id', $accountIds)
->update(['pro_plan_paid' => $proPlanPaid]);
}
}
public function associateAccounts($userId1, $userId2) { public function associateAccounts($userId1, $userId2) {
$record = self::findUserAccounts($userId1, $userId2); $record = self::findUserAccounts($userId1, $userId2);
@ -564,8 +532,59 @@ class AccountRepository
$record->save(); $record->save();
$users = self::prepareUsersData($record); $users = $this->getUserAccounts($record);
self::syncUserAccounts($users);
// Pick the primary user
foreach ($users as $user) {
if (!$user->public_id) {
$useAsPrimary = false;
if(empty($primaryUser)) {
$useAsPrimary = true;
}
$planDetails = $user->account->getPlanDetails(false, false);
$planLevel = 0;
if ($planDetails) {
$planLevel = 1;
if ($planDetails['plan'] == PLAN_ENTERPRISE) {
$planLevel = 2;
}
if (!$useAsPrimary && (
$planLevel > $primaryUserPlanLevel
|| ($planLevel == $primaryUserPlanLevel && $planDetails['expires'] > $primaryUserPlanExpires)
)) {
$useAsPrimary = true;
}
}
if ($useAsPrimary) {
$primaryUser = $user;
$primaryUserPlanLevel = $planLevel;
if ($planDetails) {
$primaryUserPlanExpires = $planDetails['expires'];
}
}
}
}
// Merge other companies into the primary user's company
if (!empty($primaryUser)) {
foreach ($users as $user) {
if ($user == $primaryUser || $user->public_id) {
continue;
}
if ($user->account->company_id != $primaryUser->account->company_id) {
foreach ($user->account->company->accounts as $account) {
$account->company_id = $primaryUser->account->company_id;
$account->save();
}
$user->account->company->forceDelete();
}
}
}
return $users; return $users;
} }
@ -585,6 +604,15 @@ class AccountRepository
$userAccount->removeUserId($userId); $userAccount->removeUserId($userId);
$userAccount->save(); $userAccount->save();
} }
$user = User::whereId($userId)->first();
if (!$user->public_id && $user->account->company->accounts->count() > 1) {
$company = Company::create();
$company->save();
$user->account->company_id = $company->id;
$user->account->save();
}
} }
public function findWithReminders() public function findWithReminders()

View File

@ -4,7 +4,7 @@ use App\Models\Account;
class NinjaRepository class NinjaRepository
{ {
public function updateProPlanPaid($clientPublicId, $proPlanPaid) public function updatePlanDetails($clientPublicId, $data)
{ {
$account = Account::whereId($clientPublicId)->first(); $account = Account::whereId($clientPublicId)->first();
@ -12,7 +12,13 @@ class NinjaRepository
return; return;
} }
$account->pro_plan_paid = $proPlanPaid; $company = $account->company;
$account->save(); $company->plan = !empty($data['plan']) && $data['plan'] != PLAN_FREE?$data['plan']:null;
$company->plan_term = !empty($data['plan_term'])?$data['plan_term']:null;
$company->plan_paid = !empty($data['plan_paid'])?$data['plan_paid']:null;
$company->plan_started = !empty($data['plan_started'])?$data['plan_started']:null;
$company->plan_expires = !empty($data['plan_expires'])?$data['plan_expires']:null;
$company->save();
} }
} }

View File

@ -1,15 +1,13 @@
<?php namespace App\Ninja\Repositories; <?php namespace App\Ninja\Repositories;
use DB; use App\Models\Account;
use Utils; use Utils;
class ReferralRepository class ReferralRepository
{ {
public function getCounts($userId) public function getCounts($userId)
{ {
$accounts = DB::table('accounts') $accounts = Account::where('referral_user_id', $userId);
->where('referral_user_id', $userId)
->get(['id', 'pro_plan_paid']);
$counts = [ $counts = [
'free' => 0, 'free' => 0,
@ -19,9 +17,11 @@ class ReferralRepository
foreach ($accounts as $account) { foreach ($accounts as $account) {
$counts['free']++; $counts['free']++;
if ($account->isPro()) { $plan = $account->getPlanDetails(false, false);
if ($plan) {
$counts['pro']++; $counts['pro']++;
if ($account->isEnterprise()) { if ($plan['plan'] == PLAN_ENTERPRISE) {
$counts['enterprise']++; $counts['enterprise']++;
} }
} }
@ -29,7 +29,4 @@ class ReferralRepository
return $counts; return $counts;
} }
} }

View File

@ -68,7 +68,7 @@ class AppServiceProvider extends ServiceProvider {
if(!empty($items))$items[] = '<li class="divider"></li>'; if(!empty($items))$items[] = '<li class="divider"></li>';
$items[] = '<li><a href="'.URL::to('recurring_invoices').'">'.trans("texts.recurring_invoices").'</a></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(Invoice::canCreate())$items[] = '<li><a href="'.URL::to('recurring_invoices/create').'">'.trans("texts.new_recurring_invoice").'</a></li>';
if (Auth::user()->isPro()) { if (Auth::user()->hasFeature(FEATURE_QUOTES)) {
$items[] = '<li class="divider"></li>'; $items[] = '<li class="divider"></li>';
$items[] = '<li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></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>'; if(Invoice::canCreate())$items[] = '<li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>';

View File

@ -32,8 +32,8 @@ class ClientService extends BaseService
public function save($data) public function save($data)
{ {
if (Auth::user()->account->isNinjaAccount() && isset($data['pro_plan_paid'])) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) {
$this->ninjaRepo->updateProPlanPaid($data['public_id'], $data['pro_plan_paid']); $this->ninjaRepo->updatePlanDetails($data['public_id'], $data);
} }
return $this->clientRepo->save($data); return $this->clientRepo->save($data);
@ -134,7 +134,7 @@ class ClientService extends BaseService
return URL::to("quotes/create/{$model->public_id}"); return URL::to("quotes/create/{$model->public_id}");
}, },
function ($model) { function ($model) {
return Auth::user()->isPro() && Invoice::canCreate(); return Auth::user()->hasFeature(FEATURE_QUOTES) && Invoice::canCreate();
} }
], ],
[ [

View File

@ -100,7 +100,7 @@ class InvoiceService extends BaseService
return null; return null;
} }
if ($account->auto_convert_quote || ! $account->isPro()) { if ($account->auto_convert_quote || ! $account->hasFeature(FEATURE_QUOTES)) {
$invoice = $this->convertQuote($quote, $invitation); $invoice = $this->convertQuote($quote, $invitation);
event(new QuoteInvitationWasApproved($quote, $invoice, $invitation)); event(new QuoteInvitationWasApproved($quote, $invoice, $invitation));

View File

@ -28,8 +28,8 @@ class VendorService extends BaseService
public function save($data) public function save($data)
{ {
if (Auth::user()->account->isNinjaAccount() && isset($data['pro_plan_paid'])) { if (Auth::user()->account->isNinjaAccount() && isset($data['plan'])) {
$this->ninjaRepo->updateProPlanPaid($data['public_id'], $data['pro_plan_paid']); $this->ninjaRepo->updatePlanDetails($data['public_id'], $data);
} }
return $this->vendorRepo->save($data); return $this->vendorRepo->save($data);

View File

@ -18,7 +18,7 @@ class EnterprisePlan extends Migration
{ {
$table->increments('id'); $table->increments('id');
$table->enum('plan', array('pro', 'enterprise'))->nullable(); $table->enum('plan', array('pro', 'enterprise', 'white_label'))->nullable();
$table->enum('plan_term', array('month', 'year'))->nullable(); $table->enum('plan_term', array('month', 'year'))->nullable();
$table->date('plan_started')->nullable(); $table->date('plan_started')->nullable();
$table->date('plan_paid')->nullable(); $table->date('plan_paid')->nullable();
@ -33,10 +33,6 @@ class EnterprisePlan extends Migration
$table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable(); $table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable();
$table->enum('pending_term', array('month', 'year'))->nullable(); $table->enum('pending_term', array('month', 'year'))->nullable();
// Used when a user has started changing a plan but hasn't finished paying yet
$table->enum('temp_pending_plan', array('pro', 'enterprise'))->nullable();
$table->enum('temp_pending_term', array('month', 'year'))->nullable();
$table->timestamps(); $table->timestamps();
$table->softDeletes(); $table->softDeletes();
}); });
@ -98,11 +94,14 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun
$company->plan_started = $primaryAccount->pro_plan_paid; $company->plan_started = $primaryAccount->pro_plan_paid;
$company->plan_paid = $primaryAccount->pro_plan_paid; $company->plan_paid = $primaryAccount->pro_plan_paid;
if ($company->plan_paid != NINJA_DATE) { if (!Utils::isNinjaProd()) {
$company->plan = 'white_label';
$company->plan_term = null;
} elseif ($company->plan_paid != '2000-01-01'/* NINJA_DATE*/) {
$expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid);
$expires->modify('+1 year'); $expires->modify('+1 year');
$company->plan_expires = $expires->format('Y-m-d'); $company->plan_expires = $expires->format('Y-m-d');
} }
} }
if ($primaryAccount->pro_plan_trial && $primaryAccount->pro_plan_trial != '0000-00-00') { if ($primaryAccount->pro_plan_trial && $primaryAccount->pro_plan_trial != '0000-00-00') {

View File

@ -13,10 +13,13 @@ class UserTableSeeder extends Seeder
Eloquent::unguard(); Eloquent::unguard();
$company = Company::create();
$account = Account::create([ $account = Account::create([
//'name' => 'Test Account', //'name' => 'Test Account',
'account_key' => str_random(RANDOM_KEY_LENGTH), 'account_key' => str_random(RANDOM_KEY_LENGTH),
'timezone_id' => 1, 'timezone_id' => 1,
'company_id' => $company,
]); ]);
User::create([ User::create([

File diff suppressed because one or more lines are too long

View File

@ -55,7 +55,7 @@ function GetPdfMake(invoice, javascript, callback) {
} }
// determine whether or not to show the header/footer // determine whether or not to show the header/footer
if (invoice.is_pro) { if (invoice.features.customize_invoice_design) {
if (key === 'header') { if (key === 'header') {
return function(page, pages) { return function(page, pages) {
return page === 1 || invoice.account.all_pages_header == '1' ? val : ''; return page === 1 || invoice.account.all_pages_header == '1' ? val : '';
@ -86,7 +86,7 @@ function GetPdfMake(invoice, javascript, callback) {
// Add ninja logo to the footer // Add ninja logo to the footer
var dd = JSON.parse(javascript, jsonCallBack); var dd = JSON.parse(javascript, jsonCallBack);
var designId = invoice.invoice_design_id; var designId = invoice.invoice_design_id;
if (!invoice.is_pro) { if (!invoice.features.remove_created_by) {
if (designId == NINJA.TEMPLATES.CLEAN || designId == NINJA.TEMPLATES.NORMAL) { if (designId == NINJA.TEMPLATES.CLEAN || designId == NINJA.TEMPLATES.NORMAL) {
dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, 0, 0, 0]}) dd.footer.columns.push({image: logoImages.imageLogo1, alignment: 'right', width: 130, margin: [0, 0, 0, 0]})
} else if (designId == NINJA.TEMPLATES.BOLD) { } else if (designId == NINJA.TEMPLATES.BOLD) {
@ -269,10 +269,10 @@ NINJA.invoiceColumns = function(invoice)
columns.push("*") columns.push("*")
if (invoice.is_pro && account.custom_invoice_item_label1) { if (invoice.features.invoice_settings && account.custom_invoice_item_label1) {
columns.push("10%"); columns.push("10%");
} }
if (invoice.is_pro && account.custom_invoice_item_label2) { if (invoice.features.invoice_settings && account.custom_invoice_item_label2) {
columns.push("10%"); columns.push("10%");
} }
@ -292,7 +292,7 @@ NINJA.invoiceColumns = function(invoice)
NINJA.invoiceFooter = function(invoice) NINJA.invoiceFooter = function(invoice)
{ {
if (!invoice.is_pro && invoice.invoice_design_id == 3) { if (!invoice.features.invoice_settings && invoice.invoice_design_id == 3) {
return invoice.invoice_footer ? invoice.invoice_footer.substring(0, 200) : ' '; return invoice.invoice_footer ? invoice.invoice_footer.substring(0, 200) : ' ';
} else { } else {
return invoice.invoice_footer || ' '; return invoice.invoice_footer || ' ';
@ -324,10 +324,10 @@ NINJA.invoiceLines = function(invoice) {
grid[0].push({text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']}); grid[0].push({text: invoiceLabels.description, style: ['tableHeader', 'descriptionTableHeader']});
if (invoice.is_pro && account.custom_invoice_item_label1) { if (invoice.features.invoice_settings && account.custom_invoice_item_label1) {
grid[0].push({text: account.custom_invoice_item_label1, style: ['tableHeader', 'custom1TableHeader']}); grid[0].push({text: account.custom_invoice_item_label1, style: ['tableHeader', 'custom1TableHeader']});
} }
if (invoice.is_pro && account.custom_invoice_item_label2) { if (invoice.features.invoice_ettings && account.custom_invoice_item_label2) {
grid[0].push({text: account.custom_invoice_item_label2, style: ['tableHeader', 'custom2TableHeader']}); grid[0].push({text: account.custom_invoice_item_label2, style: ['tableHeader', 'custom2TableHeader']});
} }
@ -384,10 +384,10 @@ NINJA.invoiceLines = function(invoice) {
row.push({style:["productKey", rowStyle], text:productKey || ' '}); // product key can be blank when selecting from a datalist row.push({style:["productKey", rowStyle], text:productKey || ' '}); // product key can be blank when selecting from a datalist
} }
row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]}); row.push({style:["notes", rowStyle], stack:[{text:notes || ' '}]});
if (invoice.is_pro && account.custom_invoice_item_label1) { if (invoice.features.invoice_settings && account.custom_invoice_item_label1) {
row.push({style:["customValue1", rowStyle], text:item.custom_value1 || ' '}); row.push({style:["customValue1", rowStyle], text:item.custom_value1 || ' '});
} }
if (invoice.is_pro && account.custom_invoice_item_label2) { if (invoice.features.invoice_settings && account.custom_invoice_item_label2) {
row.push({style:["customValue2", rowStyle], text:item.custom_value2 || ' '}); row.push({style:["customValue2", rowStyle], text:item.custom_value2 || ' '});
} }
row.push({style:["cost", rowStyle], text:cost}); row.push({style:["cost", rowStyle], text:cost});
@ -554,7 +554,7 @@ NINJA.accountAddress = function(invoice) {
{text: account.country ? account.country.name : ''}, {text: account.country ? account.country.name : ''},
]; ];
if (invoice.is_pro) { if (invoice.features.invoice_settings) {
data.push({text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false}); data.push({text: invoice.account.custom_value1 ? invoice.account.custom_label1 + ' ' + invoice.account.custom_value1 : false});
data.push({text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false}); data.push({text: invoice.account.custom_value2 ? invoice.account.custom_label2 + ' ' + invoice.account.custom_value2 : false});
} }

View File

@ -1142,10 +1142,12 @@ $LANG = array(
'renews' => 'Renews', 'renews' => 'Renews',
'plan_expired' => ':plan Plan Expired', 'plan_expired' => ':plan Plan Expired',
'trial_expired' => ':plan Plan Trial Ended', 'trial_expired' => ':plan Plan Trial Ended',
'never' => 'never', 'never' => 'Never',
'plan_free' => 'Free', 'plan_free' => 'Free',
'plan_pro' => 'Pro', 'plan_pro' => 'Pro',
'plan_enterprise' => 'Enterprise', 'plan_enterprise' => 'Enterprise',
'plan_white_label' => 'Self Hosted (White labeled)',
'plan_free_self_hosted' => 'Self Hosted (Free)',
'plan_trial' => 'Trial', 'plan_trial' => 'Trial',
'plan_term' => 'Term', 'plan_term' => 'Term',
'plan_term_monthly' => 'Monthly', 'plan_term_monthly' => 'Monthly',
@ -1155,6 +1157,11 @@ $LANG = array(
'plan_price_monthly' => '$:price/Month', 'plan_price_monthly' => '$:price/Month',
'plan_price_yearly' => '$:price/Year', 'plan_price_yearly' => '$:price/Year',
'updated_plan' => 'Updated plan settings', 'updated_plan' => 'Updated plan settings',
'plan_paid' => 'Term Started',
'plan_started' => 'Plan Started',
'plan_expires' => 'Plan Expires',
'white_label_button' => 'White Label',
'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.',
'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.',

View File

@ -9,7 +9,7 @@
@if (Utils::isNinja()) @if (Utils::isNinja())
{!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!} {!! Button::normal(trans('texts.zapier'))->asLinkTo(ZAPIER_URL)->withAttributes(['target' => '_blank']) !!}
@endif @endif
@if (Utils::isPro()) @if (Utils::hasFeature(FEATURE_API))
{!! Button::primary(trans('texts.add_token'))->asLinkTo(URL::to('/tokens/create'))->appendIcon(Icon::create('plus-sign')) !!} {!! Button::primary(trans('texts.add_token'))->asLinkTo(URL::to('/tokens/create'))->appendIcon(Icon::create('plus-sign')) !!}
@endif @endif
</div> </div>

View File

@ -18,7 +18,7 @@
{!! Former::populateField('enable_portal_password', intval($enable_portal_password)) !!} {!! Former::populateField('enable_portal_password', intval($enable_portal_password)) !!}
{!! Former::populateField('send_portal_password', intval($send_portal_password)) !!} {!! Former::populateField('send_portal_password', intval($send_portal_password)) !!}
@if (!Utils::isNinja() && !Auth::user()->account->isWhiteLabel()) @if (!Utils::isNinja() && !Auth::user()->account->hasFeature(FEATURE_WHITE_LABEL))
<div class="alert alert-warning" style="font-size:larger;"> <div class="alert alert-warning" style="font-size:larger;">
<center> <center>
{!! trans('texts.white_label_custom_css', ['link'=>'<a href="#" onclick="$(\'#whiteLabelModal\').modal(\'show\');">'.trans('texts.white_label_purchase_link').'</a>']) !!} {!! trans('texts.white_label_custom_css', ['link'=>'<a href="#" onclick="$(\'#whiteLabelModal\').modal(\'show\');">'.trans('texts.white_label_purchase_link').'</a>']) !!}
@ -59,6 +59,7 @@
</div> </div>
</div> </div>
</div> </div>
@if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_CSS))
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.custom_css') !!}</h3> <h3 class="panel-title">{!! trans('texts.custom_css') !!}</h3>
@ -74,6 +75,7 @@
->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!} ->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!}
</div> </div>
</div> </div>
@endif
</div> </div>
</div> </div>
</div> </div>

View File

@ -45,7 +45,11 @@
var customDesign = origCustomDesign = {!! $customDesign ?: 'JSON.parse(invoiceDesigns[0].javascript);' !!}; var customDesign = origCustomDesign = {!! $customDesign ?: 'JSON.parse(invoiceDesigns[0].javascript);' !!};
function getPDFString(cb, force) { function getPDFString(cb, force) {
invoice.is_pro = {!! Auth::user()->isPro() ? 'true' : 'false' !!}; invoice.features = {
customize_invoice_design:{{ Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }},
remove_created_by:{{ Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }},
invoice_settings:{{ Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }}
};
invoice.account.hide_quantity = {!! Auth::user()->account->hide_quantity ? 'true' : 'false' !!}; invoice.account.hide_quantity = {!! Auth::user()->account->hide_quantity ? 'true' : 'false' !!};
invoice.account.hide_paid_to_date = {!! Auth::user()->account->hide_paid_to_date ? 'true' : 'false' !!}; invoice.account.hide_paid_to_date = {!! Auth::user()->account->hide_paid_to_date ? 'true' : 'false' !!};
invoice.invoice_design_id = {!! Auth::user()->account->invoice_design_id !!}; invoice.invoice_design_id = {!! Auth::user()->account->invoice_design_id !!};
@ -194,7 +198,7 @@
</div> </div>
<script> <script>
@if (!Auth::user()->isPro()) @if (!Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN))
$(function() { $(function() {
$('form.warn-on-exit input, .save-button').prop('disabled', true); $('form.warn-on-exit input, .save-button').prop('disabled', true);
}); });

View File

@ -86,7 +86,7 @@
</div> </div>
</div> </div>
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_CUSTOM_EMAILS))
<center> <center>
{!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!} {!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!}
</center> </center>

View File

@ -46,7 +46,11 @@
} }
function getPDFString(cb) { function getPDFString(cb) {
invoice.is_pro = {!! Auth::user()->isPro() ? 'true' : 'false' !!}; invoice.features = {
customize_invoice_design:{{ Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }},
remove_created_by:{{ Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }},
invoice_settings:{{ Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }}
};
invoice.account.hide_quantity = $('#hide_quantity').is(":checked"); invoice.account.hide_quantity = $('#hide_quantity').is(":checked");
invoice.account.invoice_embed_documents = $('#invoice_embed_documents').is(":checked"); invoice.account.invoice_embed_documents = $('#invoice_embed_documents').is(":checked");
invoice.account.hide_paid_to_date = $('#hide_paid_to_date').is(":checked"); invoice.account.hide_paid_to_date = $('#hide_paid_to_date').is(":checked");
@ -55,7 +59,7 @@
NINJA.primaryColor = $('#primary_color').val(); NINJA.primaryColor = $('#primary_color').val();
NINJA.secondaryColor = $('#secondary_color').val(); NINJA.secondaryColor = $('#secondary_color').val();
NINJA.fontSize = parseInt($('#font_size').val()); NINJA.fontSize = parseInt($('#font_size').val());
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN))
NINJA.headerFont = $('#header_font_id option:selected').text(); NINJA.headerFont = $('#header_font_id option:selected').text();
NINJA.bodyFont = $('#body_font_id option:selected').text(); NINJA.bodyFont = $('#body_font_id option:selected').text();
@else @else
@ -89,7 +93,7 @@
$(function() { $(function() {
var options = { var options = {
preferredFormat: 'hex', preferredFormat: 'hex',
disabled: {!! Auth::user()->isPro() ? 'false' : 'true' !!}, disabled: {!! Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'false' : 'true' !!},
showInitial: false, showInitial: false,
showInput: true, showInput: true,
allowEmpty: true, allowEmpty: true,
@ -143,7 +147,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) @if (!Utils::hasFeature(FEATURE_MORE_INVOICE_DESIGNS) || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id') {!! Former::select('invoice_design_id')
->fromQuery($invoiceDesigns, 'name', 'id') ->fromQuery($invoiceDesigns, 'name', 'id')
->addOption(trans('texts.more_designs') . '...', '-1') !!} ->addOption(trans('texts.more_designs') . '...', '-1') !!}
@ -249,7 +253,7 @@
) !!} ) !!}
<br/> <br/>
@if (!Auth::user()->isPro()) @if (!Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN))
<script> <script>
$(function() { $(function() {
$('form.warn-on-exit input, .save-button').prop('disabled', true); $('form.warn-on-exit input, .save-button').prop('disabled', true);

View File

@ -264,7 +264,7 @@
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS))
<center> <center>
{!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!} {!! Button::success(trans('texts.save'))->large()->submit()->appendIcon(Icon::create('floppy-disk')) !!}
</center> </center>

View File

@ -7,7 +7,6 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
@if (Utils::isNinjaProd())
{!! Former::open('settings/change_plan')->addClass('change-plan') !!} {!! Former::open('settings/change_plan')->addClass('change-plan') !!}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
@ -25,8 +24,10 @@
@elseif ($planDetails['expires']) @elseif ($planDetails['expires'])
({{ trans('texts.plan_term_'.$planDetails['term'].'ly') }}) ({{ trans('texts.plan_term_'.$planDetails['term'].'ly') }})
@endif @endif
@else @elseif(Utils::isNinjaProd())
{{ trans('texts.plan_free') }} {{ trans('texts.plan_free') }}
@else
{{ trans('texts.plan_free_self_hosted') }}
@endif @endif
</p> </p>
</div> </div>
@ -34,7 +35,7 @@
@if ($planDetails && $planDetails['active']) @if ($planDetails && $planDetails['active'])
<div class="form-group"> <div class="form-group">
<label class="col-sm-4 control-label"> <label class="col-sm-4 control-label">
@if(!$account->company->pending_plan || $account->company->pending_plan == $planDetails['plan']) @if((!$account->company->pending_plan || $account->company->pending_plan == $planDetails['plan']) && $planDetails['expires'] && !$planDetails['trial'])
{{ trans('texts.renews') }} {{ trans('texts.renews') }}
@else @else
{{ trans('texts.expires') }} {{ trans('texts.expires') }}
@ -72,7 +73,9 @@
</div> </div>
</div> </div>
@endif @endif
{!! Former::actions( Button::info(trans('texts.plan_change'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('edit'))) !!} @if (Utils::isNinjaProd())
{!! Former::actions( Button::info(trans('texts.plan_change'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('edit'))) !!}
@endif
@else @else
@if ($planDetails) @if ($planDetails)
<div class="form-group"> <div class="form-group">
@ -90,11 +93,15 @@
</div> </div>
</div> </div>
@endif @endif
@if (Utils::isNinjaProd())
{!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!} {!! Former::actions( Button::success(trans('texts.plan_upgrade'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('plus-sign'))) !!}
@else
{!! Former::actions( Button::success(trans('texts.white_label_button'))->large()->withAttributes(['onclick' => 'loadImages("#whiteLabelModal");$("#whiteLabelModal").modal("show");'])->appendIcon(Icon::create('plus-sign'))) !!}
@endif
@endif @endif
</div> </div>
</div> </div>
@if (Utils::isNinjaProd())
<div class="modal fade" id="changePlanModel" tabindex="-1" role="dialog" aria-labelledby="changePlanModelLabel" aria-hidden="true"> <div class="modal fade" id="changePlanModel" tabindex="-1" role="dialog" aria-labelledby="changePlanModelLabel" aria-hidden="true">
<div class="modal-dialog" style="min-width:150px"> <div class="modal-dialog" style="min-width:150px">
<div class="modal-content"> <div class="modal-content">
@ -134,8 +141,8 @@
</div> </div>
</div> </div>
</div> </div>
{!! Former::close() !!}
@endif @endif
{!! Former::close() !!}
{!! Former::open('settings/cancel_account')->addClass('cancel-account') !!} {!! Former::open('settings/cancel_account')->addClass('cancel-account') !!}
<div class="panel panel-default"> <div class="panel panel-default">

View File

@ -125,7 +125,7 @@
</div> </div>
</div> </div>
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS))
<center> <center>
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!} {!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
</center> </center>

View File

@ -23,7 +23,7 @@
</div> </div>
</div> </div>
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_API))
{!! Former::actions( {!! Former::actions(
Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/api_tokens'))->appendIcon(Icon::create('remove-circle'))->large(), Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/api_tokens'))->appendIcon(Icon::create('remove-circle'))->large(),
Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk'))

View File

@ -6,7 +6,7 @@
<div class="pull-right"> <div class="pull-right">
@if (Utils::isPro() && ! Utils::isTrial()) @if (Utils::hasFeature(FEATURE_USERS))
{!! Button::primary(trans('texts.add_user'))->asLinkTo(URL::to('/users/create'))->appendIcon(Icon::create('plus-sign')) !!} {!! Button::primary(trans('texts.add_user'))->asLinkTo(URL::to('/users/create'))->appendIcon(Icon::create('plus-sign')) !!}
@endif @endif
</div> </div>

View File

@ -32,7 +32,7 @@
{!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!} {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown'") !!} {!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown'") !!}
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!} {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!}
@if ($account->isPro() && $account->enable_portal_password) @if ($account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password)
{!! Former::password('password')->data_bind("value: password()?'-%unchanged%-':'', valueUpdate: 'afterkeydown'") !!} {!! Former::password('password')->data_bind("value: password()?'-%unchanged%-':'', valueUpdate: 'afterkeydown'") !!}
@endif @endif

View File

@ -43,7 +43,7 @@
{!! Former::text('website') !!} {!! Former::text('website') !!}
{!! Former::text('work_phone') !!} {!! Former::text('work_phone') !!}
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS))
@if ($customLabel1) @if ($customLabel1)
{!! Former::text('custom_value1')->label($customLabel1) !!} {!! Former::text('custom_value1')->label($customLabel1) !!}
@endif @endif
@ -93,7 +93,7 @@
attr: {name: 'contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!} attr: {name: 'contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!}
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown',
attr: {name: 'contacts[' + \$index() + '][phone]'}") !!} attr: {name: 'contacts[' + \$index() + '][phone]'}") !!}
@if ($account->isPro() && $account->enable_portal_password) @if ($account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password)
{!! Former::password('password')->data_bind("value: password()?'-%unchanged%-':'', valueUpdate: 'afterkeydown', {!! Former::password('password')->data_bind("value: password()?'-%unchanged%-':'', valueUpdate: 'afterkeydown',
attr: {name: 'contacts[' + \$index() + '][password]'}") !!} attr: {name: 'contacts[' + \$index() + '][password]'}") !!}
@endif @endif
@ -134,15 +134,43 @@
{!! Former::textarea('private_notes') !!} {!! Former::textarea('private_notes') !!}
@if (isset($proPlanPaid)) @if (Auth::user()->account->isNinjaAccount())
{!! Former::populateField('pro_plan_paid', $proPlanPaid) !!} @if (isset($planDetails))
{!! Former::text('pro_plan_paid') {!! 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') ->data_date_format('yyyy-mm-dd')
->addGroupClass('pro_plan_paid_date') ->addGroupClass('plan_start_date')
->append('<i class="glyphicon glyphicon-calendar"></i>') !!}
{!! Former::text('plan_paid')
->data_date_format('yyyy-mm-dd')
->addGroupClass('plan_paid_date')
->append('<i class="glyphicon glyphicon-calendar"></i>') !!}
{!! Former::text('plan_expires')
->data_date_format('yyyy-mm-dd')
->addGroupClass('plan_expire_date')
->append('<i class="glyphicon glyphicon-calendar"></i>') !!} ->append('<i class="glyphicon glyphicon-calendar"></i>') !!}
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
$('#pro_plan_paid').datepicker(); $('#plan_started, #plan_paid, #plan_expires').datepicker();
}); });
</script> </script>
@endif @endif

View File

@ -230,7 +230,7 @@
@endif @endif
@if (Utils::isPro() && $hasQuotes) @if (Utils::hasFeature(FEATURE_QUOTES) && $hasQuotes)
<div class="tab-pane" id="quotes"> <div class="tab-pane" id="quotes">
{!! Datatable::table() {!! Datatable::table()

View File

@ -362,7 +362,7 @@
} }
} }
@if (Auth::user()->account->isPro()) @if (Auth::user()->account->hasFeature(FEATURE_DOCUMENTS))
function handleDocumentAdded(file){ function handleDocumentAdded(file){
if(file.mock)return; if(file.mock)return;
file.index = model.documents().length; file.index = model.documents().length;

View File

@ -431,7 +431,7 @@
<div class="btn-group user-dropdown"> <div class="btn-group user-dropdown">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown"> <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
<div id="myAccountButton" class="ellipsis nav-account-name" style="max-width:{{ Utils::isPro() && ! Utils::isTrial() ? '130' : '100' }}px;"> <div id="myAccountButton" class="ellipsis nav-account-name" style="max-width:{{ Utils::hasFeature(FEATURE_USERS) ? '130' : '100' }}px;">
@if (session(SESSION_USER_ACCOUNTS) && count(session(SESSION_USER_ACCOUNTS))) @if (session(SESSION_USER_ACCOUNTS) && count(session(SESSION_USER_ACCOUNTS)))
{{ Auth::user()->account->getDisplayName() }} {{ Auth::user()->account->getDisplayName() }}
@else @else
@ -769,7 +769,7 @@
{{-- Per our license, please do not remove or modify this section. --}} {{-- Per our license, please do not remove or modify this section. --}}
{!! link_to('https://www.invoiceninja.com/?utm_source=powered_by', 'InvoiceNinja.com', ['target' => '_blank', 'title' => 'invoiceninja.com']) !!} - {!! link_to('https://www.invoiceninja.com/?utm_source=powered_by', 'InvoiceNinja.com', ['target' => '_blank', 'title' => 'invoiceninja.com']) !!} -
{!! link_to(RELEASES_URL, 'v' . NINJA_VERSION, ['target' => '_blank', 'title' => trans('texts.trello_roadmap')]) !!} | {!! link_to(RELEASES_URL, 'v' . NINJA_VERSION, ['target' => '_blank', 'title' => trans('texts.trello_roadmap')]) !!} |
@if (Auth::user()->account->isWhiteLabel()) @if (Auth::user()->account->hasFeature(FEATURE_WHITE_LABEL))
{{ trans('texts.white_labeled') }} {{ trans('texts.white_labeled') }}
@else @else
<a href="#" onclick="loadImages('#whiteLabelModal');$('#whiteLabelModal').modal('show');">{{ trans('texts.white_label_link') }}</a> <a href="#" onclick="loadImages('#whiteLabelModal');$('#whiteLabelModal').modal('show');">{{ trans('texts.white_label_link') }}</a>

View File

@ -296,7 +296,7 @@
<li role="presentation" class="active"><a href="#notes" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.note_to_client') }}</a></li> <li role="presentation" class="active"><a href="#notes" aria-controls="notes" role="tab" data-toggle="tab">{{ trans('texts.note_to_client') }}</a></li>
<li role="presentation"><a href="#terms" aria-controls="terms" role="tab" data-toggle="tab">{{ trans("texts.terms") }}</a></li> <li role="presentation"><a href="#terms" aria-controls="terms" role="tab" data-toggle="tab">{{ trans("texts.terms") }}</a></li>
<li role="presentation"><a href="#footer" aria-controls="footer" role="tab" data-toggle="tab">{{ trans("texts.footer") }}</a></li> <li role="presentation"><a href="#footer" aria-controls="footer" role="tab" data-toggle="tab">{{ trans("texts.footer") }}</a></li>
@if ($account->isPro()) @if ($account->hasFeature(FEATURE_DOCUMENTS))
<li role="presentation"><a href="#attached-documents" aria-controls="attached-documents" role="tab" data-toggle="tab">{{ trans("texts.invoice_documents") }}</a></li> <li role="presentation"><a href="#attached-documents" aria-controls="attached-documents" role="tab" data-toggle="tab">{{ trans("texts.invoice_documents") }}</a></li>
@endif @endif
</ul> </ul>
@ -330,7 +330,7 @@
</div> </div>
</div>') !!} </div>') !!}
</div> </div>
@if ($account->isPro()) @if ($account->hasFeature(FEATURE_DOCUMENTS))
<div role="tabpanel" class="tab-pane" id="attached-documents" style="position:relative;z-index:9"> <div role="tabpanel" class="tab-pane" id="attached-documents" style="position:relative;z-index:9">
<div id="document-upload"> <div id="document-upload">
<div class="dropzone"> <div class="dropzone">
@ -499,7 +499,7 @@
</label> </label>
@endif @endif
@if (!Utils::isPro() || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST) @if (!Utils::hasFeature(FEATURE_MORE_INVOICE_DESIGNS) || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!} {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id")->addOption(trans('texts.more_designs') . '...', '-1') !!}
@else @else
{!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!} {!! Former::select('invoice_design_id')->style('display:inline;width:150px;background-color:white !important')->raw()->fromQuery($invoiceDesigns, 'name', 'id')->data_bind("value: invoice_design_id") !!}
@ -571,7 +571,7 @@
</span> </span>
@if (Auth::user()->isPro()) @if (Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS))
@if ($account->custom_client_label1) @if ($account->custom_client_label1)
{!! Former::text('client[custom_value1]') {!! Former::text('client[custom_value1]')
->label($account->custom_client_label1) ->label($account->custom_client_label1)
@ -626,7 +626,7 @@
->addClass('client-email') !!} ->addClass('client-email') !!}
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown', {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown',
attr: {name: 'client[contacts][' + \$index() + '][phone]'}") !!} attr: {name: 'client[contacts][' + \$index() + '][phone]'}") !!}
@if ($account->isPro() && $account->enable_portal_password) @if ($account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password)
{!! Former::password('password')->data_bind("value: (typeof password=='function'?password():null)?'-%unchanged%-':'', valueUpdate: 'afterkeydown', {!! Former::password('password')->data_bind("value: (typeof password=='function'?password():null)?'-%unchanged%-':'', valueUpdate: 'afterkeydown',
attr: {name: 'client[contacts][' + \$index() + '][password]'}") !!} attr: {name: 'client[contacts][' + \$index() + '][password]'}") !!}
@endif @endif
@ -966,7 +966,7 @@
applyComboboxListeners(); applyComboboxListeners();
@if (Auth::user()->account->isPro()) @if (Auth::user()->account->hasFeature(FEATURE_DOCUMENTS))
$('.main-form').submit(function(){ $('.main-form').submit(function(){
if($('#document-upload .dropzone .fallback input').val())$(this).attr('enctype', 'multipart/form-data') if($('#document-upload .dropzone .fallback input').val())$(this).attr('enctype', 'multipart/form-data')
else $(this).removeAttr('enctype') else $(this).removeAttr('enctype')
@ -1062,7 +1062,11 @@
var model = ko.toJS(window.model); var model = ko.toJS(window.model);
if(!model)return; if(!model)return;
var invoice = model.invoice; var invoice = model.invoice;
invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }}; invoice.features = {
customize_invoice_design:{{ Auth::user()->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }},
remove_created_by:{{ Auth::user()->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }},
invoice_settings:{{ Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }}
};
invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }}; invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }};
invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true}); invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true});
@ -1375,7 +1379,7 @@
model.invoice().invoice_number(number); model.invoice().invoice_number(number);
} }
@if ($account->isPro()) @if ($account->hasFeature(FEATURE_DOCUMENTS))
function handleDocumentAdded(file){ function handleDocumentAdded(file){
if(file.mock)return; if(file.mock)return;
file.index = model.invoice().documents().length; file.index = model.invoice().documents().length;
@ -1399,7 +1403,7 @@
@endif @endif
</script> </script>
@if ($account->isPro() && $account->invoice_embed_documents) @if ($account->hasFeature(FEATURE_DOCUMENTS) && $account->invoice_embed_documents)
@foreach ($invoice->documents as $document) @foreach ($invoice->documents as $document)
@if($document->isPDFEmbeddable()) @if($document->isPDFEmbeddable())
<script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script> <script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script>

View File

@ -57,7 +57,7 @@
@include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800]) @include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800])
@if (Utils::isPro() && $invoice->account->invoice_embed_documents) @if (Utils::hasFeature(FEATURE_DOCUMENTS) && $invoice->account->invoice_embed_documents)
@foreach ($invoice->documents as $document) @foreach ($invoice->documents as $document)
@if($document->isPDFEmbeddable()) @if($document->isPDFEmbeddable())
<script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script> <script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script>

View File

@ -84,7 +84,7 @@
@endif @endif
var NINJA = NINJA || {}; var NINJA = NINJA || {};
@if ($account->isPro()) @if ($account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN))
NINJA.primaryColor = "{{ $account->primary_color }}"; NINJA.primaryColor = "{{ $account->primary_color }}";
NINJA.secondaryColor = "{{ $account->secondary_color }}"; NINJA.secondaryColor = "{{ $account->secondary_color }}";
NINJA.fontSize = {{ $account->font_size }}; NINJA.fontSize = {{ $account->font_size }};

View File

@ -65,7 +65,7 @@
</div> </div>
@endif @endif
@if ($account->isPro() && $account->invoice_embed_documents) @if ($account->hasFeature(FEATURE_DOCUMENTS) && $account->invoice_embed_documents)
@foreach ($invoice->documents as $document) @foreach ($invoice->documents as $document)
@if($document->isPDFEmbeddable()) @if($document->isPDFEmbeddable())
<script src="{{ $document->getClientVFSJSUrl() }}" type="text/javascript" async></script> <script src="{{ $document->getClientVFSJSUrl() }}" type="text/javascript" async></script>
@ -82,7 +82,11 @@
<script type="text/javascript"> <script type="text/javascript">
window.invoice = {!! $invoice->toJson() !!}; window.invoice = {!! $invoice->toJson() !!};
invoice.is_pro = {{ $invoice->client->account->isPro() ? 'true' : 'false' }}; invoice.features = {
customize_invoice_design:{{ $invoice->client->account->hasFeature(FEATURE_CUSTOMIZE_INVOICE_DESIGN) ? 'true' : 'false' }},
remove_created_by:{{ $invoice->client->account)->hasFeature(FEATURE_REMOVE_CREATED_BY) ? 'true' : 'false' }},
invoice_settings:{{ $invoice->client->account->hasFeature(FEATURE_INVOICE_SETTINGS) ? 'true' : 'false' }}
};
invoice.is_quote = {{ $invoice->is_quote ? 'true' : 'false' }}; invoice.is_quote = {{ $invoice->is_quote ? 'true' : 'false' }};
invoice.contact = {!! $contact->toJson() !!}; invoice.contact = {!! $contact->toJson() !!};

View File

@ -31,7 +31,7 @@
<div id="top_right_buttons" class="pull-right"> <div id="top_right_buttons" class="pull-right">
<input id="tableFilter" type="text" style="width:140px;margin-right:17px;background-color: white !important" <input id="tableFilter" type="text" style="width:140px;margin-right:17px;background-color: white !important"
class="form-control pull-left" placeholder="{{ trans('texts.filter') }}" value="{{ Input::get('filter') }}"/> class="form-control pull-left" placeholder="{{ trans('texts.filter') }}" value="{{ Input::get('filter') }}"/>
@if (Auth::user()->isPro() && $entityType == ENTITY_INVOICE) @if (Auth::user()->hasFeature(FEATURE_QUOTES) && $entityType == ENTITY_INVOICE)
{!! Button::normal(trans('texts.quotes'))->asLinkTo(URL::to('/quotes'))->appendIcon(Icon::create('list')) !!} {!! Button::normal(trans('texts.quotes'))->asLinkTo(URL::to('/quotes'))->appendIcon(Icon::create('list')) !!}
{!! Button::normal(trans('texts.recurring'))->asLinkTo(URL::to('/recurring_invoices'))->appendIcon(Icon::create('list')) !!} {!! Button::normal(trans('texts.recurring'))->asLinkTo(URL::to('/recurring_invoices'))->appendIcon(Icon::create('list')) !!}
@elseif ($entityType == ENTITY_EXPENSE) @elseif ($entityType == ENTITY_EXPENSE)

View File

@ -45,7 +45,7 @@
Button::success(trans('texts.run'))->withAttributes(array('id' => 'submitButton'))->submit()->appendIcon(Icon::create('play')) Button::success(trans('texts.run'))->withAttributes(array('id' => 'submitButton'))->submit()->appendIcon(Icon::create('play'))
) !!} ) !!}
@if (!Auth::user()->isPro()) @if (!Auth::user()->hasFeature(FEATURE_REPORTS))
<script> <script>
$(function() { $(function() {
$('form.warn-on-exit').find('input, button').prop('disabled', true); $('form.warn-on-exit').find('input, button').prop('disabled', true);

View File

@ -268,7 +268,7 @@
return -1; return -1;
} }
var dayInSeconds = 1000*60*60*24; var dayInSeconds = 1000*60*60*24;
@if (Auth::user()->account->isPro()) @if (Auth::user()->account->hasFeature(FEATURE_REPORTS))
var date = convertToJsDate(invoice.created_at); var date = convertToJsDate(invoice.created_at);
@else @else
var date = new Date().getTime() - (dayInSeconds * Math.random() * 100); var date = new Date().getTime() - (dayInSeconds * Math.random() * 100);

View File

@ -27,6 +27,7 @@
</div> </div>
</div> </div>
@if (Utils::hasFeature(FEATURE_USER_PERMISSIONS))
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.permissions') !!}</h3> <h3 class="panel-title">{!! trans('texts.permissions') !!}</h3>
@ -59,6 +60,7 @@
</div> </div>
</div> </div>
@endif
{!! Former::actions( {!! Former::actions(
Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/user_management'))->appendIcon(Icon::create('remove-circle'))->large(), Button::normal(trans('texts.cancel'))->asLinkTo(URL::to('/settings/user_management'))->appendIcon(Icon::create('remove-circle'))->large(),

View File

@ -114,15 +114,43 @@
{!! Former::textarea('private_notes')->rows(6) !!} {!! Former::textarea('private_notes')->rows(6) !!}
@if (isset($proPlanPaid)) @if (Auth::user()->account->isNinjaAccount())
{!! Former::populateField('pro_plan_paid', $proPlanPaid) !!} @if (isset($planDetails))
{!! Former::text('pro_plan_paid') {!! 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') ->data_date_format('yyyy-mm-dd')
->addGroupClass('pro_plan_paid_date') ->addGroupClass('plan_start_date')
->append('<i class="glyphicon glyphicon-calendar"></i>') !!}
{!! Former::text('plan_paid')
->data_date_format('yyyy-mm-dd')
->addGroupClass('plan_paid_date')
->append('<i class="glyphicon glyphicon-calendar"></i>') !!}
{!! Former::text('plan_expires')
->data_date_format('yyyy-mm-dd')
->addGroupClass('plan_expire_date')
->append('<i class="glyphicon glyphicon-calendar"></i>') !!} ->append('<i class="glyphicon glyphicon-calendar"></i>') !!}
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
$('#pro_plan_paid').datepicker(); $('#plan_started, #plan_paid, #plan_expires').datepicker();
}); });
</script> </script>
@endif @endif