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

Merge branch 'develop' of https://github.com/turbo124/invoiceninja into develop

iiiiiespecially if it merges an updated upstream into a topic branch.
This commit is contained in:
David Bomba 2016-04-21 12:32:06 +10:00
commit b6c862aa46
77 changed files with 1781 additions and 679 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

@ -5,7 +5,7 @@ use DateTime;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
use App\Models\Account; use App\Models\Company;
use App\Ninja\Mailers\ContactMailer as Mailer; use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\AccountRepository; use App\Ninja\Repositories\AccountRepository;
@ -30,24 +30,32 @@ class SendRenewalInvoices extends Command
$today = new DateTime(); $today = new DateTime();
$sentTo = []; $sentTo = [];
// get all accounts with pro plans expiring in 10 days // get all accounts with plans expiring in 10 days
$accounts = Account::whereRaw('datediff(curdate(), pro_plan_paid) = 355') $companies = Company::whereRaw('datediff(plan_expires, curdate()) = 10')
->orderBy('id') ->orderBy('id')
->get(); ->get();
$this->info(count($accounts).' accounts found'); $this->info(count($companies).' companies found');
foreach ($accounts as $account) { foreach ($companies as $company) {
// don't send multiple invoices to multi-company users if (!count($company->accounts)) {
if ($userAccountId = $this->accountRepo->getUserAccountId($account)) { continue;
if (isset($sentTo[$userAccountId])) {
continue;
} else {
$sentTo[$userAccountId] = true;
}
} }
$account = $company->accounts->sortBy('id')->first();
$plan = $company->plan;
$term = $company->plan_term;
if ($company->pending_plan) {
$plan = $company->pending_plan;
$term = $company->pending_term;
}
if ($plan == PLAN_FREE || !$plan || !$term ){
continue;
}
$client = $this->accountRepo->getNinjaClient($account); $client = $this->accountRepo->getNinjaClient($account);
$invitation = $this->accountRepo->createNinjaInvoice($client, $account); $invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, $term);
// set the due date to 10 days from now // set the due date to 10 days from now
$invoice = $invitation->invoice; $invoice = $invitation->invoice;

View File

@ -71,11 +71,10 @@ class Handler extends ExceptionHandler {
} }
} }
return parent::render($request, $e);
/*
// In production, except for maintenance mode, we'll show a custom error screen // In production, except for maintenance mode, we'll show a custom error screen
if (Utils::isNinjaProd() && !Utils::isDownForMaintenance()) { if (Utils::isNinjaProd()
&& !Utils::isDownForMaintenance()
&& !($e instanceof HttpResponseException)) {
$data = [ $data = [
'error' => get_class($e), 'error' => get_class($e),
'hideHeader' => true, 'hideHeader' => true,
@ -85,6 +84,5 @@ class Handler extends ExceptionHandler {
} else { } else {
return parent::render($request, $e); return parent::render($request, $e);
} }
*/
} }
} }

View File

