mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-09-19 16:01:34 +02:00
Working on new hosted pricing
This commit is contained in:
parent
28d8e53764
commit
986487b4c9
@ -76,7 +76,7 @@ script:
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance QuoteCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceDesignCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run acceptance OnlinePaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance OnlinePaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
|
||||
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Console\Commands;
|
||||
|
||||
use Utils;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Company;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
@ -43,7 +44,7 @@ class SendRenewalInvoices extends Command
|
||||
$this->mailer = $mailer;
|
||||
$this->accountRepo = $repo;
|
||||
}
|
||||
|
||||
|
||||
public function fire()
|
||||
{
|
||||
$this->info(date('Y-m-d').' Running SendRenewalInvoices...');
|
||||
@ -58,28 +59,36 @@ class SendRenewalInvoices extends Command
|
||||
if (!count($company->accounts)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$account = $company->accounts->sortBy('id')->first();
|
||||
$plan = $company->plan;
|
||||
$term = $company->plan_term;
|
||||
|
||||
$plan = [];
|
||||
$plan['plan'] = $company->plan;
|
||||
$plan['term'] = $company->plan_term;
|
||||
$plan['num_users'] = $company->num_users;
|
||||
$plan['price'] = min($company->plan_price, Utils::getPlanPrice($plan));
|
||||
|
||||
if ($company->pending_plan) {
|
||||
$plan = $company->pending_plan;
|
||||
$term = $company->pending_term;
|
||||
$plan['plan'] = $company->pending_plan;
|
||||
$plan['term'] = $company->pending_term;
|
||||
$plan['num_users'] = $company->pending_num_users;
|
||||
$plan['price'] = min($company->pending_plan_price, Utils::getPlanPrice($plan));
|
||||
}
|
||||
|
||||
if ($plan == PLAN_FREE || !$plan || !$term ){
|
||||
|
||||
if ($plan['plan'] == PLAN_FREE || !$plan['plan'] || !$plan['term'] || !$plan['price']){
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$client = $this->accountRepo->getNinjaClient($account);
|
||||
$invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, $term);
|
||||
$invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, 0, false);
|
||||
|
||||
// set the due date to 10 days from now
|
||||
$invoice = $invitation->invoice;
|
||||
$invoice->due_date = date('Y-m-d', strtotime('+ 10 days'));
|
||||
$invoice->save();
|
||||
|
||||
$term = $plan['term'];
|
||||
$plan = $plan['plan'];
|
||||
|
||||
if ($term == PLAN_TERM_YEARLY) {
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
$this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}");
|
||||
|
@ -154,20 +154,6 @@ class AccountController extends BaseController
|
||||
return Redirect::to($redirectTo)->with('sign_up', Input::get('sign_up'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function enableProPlan()
|
||||
{
|
||||
if (Auth::user()->isPro() && ! Auth::user()->isTrial()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$invitation = $this->accountRepo->enablePlan();
|
||||
|
||||
return $invitation->invitation_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
@ -201,7 +187,8 @@ class AccountController extends BaseController
|
||||
'plan' => $plan,
|
||||
'term' => $term
|
||||
];
|
||||
} elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY) {
|
||||
} elseif ($planDetails['term'] == PLAN_TERM_MONTHLY && $term == PLAN_TERM_YEARLY
|
||||
|| $planDetails['num_users'] != Input::get('num_users')) {
|
||||
$new_plan = [
|
||||
'plan' => $plan,
|
||||
'term' => $term,
|
||||
@ -260,8 +247,7 @@ class AccountController extends BaseController
|
||||
$days_total = $planDetails['paid']->diff($planDetails['expires'])->days;
|
||||
|
||||
$percent_used = $days_used / $days_total;
|
||||
$old_plan_price = Account::$plan_prices[$planDetails['plan']][$planDetails['term']];
|
||||
$credit = $old_plan_price * (1 - $percent_used);
|
||||
$credit = $planDetails['plan_price'] * (1 - $percent_used);
|
||||
}
|
||||
} else {
|
||||
$new_plan = [
|
||||
@ -271,15 +257,23 @@ class AccountController extends BaseController
|
||||
}
|
||||
|
||||
if (!empty($pending_change) && empty($new_plan)) {
|
||||
$pending_change['num_users'] = Input::get('num_users');
|
||||
$account->company->pending_plan = $pending_change['plan'];
|
||||
$account->company->pending_term = $pending_change['term'];
|
||||
$account->company->pending_num_users = $pending_change['num_users'];
|
||||
$account->company->pending_plan_price = Utils::getPlanPrice($pending_change);
|
||||
$account->company->save();
|
||||
|
||||
Session::flash('message', trans('texts.updated_plan'));
|
||||
}
|
||||
|
||||
if (!empty($new_plan) && $new_plan['plan'] != PLAN_FREE) {
|
||||
$invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly));
|
||||
$new_plan['num_users'] = 1;
|
||||
if ($new_plan['plan'] == PLAN_ENTERPRISE) {
|
||||
$new_plan['num_users'] = Input::get('num_users');
|
||||
}
|
||||
$new_plan['price'] = Utils::getPlanPrice($new_plan);
|
||||
$invitation = $this->accountRepo->enablePlan($new_plan, $credit, !empty($pending_monthly));
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
}
|
||||
|
||||
@ -483,7 +477,8 @@ class AccountController extends BaseController
|
||||
private function showBankAccounts()
|
||||
{
|
||||
return View::make('accounts.banks', [
|
||||
'title' => trans('texts.bank_accounts')
|
||||
'title' => trans('texts.bank_accounts'),
|
||||
'advanced' => ! Auth::user()->hasFeature(FEATURE_EXPENSES),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -41,10 +41,6 @@ class QuoteController extends BaseController
|
||||
|
||||
public function index()
|
||||
{
|
||||
if (!Utils::hasFeature(FEATURE_QUOTES)) {
|
||||
return Redirect::to('/invoices/create');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'title' => trans('texts.quotes'),
|
||||
'entityType' => ENTITY_QUOTE,
|
||||
|
@ -93,21 +93,19 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
if (!Auth::user()->registered) {
|
||||
if ( ! Auth::user()->registered) {
|
||||
Session::flash('error', trans('texts.register_to_add_user'));
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
}
|
||||
if (!Auth::user()->confirmed) {
|
||||
|
||||
if ( ! Auth::user()->confirmed) {
|
||||
Session::flash('error', trans('texts.confirmation_required'));
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
}
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
$count = User::where('account_id', '=', Auth::user()->account_id)->count();
|
||||
if ($count >= MAX_NUM_USERS) {
|
||||
Session::flash('error', trans('texts.limit_users', ['limit' => MAX_NUM_USERS]));
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
}
|
||||
if (Utils::isNinja() && ! Auth::user()->caddAddUsers()) {
|
||||
Session::flash('error', trans('texts.max_users_reached'));
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
}
|
||||
|
||||
$data = [
|
||||
@ -132,6 +130,11 @@ class UserController extends BaseController
|
||||
if ($action === 'archive') {
|
||||
$user->delete();
|
||||
} else {
|
||||
if ( ! Auth::user()->caddAddUsers()) {
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT)
|
||||
->with('error', trans('texts.max_users_reached'));
|
||||
}
|
||||
|
||||
$user->restore();
|
||||
}
|
||||
|
||||
@ -140,19 +143,6 @@ class UserController extends BaseController
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
}
|
||||
|
||||
public function restoreUser($userPublicId)
|
||||
{
|
||||
$user = User::where('account_id', '=', Auth::user()->account_id)
|
||||
->where('public_id', '=', $userPublicId)
|
||||
->withTrashed()->firstOrFail();
|
||||
|
||||
$user->restore();
|
||||
|
||||
Session::flash('message', trans('texts.restored_user'));
|
||||
|
||||
return Redirect::to('settings/' . ACCOUNT_USER_MANAGEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores new account
|
||||
*
|
||||
@ -257,7 +247,7 @@ class UserController extends BaseController
|
||||
$token = Password::getRepository()->create($user);
|
||||
|
||||
return Redirect::to("/password/reset/{$token}");
|
||||
} else {
|
||||
} else {
|
||||
if (Auth::check()) {
|
||||
if (Session::has(REQUESTED_PRO_PLAN)) {
|
||||
Session::forget(REQUESTED_PRO_PLAN);
|
||||
|
@ -214,7 +214,6 @@ Route::group([
|
||||
Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation');
|
||||
Route::get('start_trial/{plan}', 'AccountController@startTrial')
|
||||
->where(['plan'=>'pro']);
|
||||
Route::get('restore_user/{user_id}', 'UserController@restoreUser');
|
||||
Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
|
||||
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
|
||||
Route::get('/manage_companies', 'UserController@manageCompanies');
|
||||
@ -245,7 +244,6 @@ Route::group([
|
||||
|
||||
Route::post('user/setTheme', 'UserController@setTheme');
|
||||
Route::post('remove_logo', 'AccountController@removeLogo');
|
||||
Route::post('account/go_pro', 'AccountController@enableProPlan');
|
||||
|
||||
Route::post('/export', 'ExportController@doExport');
|
||||
Route::post('/import', 'ImportController@doImport');
|
||||
@ -466,7 +464,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35);
|
||||
define('ACTIVITY_TYPE_DELETE_EXPENSE', 36);
|
||||
define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37);
|
||||
|
||||
|
||||
// tasks
|
||||
define('ACTIVITY_TYPE_CREATE_TASK', 42);
|
||||
define('ACTIVITY_TYPE_UPDATE_TASK', 43);
|
||||
@ -638,10 +636,10 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74');
|
||||
define('SELF_HOST_AFFILIATE_KEY', '8S69AD');
|
||||
|
||||
define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 5));
|
||||
define('PLAN_PRICE_PRO_YEARLY', env('PLAN_PRICE_PRO_YEARLY', 50));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY', env('PLAN_PRICE_ENTERPRISE_MONTHLY', 10));
|
||||
define('PLAN_PRICE_ENTERPRISE_YEARLY', env('PLAN_PRICE_ENTERPRISE_YEARLY', 100));
|
||||
define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 12));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 18));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 26));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 38));
|
||||
define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20));
|
||||
define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10));
|
||||
|
||||
@ -751,6 +749,8 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('FEATURE_PDF_ATTACHMENT', 'pdf_attachment');
|
||||
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
|
||||
define('FEATURE_QUOTES', 'quotes');
|
||||
define('FEATURE_TASKS', 'tasks');
|
||||
define('FEATURE_EXPENSES', 'expenses');
|
||||
define('FEATURE_REPORTS', 'reports');
|
||||
define('FEATURE_API', 'api');
|
||||
define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password');
|
||||
@ -771,6 +771,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
|
||||
// Pro users who started paying on or before this date will be able to manage users
|
||||
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-06-04');
|
||||
define('EXTRAS_GRANDFATHER_COMPANY_ID', 0);
|
||||
|
||||
// WePay
|
||||
define('WEPAY_PRODUCTION', 'production');
|
||||
|
@ -215,6 +215,46 @@ class Utils
|
||||
}
|
||||
}
|
||||
|
||||
public static function getPlanPrice($plan)
|
||||
{
|
||||
$term = $plan['term'];
|
||||
$numUsers = $plan['num_users'];
|
||||
$plan = $plan['plan'];
|
||||
|
||||
if ($plan == PLAN_FREE) {
|
||||
$price = 0;
|
||||
} elseif ($plan == PLAN_PRO) {
|
||||
$price = PLAN_PRICE_PRO_MONTHLY;
|
||||
} elseif ($plan == PLAN_ENTERPRISE) {
|
||||
if ($numUsers <= 2) {
|
||||
$price = PLAN_PRICE_ENTERPRISE_MONTHLY_2;
|
||||
} elseif ($numUsers <= 5) {
|
||||
$price = PLAN_PRICE_ENTERPRISE_MONTHLY_5;
|
||||
} elseif ($numUsers <= 10) {
|
||||
$price = PLAN_PRICE_ENTERPRISE_MONTHLY_10;
|
||||
} else {
|
||||
static::fatalError('Invalid number of users: ' . $numUsers);
|
||||
}
|
||||
}
|
||||
|
||||
if ($term == PLAN_TERM_YEARLY) {
|
||||
$price = $price * 10;
|
||||
}
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
public static function getMinNumUsers($max)
|
||||
{
|
||||
if ($max <= 2) {
|
||||
return 1;
|
||||
} elseif ($max <= 5) {
|
||||
return 3;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
public static function basePath()
|
||||
{
|
||||
return substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1);
|
||||
|
@ -20,20 +20,6 @@ class Account extends Eloquent
|
||||
use PresentableTrait;
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $plan_prices = [
|
||||
PLAN_PRO => [
|
||||
PLAN_TERM_MONTHLY => PLAN_PRICE_PRO_MONTHLY,
|
||||
PLAN_TERM_YEARLY => PLAN_PRICE_PRO_YEARLY,
|
||||
],
|
||||
PLAN_ENTERPRISE => [
|
||||
PLAN_TERM_MONTHLY => PLAN_PRICE_ENTERPRISE_MONTHLY,
|
||||
PLAN_TERM_YEARLY => PLAN_PRICE_ENTERPRISE_YEARLY,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@ -90,7 +76,6 @@ class Account extends Eloquent
|
||||
ACCOUNT_USER_DETAILS,
|
||||
ACCOUNT_LOCALIZATION,
|
||||
ACCOUNT_PAYMENTS,
|
||||
ACCOUNT_BANKS,
|
||||
ACCOUNT_TAX_RATES,
|
||||
ACCOUNT_PRODUCTS,
|
||||
ACCOUNT_NOTIFICATIONS,
|
||||
@ -106,6 +91,7 @@ class Account extends Eloquent
|
||||
ACCOUNT_INVOICE_DESIGN,
|
||||
ACCOUNT_EMAIL_SETTINGS,
|
||||
ACCOUNT_TEMPLATES_AND_REMINDERS,
|
||||
ACCOUNT_BANKS,
|
||||
ACCOUNT_CLIENT_PORTAL,
|
||||
ACCOUNT_CHARTS_AND_REPORTS,
|
||||
ACCOUNT_DATA_VISUALIZATIONS,
|
||||
@ -1189,6 +1175,10 @@ class Account extends Eloquent
|
||||
case FEATURE_CUSTOM_URL:
|
||||
return $selfHost || !empty($planDetails);
|
||||
|
||||
case FEATURE_TASKS:
|
||||
case FEATURE_EXPENSES:
|
||||
return $selfHost || !empty($planDetails) || $planDetails['company_id'] < EXTRAS_GRANDFATHER_COMPANY_ID;
|
||||
|
||||
// Pro; No trial allowed, unless they're trialing enterprise with an active pro plan
|
||||
case FEATURE_MORE_CLIENTS:
|
||||
return $selfHost || !empty($planDetails) && (!$planDetails['trial'] || !empty($this->getPlanDetails(false, false)));
|
||||
@ -1272,6 +1262,7 @@ class Account extends Eloquent
|
||||
}
|
||||
|
||||
$plan = $this->company->plan;
|
||||
$price = $this->company->plan_price;
|
||||
$trial_plan = $this->company->trial_plan;
|
||||
|
||||
if(!$plan && (!$trial_plan || !$include_trial)) {
|
||||
@ -1335,6 +1326,9 @@ class Account extends Eloquent
|
||||
|
||||
if ($use_plan) {
|
||||
return [
|
||||
'company_id' => $this->company->id,
|
||||
'num_users' => $this->company->num_users,
|
||||
'plan_price' => $price,
|
||||
'trial' => false,
|
||||
'plan' => $plan,
|
||||
'started' => DateTime::createFromFormat('Y-m-d', $this->company->plan_started),
|
||||
@ -1345,6 +1339,9 @@ class Account extends Eloquent
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'company_id' => $this->company->id,
|
||||
'num_users' => 1,
|
||||
'plan_price' => 0,
|
||||
'trial' => true,
|
||||
'plan' => $trial_plan,
|
||||
'started' => $trial_started,
|
||||
|
@ -430,6 +430,23 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
|
||||
public function filterId() {
|
||||
return $this->hasPermission('view_all') ? false : $this->id;
|
||||
}
|
||||
|
||||
|
||||
public function caddAddUsers() {
|
||||
if ( ! $this->hasFeature(FEATURE_USERS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$account = $this->account;
|
||||
$company = $account->company;
|
||||
|
||||
$numUsers = 1;
|
||||
foreach ($company->accounts as $account) {
|
||||
$numUsers += $account->users->count() - 1;
|
||||
}
|
||||
|
||||
return $numUsers < $company->num_users;
|
||||
}
|
||||
}
|
||||
|
||||
User::updating(function ($user) {
|
||||
|
@ -576,6 +576,12 @@ class BasePaymentDriver
|
||||
if (1 == preg_match('/^Plan - (.+) \((.+)\)$/', $invoice_item->product_key, $matches)) {
|
||||
$plan = strtolower($matches[1]);
|
||||
$term = strtolower($matches[2]);
|
||||
if ($plan == PLAN_ENTERPRISE) {
|
||||
preg_match('/###[\d] [\w]* (\d*)/', $invoice_item->notes, $matches);
|
||||
$numUsers = $matches[1];
|
||||
} else {
|
||||
$numUsers = 1;
|
||||
}
|
||||
} elseif ($invoice_item->product_key == 'Pending Monthly') {
|
||||
$pending_monthly = true;
|
||||
}
|
||||
@ -607,6 +613,8 @@ class BasePaymentDriver
|
||||
$account->company->payment_id = $payment->id;
|
||||
$account->company->plan = $plan;
|
||||
$account->company->plan_term = $term;
|
||||
$account->company->plan_price = $payment->amount;
|
||||
$account->company->num_users = $numUsers;
|
||||
$account->company->plan_expires = DateTime::createFromFormat('Y-m-d', $account->company->plan_paid)
|
||||
->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d');
|
||||
|
||||
|
@ -228,23 +228,26 @@ class AccountRepository
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function enablePlan($plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false)
|
||||
public function enablePlan($plan, $credit = 0, $pending_monthly = false)
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$client = $this->getNinjaClient($account);
|
||||
$invitation = $this->createNinjaInvoice($client, $account, $plan, $term, $credit, $pending_monthly);
|
||||
$invitation = $this->createNinjaInvoice($client, $account, $plan, $credit, $pending_monthly);
|
||||
|
||||
return $invitation;
|
||||
}
|
||||
|
||||
public function createNinjaInvoice($client, $clientAccount, $plan = PLAN_PRO, $term = PLAN_TERM_MONTHLY, $credit = 0, $pending_monthly = false)
|
||||
public function createNinjaInvoice($client, $clientAccount, $plan, $credit = 0, $pending_monthly = false)
|
||||
{
|
||||
$term = $plan['term'];
|
||||
$plan_cost = $plan['price'];
|
||||
$num_users = $plan['num_users'];
|
||||
$plan = $plan['plan'];
|
||||
|
||||
if ($credit < 0) {
|
||||
$credit = 0;
|
||||
}
|
||||
|
||||
$plan_cost = Account::$plan_prices[$plan][$term];
|
||||
|
||||
$account = $this->getNinjaAccount();
|
||||
$lastInvoice = Invoice::withTrashed()->whereAccountId($account->id)->orderBy('public_id', 'DESC')->first();
|
||||
$publicId = $lastInvoice ? ($lastInvoice->public_id + 1) : 1;
|
||||
@ -272,6 +275,11 @@ class AccountRepository
|
||||
$item->cost = $plan_cost;
|
||||
$item->notes = trans("texts.{$plan}_plan_{$term}_description");
|
||||
|
||||
if ($plan == PLAN_ENTERPRISE) {
|
||||
$min = Utils::getMinNumUsers($num_users);
|
||||
$item->notes .= "\n\n###" . trans('texts.min_to_max_users', ['min' => $min, 'max' => $num_users]);
|
||||
}
|
||||
|
||||
// Don't change this without updating the regex in PaymentService->createPayment()
|
||||
$item->product_key = 'Plan - '.ucfirst($plan).' ('.ucfirst($term).')';
|
||||
$invoice->invoice_items()->save($item);
|
||||
|
@ -2,4 +2,20 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
class ExpensePolicy extends EntityPolicy {}
|
||||
use App\Models\User;
|
||||
|
||||
class ExpensePolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function create(User $user) {
|
||||
if ( ! parent::create($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->hasFeature(FEATURE_EXPENSES);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
class InvoicePolicy extends EntityPolicy {}
|
||||
class InvoicePolicy extends EntityPolicy {}
|
||||
|
21
app/Policies/QuotePolicy.php
Normal file
21
app/Policies/QuotePolicy.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
class QuotePolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function create(User $user) {
|
||||
if ( ! parent::create($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->hasFeature(FEATURE_QUOTES);
|
||||
}
|
||||
|
||||
}
|
@ -2,4 +2,20 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
class TaskPolicy extends EntityPolicy {}
|
||||
use App\Models\User;
|
||||
|
||||
class TaskPolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function create(User $user) {
|
||||
if ( ! parent::create($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->hasFeature(FEATURE_TASKS);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,4 +2,20 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
class VendorPolicy extends EntityPolicy {}
|
||||
use App\Models\User;
|
||||
|
||||
class VendorPolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function create(User $user) {
|
||||
if ( ! parent::create($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->hasFeature(FEATURE_EXPENSES);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,17 +60,17 @@ class AppServiceProvider extends ServiceProvider
|
||||
|
||||
$items = [];
|
||||
|
||||
if($user->can('create', $type))$items[] = '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
|
||||
if ($user->can('create', $type)) {
|
||||
$items[] = '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
|
||||
}
|
||||
|
||||
if ($type == ENTITY_INVOICE) {
|
||||
if(!empty($items))$items[] = '<li class="divider"></li>';
|
||||
$items[] = '<li><a href="'.URL::to('recurring_invoices').'">'.trans('texts.recurring_invoices').'</a></li>';
|
||||
if($user->can('create', ENTITY_INVOICE))$items[] = '<li><a href="'.URL::to('recurring_invoices/create').'">'.trans('texts.new_recurring_invoice').'</a></li>';
|
||||
if ($user->hasFeature(FEATURE_QUOTES)) {
|
||||
$items[] = '<li class="divider"></li>';
|
||||
$items[] = '<li><a href="'.URL::to('quotes').'">'.trans('texts.quotes').'</a></li>';
|
||||
if($user->can('create', ENTITY_INVOICE))$items[] = '<li><a href="'.URL::to('quotes/create').'">'.trans('texts.new_quote').'</a></li>';
|
||||
}
|
||||
$items[] = '<li class="divider"></li>';
|
||||
$items[] = '<li><a href="'.URL::to('quotes').'">'.trans('texts.quotes').'</a></li>';
|
||||
if($user->can('create', ENTITY_QUOTE))$items[] = '<li><a href="'.URL::to('quotes/create').'">'.trans('texts.new_quote').'</a></li>';
|
||||
} else if ($type == ENTITY_CLIENT) {
|
||||
if(!empty($items))$items[] = '<li class="divider"></li>';
|
||||
$items[] = '<li><a href="'.URL::to('credits').'">'.trans('texts.credits').'</a></li>';
|
||||
|
@ -2026,7 +2026,8 @@ $LANG = array(
|
||||
'restore_expense_category' => 'Restore expense category',
|
||||
'restored_expense_category' => 'Successfully restored expense category',
|
||||
'apply_taxes' => 'Apply taxes',
|
||||
|
||||
'min_to_max_users' => ':min to :max users',
|
||||
'max_users_reached' => 'The maximum number of users has been reached.'
|
||||
|
||||
);
|
||||
|
||||
|
@ -161,39 +161,41 @@
|
||||
|
||||
<p/> <p/>
|
||||
|
||||
{!! Former::actions(
|
||||
count(Cache::get('banks')) > 0 ?
|
||||
Button::normal(trans('texts.cancel'))
|
||||
@if (Auth::user()->hasFeature(FEATURE_EXPENSES))
|
||||
{!! Former::actions(
|
||||
count(Cache::get('banks')) > 0 ?
|
||||
Button::normal(trans('texts.cancel'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'visible: !importResults()',
|
||||
])
|
||||
->large()
|
||||
->asLinkTo(URL::to('/settings/bank_accounts'))
|
||||
->appendIcon(Icon::create('remove-circle')) : false,
|
||||
Button::success(trans('texts.validate'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'visible: !importResults()',
|
||||
'data-bind' => 'css: {disabled: disableValidate}, visible: page() == "login"',
|
||||
'onclick' => 'validate()'
|
||||
])
|
||||
->large()
|
||||
->asLinkTo(URL::to('/settings/bank_accounts'))
|
||||
->appendIcon(Icon::create('remove-circle')) : false,
|
||||
Button::success(trans('texts.validate'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'css: {disabled: disableValidate}, visible: page() == "login"',
|
||||
'onclick' => 'validate()'
|
||||
])
|
||||
->large()
|
||||
->appendIcon(Icon::create('lock')),
|
||||
Button::success(trans('texts.save'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'css: {disabled: disableSave}, visible: page() == "setup"',
|
||||
'style' => 'display:none',
|
||||
'onclick' => 'save()'
|
||||
])
|
||||
->large()
|
||||
->appendIcon(Icon::create('floppy-disk')) ,
|
||||
Button::success(trans('texts.import'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'css: {disabled: disableSaveExpenses}, visible: page() == "import"',
|
||||
'style' => 'display:none',
|
||||
'onclick' => 'saveExpenses()'
|
||||
])
|
||||
->large()
|
||||
->appendIcon(Icon::create('floppy-disk'))) !!}
|
||||
|
||||
->appendIcon(Icon::create('lock')),
|
||||
Button::success(trans('texts.save'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'css: {disabled: disableSave}, visible: page() == "setup"',
|
||||
'style' => 'display:none',
|
||||
'onclick' => 'save()'
|
||||
])
|
||||
->large()
|
||||
->appendIcon(Icon::create('floppy-disk')) ,
|
||||
Button::success(trans('texts.import'))
|
||||
->withAttributes([
|
||||
'data-bind' => 'css: {disabled: disableSaveExpenses}, visible: page() == "import"',
|
||||
'style' => 'display:none',
|
||||
'onclick' => 'saveExpenses()'
|
||||
])
|
||||
->large()
|
||||
->appendIcon(Icon::create('floppy-disk'))) !!}
|
||||
@endif
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -4,14 +4,16 @@
|
||||
@parent
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_BANKS])
|
||||
|
||||
<div class="pull-right">
|
||||
{!! Button::normal(trans('texts.import_ofx'))
|
||||
->asLinkTo(URL::to('/bank_accounts/import_ofx'))
|
||||
->appendIcon(Icon::create('open')) !!}
|
||||
{!! Button::primary(trans('texts.add_bank_account'))
|
||||
->asLinkTo(URL::to('/bank_accounts/create'))
|
||||
->appendIcon(Icon::create('plus-sign')) !!}
|
||||
</div>
|
||||
@if (Auth::user()->hasFeature(FEATURE_EXPENSES))
|
||||
<div class="pull-right">
|
||||
{!! Button::normal(trans('texts.import_ofx'))
|
||||
->asLinkTo(URL::to('/bank_accounts/import_ofx'))
|
||||
->appendIcon(Icon::create('open')) !!}
|
||||
{!! Button::primary(trans('texts.add_bank_account'))
|
||||
->asLinkTo(URL::to('/bank_accounts/create'))
|
||||
->appendIcon(Icon::create('plus-sign')) !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include('partials.bulk_form', ['entityType' => ENTITY_BANK_ACCOUNT])
|
||||
|
||||
|
@ -24,6 +24,9 @@
|
||||
@elseif ($planDetails['expires'])
|
||||
({{ trans('texts.plan_term_'.$planDetails['term'].'ly') }})
|
||||
@endif
|
||||
@if ($planDetails['plan'] == PLAN_ENTERPRISE)
|
||||
{{ trans('texts.min_to_max_users', ['min' => Utils::getMinNumUsers($planDetails['num_users']), 'max' => $planDetails['num_users']])}}
|
||||
@endif
|
||||
@elseif(Utils::isNinjaProd())
|
||||
{{ trans('texts.plan_free') }}
|
||||
@else
|
||||
@ -116,20 +119,33 @@
|
||||
</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)!!}
|
||||
{!! Former::select('plan')
|
||||
->onchange('onPlanChange()')
|
||||
->addOption(trans('texts.plan_free'), PLAN_FREE)
|
||||
->addOption(trans('texts.plan_pro'), PLAN_PRO)
|
||||
->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE) !!}
|
||||
@else
|
||||
{!! Former::select('plan')
|
||||
->addOption(trans('texts.plan_pro'), PLAN_PRO)
|
||||
->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE) !!}
|
||||
{!! Former::select('plan')
|
||||
->onchange('onPlanChange()')
|
||||
->addOption(trans('texts.plan_pro'), PLAN_PRO)
|
||||
->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE) !!}
|
||||
@endif
|
||||
|
||||
<div id="numUsersDiv">
|
||||
{!! Former::select('num_users')
|
||||
->label(trans('texts.users'))
|
||||
->addOption('1 to 2', 2)
|
||||
->addOption('3 to 5', 5)
|
||||
->addOption('6 to 10', 10) !!}
|
||||
</div>
|
||||
|
||||
{!! Former::select('plan_term')
|
||||
->addOption(trans('texts.plan_term_monthly'), PLAN_TERM_MONTHLY)
|
||||
->addOption(trans('texts.plan_term_yearly'), PLAN_TERM_YEARLY)
|
||||
->inlineHelp(trans('texts.enterprise_plan_features', ['link' => link_to(NINJA_WEB_URL . '/plans-pricing', trans('texts.click_here'), ['target' => '_blank'])])) !!}
|
||||
|
||||
</div>
|
||||
<div class="modal-footer" style="margin-top: 0px">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.go_back') }}</button>
|
||||
@ -197,30 +213,48 @@
|
||||
$('form.cancel-account').submit();
|
||||
}
|
||||
|
||||
function onPlanChange() {
|
||||
if ($('#plan').val() == '{{ PLAN_ENTERPRISE }}') {
|
||||
$('#numUsersDiv').show();
|
||||
} else {
|
||||
$('#numUsersDiv').hide();
|
||||
}
|
||||
}
|
||||
|
||||
@if ($account->company->pending_plan)
|
||||
function cancelPendingChange(){
|
||||
$('#plan').val('{{ $planDetails['plan'] }}')
|
||||
$('#plan_term').val('{{ $planDetails['term'] }}')
|
||||
confirmChangePlan();
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
var numUsers = $('#num_users').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])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>PLAN_PRICE_PRO_MONTHLY * 10])) !!});
|
||||
} 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])) !!});
|
||||
if (numUsers == 2) {
|
||||
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY_2])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY_2 * 10])) !!});
|
||||
} else if (numUsers == 5) {
|
||||
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY_5])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY_5 * 10])) !!});
|
||||
} else {
|
||||
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY_10])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>PLAN_PRICE_ENTERPRISE_MONTHLY_10 * 10])) !!});
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#plan_term, #plan').change(updatePlanModal);
|
||||
$('#plan_term, #plan, #num_users').change(updatePlanModal);
|
||||
updatePlanModal();
|
||||
onPlanChange();
|
||||
|
||||
if(window.location.hash) {
|
||||
var hash = window.location.hash;
|
||||
|
@ -5,11 +5,13 @@
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_USER_MANAGEMENT, 'advanced' => true])
|
||||
|
||||
@if (Utils::hasFeature(FEATURE_USERS))
|
||||
<div class="pull-right">
|
||||
{!! Button::primary(trans('texts.add_user'))->asLinkTo(URL::to('/users/create'))->appendIcon(Icon::create('plus-sign')) !!}
|
||||
</div>
|
||||
@if (Auth::user()->caddAddUsers())
|
||||
<div class="pull-right">
|
||||
{!! Button::primary(trans('texts.add_user'))->asLinkTo(URL::to('/users/create'))->appendIcon(Icon::create('plus-sign')) !!}
|
||||
</div>
|
||||
@endif
|
||||
@elseif (Utils::isTrial())
|
||||
<div class="alert alert-warning">{!! trans('texts.add_users_not_supported') !!}</div>
|
||||
<div class="alert alert-warning">{!! trans('texts.add_users_not_supported') !!}</div>
|
||||
@endif
|
||||
|
||||
<label for="trashed" style="font-weight:normal; margin-left: 10px;">
|
||||
@ -34,7 +36,7 @@
|
||||
->render('datatable') !!}
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
window.onDatatableReady = actionListHandler;
|
||||
|
||||
function setTrashVisible() {
|
||||
|
@ -173,16 +173,18 @@
|
||||
->appendIcon(Icon::create('remove-circle'))
|
||||
->large() !!}
|
||||
|
||||
{!! Button::success(trans('texts.save'))
|
||||
->appendIcon(Icon::create('floppy-disk'))
|
||||
->large()
|
||||
->submit() !!}
|
||||
@if (Auth::user()->hasFeature(FEATURE_EXPENSES))
|
||||
{!! Button::success(trans('texts.save'))
|
||||
->appendIcon(Icon::create('floppy-disk'))
|
||||
->large()
|
||||
->submit() !!}
|
||||
|
||||
@if ($expense)
|
||||
{!! DropdownButton::normal(trans('texts.more_actions'))
|
||||
->withContents($actions)
|
||||
->large()
|
||||
->dropup() !!}
|
||||
@if ($expense)
|
||||
{!! DropdownButton::normal(trans('texts.more_actions'))
|
||||
->withContents($actions)
|
||||
->large()
|
||||
->dropup() !!}
|
||||
@endif
|
||||
@endif
|
||||
</center>
|
||||
|
||||
|
@ -303,6 +303,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
/* Set the defaults for Bootstrap datepicker */
|
||||
$.extend(true, $.fn.datepicker.defaults, {
|
||||
weekStart: {{ Session::get('start_of_week') }}
|
||||
});
|
||||
|
||||
if (isStorageSupported()) {
|
||||
@if (Auth::check() && !Auth::user()->registered)
|
||||
localStorage.setItem('guest_key', '{{ Auth::user()->password }}');
|
||||
|
@ -496,6 +496,7 @@ function InvoiceModel(data) {
|
||||
for (var key in taxes) {
|
||||
if (taxes.hasOwnProperty(key)) {
|
||||
total += taxes[key].amount;
|
||||
total = roundToTwo(total);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,11 +93,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
/* Set the defaults for Bootstrap datepicker */
|
||||
$.extend(true, $.fn.datepicker.defaults, {
|
||||
weekStart: {{ Session::get('start_of_week') }}
|
||||
});
|
||||
|
||||
/* This causes problems with some languages. ie, fr_CA
|
||||
var appLocale = '{{App::getLocale()}}';
|
||||
*/
|
||||
|
@ -38,7 +38,7 @@
|
||||
{!! Former::text('action') !!}
|
||||
{!! Former::text('time_log') !!}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
@ -51,10 +51,10 @@
|
||||
@if ($task)
|
||||
|
||||
<div class="form-group simple-time" id="editDetailsLink">
|
||||
<label for="simple-time" class="control-label col-lg-4 col-sm-4">
|
||||
<label for="simple-time" class="control-label col-lg-4 col-sm-4">
|
||||
</label>
|
||||
<div class="col-lg-8 col-sm-8" style="padding-top: 10px">
|
||||
<p>{{ $task->getStartTime() }} -
|
||||
<p>{{ $task->getStartTime() }} -
|
||||
@if (Auth::user()->account->timezone_id)
|
||||
{{ $timezone }}
|
||||
@else
|
||||
@ -73,7 +73,7 @@
|
||||
</div>
|
||||
|
||||
@if ($task->is_running)
|
||||
<center>
|
||||
<center>
|
||||
<div id="duration-text" style="font-size: 36px; font-weight: 300; padding: 30px 0 20px 0"/>
|
||||
</center>
|
||||
@endif
|
||||
@ -96,20 +96,20 @@
|
||||
<tr data-bind="event: { mouseover: showActions, mouseout: hideActions }">
|
||||
<td style="padding: 0px 12px 12px 0 !important">
|
||||
<div data-bind="css: { 'has-error': !isStartValid() }">
|
||||
<input type="text" data-bind="dateTimePicker: startTime.pretty, event:{ change: $root.refresh }"
|
||||
<input type="text" data-bind="dateTimePicker: startTime.pretty, event:{ change: $root.refresh }"
|
||||
class="form-control time-input" placeholder="{{ trans('texts.start_time') }}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 0px 12px 12px 0 !important">
|
||||
<div data-bind="css: { 'has-error': !isEndValid() }">
|
||||
<input type="text" data-bind="dateTimePicker: endTime.pretty, event:{ change: $root.refresh }"
|
||||
<input type="text" data-bind="dateTimePicker: endTime.pretty, event:{ change: $root.refresh }"
|
||||
class="form-control time-input" placeholder="{{ trans('texts.end_time') }}"/>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width:100px">
|
||||
<td style="width:100px">
|
||||
<div data-bind="text: duration.pretty, visible: !isEmpty()"></div>
|
||||
<a href="#" data-bind="click: function() { setNow(), $root.refresh() }, visible: isEmpty()">{{ trans('texts.set_now') }}</a>
|
||||
</td>
|
||||
</td>
|
||||
<td style="width:30px" class="td-icon">
|
||||
<i style="width:12px;cursor:pointer" data-bind="click: $root.removeItem, visible: actionsVisible() && !isEmpty()" class="fa fa-minus-circle redlink" title="Remove item"/>
|
||||
</td>
|
||||
@ -122,30 +122,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<center class="buttons">
|
||||
@if ($task && $task->is_running)
|
||||
{!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!}
|
||||
{!! Button::primary(trans('texts.stop'))->large()->appendIcon(Icon::create('stop'))->withAttributes(['id' => 'stop-button']) !!}
|
||||
@elseif ($task && $task->trashed())
|
||||
{!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/tasks'))->appendIcon(Icon::create('remove-circle')) !!}
|
||||
{!! Button::success(trans('texts.restore'))->large()->withAttributes(['onclick' => 'submitAction("restore")'])->appendIcon(Icon::create('cloud-download')) !!}
|
||||
@if (Auth::user()->hasFeature(FEATURE_TASKS))
|
||||
@if ($task && $task->is_running)
|
||||
{!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!}
|
||||
{!! Button::primary(trans('texts.stop'))->large()->appendIcon(Icon::create('stop'))->withAttributes(['id' => 'stop-button']) !!}
|
||||
@elseif ($task && $task->trashed())
|
||||
{!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/tasks'))->appendIcon(Icon::create('remove-circle')) !!}
|
||||
{!! Button::success(trans('texts.restore'))->large()->withAttributes(['onclick' => 'submitAction("restore")'])->appendIcon(Icon::create('cloud-download')) !!}
|
||||
@else
|
||||
{!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/tasks'))->appendIcon(Icon::create('remove-circle')) !!}
|
||||
@if ($task)
|
||||
{!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!}
|
||||
{!! Button::primary(trans('texts.resume'))->large()->appendIcon(Icon::create('play'))->withAttributes(['id' => 'resume-button']) !!}
|
||||
{!! DropdownButton::normal(trans('texts.more_actions'))
|
||||
->withContents($actions)
|
||||
->large()
|
||||
->dropup() !!}
|
||||
@else
|
||||
{!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!}
|
||||
{!! Button::success(trans('texts.start'))->large()->appendIcon(Icon::create('play'))->withAttributes(['id' => 'start-button']) !!}
|
||||
@endif
|
||||
@endif
|
||||
@else
|
||||
{!! Button::normal(trans('texts.cancel'))->large()->asLinkTo(URL::to('/tasks'))->appendIcon(Icon::create('remove-circle')) !!}
|
||||
@if ($task)
|
||||
{!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!}
|
||||
{!! Button::primary(trans('texts.resume'))->large()->appendIcon(Icon::create('play'))->withAttributes(['id' => 'resume-button']) !!}
|
||||
{!! DropdownButton::normal(trans('texts.more_actions'))
|
||||
->withContents($actions)
|
||||
->large()
|
||||
->dropup() !!}
|
||||
@else
|
||||
{!! Button::success(trans('texts.save'))->large()->appendIcon(Icon::create('floppy-disk'))->withAttributes(['id' => 'save-button']) !!}
|
||||
{!! Button::success(trans('texts.start'))->large()->appendIcon(Icon::create('play'))->withAttributes(['id' => 'start-button']) !!}
|
||||
@endif
|
||||
@endif
|
||||
</center>
|
||||
|
||||
@ -199,7 +203,7 @@
|
||||
timeLabels['{{ $period }}'] = '{{ trans("texts.{$period}") }}';
|
||||
timeLabels['{{ $period }}s'] = '{{ trans("texts.{$period}s") }}';
|
||||
@endforeach
|
||||
|
||||
|
||||
function tock(duration) {
|
||||
var str = convertDurationToString(duration);
|
||||
$('#duration-text').html(str);
|
||||
@ -217,7 +221,7 @@
|
||||
for (var i=0; i<periods.length; i++) {
|
||||
var period = periods[i];
|
||||
var letter = period.charAt(0);
|
||||
var value = parts[letter];
|
||||
var value = parts[letter];
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
@ -244,9 +248,9 @@
|
||||
}
|
||||
|
||||
function onDeleteClick() {
|
||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||
submitAction('delete');
|
||||
}
|
||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||
submitAction('delete');
|
||||
}
|
||||
}
|
||||
|
||||
function showTimeDetails() {
|
||||
@ -277,9 +281,9 @@
|
||||
});
|
||||
|
||||
self.startTime.pretty = ko.computed({
|
||||
read: function() {
|
||||
return self.startTime() ? moment.unix(self.startTime()).tz(timezone).format(dateTimeFormat) : '';
|
||||
},
|
||||
read: function() {
|
||||
return self.startTime() ? moment.unix(self.startTime()).tz(timezone).format(dateTimeFormat) : '';
|
||||
},
|
||||
write: function(data) {
|
||||
self.startTime(moment(data, dateTimeFormat).tz(timezone).unix());
|
||||
}
|
||||
@ -288,7 +292,7 @@
|
||||
self.endTime.pretty = ko.computed({
|
||||
read: function() {
|
||||
return self.endTime() ? moment.unix(self.endTime()).tz(timezone).format(dateTimeFormat) : '';
|
||||
},
|
||||
},
|
||||
write: function(data) {
|
||||
self.endTime(moment(data, dateTimeFormat).tz(timezone).unix());
|
||||
}
|
||||
@ -316,7 +320,7 @@
|
||||
self.isEmpty = function() {
|
||||
return false;
|
||||
};
|
||||
*/
|
||||
*/
|
||||
|
||||
self.hideActions = function() {
|
||||
self.actionsVisible(false);
|
||||
@ -324,7 +328,7 @@
|
||||
|
||||
self.showActions = function() {
|
||||
self.actionsVisible(true);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function loadTimeLog(data) {
|
||||
@ -385,7 +389,7 @@
|
||||
if (timeLog.endTime() < Math.min(timeLog.startTime(), lastTime)) {
|
||||
endValid = false;
|
||||
}
|
||||
lastTime = Math.max(lastTime, timeLog.endTime());
|
||||
lastTime = Math.max(lastTime, timeLog.endTime());
|
||||
}
|
||||
timeLog.isStartValid(startValid);
|
||||
timeLog.isEndValid(endValid);
|
||||
@ -394,25 +398,25 @@
|
||||
|
||||
self.addItem = function() {
|
||||
self.time_log.push(new TimeModel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.model = new ViewModel({!! $task !!});
|
||||
ko.applyBindings(model);
|
||||
|
||||
$(function() {
|
||||
var $clientSelect = $('select#client');
|
||||
var $clientSelect = $('select#client');
|
||||
for (var i=0; i<clients.length; i++) {
|
||||
var client = clients[i];
|
||||
$clientSelect.append(new Option(getClientDisplayName(client), client.public_id));
|
||||
}
|
||||
}
|
||||
|
||||
if ({{ $clientPublicId ? 'true' : 'false' }}) {
|
||||
$clientSelect.val({{ $clientPublicId }});
|
||||
}
|
||||
|
||||
$clientSelect.combobox();
|
||||
|
||||
|
||||
@if (!$task && !$clientPublicId)
|
||||
$('.client-select input.form-control').focus();
|
||||
@else
|
||||
@ -466,7 +470,7 @@
|
||||
model.showTimeOverlaps();
|
||||
showTimeDetails();
|
||||
@endif
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
42
resources/views/vendors/edit.blade.php
vendored
42
resources/views/vendors/edit.blade.php
vendored
@ -113,48 +113,6 @@
|
||||
->fromQuery($currencies, 'name', 'id') !!}
|
||||
{!! Former::textarea('private_notes')->rows(6) !!}
|
||||
|
||||
|
||||
@if (Auth::user()->account->isNinjaAccount())
|
||||
@if (isset($planDetails))
|
||||
{!! Former::populateField('plan', $planDetails['plan']) !!}
|
||||
{!! Former::populateField('plan_term', $planDetails['term']) !!}
|
||||
@if (!empty($planDetails['paid']))
|
||||
{!! Former::populateField('plan_paid', $planDetails['paid']->format('Y-m-d')) !!}
|
||||
@endif
|
||||
@if (!empty($planDetails['expires']))
|
||||
{!! Former::populateField('plan_expires', $planDetails['expires']->format('Y-m-d')) !!}
|
||||
@endif
|
||||
@if (!empty($planDetails['started']))
|
||||
{!! Former::populateField('plan_started', $planDetails['started']->format('Y-m-d')) !!}
|
||||
@endif
|
||||
@endif
|
||||
{!! Former::select('plan')
|
||||
->addOption(trans('texts.plan_free'), PLAN_FREE)
|
||||
->addOption(trans('texts.plan_pro'), PLAN_PRO)
|
||||
->addOption(trans('texts.plan_enterprise'), PLAN_ENTERPRISE)!!}
|
||||
{!! Former::select('plan_term')
|
||||
->addOption()
|
||||
->addOption(trans('texts.plan_term_yearly'), PLAN_TERM_YEARLY)
|
||||
->addOption(trans('texts.plan_term_monthly'), PLAN_TERM_MONTHLY)!!}
|
||||
{!! Former::text('plan_started')
|
||||
->data_date_format('yyyy-mm-dd')
|
||||
->addGroupClass('plan_start_date')
|
||||
->append('<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>') !!}
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#plan_started, #plan_paid, #plan_expires').datepicker();
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user