@ -31,6 +31,7 @@ use App\Ninja\Mailers\ContactMailer;
use App\Events\UserSignedUp; use App\Events\UserSignedUp;
use App\Events\UserSettingsChanged; use App\Events\UserSettingsChanged;
use App\Services\AuthService; use App\Services\AuthService;
use App\Services\PaymentService;
use App\Http\Requests\UpdateAccountRequest; use App\Http\Requests\UpdateAccountRequest;
@ -40,8 +41,9 @@ class AccountController extends BaseController
protected $userMailer; protected $userMailer;
protected $contactMailer; protected $contactMailer;
protected $referralRepository; protected $referralRepository;
protected $paymentService;
public function __construct(AccountRepository $accountRepo, UserMailer $userMailer, ContactMailer $contactMailer, ReferralRepository $referralRepository) public function __construct(AccountRepository $accountRepo, UserMailer $userMailer, ContactMailer $contactMailer, ReferralRepository $referralRepository, PaymentService $paymentService)
{ {
//parent::__construct(); //parent::__construct();
@ -49,6 +51,7 @@ class AccountController extends BaseController
$this->userMailer = $userMailer; $this->userMailer = $userMailer;
$this->contactMailer = $contactMailer; $this->contactMailer = $contactMailer;
$this->referralRepository = $referralRepository; $this->referralRepository = $referralRepository;
$this->paymentService = $paymentService;
} }
public function demo() public function demo()
@ -111,10 +114,135 @@ class AccountController extends BaseController
public function enableProPlan() public function enableProPlan()
{ {
$invitation = $this->accountRepo->enableProPlan(); if (Auth::user()->isPro() && ! Auth::user()->isTrial()) {
return false;
}
$invitation = $this->accountRepo->enablePlan();
return $invitation->invitation_key; return $invitation->invitation_key;
} }
public function changePlan() {
$user = Auth::user();
$account = $user->account;
$plan = Input::get('plan');
$term = Input::get('plan_term');
$planDetails = $account->getPlanDetails(false, false);
$credit = 0;
if ($planDetails) {
if ($planDetails['plan'] == PLAN_PRO && $plan == PLAN_ENTERPRISE) {
// Upgrade from pro to enterprise
if($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) {
// Upgrade to yearly for now; switch to monthly in a year
$pending_monthly = true;
$term = PLAN_TERM_YEARLY;
}
$new_plan = array(
'plan' => PLAN_ENTERPRISE,
'term' => $term,
);
} elseif ($planDetails['plan'] == $plan) {
// Term switch
if ($planDetails['term'] == PLAN_TERM_YEARLY && $term == PLAN_TERM_MONTHLY) {
$pending_change = array(
'plan' => $plan,
'term' => $term
);
} elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY) {
$new_plan = array(
'plan' => $plan,
'term' => $term,
);
} else {
// Cancel the pending change
$account->company->pending_plan = null;
$account->company->pending_term = null;
$account->company->save();
Session::flash('message', trans('texts.updated_plan'));
}
} else {
// Downgrade
$refund_deadline = clone $planDetails['started'];
$refund_deadline->modify('+30 days');
if ($plan == PLAN_FREE && $refund_deadline >= date_create()) {
// Refund
$account->company->plan = null;
$account->company->plan_term = null;
$account->company->plan_started = null;
$account->company->plan_expires = null;
$account->company->plan_paid = null;
$account->company->pending_plan = null;
$account->company->pending_term = null;
if ($account->company->payment) {
$payment = $account->company->payment;
$gateway = $this->paymentService->createGateway($payment->account_gateway);
$refund = $gateway->refund(array(
'transactionReference' => $payment->transaction_reference,
'amount' => $payment->amount * 100
));
$refund->send();
$payment->delete();
Session::flash('message', trans('texts.plan_refunded'));
\Log::info("Refunded Plan Payment: {$account->name} - {$user->email}");
} else {
Session::flash('message', trans('texts.updated_plan'));
}
$account->company->save();
} else {
$pending_change = array(
'plan' => $plan,
'term' => $plan == PLAN_FREE ? null : $term,
);
}
}
if (!empty($new_plan)) {
$time_used = $planDetails['paid']->diff(date_create());
$days_used = $time_used->days;
if ($time_used->invert) {
// They paid in advance
$days_used *= -1;
}
$days_total = $planDetails['paid']->diff($planDetails['expires'])->days;
$percent_used = $days_used / $days_total;
$old_plan_price = Account::$plan_prices[$planDetails['plan']][$planDetails['term']];
$credit = $old_plan_price * (1 - $percent_used);
}
} else {
$new_plan = array(
'plan' => $plan,
'term' => $term,
);
}
if (!empty($pending_change) && empty($new_plan)) {
$account->company->pending_plan = $pending_change['plan'];
$account->company->pending_term = $pending_change['term'];
$account->company->save();
Session::flash('message', trans('texts.updated_plan'));
}
if (!empty($new_plan)) {
$invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly));
return Redirect::to('payment/'.$invitation->invitation_key);
}
return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301);
}
public function setTrashVisible($entityType, $visible) public function setTrashVisible($entityType, $visible)
{ {
@ -149,6 +277,8 @@ class AccountController extends BaseController
return self::showInvoiceSettings(); return self::showInvoiceSettings();
} elseif ($section == ACCOUNT_IMPORT_EXPORT) { } elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]); return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_MANAGEMENT) {
return self::showAccountManagement();
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) { } elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
return self::showInvoiceDesign($section); return self::showInvoiceDesign($section);
} elseif ($section == ACCOUNT_CLIENT_PORTAL) { } elseif ($section == ACCOUNT_CLIENT_PORTAL) {
@ -232,6 +362,18 @@ class AccountController extends BaseController
return View::make('accounts.details', $data); return View::make('accounts.details', $data);
} }
private function showAccountManagement()
{
$account = Auth::user()->account;
$data = [
'account' => $account,
'planDetails' => $account->getPlanDetails(true),
'title' => trans('texts.account_management'),
];
return View::make('accounts.management', $data);
}
public function showUserDetails() public function showUserDetails()
{ {
$oauthLoginUrls = []; $oauthLoginUrls = [];
@ -379,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;
@ -389,6 +531,58 @@ class AccountController extends BaseController
$data['invoiceDesigns'] = InvoiceDesign::getDesigns(); $data['invoiceDesigns'] = InvoiceDesign::getDesigns();
$data['invoiceFonts'] = Cache::get('fonts'); $data['invoiceFonts'] = Cache::get('fonts');
$data['section'] = $section; $data['section'] = $section;
$pageSizes = [
'A0',
'A1',
'A2',
'A3',
'A4',
'A5',
'A6',
'A7',
'A8',
'A9',
'A10',
'B0',
'B1',
'B2',
'B3',
'B4',
'B5',
'B6',
'B7',
'B8',
'B9',
'B10',
'C0',
'C1',
'C2',
'C3',
'C4',
'C5',
'C6',
'C7',
'C8',
'C9',
'C10',
'RA0',
'RA1',
'RA2',
'RA3',
'RA4',
'SRA0',
'SRA1',
'SRA2',
'SRA3',
'SRA4',
'Executive',
'Folio',
'Legal',
'Letter',
'Tabloid',
];
$data['pageSizes'] = array_combine($pageSizes, $pageSizes);
$design = false; $design = false;
foreach ($data['invoiceDesigns'] as $item) { foreach ($data['invoiceDesigns'] as $item) {
@ -501,7 +695,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;
@ -516,7 +710,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
@ -567,7 +761,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) {
@ -629,7 +823,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));
@ -672,7 +866,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',
@ -701,6 +895,7 @@ class AccountController extends BaseController
$account->custom_invoice_item_label1 = trim(Input::get('custom_invoice_item_label1')); $account->custom_invoice_item_label1 = trim(Input::get('custom_invoice_item_label1'));
$account->custom_invoice_item_label2 = trim(Input::get('custom_invoice_item_label2')); $account->custom_invoice_item_label2 = trim(Input::get('custom_invoice_item_label2'));
$account->invoice_number_padding = Input::get('invoice_number_padding');
$account->invoice_number_counter = Input::get('invoice_number_counter'); $account->invoice_number_counter = Input::get('invoice_number_counter');
$account->quote_number_prefix = Input::get('quote_number_prefix'); $account->quote_number_prefix = Input::get('quote_number_prefix');
$account->share_counter = Input::get('share_counter') ? true : false; $account->share_counter = Input::get('share_counter') ? true : false;
@ -752,7 +947,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;
@ -764,9 +959,20 @@ class AccountController extends BaseController
$account->primary_color = Input::get('primary_color'); $account->primary_color = Input::get('primary_color');
$account->secondary_color = Input::get('secondary_color'); $account->secondary_color = Input::get('secondary_color');
$account->invoice_design_id = Input::get('invoice_design_id'); $account->invoice_design_id = Input::get('invoice_design_id');
$account->font_size = intval(Input::get('font_size'));
$account->page_size = Input::get('page_size');
$account->live_preview = Input::get('live_preview') ? true : false;
if (Input::has('font_size')) { // Automatically disable live preview when using a large font
$account->font_size = intval(Input::get('font_size')); $fonts = Cache::get('fonts')->filter(function($font) use ($account) {
if ($font->google_font) {
return false;
}
return $font->id == $account->header_font_id || $font->id == $account->body_font_id;
});
if ($account->live_preview && count($fonts)) {
$account->live_preview = false;
Session::flash('warning', trans('texts.live_preview_disabled'));
} }
$labels = []; $labels = [];
@ -990,7 +1196,7 @@ class AccountController extends BaseController
$user->registered = true; $user->registered = true;
$user->save(); $user->save();
$user->account->startTrial(); $user->account->startTrial(PLAN_PRO);
if (Input::get('go_pro') == 'true') { if (Input::get('go_pro') == 'true') {
Session::set(REQUESTED_PRO_PLAN, true); Session::set(REQUESTED_PRO_PLAN, true);
@ -1046,6 +1252,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();
@ -1062,12 +1271,12 @@ class AccountController extends BaseController
return Redirect::to('/settings/'.ACCOUNT_USER_DETAILS)->with('message', trans('texts.confirmation_resent')); return Redirect::to('/settings/'.ACCOUNT_USER_DETAILS)->with('message', trans('texts.confirmation_resent'));
} }
public function startTrial() public function startTrial($plan)
{ {
$user = Auth::user(); $user = Auth::user();
if ($user->isEligibleForTrial()) { if ($user->isEligibleForTrial($plan)) {
$user->account->startTrial(); $user->account->startTrial($plan);
} }
return Redirect::back()->with('message', trans('texts.trial_success')); return Redirect::back()->with('message', trans('texts.trial_success'));

View File

@ -178,9 +178,12 @@ class AppController extends BaseController
$config = ''; $config = '';
foreach ($_ENV as $key => $val) { foreach ($_ENV as $key => $val) {
if (preg_match('/\s/',$val)) { if (is_array($val)) {
$val = "'{$val}'"; continue;
} }
if (preg_match('/\s/', $val)) {
$val = "'{$val}'";
}
$config .= "{$key}={$val}\n"; $config .= "{$key}={$val}\n";
} }

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

@ -188,7 +188,8 @@ Route::group([
Route::resource('users', 'UserController'); Route::resource('users', 'UserController');
Route::post('users/bulk', 'UserController@bulk'); Route::post('users/bulk', 'UserController@bulk');
Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation');
Route::get('start_trial', 'AccountController@startTrial'); Route::get('start_trial/{plan}', 'AccountController@startTrial')
->where(['plan'=>'pro']);
Route::get('restore_user/{user_id}', 'UserController@restoreUser'); Route::get('restore_user/{user_id}', 'UserController@restoreUser');
Route::post('users/change_password', 'UserController@changePassword'); Route::post('users/change_password', 'UserController@changePassword');
Route::get('/switch_account/{user_id}', 'UserController@switchAccount'); Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
@ -212,6 +213,7 @@ Route::group([
Route::get('settings/charts_and_reports', 'ReportController@showReports'); Route::get('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/charts_and_reports', 'ReportController@showReports'); Route::post('settings/charts_and_reports', 'ReportController@showReports');
Route::post('settings/change_plan', 'AccountController@changePlan');
Route::post('settings/cancel_account', 'AccountController@cancelAccount'); Route::post('settings/cancel_account', 'AccountController@cancelAccount');
Route::post('settings/company_details', 'AccountController@updateDetails'); Route::post('settings/company_details', 'AccountController@updateDetails');
Route::get('settings/{section?}', 'AccountController@showSection'); Route::get('settings/{section?}', 'AccountController@showSection');
@ -354,6 +356,7 @@ if (!defined('CONTACT_EMAIL')) {
define('ACCOUNT_LOCALIZATION', 'localization'); define('ACCOUNT_LOCALIZATION', 'localization');
define('ACCOUNT_NOTIFICATIONS', 'notifications'); define('ACCOUNT_NOTIFICATIONS', 'notifications');
define('ACCOUNT_IMPORT_EXPORT', 'import_export'); define('ACCOUNT_IMPORT_EXPORT', 'import_export');
define('ACCOUNT_MANAGEMENT', 'account_management');
define('ACCOUNT_PAYMENTS', 'online_payments'); define('ACCOUNT_PAYMENTS', 'online_payments');
define('ACCOUNT_BANKS', 'bank_accounts'); define('ACCOUNT_BANKS', 'bank_accounts');
define('ACCOUNT_IMPORT_EXPENSES', 'import_expenses'); define('ACCOUNT_IMPORT_EXPENSES', 'import_expenses');
@ -552,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');
@ -582,6 +584,10 @@ if (!defined('CONTACT_EMAIL')) {
define('SELF_HOST_AFFILIATE_KEY', '8S69AD'); define('SELF_HOST_AFFILIATE_KEY', '8S69AD');
define('PRO_PLAN_PRICE', 50); define('PRO_PLAN_PRICE', 50);
define('PLAN_PRICE_PRO_MONTHLY', 5);
define('PLAN_PRICE_PRO_YEARLY', 50);
define('PLAN_PRICE_ENTERPRISE_MONTHLY', 10);
define('PLAN_PRICE_ENTERPRISE_YEARLY', 100);
define('WHITE_LABEL_PRICE', 20); define('WHITE_LABEL_PRICE', 20);
define('INVOICE_DESIGNS_PRICE', 10); define('INVOICE_DESIGNS_PRICE', 10);
@ -644,7 +650,46 @@ if (!defined('CONTACT_EMAIL')) {
define('RESELLER_REVENUE_SHARE', 'A'); define('RESELLER_REVENUE_SHARE', 'A');
define('RESELLER_LIMITED_USERS', 'B'); define('RESELLER_LIMITED_USERS', 'B');
// These must be lowercase
define('PLAN_FREE', 'free');
define('PLAN_PRO', 'pro');
define('PLAN_ENTERPRISE', 'enterprise');
define('PLAN_WHITE_LABEL', 'white_label');
define('PLAN_TERM_MONTHLY', 'month');
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;
@ -440,7 +445,12 @@ class Utils
return false; return false;
} }
$dateTime = new DateTime($date); if ($date instanceof DateTime) {
$dateTime = $date;
} else {
$dateTime = new DateTime($date);
}
$timestamp = $dateTime->getTimestamp(); $timestamp = $dateTime->getTimestamp();
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT); $format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
@ -961,38 +971,6 @@ class Utils
return $entity1; return $entity1;
} }
public static function withinPastYear($date)
{
if (!$date || $date == '0000-00-00') {
return false;
}
$today = new DateTime('now');
$datePaid = DateTime::createFromFormat('Y-m-d', $date);
$interval = $today->diff($datePaid);
return $interval->y == 0;
}
public static function getInterval($date)
{
if (!$date || $date == '0000-00-00') {
return false;
}
$today = new DateTime('now');
$datePaid = DateTime::createFromFormat('Y-m-d', $date);
return $today->diff($datePaid);
}
public static function withinPastTwoWeeks($date)
{
$interval = Utils::getInterval($date);
return $interval && $interval->d <= 14;
}
public static function addHttp($url) public static function addHttp($url)
{ {
if (!preg_match("~^(?:f|ht)tps?://~i", $url)) { if (!preg_match("~^(?:f|ht)tps?://~i", $url)) {

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

@ -18,6 +18,17 @@ class Account extends Eloquent
{ {
use PresentableTrait; use PresentableTrait;
use SoftDeletes; use SoftDeletes;
public static $plan_prices = array(
PLAN_PRO => array(
PLAN_TERM_MONTHLY => PLAN_PRICE_PRO_MONTHLY,
PLAN_TERM_YEARLY => PLAN_PRICE_PRO_YEARLY,
),
PLAN_ENTERPRISE => array(
PLAN_TERM_MONTHLY => PLAN_PRICE_ENTERPRISE_MONTHLY,
PLAN_TERM_YEARLY => PLAN_PRICE_ENTERPRISE_YEARLY,
),
);
protected $presenter = 'App\Ninja\Presenters\AccountPresenter'; protected $presenter = 'App\Ninja\Presenters\AccountPresenter';
protected $dates = ['deleted_at']; protected $dates = ['deleted_at'];
@ -57,6 +68,7 @@ class Account extends Eloquent
ACCOUNT_PRODUCTS, ACCOUNT_PRODUCTS,
ACCOUNT_NOTIFICATIONS, ACCOUNT_NOTIFICATIONS,
ACCOUNT_IMPORT_EXPORT, ACCOUNT_IMPORT_EXPORT,
ACCOUNT_MANAGEMENT,
]; ];
public static $advancedSettings = [ public static $advancedSettings = [
@ -176,6 +188,11 @@ class Account extends Eloquent
return $this->hasMany('App\Models\Payment','account_id','id')->withTrashed(); return $this->hasMany('App\Models\Payment','account_id','id')->withTrashed();
} }
public function company()
{
return $this->belongsTo('App\Models\Company');
}
public function setIndustryIdAttribute($value) public function setIndustryIdAttribute($value)
{ {
$this->attributes['industry_id'] = $value ?: null; $this->attributes['industry_id'] = $value ?: null;
@ -516,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 '';
} }
@ -525,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;
} }
@ -551,7 +568,7 @@ class Account extends Eloquent
$replace = [date('Y')]; $replace = [date('Y')];
$search[] = '{$counter}'; $search[] = '{$counter}';
$replace[] = str_pad($this->getCounter($invoice->is_quote), 4, '0', STR_PAD_LEFT); $replace[] = str_pad($this->getCounter($invoice->is_quote), $this->invoice_number_padding, '0', STR_PAD_LEFT);
if (strstr($pattern, '{$userId}')) { if (strstr($pattern, '{$userId}')) {
$search[] = '{$userId}'; $search[] = '{$userId}';
@ -617,7 +634,7 @@ class Account extends Eloquent
// confirm the invoice number isn't already taken // confirm the invoice number isn't already taken
do { do {
$number = $prefix . str_pad($counter, 4, '0', STR_PAD_LEFT); $number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first(); $check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
$counter++; $counter++;
$counterOffset++; $counterOffset++;
@ -645,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;
@ -766,17 +783,80 @@ class Account extends Eloquent
return $this->account_key === NINJA_ACCOUNT_KEY; return $this->account_key === NINJA_ACCOUNT_KEY;
} }
public function startTrial() public function startTrial($plan)
{ {
if ( ! Utils::isNinja()) { if ( ! Utils::isNinja()) {
return; return;
} }
$this->pro_plan_trial = date_create()->format('Y-m-d'); $this->company->trial_plan = $plan;
$this->save(); $this->company->trial_started = date_create()->format('Y-m-d');
$this->company->save();
} }
public function isPro() public function hasFeature($feature)
{
$planDetails = $this->getPlanDetails();
$selfHost = !Utils::isNinjaProd();
if (!$selfHost && function_exists('ninja_account_features')) {
$result = ninja_account_features($this, $feature);
if ($result != null) {
return $result;
}
}
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 && !$planDetails['expires'])) {
return false;
}
// Fallthrough
case FEATURE_CLIENT_PORTAL_CSS:
return !empty($planDetails);// A plan is required even for self-hosted users
// 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_DOCUMENTS:
case FEATURE_USER_PERMISSIONS:
return $selfHost || !empty($planDetails) && $planDetails['plan'] == PLAN_ENTERPRISE && !$planDetails['trial'];
default:
return false;
}
}
public function isPro(&$plan_details = null)
{ {
if (!Utils::isNinjaProd()) { if (!Utils::isNinjaProd()) {
return true; return true;
@ -786,14 +866,113 @@ class Account extends Eloquent
return true; return true;
} }
$datePaid = $this->pro_plan_paid; $plan_details = $this->getPlanDetails();
$trialStart = $this->pro_plan_trial;
return !empty($plan_details);
}
if ($datePaid == NINJA_DATE) { public function isEnterprise(&$plan_details = null)
{
if (!Utils::isNinjaProd()) {
return true; return true;
} }
return Utils::withinPastTwoWeeks($trialStart) || Utils::withinPastYear($datePaid); if ($this->isNinjaAccount()) {
return true;
}
$plan_details = $this->getPlanDetails();
return $plan_details && $plan_details['plan'] == PLAN_ENTERPRISE;
}
public function getPlanDetails($include_inactive = false, $include_trial = true)
{
if (!$this->company) {
return null;
}
$plan = $this->company->plan;
$trial_plan = $this->company->trial_plan;
if(!$plan && (!$trial_plan || !$include_trial)) {
return null;
}
$trial_active = false;
if ($trial_plan && $include_trial) {
$trial_started = DateTime::createFromFormat('Y-m-d', $this->company->trial_started);
$trial_expires = clone $trial_started;
$trial_expires->modify('+2 weeks');
if ($trial_expires >= date_create()) {
$trial_active = true;
}
}
$plan_active = false;
if ($plan) {
if ($this->company->plan_expires == null) {
$plan_active = true;
$plan_expires = false;
} else {
$plan_expires = DateTime::createFromFormat('Y-m-d', $this->company->plan_expires);
if ($plan_expires >= date_create()) {
$plan_active = true;
}
}
}
if (!$include_inactive && !$plan_active && !$trial_active) {
return null;
}
// Should we show plan details or trial details?
if (($plan && !$trial_plan) || !$include_trial) {
$use_plan = true;
} elseif (!$plan && $trial_plan) {
$use_plan = false;
} else {
// There is both a plan and a trial
if (!empty($plan_active) && empty($trial_active)) {
$use_plan = true;
} elseif (empty($plan_active) && !empty($trial_active)) {
$use_plan = false;
} elseif (!empty($plan_active) && !empty($trial_active)) {
// Both are active; use whichever is a better plan
if ($plan == PLAN_ENTERPRISE) {
$use_plan = true;
} elseif ($trial_plan == PLAN_ENTERPRISE) {
$use_plan = false;
} else {
// They're both the same; show the plan
$use_plan = true;
}
} else {
// Neither are active; use whichever expired most recently
$use_plan = $plan_expires >= $trial_expires;
}
}
if ($use_plan) {
return array(
'trial' => false,
'plan' => $plan,
'started' => DateTime::createFromFormat('Y-m-d', $this->company->plan_started),
'expires' => $plan_expires,
'paid' => DateTime::createFromFormat('Y-m-d', $this->company->plan_paid),
'term' => $this->company->plan_term,
'active' => $plan_active,
);
} else {
return array(
'trial' => true,
'plan' => $trial_plan,
'started' => $trial_started,
'expires' => $trial_expires,
'active' => $trial_active,
);
}
} }
public function isTrial() public function isTrial()
@ -801,35 +980,54 @@ class Account extends Eloquent
if (!Utils::isNinjaProd()) { if (!Utils::isNinjaProd()) {
return false; return false;
} }
$plan_details = $this->getPlanDetails();
if ($this->pro_plan_paid && $this->pro_plan_paid != '0000-00-00') { return $plan_details && $plan_details['trial'];
return false;
}
return Utils::withinPastTwoWeeks($this->pro_plan_trial);
} }
public function isEligibleForTrial() public function isEligibleForTrial($plan = null)
{ {
return ! $this->pro_plan_trial || $this->pro_plan_trial == '0000-00-00'; if (!$this->company->trial_plan) {
if ($plan) {
return $plan == PLAN_PRO || $plan == PLAN_ENTERPRISE;
} else {
return array(PLAN_PRO, PLAN_ENTERPRISE);
}
}
if ($this->company->trial_plan == PLAN_PRO) {
if ($plan) {
return $plan != PLAN_PRO;
} else {
return array(PLAN_ENTERPRISE);
}
}
return false;
} }
public function getCountTrialDaysLeft() public function getCountTrialDaysLeft()
{ {
$interval = Utils::getInterval($this->pro_plan_trial); $planDetails = $this->getPlanDetails(true);
return $interval ? 14 - $interval->d : 0; if(!$planDetails || !$planDetails['trial']) {
return 0;
}
$today = new DateTime('now');
$interval = $today->diff($planDetails['expires']);
return $interval ? $interval->d : 0;
} }
public function getRenewalDate() public function getRenewalDate()
{ {
if ($this->pro_plan_paid && $this->pro_plan_paid != '0000-00-00') { $planDetails = $this->getPlanDetails();
$date = DateTime::createFromFormat('Y-m-d', $this->pro_plan_paid);
$date->modify('+1 year'); if ($planDetails) {
$date = $planDetails['expires'];
$date = max($date, date_create()); $date = max($date, date_create());
} elseif ($this->isTrial()) {
$date = date_create();
$date->modify('+'.$this->getCountTrialDaysLeft().' day');
} else { } else {
$date = date_create(); $date = date_create();
} }
@ -837,23 +1035,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() && $this->pro_plan_paid != NINJA_DATE;
} else {
if ($this->pro_plan_paid == NINJA_DATE) {
return true;
}
return Utils::withinPastYear($this->pro_plan_paid);
}
}
public function getLogoSize() public function getLogoSize()
{ {
if(!$this->hasLogo()){ if(!$this->hasLogo()){
@ -930,7 +1111,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;
@ -950,7 +1131,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 {
@ -969,7 +1150,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;
} }
@ -1065,7 +1246,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;
} }
@ -1081,18 +1262,18 @@ class Account extends Eloquent
public function attatchPDF() public function attatchPDF()
{ {
return $this->isPro() && $this->pdf_email_attachment; return $this->hasFeature(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();
@ -1100,27 +1281,15 @@ 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;
} }
public function hasLargeFont()
{
foreach (['chinese', 'japanese'] as $language) {
if (stripos($this->getBodyFontName(), $language) || stripos($this->getHeaderFontName(), $language)) {
return true;
}
}
return false;
}
public function getFontsUrl($protocol = ''){ public function getFontsUrl($protocol = ''){
$bodyFont = $this->getHeaderFontId(); $bodyFont = $this->getHeaderFontId();
$headerFont = $this->getBodyFontId(); $headerFont = $this->getBodyFontId();
@ -1137,11 +1306,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'])){

21
app/Models/Company.php Normal file
View File

@ -0,0 +1,21 @@
<?php namespace App\Models;
use Eloquent;
use Illuminate\Database\Eloquent\SoftDeletes;
class Company extends Eloquent
{
use SoftDeletes;
protected $dates = ['deleted_at'];
public function accounts()
{
return $this->hasMany('App\Models\Account');
}
public function payment()
{
return $this->belongsTo('App\Models\Payment');
}
}

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',
@ -474,7 +474,8 @@ class Invoice extends EntityModel implements BalanceAffecting
'custom_invoice_text_label2', 'custom_invoice_text_label2',
'custom_invoice_item_label1', 'custom_invoice_item_label1',
'custom_invoice_item_label2', 'custom_invoice_item_label2',
'invoice_embed_documents' 'invoice_embed_documents',
'page_size',
]); ]);
foreach ($this->invoice_items as $invoiceItem) { foreach ($this->invoice_items as $invoiceItem) {

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()
@ -122,14 +127,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return $this->account->isTrial(); return $this->account->isTrial();
} }
public function isEligibleForTrial() public function isEligibleForTrial($plan = null)
{ {
return $this->account->isEligibleForTrial(); return $this->account->isEligibleForTrial($plan);
} }
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)) {
@ -205,21 +210,23 @@ class AccountRepository
return $data; return $data;
} }
public function enableProPlan() public function enablePlan($plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false)
{ {
if (Auth::user()->isPro() && ! Auth::user()->isTrial()) {
return false;
}
$account = Auth::user()->account; $account = Auth::user()->account;
$client = $this->getNinjaClient($account); $client = $this->getNinjaClient($account);
$invitation = $this->createNinjaInvoice($client, $account); $invitation = $this->createNinjaInvoice($client, $account, $plan, $term, $credit, $pending_monthly);
return $invitation; return $invitation;
} }
public function createNinjaInvoice($client, $clientAccount) public function createNinjaInvoice($client, $clientAccount, $plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false)
{ {
if ($credit < 0) {
$credit = 0;
}
$plan_cost = Account::$plan_prices[$plan][$term];
$account = $this->getNinjaAccount(); $account = $this->getNinjaAccount();
$lastInvoice = Invoice::withTrashed()->whereAccountId($account->id)->orderBy('public_id', 'DESC')->first(); $lastInvoice = Invoice::withTrashed()->whereAccountId($account->id)->orderBy('public_id', 'DESC')->first();
$publicId = $lastInvoice ? ($lastInvoice->public_id + 1) : 1; $publicId = $lastInvoice ? ($lastInvoice->public_id + 1) : 1;
@ -230,19 +237,39 @@ class AccountRepository
$invoice->client_id = $client->id; $invoice->client_id = $client->id;
$invoice->invoice_number = $account->getNextInvoiceNumber($invoice); $invoice->invoice_number = $account->getNextInvoiceNumber($invoice);
$invoice->invoice_date = $clientAccount->getRenewalDate(); $invoice->invoice_date = $clientAccount->getRenewalDate();
$invoice->amount = PRO_PLAN_PRICE; $invoice->amount = $invoice->balance = $plan_cost - $credit;
$invoice->balance = PRO_PLAN_PRICE;
$invoice->save(); $invoice->save();
$item = new InvoiceItem(); if ($credit) {
$item->account_id = $account->id; $credit_item = InvoiceItem::createNew($invoice);
$item->user_id = $account->users()->first()->id; $credit_item->qty = 1;
$item->public_id = $publicId; $credit_item->cost = -$credit;
$credit_item->notes = trans('texts.plan_credit_description');
$credit_item->product_key = trans('texts.plan_credit_product');
$invoice->invoice_items()->save($credit_item);
}
$item = InvoiceItem::createNew($invoice);
$item->qty = 1; $item->qty = 1;
$item->cost = PRO_PLAN_PRICE; $item->cost = $plan_cost;
$item->notes = trans('texts.pro_plan_description'); $item->notes = trans("texts.{$plan}_plan_{$term}_description");
$item->product_key = trans('texts.pro_plan_product');
// Don't change this without updating the regex in PaymentService->createPayment()
$item->product_key = 'Plan - '.ucfirst($plan).' ('.ucfirst($term).')';
$invoice->invoice_items()->save($item); $invoice->invoice_items()->save($item);
if ($pending_monthly) {
$term_end = $term == PLAN_MONTHLY ? date_create('+1 month') : date_create('+1 year');
$pending_monthly_item = InvoiceItem::createNew($invoice);
$item->qty = 1;
$pending_monthly_item->cost = 0;
$pending_monthly_item->notes = trans("texts.plan_pending_monthly", array('date', Utils::dateToString($term_end)));
// Don't change this without updating the text in PaymentService->createPayment()
$pending_monthly_item->product_key = 'Pending Monthly';
$invoice->invoice_items()->save($pending_monthly_item);
}
$invitation = new Invitation(); $invitation = new Invitation();
$invitation->account_id = $account->id; $invitation->account_id = $account->id;
@ -355,7 +382,7 @@ class AccountRepository
$user->last_name = $lastName; $user->last_name = $lastName;
$user->registered = true; $user->registered = true;
$user->account->startTrial(); $user->account->startTrial(PLAN_PRO);
} }
$user->oauth_provider_id = $providerId; $user->oauth_provider_id = $providerId;
@ -471,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;
} }
@ -487,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);
@ -542,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;
} }
@ -563,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

@ -284,7 +284,14 @@ class InvoiceRepository extends BaseRepository
$invoice->end_date = null; $invoice->end_date = null;
} }
$invoice->terms = (isset($data['terms']) && trim($data['terms'])) ? trim($data['terms']) : (!$publicId && $account->invoice_terms ? $account->invoice_terms : ''); if (isset($data['terms']) && trim($data['terms'])) {
$invoice->terms = trim($data['terms']);
} elseif ($isNew && $account->{"{$entityType}_terms"}) {
$invoice->terms = $account->{"{$entityType}_terms"};
} else {
$invoice->terms = '';
}
$invoice->invoice_footer = (isset($data['invoice_footer']) && trim($data['invoice_footer'])) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : ''); $invoice->invoice_footer = (isset($data['invoice_footer']) && trim($data['invoice_footer'])) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : '');
$invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null; $invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null;

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,31 +1,32 @@
<?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,
'pro' => 0 'pro' => 0,
'enterprise' => 0
]; ];
foreach ($accounts as $account) { foreach ($accounts as $account) {
$counts['free']++; $counts['free']++;
if (Utils::withinPastYear($account->pro_plan_paid)) { $plan = $account->getPlanDetails(false, false);
if ($plan) {
$counts['pro']++; $counts['pro']++;
if ($plan['plan'] == PLAN_ENTERPRISE) {
$counts['enterprise']++;
}
} }
} }
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

@ -39,18 +39,7 @@ class PaymentService extends BaseService
public function createGateway($accountGateway) public function createGateway($accountGateway)
{ {
$gateway = Omnipay::create($accountGateway->gateway->provider); $gateway = Omnipay::create($accountGateway->gateway->provider);
$config = $accountGateway->getConfig(); $gateway->initialize((array) $accountGateway->getConfig());
foreach ($config as $key => $val) {
if (!$val) {
continue;
}
$function = "set".ucfirst($key);
if (method_exists($gateway, $function)) {
$gateway->$function($val);
}
}
if ($accountGateway->isGateway(GATEWAY_DWOLLA)) { if ($accountGateway->isGateway(GATEWAY_DWOLLA)) {
if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) { if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) {
@ -216,17 +205,6 @@ class PaymentService extends BaseService
{ {
$invoice = $invitation->invoice; $invoice = $invitation->invoice;
// enable pro plan for hosted users
if ($invoice->account->account_key == NINJA_ACCOUNT_KEY && $invoice->amount == PRO_PLAN_PRICE) {
$account = Account::with('users')->find($invoice->client->public_id);
$account->pro_plan_paid = $account->getRenewalDate();
$account->save();
// sync pro accounts
$user = $account->users()->first();
$this->accountRepo->syncAccounts($user->id, $account->pro_plan_paid);
}
$payment = Payment::createNew($invitation); $payment = Payment::createNew($invitation);
$payment->invitation_id = $invitation->id; $payment->invitation_id = $invitation->id;
$payment->account_gateway_id = $accountGateway->id; $payment->account_gateway_id = $accountGateway->id;
@ -236,13 +214,66 @@ class PaymentService extends BaseService
$payment->contact_id = $invitation->contact_id; $payment->contact_id = $invitation->contact_id;
$payment->transaction_reference = $ref; $payment->transaction_reference = $ref;
$payment->payment_date = date_create()->format('Y-m-d'); $payment->payment_date = date_create()->format('Y-m-d');
if ($payerId) { if ($payerId) {
$payment->payer_id = $payerId; $payment->payer_id = $payerId;
} }
$payment->save(); $payment->save();
// enable pro plan for hosted users
if ($invoice->account->account_key == NINJA_ACCOUNT_KEY) {
foreach ($invoice->invoice_items as $invoice_item) {
// Hacky, but invoices don't have meta fields to allow us to store this easily
if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) {
$plan = strtolower($matches[1]);
$term = strtolower($matches[2]);
} elseif ($invoice_item->product_key == 'Pending Monthly') {
$pending_monthly = true;
}
}
if (!empty($plan)) {
$account = Account::with('users')->find($invoice->client->public_id);
if(
$account->company->plan != $plan
|| DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create('-7 days')
) {
// Either this is a different plan, or the subscription expired more than a week ago
// Reset any grandfathering
$account->company->plan_started = date_create()->format('Y-m-d');
}
if (
$account->company->plan == $plan
&& $account->company->plan_term == $term
&& DateTime::createFromFormat('Y-m-d', $account->company->plan_expires) >= date_create()
) {
// This is a renewal; mark it paid as of when this term expires
$account->company->plan_paid = $account->company->plan_expires;
} else {
$account->company->plan_paid = date_create()->format('Y-m-d');
}
$account->company->payment_id = $payment->id;
$account->company->plan = $plan;
$account->company->plan_term = $term;
$account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid)
->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d');
if (!empty($pending_monthly)) {
$account->company->pending_plan = $plan;
$account->company->pending_term = PLAN_TERM_MONTHLY;
} else {
$account->company->pending_plan = null;
$account->company->pending_term = null;
}
$account->company->save();
}
}
return $payment; return $payment;
} }

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

@ -26,7 +26,7 @@
"anahkiasen/former": "4.0.*@dev", "anahkiasen/former": "4.0.*@dev",
"barryvdh/laravel-debugbar": "~2.0", "barryvdh/laravel-debugbar": "~2.0",
"chumper/datatable": "dev-develop#04ef2bf", "chumper/datatable": "dev-develop#04ef2bf",
"omnipay/omnipay": "~2.3.0", "omnipay/omnipay": "~2.3",
"intervention/image": "dev-master", "intervention/image": "dev-master",
"webpatser/laravel-countries": "dev-master", "webpatser/laravel-countries": "dev-master",
"barryvdh/laravel-ide-helper": "dev-master", "barryvdh/laravel-ide-helper": "dev-master",

8
composer.lock generated
View File

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "2ab7ab9013e31d8a2f0dcf43b31beefa", "hash": "cf82d2ddb25cb1a7d6b4867bcc8692b8",
"content-hash": "188fba7fcc31b702098d5417bc0e63e2", "content-hash": "481a95753b873249aebceb99e7426421",
"packages": [ "packages": [
{ {
"name": "agmscode/omnipay-agms", "name": "agmscode/omnipay-agms",
@ -127,7 +127,7 @@
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/formers/former/zipball/d97f907741323b390f43954a90a227921ecc6b96", "url": "https://api.github.com/repos/formers/former/zipball/78ae8c65b1f8134e2db1c9491c251c03638823ca",
"reference": "d97f907741323b390f43954a90a227921ecc6b96", "reference": "d97f907741323b390f43954a90a227921ecc6b96",
"shasum": "" "shasum": ""
}, },
@ -4229,7 +4229,7 @@
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/omnipay-bitpay/zipball/cf813f1d5436a1d2f942d3df6666695d1e2b5280", "url": "https://api.github.com/repos/thephpleague/omnipay-bitpay/zipball/9cadfb7955bd361d1a00ac8f0570aee4c05c6bb4",
"reference": "cf813f1d5436a1d2f942d3df6666695d1e2b5280", "reference": "cf813f1d5436a1d2f942d3df6666695d1e2b5280",
"shasum": "" "shasum": ""
}, },

View File

@ -63,6 +63,7 @@ class AddDocuments extends Migration {
$table->dropColumn('logo_height'); $table->dropColumn('logo_height');
$table->dropColumn('logo_size'); $table->dropColumn('logo_size');
$table->dropColumn('invoice_embed_documents'); $table->dropColumn('invoice_embed_documents');
$table->dropColumn('document_email_attachment');
}); });
Schema::dropIfExists('documents'); Schema::dropIfExists('documents');

View File

@ -0,0 +1,156 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Models\Company;
use App\Models\Account;
class EnterprisePlan extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('companies', function($table)
{
$table->increments('id');
$table->enum('plan', array('pro', 'enterprise', 'white_label'))->nullable();
$table->enum('plan_term', array('month', 'year'))->nullable();
$table->date('plan_started')->nullable();
$table->date('plan_paid')->nullable();
$table->date('plan_expires')->nullable();
$table->unsignedInteger('payment_id')->nullable();
$table->foreign('payment_id')->references('id')->on('payments');
$table->date('trial_started')->nullable();
$table->enum('trial_plan', array('pro', 'enterprise'))->nullable();
$table->enum('pending_plan', array('pro', 'enterprise', 'free'))->nullable();
$table->enum('pending_term', array('month', 'year'))->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::table('accounts', function($table)
{
$table->unsignedInteger('company_id')->nullable();
$table->foreign('company_id')->references('id')->on('companies');
});
$single_account_ids = \DB::table('users')
->leftJoin('user_accounts', function ($join) {
$join->on('user_accounts.user_id1', '=', 'users.id');
$join->orOn('user_accounts.user_id2', '=', 'users.id');
$join->orOn('user_accounts.user_id3', '=', 'users.id');
$join->orOn('user_accounts.user_id4', '=', 'users.id');
$join->orOn('user_accounts.user_id5', '=', 'users.id');
})
->whereNull('user_accounts.id')
->where(function ($query) {
$query->whereNull('users.public_id');
$query->orWhere('users.public_id', '=', 0);
})
->lists('users.account_id');
$group_accounts = \DB::select(
'SELECT u1.account_id as account1, u2.account_id as account2, u3.account_id as account3, u4.account_id as account4, u5.account_id as account5 FROM `user_accounts`
LEFT JOIN users u1 ON (u1.public_id IS NULL OR u1.public_id = 0) AND user_accounts.user_id1 = u1.id
LEFT JOIN users u2 ON (u2.public_id IS NULL OR u2.public_id = 0) AND user_accounts.user_id2 = u2.id
LEFT JOIN users u3 ON (u3.public_id IS NULL OR u3.public_id = 0) AND user_accounts.user_id3 = u3.id
LEFT JOIN users u4 ON (u4.public_id IS NULL OR u4.public_id = 0) AND user_accounts.user_id4 = u4.id
LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accounts.user_id5 = u5.id');
foreach (Account::find($single_account_ids) as $account) {
$this->upAccounts($account);
}
foreach ($group_accounts as $group_account) {
$this->upAccounts(null, Account::find(get_object_vars($group_account)));
}
Schema::table('accounts', function($table)
{
$table->dropColumn('pro_plan_paid');
$table->dropColumn('pro_plan_trial');
});
}
private function upAccounts($primaryAccount, $otherAccounts = array()) {
if(!$primaryAccount) {
$primaryAccount = $otherAccounts->first();
}
$company = Company::create();
if ($primaryAccount->pro_plan_paid && $primaryAccount->pro_plan_paid != '0000-00-00') {
$company->plan = 'pro';
$company->plan_term = 'year';
$company->plan_started = $primaryAccount->pro_plan_paid;
$company->plan_paid = $primaryAccount->pro_plan_paid;
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->modify('+1 year');
$company->plan_expires = $expires->format('Y-m-d');
}
}
if ($primaryAccount->pro_plan_trial && $primaryAccount->pro_plan_trial != '0000-00-00') {
$company->trial_started = $primaryAccount->pro_plan_trial;
$company->trial_plan = 'pro';
}
$company->save();
$primaryAccount->company_id = $company->id;
$primaryAccount->save();
if (!empty($otherAccounts)) {
foreach ($otherAccounts as $account) {
if ($account && $account->id != $primaryAccount->id) {
$account->company_id = $company->id;
$account->save();
}
}
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function($table)
{
$table->date('pro_plan_paid')->nullable();
$table->date('pro_plan_trial')->nullable();
});
foreach (Company::all() as $company) {
foreach ($company->accounts as $account) {
$account->pro_plan_paid = $company->plan_paid;
$account->pro_plan_trial = $company->trial_started;
$account->save();
}
}
Schema::table('accounts', function($table)
{
$table->dropForeign('accounts_company_id_foreign');
$table->dropColumn('company_id');
});
Schema::dropIfExists('companies');
}
}

View File

@ -0,0 +1,72 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddPageSize extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function ($table) {
$table->string('page_size')->default('A4');
$table->boolean('live_preview')->default(true);
$table->smallInteger('invoice_number_padding')->default(4);
});
Schema::table('fonts', function ($table) {
$table->dropColumn('is_early_access');
});
Schema::create('expense_categories', function($table)
{
$table->increments('id');
$table->unsignedInteger('user_id');
$table->unsignedInteger('account_id')->index();
$table->timestamps();
$table->softDeletes();
$table->string('name')->nullable();
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedInteger('public_id')->index();
$table->unique( array('account_id','public_id') );
});
Schema::table('expenses', function ($table) {
$table->unsignedInteger('expense_category_id')->nullable()->index();
$table->foreign('expense_category_id')->references('id')->on('expense_categories')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('accounts', function ($table) {
$table->dropColumn('page_size');
$table->dropColumn('live_preview');
$table->dropColumn('invoice_number_padding');
});
Schema::table('fonts', function ($table) {
$table->boolean('is_early_access');
});
Schema::dropIfExists('expense_categories');
Schema::table('expenses', function ($table) {
$table->dropColumn('expense_category_id');
});
}
}

View File

@ -246,7 +246,6 @@ class FontsSeeder extends Seeder
foreach ($fonts as $font) { foreach ($fonts as $font) {
if (!DB::table('fonts')->where('name', '=', $font['name'])->get()) { if (!DB::table('fonts')->where('name', '=', $font['name'])->get()) {
$font['is_early_access'] = false;
Font::create($font); Font::create($font);
} }
} }

View File

@ -2,6 +2,7 @@
use App\Models\User; use App\Models\User;
use App\Models\Account; use App\Models\Account;
use App\Models\Company;
use App\Models\Affiliate; use App\Models\Affiliate;
class UserTableSeeder extends Seeder class UserTableSeeder extends Seeder
@ -13,10 +14,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->id,
]); ]);
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) {
@ -96,7 +96,8 @@ function GetPdfMake(invoice, javascript, callback) {
} }
} }
// set page size
dd.pageSize = invoice.account.page_size;
pdfMake.fonts = {} pdfMake.fonts = {}
fonts = window.invoiceFonts || invoice.invoice_fonts; fonts = window.invoiceFonts || invoice.invoice_fonts;
@ -269,10 +270,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 +293,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 +325,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 +385,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 +555,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

@ -81,7 +81,7 @@ $LANG = array(
'company_details' => 'Company Details', 'company_details' => 'Company Details',
'online_payments' => 'Online Payments', 'online_payments' => 'Online Payments',
'notifications' => 'Email Notifications', 'notifications' => 'Email Notifications',
'import_export' => 'Import | Export | Cancel', 'import_export' => 'Import | Export',
'done' => 'Done', 'done' => 'Done',
'save' => 'Save', 'save' => 'Save',
'create' => 'Create', 'create' => 'Create',
@ -268,7 +268,6 @@ $LANG = array(
'erase_data' => 'This will permanently erase your data.', 'erase_data' => 'This will permanently erase your data.',
'password' => 'Password', 'password' => 'Password',
'pro_plan_product' => 'Pro Plan', 'pro_plan_product' => 'Pro Plan',
'pro_plan_description' => 'One year enrollment in the Invoice Ninja Pro Plan.',
'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!<p/>&nbsp;<br/> 'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!<p/>&nbsp;<br/>
<b>Next Steps</b><p/>A payable invoice has been sent to the email <b>Next Steps</b><p/>A payable invoice has been sent to the email
address associated with your account. To unlock all of the awesome address associated with your account. To unlock all of the awesome
@ -368,7 +367,7 @@ $LANG = array(
'confirm_email_invoice' => 'Are you sure you want to email this invoice?', 'confirm_email_invoice' => 'Are you sure you want to email this invoice?',
'confirm_email_quote' => 'Are you sure you want to email this quote?', 'confirm_email_quote' => 'Are you sure you want to email this quote?',
'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?', 'confirm_recurring_email_invoice' => 'Are you sure you want this invoice emailed?',
'cancel_account' => 'Cancel Account', 'cancel_account' => 'Delete Account',
'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.', 'cancel_account_message' => 'Warning: This will permanently erase all of your data, there is no undo.',
'go_back' => 'Go Back', 'go_back' => 'Go Back',
'data_visualizations' => 'Data Visualizations', 'data_visualizations' => 'Data Visualizations',
@ -976,9 +975,9 @@ $LANG = array(
'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', 'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.',
'trello_roadmap' => 'Trello Roadmap', 'trello_roadmap' => 'Trello Roadmap',
'header_footer' => 'Header/Footer', 'header_footer' => 'Header/Footer',
'first_page' => 'first page', 'first_page' => 'First page',
'all_pages' => 'all pages', 'all_pages' => 'All pages',
'last_page' => 'last page', 'last_page' => 'Last page',
'all_pages_header' => 'Show header on', 'all_pages_header' => 'Show header on',
'all_pages_footer' => 'Show footer on', 'all_pages_footer' => 'Show footer on',
'invoice_currency' => 'Invoice Currency', 'invoice_currency' => 'Invoice Currency',
@ -1126,6 +1125,57 @@ $LANG = array(
'enable_client_portal_help' => 'Show/hide the client portal.', 'enable_client_portal_help' => 'Show/hide the client portal.',
'enable_client_portal_dashboard' => 'Dashboard', 'enable_client_portal_dashboard' => 'Dashboard',
'enable_client_portal_dashboard_help' => 'Show/hide the dashboard page in the client portal.', 'enable_client_portal_dashboard_help' => 'Show/hide the dashboard page in the client portal.',
// Plans
'account_management' => 'Account Management',
'plan_status' => 'Plan Status',
'plan_upgrade' => 'Upgrade',
'plan_change' => 'Change Plan',
'pending_change_to' => 'Changes To',
'plan_changes_to' => ':plan on :date',
'plan_term_changes_to' => ':plan (:term) on :date',
'cancel_plan_change' => 'Cancel Change',
'plan' => 'Plan',
'expires' => 'Expires',
'renews' => 'Renews',
'plan_expired' => ':plan Plan Expired',
'trial_expired' => ':plan Plan Trial Ended',
'never' => 'Never',
'plan_free' => 'Free',
'plan_pro' => 'Pro',
'plan_enterprise' => 'Enterprise',
'plan_white_label' => 'Self Hosted (White labeled)',
'plan_free_self_hosted' => 'Self Hosted (Free)',
'plan_trial' => 'Trial',
'plan_term' => 'Term',
'plan_term_monthly' => 'Monthly',
'plan_term_yearly' => 'Yearly',
'plan_term_month' => 'Month',
'plan_term_year' => 'Year',
'plan_price_monthly' => '$:price/Month',
'plan_price_yearly' => '$:price/Year',
'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_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.',
'enterprise_plan_product' => 'Enterprise Plan',
'enterprise_plan_year_description' => 'One year enrollment in the Invoice Ninja Enterprise Plan.',
'enterprise_plan_month_description' => 'One month enrollment in the Invoice Ninja Enterprise Plan.',
'plan_credit_product' => 'Credit',
'plan_credit_description' => 'Credit for unused time',
'plan_pending_monthly' => 'Will switch to monthly on :date',
'plan_refunded' => 'A refund has been issued.',
'live_preview' => 'Live Preview',
'page_size' => 'Page Size',
'live_preview_disabled' => 'Live preview has been disabled to support selected font',
'invoice_number_padding' => 'Padding',
); );

View File

@ -929,267 +929,264 @@ return array(
'client_portal' => 'Portal do Cliente', 'client_portal' => 'Portal do Cliente',
'admin' => 'Admin', 'admin' => 'Admin',
'disabled' => 'Disabilitado', 'disabled' => 'Desabilitado',
'show_archived_users' => 'Mostrar usuários arquivados', 'show_archived_users' => 'Mostrar usuários arquivados',
'notes' => 'Observações', 'notes' => 'Observações',
'invoice_will_create' => 'cliente será criado', 'invoice_will_create' => 'cliente será criado',
'invoices_will_create' => 'faturas serão criadas', 'invoices_will_create' => 'faturas serão criadas',
'failed_to_import' => 'A importação dos seguintes registros falhou', 'failed_to_import' => 'A importação dos seguintes registros falhou',
'publishable_key' => 'Publishable Key', 'publishable_key' => 'Chave Publicável',
'secret_key' => 'Secret Key', 'secret_key' => 'Chave Secreta',
'missing_publishable_key' => 'Set your Stripe publishable key for an improved checkout process', 'missing_publishable_key' => 'Defina o sua chave publicável do Stripe para um processo de pagamento melhorado',
'email_design' => 'Email Design', 'email_design' => 'Template de E-mail',
'due_by' => 'Due by :date', 'due_by' => 'Vencido em :date',
'enable_email_markup' => 'Enable Markup', 'enable_email_markup' => 'Habilitar Marcação',
'enable_email_markup_help' => 'Make it easier for your clients to pay you by adding schema.org markup to your emails.', 'enable_email_markup_help' => 'Tornar mais fácil para os seus clientes efetuarem seus pagamentos, acrescentando marcação schema.org a seus e-mails.',
'template_help_title' => 'Templates Help', 'template_help_title' => 'Ajuda de Templates',
'template_help_1' => 'Available variables:', 'template_help_1' => 'Variáveis disponíveis:',
'email_design_id' => 'Email Style', 'email_design_id' => 'Estilo de e-mails',
'email_design_help' => 'Make your emails look more professional with HTML layouts', 'email_design_help' => 'Deixe seus e-mails mais profissionais com layouts HTML',
'plain' => 'Plain', 'plain' => 'Plano',
'light' => 'Light', 'light' => 'Claro',
'dark' => 'Dark', 'dark' => 'Escuro',
'industry_help' => 'Used to provide comparisons against the averages of companies of similar size and industry.', 'industry_help' => 'Usado para fornecer comparações contra as médias das empresas de tamanho e indústria similar.',
'subdomain_help' => 'Customize the invoice link subdomain or display the invoice on your own website.', 'subdomain_help' => 'Personalizar o link da fatura ou exibir a fatura em seu próprio site.',
'invoice_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the invoice number.', 'invoice_number_help' => 'Especifique um prefixo ou usar um padrão personalizado para definir dinamicamente o número da fatura.',
'quote_number_help' => 'Specify a prefix or use a custom pattern to dynamically set the quote number.', 'quote_number_help' => 'Especifique um prefixo ou usar um padrão personalizado para definir dinamicamente o número do orçamento.',
'custom_client_fields_helps' => 'Add a text input to the client create/edit page and display the label and value on the PDF.', 'custom_client_fields_helps' => 'Adicionar uma entrada de texto na página Criar/Editar Cliente e exibir no PDF.',
'custom_account_fields_helps' => 'Add a label and value to the company details section of the PDF.', 'custom_account_fields_helps' => 'Adicionar um rótulo e um valor para a seção detalhes da empresa do PDF.',
'custom_invoice_fields_helps' => 'Add a text input to the invoice create/edit page and display the label and value on the PDF.', 'custom_invoice_fields_helps' => 'Adicionar uma entrada de texto na página Criar/Editar Fatura e exibir no PDF.',
'custom_invoice_charges_helps' => 'Add a text input to the invoice create/edit page and include the charge in the invoice subtotals.', 'custom_invoice_charges_helps' => 'Adicionar uma entrada de texto na página Criar/Editar Fatura e incluir nos subtotais da fatura.',
'color_help' => 'Note: the primary color is also used in the client portal and custom email designs.', 'color_help' => 'Nota: A cor primária também é utilizada nos projetos do portal do cliente e-mail personalizado.',
'token_expired' => 'Validation token was expired. Please try again.', 'token_expired' => 'Token de acesso expirado. Tente novamente!',
'invoice_link' => 'Invoice Link', 'invoice_link' => 'Link da Fatura',
'button_confirmation_message' => 'Click to confirm your email address.', 'button_confirmation_message' => 'Clique para confirmar seu endereço de e-mail.',
'confirm' => 'Confirm', 'confirm' => 'Confirmar',
'email_preferences' => 'Email Preferences', 'email_preferences' => 'Preferências de E-mails',
'created_invoices' => 'Successfully created :count invoice(s)', 'created_invoices' => ':count fatura(s) criadas com sucesso',
'next_invoice_number' => 'The next invoice number is :number.', 'next_invoice_number' => 'O número da próxima fatura será :number.',
'next_quote_number' => 'The next quote number is :number.', 'next_quote_number' => 'O número do próximo orçamento será :number.',
'days_before' => 'days before', 'days_before' => 'dias antes',
'days_after' => 'days after', 'days_after' => 'dias depois',
'field_due_date' => 'due date', 'field_due_date' => 'data de vencimento',
'field_invoice_date' => 'invoice date', 'field_invoice_date' => 'data da fatura',
'schedule' => 'Schedule', 'schedule' => 'Agendamento',
'email_designs' => 'Email Designs', 'email_designs' => 'Design de E-mails',
'assigned_when_sent' => 'Assigned when sent', 'assigned_when_sent' => 'Assinar quando enviar',
'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.', 'white_label_custom_css' => ':link apenas $'.WHITE_LABEL_PRICE.' para permitir um estilo personalizado e apoiar o nosso projecto.',
'white_label_purchase_link' => 'Purchase a white label license', 'white_label_purchase_link' => 'Adquira uma licença white label',
// Expense / vendor // Expense / vendor
'expense' => 'Expense', 'expense' => 'Despesa',
'expenses' => 'Expenses', 'expenses' => 'Despesas',
'new_expense' => 'Enter Expense', 'new_expense' => 'Adicionar Despesa',
'enter_expense' => 'Enter Expense', 'enter_expense' => 'Incluir Despesa',
'vendors' => 'Vendors', 'vendors' => 'Fornecedor',
'new_vendor' => 'New Vendor', 'new_vendor' => 'Novo Fornecedor',
'payment_terms_net' => 'Net', 'payment_terms_net' => 'Rede',
'vendor' => 'Vendor', 'vendor' => 'Fornecedor',
'edit_vendor' => 'Edit Vendor', 'edit_vendor' => 'Editar Fornecedor',
'archive_vendor' => 'Archive Vendor', 'archive_vendor' => 'Arquivar Fornecedor',
'delete_vendor' => 'Delete Vendor', 'delete_vendor' => 'Deletar Fornecedor',
'view_vendor' => 'View Vendor', 'view_vendor' => 'Visualizar Fornecedor',
'deleted_expense' => 'Successfully deleted expense', 'deleted_expense' => 'Despesa excluída com sucesso',
'archived_expense' => 'Successfully archived expense', 'archived_expense' => 'Despesa arquivada com sucesso',
'deleted_expenses' => 'Successfully deleted expenses', 'deleted_expenses' => 'Despesas excluídas com sucesso',
'archived_expenses' => 'Successfully archived expenses', 'archived_expenses' => 'Despesas arquivada com sucesso',
// Expenses // Expenses
'expense_amount' => 'Expense Amount', 'expense_amount' => 'Total de Despesas',
'expense_balance' => 'Expense Balance', 'expense_balance' => 'Saldo de Despesas',
'expense_date' => 'Expense Date', 'expense_date' => 'Data da Despesa',
'expense_should_be_invoiced' => 'Should this expense be invoiced?', 'expense_should_be_invoiced' => 'Esta despesa deve ser faturada?',
'public_notes' => 'Public Notes', 'public_notes' => 'Notas Públicas',
'invoice_amount' => 'Invoice Amount', 'invoice_amount' => 'Total da Fatura',
'exchange_rate' => 'Exchange Rate', 'exchange_rate' => 'Taxa de Câmbio',
'yes' => 'Yes', 'yes' => 'Sim',
'no' => 'No', 'no' => 'Não',
'should_be_invoiced' => 'Should be invoiced', 'should_be_invoiced' => 'Deve ser Faturada',
'view_expense' => 'View expense # :expense', 'view_expense' => 'Visualizar despesa # :expense',
'edit_expense' => 'Edit Expense', 'edit_expense' => 'Editar Despesa',
'archive_expense' => 'Archive Expense', 'archive_expense' => 'Arquivar Despesa',
'delete_expense' => 'Delete Expense', 'delete_expense' => 'Deletar Despesa',
'view_expense_num' => 'Expense # :expense', 'view_expense_num' => 'Despesa # :expense',
'updated_expense' => 'Successfully updated expense', 'updated_expense' => 'Despesa atualizada com sucesso',
'created_expense' => 'Successfully created expense', 'created_expense' => 'Despesa criada com sucesso',
'enter_expense' => 'Enter Expense', 'enter_expense' => 'Incluir Despesa',
'view' => 'View', 'view' => 'Visualizar',
'restore_expense' => 'Restore Expense', 'restore_expense' => 'Restaurar Despesa',
'invoice_expense' => 'Invoice Expense', 'invoice_expense' => 'Faturar Despesa',
'expense_error_multiple_clients' => 'The expenses can\'t belong to different clients', 'expense_error_multiple_clients' => 'Despesas não podem pertencer a clientes diferentes',
'expense_error_invoiced' => 'Expense has already been invoiced', 'expense_error_invoiced' => 'Despeja já faturada',
'convert_currency' => 'Convert currency', 'convert_currency' => 'Converter moeda',
// Payment terms // Payment terms
'num_days' => 'Number of days', 'num_days' => 'Número de dias',
'create_payment_term' => 'Create Payment Term', 'create_payment_term' => 'Criar Termo de Pagamento',
'edit_payment_terms' => 'Edit Payment Term', 'edit_payment_terms' => 'Editar Termos de Pagamento',
'edit_payment_term' => 'Edit Payment Term', 'edit_payment_term' => 'Editar Termo de Pagamento',
'archive_payment_term' => 'Archive Payment Term', 'archive_payment_term' => 'Arquivar Termo de Pagamento',
// recurring due dates // recurring due dates
'recurring_due_dates' => 'Recurring Invoice Due Dates', 'recurring_due_dates' => 'Data de Vencimento das Faturas Recorrentes',
'recurring_due_date_help' => '<p>Automatically sets a due date for the invoice.</p> 'recurring_due_date_help' => '<p>Definir automaticamente a data de vencimento da fatura.</p>
<p>Invoices on a monthly or yearly cycle set to be due on or before the day they are created will be due the next month. Invoices set to be due on the 29th or 30th in months that don\'t have that day will be due the last day of the month.</p> <p>Faturas em um ciclo mensal ou anual com vencimento anterior ou na data em que são criadas serão faturadas para o próximo mês. Faturas com vencimento no dia 29 ou 30 nos meses que não tem esse dia será faturada no último dia do mês..</p>
<p>Invoices on a weekly cycle set to be due on the day of the week they are created will be due the next week.</p> <p>Faturas em um clclo mensal com vencimento no dia da semana em que foi criada serão faturadas para a próxima semana.</p>
<p>For example:</p> <p>Exemplo:</p>
<ul> <ul>
<li>Today is the 15th, due date is 1st of the month. The due date should likely be the 1st of the next month.</li> <li>Hoje é dia 15, vencimento no primeiro dia do mês. O Vencimento será no primeiro dia do próximo mês.</li>
<li>Today is the 15th, due date is the last day of the month. The due date will be the last day of the this month. <li>Hoje é dia 15, vencimento no último dia do mês. O Vencimento será no último dia do mês corrente</li>
</li> <li>Hoje é dia 15, vencimento no dia 15. O venciemnto será no dia 15 do <strong>próximo</strong> mês.</li>
<li>Today is the 15th, due date is the 15th day of the month. The due date will be the 15th day of <strong>next</strong> month. <li>Hoje é Sexta-Feira, vencimento na primeira sexta-feira. O venciemnto será na próxima sexta-feira, não hoje.</li>
</li>
<li>Today is the Friday, due date is the 1st Friday after. The due date will be next Friday, not today.
</li>
</ul>', </ul>',
'due' => 'Due', 'due' => 'Vencimento',
'next_due_on' => 'Due Next: :date', 'next_due_on' => 'Próximo Vencimento: :date',
'use_client_terms' => 'Use client terms', 'use_client_terms' => 'Usar condições do cliente',
'day_of_month' => ':ordinal day of month', 'day_of_month' => ':ordinal dia do mês ',
'last_day_of_month' => 'Last day of month', 'last_day_of_month' => 'Último dia do mês',
'day_of_week_after' => ':ordinal :day after', 'day_of_week_after' => ':ordinal :day depois',
'sunday' => 'Sunday', 'sunday' => 'Domingo',
'monday' => 'Monday', 'monday' => 'Segunda-Feira',
'tuesday' => 'Tuesday', 'tuesday' => 'Terça-Feira',
'wednesday' => 'Wednesday', 'wednesday' => 'Quarta-Feira',
'thursday' => 'Thursday', 'thursday' => 'Quinta-Feira',
'friday' => 'Friday', 'friday' => 'Sexta-Feira',
'saturday' => 'Saturday', 'saturday' => 'Sábado',
// Fonts // Fonts
'header_font_id' => 'Header Font', 'header_font_id' => 'Fonte do Cabeçalho',
'body_font_id' => 'Body Font', 'body_font_id' => 'Fonte dos Textos',
'color_font_help' => 'Note: the primary color and fonts are also used in the client portal and custom email designs.', 'color_font_help' => 'Nota: A cor primária também é utilizada nos projetos do portal do cliente e-mail personalizado.',
'live_preview' => 'Live Preview', 'live_preview' => 'Preview',
'invalid_mail_config' => 'Unable to send email, please check that the mail settings are correct.', 'invalid_mail_config' => 'Falha ao enviar e-mail, verifique as configurações.',
'invoice_message_button' => 'To view your invoice for :amount, click the button below.', 'invoice_message_button' => 'Para visualizar sua fatura de :amount, clique no botão abaixo.',
'quote_message_button' => 'To view your quote for :amount, click the button below.', 'quote_message_button' => 'Para visualizar seu orçamento de :amount, clique no botão abaixo.',
'payment_message_button' => 'Thank you for your payment of :amount.', 'payment_message_button' => 'Obrigado pelo seu pagamento de :amount.',
'payment_type_direct_debit' => 'Direct Debit', 'payment_type_direct_debit' => 'Débito',
'bank_accounts' => 'Bank Accounts', 'bank_accounts' => 'Contas Bancárias',
'add_bank_account' => 'Add Bank Account', 'add_bank_account' => 'Adicionar Conta Bancária',
'setup_account' => 'Setup Account', 'setup_account' => 'Configurar Conta',
'import_expenses' => 'Import Expenses', 'import_expenses' => 'Importar Despesas',
'bank_id' => 'bank', 'bank_id' => 'banco',
'integration_type' => 'Integration Type', 'integration_type' => 'Tipo de Integração',
'updated_bank_account' => 'Successfully updated bank account', 'updated_bank_account' => 'Conta bancária atualizada com sucesso',
'edit_bank_account' => 'Edit Bank Account', 'edit_bank_account' => 'Editar Conta Bancária',
'archive_bank_account' => 'Archive Bank Account', 'archive_bank_account' => 'Arquivar Conta Bancária',
'archived_bank_account' => 'Successfully archived bank account', 'archived_bank_account' => 'Conta bancária arquivada com sucesso',
'created_bank_account' => 'Successfully created bank account', 'created_bank_account' => 'Conta bancária criada com sucesso',
'validate_bank_account' => 'Validate Bank Account', 'validate_bank_account' => 'Validar Conta Bancária',
'bank_accounts_help' => 'Connect a bank account to automatically import expenses and create vendors. Supports American Express and <a href="'.OFX_HOME_URL.'" target="_blank">400+ US banks.</a>', 'bank_accounts_help' => 'Conecte sua conta bancária para importar suas despesas e criar fornecedores. Suporte ao American Express e <a href="'.OFX_HOME_URL.'" target="_blank">400+ bancos americanos.</a>',
'bank_password_help' => 'Note: your password is transmitted securely and never stored on our servers.', 'bank_password_help' => 'Nota: sua senha é transferida de forma segura e não será armazenada em nossos servidores.',
'bank_password_warning' => 'Warning: your password may be transmitted in plain text, consider enabling HTTPS.', 'bank_password_warning' => 'Atenção: sua senha será transferida de forma não segura, considere habilitar HTTPS.',
'username' => 'Username', 'username' => 'Usuário',
'account_number' => 'Account Number', 'account_number' => 'Conta',
'account_name' => 'Account Name', 'account_name' => 'Nome da Conta',
'bank_account_error' => 'Failed to retreive account details, please check your credentials.', 'bank_account_error' => 'Falha ao receber os detalhes da sua conta, verifique seus dados de acesso.',
'status_approved' => 'Approved', 'status_approved' => 'Aprovado',
'quote_settings' => 'Quote Settings', 'quote_settings' => 'Configuração de Orçamentos',
'auto_convert_quote' => 'Auto convert quote', 'auto_convert_quote' => 'Auto converter orçamento',
'auto_convert_quote_help' => 'Automatically convert a quote to an invoice when approved by a client.', 'auto_convert_quote_help' => 'Converter automaticamente um orçamento quando for aprovado pelo cliente.',
'validate' => 'Validate', 'validate' => 'Validado',
'info' => 'Info', 'info' => 'Info',
'imported_expenses' => 'Successfully created :count_vendors vendor(s) and :count_expenses expense(s)', 'imported_expenses' => ':count_vendors fornecedor(s) e :count_expenses despesa(s) importadas com sucesso',
'iframe_url_help3' => 'Note: if you plan on accepting credit cards details we strongly recommend enabling HTTPS on your site.', 'iframe_url_help3' => 'Nota: se o seu plano aceita detalhes do cartão de crédito recomendamos que seja habilitado o HTTPS em seu site.',
'expense_error_multiple_currencies' => 'The expenses can\'t have different currencies.', 'expense_error_multiple_currencies' => 'As despesas não podem ter diferentes moedas.',
'expense_error_mismatch_currencies' => 'The client\'s currency does not match the expense currency.', 'expense_error_mismatch_currencies' => 'As configurações de moeda do cliente não coincide com a moeda nesta despesa.',
'trello_roadmap' => 'Trello Roadmap', 'trello_roadmap' => 'Trello Roadmap',
'header_footer' => 'Header/Footer', 'header_footer' => 'Cabeçalho/Rodapé',
'first_page' => 'first page', 'first_page' => 'primeira página',
'all_pages' => 'all pages', 'all_pages' => 'todas as páginas',
'last_page' => 'last page', 'last_page' => 'última página',
'all_pages_header' => 'Show header on', 'all_pages_header' => 'Mostrar cabeçalho on',
'all_pages_footer' => 'Show footer on', 'all_pages_footer' => 'Mostrar rodapé on',
'invoice_currency' => 'Invoice Currency', 'invoice_currency' => 'Moeda da Fatura',
'enable_https' => 'We strongly recommend using HTTPS to accept credit card details online.', 'enable_https' => 'Recomendamos a utilização de HTTPS para receber os detalhes do cartão de crédito online.',
'quote_issued_to' => 'Quote issued to', 'quote_issued_to' => 'Orçamento emitido para',
'show_currency_code' => 'Currency Code', 'show_currency_code' => 'Código da Moeda',
'trial_message' => 'Your account will receive a free two week trial of our pro plan.', 'trial_message' => 'Sua conta receberá duas semanas receberá duas semanas gratuitamente para testar nosso plano pro.',
'trial_footer' => 'Your free trial lasts :count more days, :link to upgrade now.', 'trial_footer' => 'Seu período de teste expira em :count dias, :link para adquirir o plano pro.',
'trial_footer_last_day' => 'This is the last day of your free trial, :link to upgrade now.', 'trial_footer_last_day' => 'Seu período de testes encerra hoje, :link para adiquirir o plano pro.',
'trial_call_to_action' => 'Start Free Trial', 'trial_call_to_action' => 'Iniciar período de testes',
'trial_success' => 'Successfully enabled two week free pro plan trial', 'trial_success' => 'Duas semanas de testes foi habilitado com sucesso',
'overdue' => 'Overdue', 'overdue' => 'Vencido',
'white_label_text' => 'Purchase a ONE YEAR white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.', 'white_label_text' => 'Adquira UM ano de licença white label por $'.WHITE_LABEL_PRICE.' para remover a marca Invoice Ninja do portal do cliente e ajudar nosso projeto.',
'navigation' => 'Navigation', 'navigation' => 'Navegação',
'list_invoices' => 'List Invoices', 'list_invoices' => 'Listar Faturas',
'list_clients' => 'List Clients', 'list_clients' => 'Listar Clientes',
'list_quotes' => 'List Quotes', 'list_quotes' => 'Listar Orçamentos',
'list_tasks' => 'List Tasks', 'list_tasks' => 'Listar Tarefas',
'list_expenses' => 'List Expenses', 'list_expenses' => 'Listar Despesas',
'list_recurring_invoices' => 'List Recurring Invoices', 'list_recurring_invoices' => 'Listar Faturas Recorrentes',
'list_payments' => 'List Payments', 'list_payments' => 'Listar Pagamentos',
'list_credits' => 'List Credits', 'list_credits' => 'Listar Créditos',
'tax_name' => 'Tax Name', 'tax_name' => 'Nome da Taxa',
'report_settings' => 'Report Settings', 'report_settings' => 'Configuração de Relatórios',
'search_hotkey' => 'shortcut is /', 'search_hotkey' => 'atalho /',
'new_user' => 'New User', 'new_user' => 'Novo Usuário',
'new_product' => 'New Product', 'new_product' => 'Novo Produto',
'new_tax_rate' => 'New Tax Rate', 'new_tax_rate' => 'Nova Taxa de Juro',
'invoiced_amount' => 'Invoiced Amount', 'invoiced_amount' => 'Total Faturado',
'invoice_item_fields' => 'Invoice Item Fields', 'invoice_item_fields' => 'Campos de Ítens da Fatura',
'custom_invoice_item_fields_help' => 'Add a field when creating an invoice item and display the label and value on the PDF.', 'custom_invoice_item_fields_help' => 'Adicionar um campo ao adicionar um ítem na fatura e exibir no PDF.',
'recurring_invoice_number' => 'Recurring Invoice Number', 'recurring_invoice_number' => 'Número da Fatura Recorrente',
'recurring_invoice_number_prefix_help' => 'Speciy a prefix to be added to the invoice number for recurring invoices. The default value is \'R\'.', 'recurring_invoice_number_prefix_help' => 'Informe um prefixo para a numeração das faturas recorrentes. O valor padrão é \'R\'.',
'enable_client_portal' => 'Dashboard', 'enable_client_portal' => 'Painel',
'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.', 'enable_client_portal_help' => 'Mostrar/Ocultar o painel no portal do cliente.',
// Client Passwords // Client Passwords
'enable_portal_password'=>'Password protect invoices', 'enable_portal_password'=>'Faturas protegidas por senha',
'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.', 'enable_portal_password_help'=>'Permite definir uma senha para cada contato. Se uma senha for definida, o contato deverá informar sua senha antes de visualizar a fatura.',
'send_portal_password'=>'Generate password automatically', 'send_portal_password'=>'Gerar senha automaticamente',
'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.', 'send_portal_password_help'=>'Se uma senha não for definida, uma senha será gerada e enviada juntamente com a primeira fatura.',
'expired' => 'Expired', 'expired' => 'Expireda',
'invalid_card_number' => 'The credit card number is not valid.', 'invalid_card_number' => 'Cartão de Crédito inválido.',
'invalid_expiry' => 'The expiration date is not valid.', 'invalid_expiry' => 'Data para expirar não é valida.',
'invalid_cvv' => 'The CVV is not valid.', 'invalid_cvv' => 'O código CVV não é válido.',
'cost' => 'Cost', 'cost' => 'Custo',
'create_invoice_for_sample' => 'Note: create your first invoice to see a preview here.', 'create_invoice_for_sample' => 'Nota: cria sua primeira fatura para visualizar aqui.',
// User Permissions // User Permissions
'owner' => 'Owner', 'owner' => 'Proprietário',
'administrator' => 'Administrator', 'administrator' => 'Administrador',
'administrator_help' => 'Allow user to manage users, change settings and modify all records', 'administrator_help' => 'Permite usuário gerenciar usuários, configurações e alterar todos os cadastros',
'user_create_all' => 'Create clients, invoices, etc.', 'user_create_all' => 'Criar clientes, faturas, etc.',
'user_view_all' => 'View all clients, invoices, etc.', 'user_view_all' => 'Visualizar todos os clientes, faturas, etc.',
'user_edit_all' => 'Edit all clients, invoices, etc.', 'user_edit_all' => 'Editar todos os clientes, faturas, etc.',
'gateway_help_20' => ':link to sign up for Sage Pay.', 'gateway_help_20' => ':link para habilitar Sage Pay.',
'gateway_help_21' => ':link to sign up for Sage Pay.', 'gateway_help_21' => ':link para habilitar Sage Pay.',
'partial_due' => 'Partial Due', 'partial_due' => 'Vencimento Parcial',
'restore_vendor' => 'Restore Vendor', 'restore_vendor' => 'Restaurar Fornecedor',
'restored_vendor' => 'Successfully restored vendor', 'restored_vendor' => 'Fornecedor restarurado com sucesso',
'restored_expense' => 'Successfully restored expense', 'restored_expense' => 'Despesa restaurada com sucesso',
'permissions' => 'Permissions', 'permissions' => 'Permissões',
'create_all_help' => 'Allow user to create and modify records', 'create_all_help' => 'Permite o usuário criar e alterar todos os regitros',
'view_all_help' => 'Allow user to view records they didn\'t create', 'view_all_help' => 'Permite usuario visualizar regitros que ele não criou',
'edit_all_help' => 'Allow user to modify records they didn\'t create', 'edit_all_help' => 'Permite usuario editar regitros que ele não criou',
'view_payment' => 'View Payment', 'view_payment' => 'Visualizar ',
'january' => 'January', 'january' => 'Janeiro',
'february' => 'February', 'february' => 'Fevereiro',
'march' => 'March', 'march' => 'Março',
'april' => 'April', 'april' => 'Abril',
'may' => 'May', 'may' => 'Maio',
'june' => 'June', 'june' => 'Junho',
'july' => 'July', 'july' => 'Julho',
'august' => 'August', 'august' => 'Agosto',
'september' => 'September', 'september' => 'Setembro',
'october' => 'October', 'october' => 'Outubro',
'november' => 'November', 'november' => 'Novembro',
'december' => 'December', 'december' => 'Dezembro',
); );

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

@ -73,51 +73,7 @@
{!! Former::close() !!} {!! Former::close() !!}
{!! Former::open('settings/cancel_account')->addClass('cancel-account') !!}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.cancel_account') !!}</h3>
</div>
<div class="panel-body">
{!! Former::actions( Button::danger(trans('texts.cancel_account'))->large()->withAttributes(['onclick' => 'showConfirm()'])->appendIcon(Icon::create('trash'))) !!}
</div>
</div>
<div class="modal fade" id="confirmCancelModal" tabindex="-1" role="dialog" aria-labelledby="confirmCancelModalLabel" aria-hidden="true">
<div class="modal-dialog" style="min-width:150px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="confirmCancelModalLabel">{!! trans('texts.cancel_account') !!}</h4>
</div>
<div style="background-color: #fff; padding-left: 16px; padding-right: 16px">
&nbsp;<p>{{ trans('texts.cancel_account_message') }}</p>&nbsp;
&nbsp;<p>{!! Former::textarea('reason')->placeholder(trans('texts.reason_for_canceling'))->raw() !!}</p>&nbsp;
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.go_back') }}</button>
<button type="button" class="btn btn-danger" onclick="confirmCancel()">{{ trans('texts.cancel_account') }}</button>
</div>
</div>
</div>
</div>
{!! Former::close() !!}
<script type="text/javascript"> <script type="text/javascript">
function showConfirm() {
$('#confirmCancelModal').modal('show');
}
function confirmCancel() {
$('form.cancel-account').submit();
}
function setEntityTypesVisible() { function setEntityTypesVisible() {
var selector = '.entity-types input[type=checkbox]'; var selector = '.entity-types input[type=checkbox]';
if ($('#format').val() === 'JSON') { if ($('#format').val() === 'JSON') {

View File

@ -46,21 +46,23 @@
} }
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");
invoice.invoice_design_id = $('#invoice_design_id').val(); invoice.invoice_design_id = $('#invoice_design_id').val();
invoice.account.page_size = $('#page_size option:selected').text();
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()) 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
NINJA.headerFont = NINJA.bodyFont = 'Roboto';
@endif
var fields = [ var fields = [
'item', 'item',
'description', 'description',
@ -89,7 +91,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,
@ -112,7 +114,16 @@
<div class="col-md-12"> <div class="col-md-12">
{!! Former::open()->addClass('warn-on-exit')->onchange('if(!window.loadingFonts)refreshPDF()') !!} {!! Former::open()->addClass('warn-on-exit')->onchange('if(!window.loadingFonts)refreshPDF()') !!}
{!! Former::populate($account) !!}
{!! Former::populateField('invoice_design_id', $account->invoice_design_id) !!}
{!! Former::populateField('body_font_id', $account->body_font_id) !!}
{!! Former::populateField('header_font_id', $account->header_font_id) !!}
{!! Former::populateField('live_preview', intval($account->live_preview)) !!}
{!! Former::populateField('font_size', $account->font_size) !!}
{!! Former::populateField('page_size', $account->page_size) !!}
{!! Former::populateField('invoice_embed_documents', intval($account->invoice_embed_documents)) !!}
{!! Former::populateField('primary_color', $account->primary_color) !!}
{!! Former::populateField('secondary_color', $account->secondary_color) !!}
{!! Former::populateField('hide_quantity', intval($account->hide_quantity)) !!} {!! Former::populateField('hide_quantity', intval($account->hide_quantity)) !!}
{!! Former::populateField('hide_paid_to_date', intval($account->hide_paid_to_date)) !!} {!! Former::populateField('hide_paid_to_date', intval($account->hide_paid_to_date)) !!}
{!! Former::populateField('all_pages_header', intval($account->all_pages_header)) !!} {!! Former::populateField('all_pages_header', intval($account->all_pages_header)) !!}
@ -143,7 +154,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') !!}
@ -156,13 +167,17 @@
{!! Former::select('header_font_id') {!! Former::select('header_font_id')
->fromQuery($invoiceFonts, 'name', 'id') !!} ->fromQuery($invoiceFonts, 'name', 'id') !!}
{!! Former::checkbox('live_preview')->text(trans('texts.enable')) !!}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
{{ Former::setOption('TwitterBootstrap3.labelWidths.large', 6) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.large', 6) }}
{{ Former::setOption('TwitterBootstrap3.labelWidths.small', 6) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.small', 6) }}
{!! Former::select('page_size')
->options($pageSizes) !!}
{!! Former::text('font_size') {!! Former::text('font_size')
->type('number') ->type('number')
->min('0') ->min('0')
@ -171,6 +186,7 @@
{!! Former::text('primary_color') !!} {!! Former::text('primary_color') !!}
{!! Former::text('secondary_color') !!} {!! Former::text('secondary_color') !!}
{{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.large', 4) }}
{{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }} {{ Former::setOption('TwitterBootstrap3.labelWidths.small', 4) }}
@ -249,7 +265,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

@ -59,12 +59,13 @@
{!! Former::text('invoice_number_prefix') {!! Former::text('invoice_number_prefix')
->addGroupClass('invoice-prefix') ->addGroupClass('invoice-prefix')
->label(' ') !!} ->label(trans('texts.prefix')) !!}
{!! Former::text('invoice_number_pattern') {!! Former::text('invoice_number_pattern')
->appendIcon('question-sign') ->appendIcon('question-sign')
->addGroupClass('invoice-pattern') ->addGroupClass('invoice-pattern')
->label(' ') ->label(trans('texts.pattern'))
->addGroupClass('number-pattern') !!} ->addGroupClass('number-pattern') !!}
{!! Former::text('invoice_number_padding') !!}
{!! Former::text('invoice_number_counter') {!! Former::text('invoice_number_counter')
->label(trans('texts.counter')) ->label(trans('texts.counter'))
->help(trans('texts.invoice_number_help') . ' ' . ->help(trans('texts.invoice_number_help') . ' ' .
@ -84,12 +85,12 @@
{!! Former::text('quote_number_prefix') {!! Former::text('quote_number_prefix')
->addGroupClass('quote-prefix') ->addGroupClass('quote-prefix')
->label(' ') !!} ->label(trans('texts.prefix')) !!}
{!! Former::text('quote_number_pattern') {!! Former::text('quote_number_pattern')
->appendIcon('question-sign') ->appendIcon('question-sign')
->addGroupClass('quote-pattern') ->addGroupClass('quote-pattern')
->addGroupClass('number-pattern') ->addGroupClass('number-pattern')
->label(' ') !!} ->label(trans('texts.pattern')) !!}
{!! Former::text('quote_number_counter') {!! Former::text('quote_number_counter')
->label(trans('texts.counter')) ->label(trans('texts.counter'))
->addGroupClass('pad-checkbox') ->addGroupClass('pad-checkbox')
@ -264,7 +265,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

@ -0,0 +1,225 @@
@extends('header')
@section('content')
@parent
@include('accounts.nav', ['selected' => ACCOUNT_MANAGEMENT])
<div class="row">
<div class="col-md-12">
{!! Former::open('settings/change_plan')->addClass('change-plan') !!}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.plan_status') !!}</h3>
</div>
<div class="panel-body">
<div class="form-group">
<label class="col-sm-4 control-label">{{ trans('texts.plan') }}</label>
<div class="col-sm-8">
<p class="form-control-static">
@if ($planDetails && $planDetails['active'])
{{ trans('texts.plan_'.$planDetails['plan']) }}
@if ($planDetails['trial'])
({{ trans('texts.plan_trial') }})
@elseif ($planDetails['expires'])
({{ trans('texts.plan_term_'.$planDetails['term'].'ly') }})
@endif
@elseif(Utils::isNinjaProd())
{{ trans('texts.plan_free') }}
@else
{{ trans('texts.plan_free_self_hosted') }}
@endif
</p>
</div>
</div>
@if ($planDetails && $planDetails['active'])
<div class="form-group">
<label class="col-sm-4 control-label">
@if((!$account->company->pending_plan || $account->company->pending_plan == $planDetails['plan']) && $planDetails['expires'] && !$planDetails['trial'])
{{ trans('texts.renews') }}
@else
{{ trans('texts.expires') }}
@endif
</label>
<div class="col-sm-8">
<p class="form-control-static">
@if ($planDetails['expires'] === false)
{{ trans('texts.never') }}
@else
{{ Utils::dateToString($planDetails['expires']) }}
@endif
</p>
</div>
</div>
@if ($account->company->pending_plan)
<div class="form-group">
<label class="col-sm-4 control-label">{{ trans('texts.pending_change_to') }}</label>
<div class="col-sm-8">
<p class="form-control-static">
@if ($account->company->pending_plan == PLAN_FREE)
{{ trans('texts.plan_changes_to', [
'plan'=>trans('texts.plan_free'),
'date'=>Utils::dateToString($planDetails['expires'])
]) }}
@else
{{ trans('texts.plan_term_changes_to', [
'plan'=>trans('texts.plan_'.$account->company->pending_plan),
'term'=>trans('texts.plan_term_'.$account->company->pending_term.'ly'),
'date'=>Utils::dateToString($planDetails['expires'])
]) }}
@endif<br>
<a href="#" onclick="cancelPendingChange()">{{ trans('texts.cancel_plan_change') }}</a>
</p>
</div>
</div>
@endif
@if (Utils::isNinjaProd())
{!! Former::actions( Button::info(trans('texts.plan_change'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('edit'))) !!}
@endif
@else
@if ($planDetails)
<div class="form-group">
<label class="col-sm-4 control-label">
@if ($planDetails['trial'])
{{ trans('texts.trial_expired', ['plan'=>trans('texts.plan_'.$planDetails['plan'])]) }}
@else
{{ trans('texts.plan_expired', ['plan'=>trans('texts.plan_'.$planDetails['plan'])]) }}
@endif
</label>
<div class="col-sm-8">
<p class="form-control-static">
{{ Utils::dateToString($planDetails['expires']) }}
</p>
</div>
</div>
@endif
@if (Utils::isNinjaProd())
{!! 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
</div>
</div>
@if (Utils::isNinjaProd())
<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-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="changePlanModelLabel">
@if ($planDetails && $planDetails['active'])
{!! trans('texts.plan_change') !!}
@else
{!! trans('texts.plan_upgrade') !!}
@endif
</h4>
</div>
<div class="modal-body">
@if ($planDetails && $planDetails['active'])
{!! Former::select('plan')
->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE)
->addOption(trans('texts.plan_pro'), PLAN_PRO)
->addOption(trans('texts.plan_free'), PLAN_FREE)!!}
@else
{!! Former::select('plan')
->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE)
->addOption(trans('texts.plan_pro'), PLAN_PRO)!!}
@endif
{!! Former::select('plan_term')
->addOption(trans('texts.plan_term_yearly'), PLAN_TERM_YEARLY)
->addOption(trans('texts.plan_term_monthly'), PLAN_TERM_MONTHLY)!!}
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.go_back') }}</button>
@if ($planDetails && $planDetails['active'])
<button type="button" class="btn btn-primary" onclick="confirmChangePlan()">{{ trans('texts.plan_change') }}</button>
@else
<button type="button" class="btn btn-success" onclick="confirmChangePlan()">{{ trans('texts.plan_upgrade') }}</button>
@endif
</div>
</div>
</div>
</div>
@endif
{!! Former::close() !!}
{!! Former::open('settings/cancel_account')->addClass('cancel-account') !!}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{!! trans('texts.cancel_account') !!}</h3>
</div>
<div class="panel-body">
{!! Former::actions( Button::danger(trans('texts.cancel_account'))->large()->withAttributes(['onclick' => 'showConfirm()'])->appendIcon(Icon::create('trash'))) !!}
</div>
</div>
<div class="modal fade" id="confirmCancelModal" tabindex="-1" role="dialog" aria-labelledby="confirmCancelModalLabel" aria-hidden="true">
<div class="modal-dialog" style="min-width:150px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="confirmCancelModalLabel">{!! trans('texts.cancel_account') !!}</h4>
</div>
<div style="background-color: #fff; padding-left: 16px; padding-right: 16px">
&nbsp;<p>{{ trans('texts.cancel_account_message') }}</p>&nbsp;
&nbsp;<p>{!! Former::textarea('reason')->placeholder(trans('texts.reason_for_canceling'))->raw() !!}</p>&nbsp;
</div>
<div class="modal-footer" style="margin-top: 0px">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.go_back') }}</button>
<button type="button" class="btn btn-danger" onclick="confirmCancel()">{{ trans('texts.cancel_account') }}</button>
</div>
</div>
</div>
</div>
{!! Former::close() !!}
</div>
</div>
<script type="text/javascript">
function showChangePlan() {
$('#changePlanModel').modal('show');
}
function confirmChangePlan() {
$('form.change-plan').submit();
}
function showConfirm() {
$('#confirmCancelModal').modal('show');
}
function confirmCancel() {
$('form.cancel-account').submit();
}
@if ($account->company->pending_plan)
function cancelPendingChange(){
$('#plan').val('{{ $planDetails['plan'] }}')
$('#plan_term').val('{{ $planDetails['term'] }}')
confirmChangePlan();
return false;
}
@endif
jQuery(document).ready(function($){
function updatePlanModal() {
var plan = $('#plan').val();
$('#plan_term').closest('.form-group').toggle(plan!='free');
if(plan=='{{PLAN_PRO}}'){
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>PLAN_PRICE_PRO_MONTHLY])) !!});
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>PLAN_PRICE_PRO_YEARLY])) !!});
} else if(plan=='{{PLAN_ENTERPRISE}}') {
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY])) !!});
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>PLAN_PRICE_ENTERPRISE_YEARLY])) !!});
}
}
$('#plan_term, #plan').change(updatePlanModal);
updatePlanModal();
});
</script>
@stop

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

@ -6,7 +6,7 @@
<link href="{{ isset($account) ? $account->getFontsUrl('http') : '' }}" rel="stylesheet" type="text/css" /> <link href="{{ isset($account) ? $account->getFontsUrl('http') : '' }}" rel="stylesheet" type="text/css" />
<!--<![endif]--> <!--<![endif]-->
</head> </head>
<body style="min-height: 700px; color: #000000;{!! isset($account) ? $account->getBodyFontCss() : '' !!}font-size: 12px; -webkit-text-size-adjust: none; -ms-text-size-adjust: none; background: #F4F5F5; margin: 0; padding: 0;" <body style="color: #000000;{!! isset($account) ? $account->getBodyFontCss() : '' !!}font-size: 12px; -webkit-text-size-adjust: none; -ms-text-size-adjust: none; background: #F4F5F5; margin: 0; padding: 0;"
alink="#FF0000" link="#FF0000" bgcolor="#F4F5F5" text="#000000" yahoo="fix"> alink="#FF0000" link="#FF0000" bgcolor="#F4F5F5" text="#000000" yahoo="fix">
@yield('markup') @yield('markup')
@ -50,8 +50,8 @@
} }
</style> </style>
<div id="body_style" style="min-height: 700px;{!! isset($account) ? $account->getBodyFontCss() : '' !!};color: #2E2B2B; font-size: 16px; <div id="body_style" style="{!! isset($account) ? $account->getBodyFontCss() : '' !!};color: #2E2B2B; font-size: 16px;
background: #F4F5F5; padding: 0px 15px;"> background: #F4F5F5; padding: 0px 0px 15px;">
<table cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" width="580" align="center"> <table cellpadding="0" cellspacing="0" border="0" bgcolor="#FFFFFF" width="580" align="center">

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
@ -726,8 +726,8 @@
<center> <center>
<h2>{{ trans('texts.pro_plan_title') }}</h2> <h2>{{ trans('texts.pro_plan_title') }}</h2>
<img class="img-responsive price" alt="Only $50 Per Year" src="{{ asset('images/pro_plan/price.png') }}"/> <img class="img-responsive price" alt="Only $50 Per Year" src="{{ asset('images/pro_plan/price.png') }}"/>
@if (Auth::user()->isEligibleForTrial()) @if (Auth::user()->isEligibleForTrial(PLAN_PRO))
<a class="button" href="{{ URL::to('start_trial') }}">{{ trans('texts.trial_call_to_action') }}</a> <a class="button" href="{{ URL::to('start_trial/'.PLAN_PRO) }}">{{ trans('texts.trial_call_to_action') }}</a>
@else @else
<a class="button" href="#" onclick="submitProPlan()">{{ trans('texts.pro_plan_call_to_action') }}</a> <a class="button" href="#" onclick="submitProPlan()">{{ trans('texts.pro_plan_call_to_action') }}</a>
@endif @endif
@ -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">
@ -493,13 +493,7 @@
{!! Former::text('pdfupload') !!} {!! Former::text('pdfupload') !!}
</div> </div>
@if ($account->hasLargeFont()) @if (!Utils::hasFeature(FEATURE_MORE_INVOICE_DESIGNS) || \App\Models\InvoiceDesign::count() == COUNT_FREE_DESIGNS_SELF_HOST)
<label for="livePreview" class="control-label" style="padding-right:10px">
<input id="livePreview" type="checkbox"/> {{ trans('texts.live_preview') }}
</label>
@endif
@if (!Utils::isPro() || \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 +565,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 +620,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 +960,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 +1056,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});
@ -1097,8 +1095,8 @@
window.generatedPDF = false; window.generatedPDF = false;
function getPDFString(cb, force) { function getPDFString(cb, force) {
@if ($account->hasLargeFont()) @if (!$account->live_preview)
if (!$('#livePreview').is(':checked') && window.generatedPDF) { if (window.generatedPDF) {
return; return;
} }
@endif @endif
@ -1375,7 +1373,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 +1397,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