Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
David Bomba 2016-04-26 11:52:43 +10:00
commit 6a7cfdeb64
17 changed files with 273 additions and 136 deletions

View File

@ -0,0 +1,57 @@
<?php namespace App\Console\Commands;
use DB;
use Illuminate\Console\Command;
class PruneData extends Command
{
protected $name = 'ninja:prune-data';
protected $description = 'Delete inactive accounts';
public function fire()
{
$this->info(date('Y-m-d').' Running PruneData...');
// delete accounts who never registered, didn't create any invoices,
// hansn't logged in within the past 6 months and isn't linked to another account
$sql = 'select a.id
from (select id, last_login from accounts) a
left join users u on u.account_id = a.id and u.public_id = 0
left join invoices i on i.account_id = a.id
left join user_accounts ua1 on ua1.user_id1 = u.id
left join user_accounts ua2 on ua2.user_id2 = u.id
left join user_accounts ua3 on ua3.user_id3 = u.id
left join user_accounts ua4 on ua4.user_id4 = u.id
left join user_accounts ua5 on ua5.user_id5 = u.id
where u.registered = 0
and a.last_login < DATE_SUB(now(), INTERVAL 6 MONTH)
and (ua1.id is null and ua2.id is null and ua3.id is null and ua4.id is null and ua5.id is null)
group by a.id
having count(i.id) = 0';
$results = DB::select($sql);
foreach ($results as $result) {
$this->info("Deleting {$result->id}");
DB::table('accounts')
->where('id', '=', $result->id)
->delete();
}
$this->info('Done');
}
protected function getArguments()
{
return array(
//array('example', InputArgument::REQUIRED, 'An example argument.'),
);
}
protected function getOptions()
{
return array(
//array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null),
);
}
}

View File

@ -62,8 +62,12 @@ class SendRenewalInvoices extends Command
$invoice->due_date = date('Y-m-d', strtotime('+ 10 days')); $invoice->due_date = date('Y-m-d', strtotime('+ 10 days'));
$invoice->save(); $invoice->save();
if ($term == PLAN_TERM_YEARLY) {
$this->mailer->sendInvoice($invoice); $this->mailer->sendInvoice($invoice);
$this->info("Sent invoice to {$client->getDisplayName()}"); $this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}");
} else {
$this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}");
}
} }
$this->info('Done'); $this->info('Done');

View File

@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel
'App\Console\Commands\RemoveOrphanedDocuments', 'App\Console\Commands\RemoveOrphanedDocuments',
'App\Console\Commands\ResetData', 'App\Console\Commands\ResetData',
'App\Console\Commands\CheckData', 'App\Console\Commands\CheckData',
'App\Console\Commands\PruneData',
'App\Console\Commands\SendRenewalInvoices', 'App\Console\Commands\SendRenewalInvoices',
'App\Console\Commands\ChargeRenewalInvoices', 'App\Console\Commands\ChargeRenewalInvoices',
'App\Console\Commands\SendReminders', 'App\Console\Commands\SendReminders',

View File

@ -165,7 +165,7 @@ class AccountController extends BaseController
$account->company->save(); $account->company->save();
Session::flash('message', trans('texts.updated_plan')); Session::flash('message', trans('texts.updated_plan'));
} }
} else { } elseif (!empty($planDetails['started'])) {
// Downgrade // Downgrade
$refund_deadline = clone $planDetails['started']; $refund_deadline = clone $planDetails['started'];
$refund_deadline->modify('+30 days'); $refund_deadline->modify('+30 days');
@ -186,7 +186,7 @@ class AccountController extends BaseController
$gateway = $this->paymentService->createGateway($payment->account_gateway); $gateway = $this->paymentService->createGateway($payment->account_gateway);
$refund = $gateway->refund(array( $refund = $gateway->refund(array(
'transactionReference' => $payment->transaction_reference, 'transactionReference' => $payment->transaction_reference,
'amount' => $payment->amount * 100 'amount' => $payment->amount
)); ));
$refund->send(); $refund->send();
$payment->delete(); $payment->delete();
@ -238,7 +238,7 @@ class AccountController extends BaseController
if (!empty($new_plan)) { if (!empty($new_plan)) {
$invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly)); $invitation = $this->accountRepo->enablePlan($new_plan['plan'], $new_plan['term'], $credit, !empty($pending_monthly));
return Redirect::to('payment/'.$invitation->invitation_key); return Redirect::to('view/'.$invitation->invitation_key);
} }
return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301); return Redirect::to('/settings/'.ACCOUNT_MANAGEMENT, 301);

View File

@ -142,7 +142,9 @@ class StartupCheck
} elseif ($productId == PRODUCT_WHITE_LABEL) { } elseif ($productId == PRODUCT_WHITE_LABEL) {
if ($data == 'valid') { if ($data == 'valid') {
$company = Auth::user()->account->company; $company = Auth::user()->account->company;
$company->plan_term = PLAN_TERM_YEARLY;
$company->plan_paid = date_create()->format('Y-m-d'); $company->plan_paid = date_create()->format('Y-m-d');
$company->plan_expires = date_create()->modify('+1 year')->format('Y-m-d');
$company->plan = PLAN_WHITE_LABEL; $company->plan = PLAN_WHITE_LABEL;
$company->save(); $company->save();

View File

@ -554,6 +554,7 @@ if (!defined('CONTACT_EMAIL')) {
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG'); define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
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_DATE', '2000-01-01');
define('NINJA_VERSION', '2.5.1.3'); define('NINJA_VERSION', '2.5.1.3');
define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'); define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja');

View File

@ -60,13 +60,7 @@ class AccountGateway extends EntityModel
public function getConfigField($field) public function getConfigField($field)
{ {
$config = $this->getConfig(); return object_get($this->getConfig(), $field, false);
if (!$field || !property_exists($config, $field)) {
return false;
}
return $config->$field;
} }
public function getPublishableStripeKey() public function getPublishableStripeKey()

View File

@ -33,17 +33,17 @@ class InvoiceService extends BaseService
public function save($data, $checkSubPermissions = false) public function save($data, $checkSubPermissions = false)
{ {
if (isset($data['client'])) { if (isset($data['client'])) {
$can_save_client = !$checkSubPermissions; $canSaveClient = !$checkSubPermissions;
if(!$can_save_client){ if( ! $canSaveClient){
if(empty($data['client']['public_id']) || $data['client']['public_id']=='-1'){ $clientPublicId = array_get($data, 'client.public_id') ?: array_get($data, 'client.id');
$can_save_client = Client::canCreate(); if (empty($clientPublicId) || $clientPublicId == '-1') {
} $canSaveClient = Client::canCreate();
else{ } else {
$can_save_client = Client::wherePublicId($data['client']['public_id'])->first()->canEdit(); $canSaveClient = Client::scope($clientPublicId)->first()->canEdit();
} }
} }
if($can_save_client){ if ($canSaveClient) {
$client = $this->clientRepo->save($data['client']); $client = $this->clientRepo->save($data['client']);
$data['client_id'] = $client->id; $data['client_id'] = $client->id;
} }

View File

@ -98,9 +98,13 @@ class PaymentService extends BaseService
'number' => isset($input['card_number']) ? $input['card_number'] : null, 'number' => isset($input['card_number']) ? $input['card_number'] : null,
'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null, 'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : null,
'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null, 'expiryYear' => isset($input['expiration_year']) ? $input['expiration_year'] : null,
'cvv' => isset($input['cvv']) ? $input['cvv'] : '',
]; ];
// allow space until there's a setting to disable
if (isset($input['cvv']) && $input['cvv'] != ' ') {
$data['cvv'] = $input['cvv'];
}
if (isset($input['country_id'])) { if (isset($input['country_id'])) {
$country = Country::find($input['country_id']); $country = Country::find($input['country_id']);

View File

@ -13,7 +13,14 @@ class EnterprisePlan extends Migration
* @return void * @return void
*/ */
public function up() { public function up() {
$timeout = ini_get('max_execution_time');
if ($timeout == 0) {
$timeout = 600;
}
$timeout = max($timeout - 10, $timeout * .9);
$startTime = time();
if (!Schema::hasTable('companies')) {
Schema::create('companies', function($table) Schema::create('companies', function($table)
{ {
$table->increments('id'); $table->increments('id');
@ -36,13 +43,15 @@ class EnterprisePlan extends Migration
$table->timestamps(); $table->timestamps();
$table->softDeletes(); $table->softDeletes();
}); });
}
if (!Schema::hasColumn('accounts', 'company_id')) {
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->unsignedInteger('company_id')->nullable(); $table->unsignedInteger('company_id')->nullable();
$table->foreign('company_id')->references('id')->on('companies'); $table->foreign('company_id')->references('id')->on('companies');
}); });
}
$single_account_ids = \DB::table('users') $single_account_ids = \DB::table('users')
->leftJoin('user_accounts', function ($join) { ->leftJoin('user_accounts', function ($join) {
@ -52,41 +61,65 @@ class EnterprisePlan extends Migration
$join->orOn('user_accounts.user_id4', '=', 'users.id'); $join->orOn('user_accounts.user_id4', '=', 'users.id');
$join->orOn('user_accounts.user_id5', '=', 'users.id'); $join->orOn('user_accounts.user_id5', '=', 'users.id');
}) })
->leftJoin('accounts', 'accounts.id', '=', 'users.account_id')
->whereNull('user_accounts.id') ->whereNull('user_accounts.id')
->whereNull('accounts.company_id')
->where(function ($query) { ->where(function ($query) {
$query->whereNull('users.public_id'); $query->whereNull('users.public_id');
$query->orWhere('users.public_id', '=', 0); $query->orWhere('users.public_id', '=', 0);
}) })
->lists('users.account_id'); ->lists('users.account_id');
$group_accounts = \DB::select( if (count($single_account_ids)) {
'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) { foreach (Account::find($single_account_ids) as $account) {
$this->upAccounts($account); $this->upAccounts($account);
$this->checkTimeout($timeout, $startTime);
}
} }
$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
LEFT JOIN accounts a1 ON a1.id = u1.account_id
LEFT JOIN accounts a2 ON a2.id = u2.account_id
LEFT JOIN accounts a3 ON a3.id = u3.account_id
LEFT JOIN accounts a4 ON a4.id = u4.account_id
LEFT JOIN accounts a5 ON a5.id = u5.account_id
WHERE (a1.id IS NOT NULL AND a1.company_id IS NULL)
OR (a2.id IS NOT NULL AND a2.company_id IS NULL)
OR (a3.id IS NOT NULL AND a3.company_id IS NULL)
OR (a4.id IS NOT NULL AND a4.company_id IS NULL)
OR (a5.id IS NOT NULL AND a5.company_id IS NULL)');
if (count($group_accounts)) {
foreach ($group_accounts as $group_account) { foreach ($group_accounts as $group_account) {
$this->upAccounts(null, Account::find(get_object_vars($group_account))); $this->upAccounts(null, Account::find(get_object_vars($group_account)));
$this->checkTimeout($timeout, $startTime);
}
} }
if (Schema::hasColumn('accounts', 'pro_plan_paid')) {
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->dropColumn('pro_plan_paid'); $table->dropColumn('pro_plan_paid');
$table->dropColumn('pro_plan_trial'); $table->dropColumn('pro_plan_trial');
}); });
} }
}
private function upAccounts($primaryAccount, $otherAccounts = array()) { private function upAccounts($primaryAccount, $otherAccounts = array()) {
if(!$primaryAccount) { if(!$primaryAccount) {
$primaryAccount = $otherAccounts->first(); $primaryAccount = $otherAccounts->first();
} }
if (empty($primaryAccount)) {
return;
}
$company = Company::create(); $company = Company::create();
if ($primaryAccount->pro_plan_paid && $primaryAccount->pro_plan_paid != '0000-00-00') { if ($primaryAccount->pro_plan_paid && $primaryAccount->pro_plan_paid != '0000-00-00') {
$company->plan = 'pro'; $company->plan = 'pro';
@ -94,13 +127,24 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun
$company->plan_started = $primaryAccount->pro_plan_paid; $company->plan_started = $primaryAccount->pro_plan_paid;
$company->plan_paid = $primaryAccount->pro_plan_paid; $company->plan_paid = $primaryAccount->pro_plan_paid;
if (!Utils::isNinjaProd()) {
$company->plan = 'white_label';
$company->plan_term = null;
} elseif ($company->plan_paid != '2000-01-01'/* NINJA_DATE*/) {
$expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid); $expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid);
$expires->modify('+1 year'); $expires->modify('+1 year');
$company->plan_expires = $expires->format('Y-m-d'); $expires = $expires->format('Y-m-d');
// check for self host white label licenses
if (!Utils::isNinjaProd()) {
if ($company->plan_paid) {
$company->plan = 'white_label';
// old ones were unlimited, new ones are yearly
if ($company->plan_paid == NINJA_DATE) {
$company->plan_term = null;
} else {
$company->plan_term = PLAN_TERM_YEARLY;
$company->plan_expires = $expires;
}
}
} elseif ($company->plan_paid != NINJA_DATE) {
$company->plan_expires = $expires;
} }
} }
@ -124,6 +168,12 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun
} }
} }
protected function checkTimeout($timeout, $startTime) {
if (time() - $startTime >= $timeout) {
exit('Migration reached time limit; please run again to continue');
}
}
/** /**
* Reverse the migrations. * Reverse the migrations.
* *
@ -131,25 +181,51 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun
*/ */
public function down() public function down()
{ {
$timeout = ini_get('max_execution_time');
if ($timeout == 0) {
$timeout = 600;
}
$timeout = max($timeout - 10, $timeout * .9);
$startTime = time();
if (!Schema::hasColumn('accounts', 'pro_plan_paid')) {
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->date('pro_plan_paid')->nullable(); $table->date('pro_plan_paid')->nullable();
$table->date('pro_plan_trial')->nullable(); $table->date('pro_plan_trial')->nullable();
}); });
}
foreach (Company::all() as $company) { $company_ids = \DB::table('companies')
->leftJoin('accounts', 'accounts.company_id', '=', 'companies.id')
->whereNull('accounts.pro_plan_paid')
->whereNull('accounts.pro_plan_trial')
->where(function ($query) {
$query->whereNotNull('companies.plan_paid');
$query->orWhereNotNull('companies.trial_started');
})
->lists('companies.id');
$company_ids = array_unique($company_ids);
if (count($company_ids)) {
foreach (Company::find($company_ids) as $company) {
foreach ($company->accounts as $account) { foreach ($company->accounts as $account) {
$account->pro_plan_paid = $company->plan_paid; $account->pro_plan_paid = $company->plan_paid;
$account->pro_plan_trial = $company->trial_started; $account->pro_plan_trial = $company->trial_started;
$account->save(); $account->save();
} }
$this->checkTimeout($timeout, $startTime);
}
} }
if (Schema::hasColumn('accounts', 'company_id')) {
Schema::table('accounts', function($table) Schema::table('accounts', function($table)
{ {
$table->dropForeign('accounts_company_id_foreign'); $table->dropForeign('accounts_company_id_foreign');
$table->dropColumn('company_id'); $table->dropColumn('company_id');
}); });
}
Schema::dropIfExists('companies'); Schema::dropIfExists('companies');
} }

View File

@ -63,10 +63,11 @@ class AddPageSize extends Migration
$table->boolean('is_early_access'); $table->boolean('is_early_access');
}); });
Schema::dropIfExists('expense_categories');
Schema::table('expenses', function ($table) { Schema::table('expenses', function ($table) {
$table->dropForeign('expenses_expense_category_id_foreign');
$table->dropColumn('expense_category_id'); $table->dropColumn('expense_category_id');
}); });
Schema::dropIfExists('expense_categories');
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -597,24 +597,17 @@ NINJA.invoiceDetails = function(invoice) {
]) ])
} }
var isPartial = NINJA.parseFloat(invoice.partial); data.push([
{text: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']}
])
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) { if (NINJA.parseFloat(invoice.partial)) {
data.push([ data.push([
{text: invoiceLabels.balance_due}, {text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.amount, invoice)}
]);
} else if (isPartial) {
data.push([
{text: invoiceLabels.balance_due},
{text: formatMoneyInvoice(invoice.total_amount, invoice)}
]);
}
data.push([
{text: isPartial ? invoiceLabels.partial_due : invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']} {text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']}
]) ])
}
return NINJA.prepareDataPairs(data, 'invoiceDetails'); return NINJA.prepareDataPairs(data, 'invoiceDetails');
} }

View File

@ -710,7 +710,7 @@ function calculateAmounts(invoice) {
total += roundToTwo(invoice.custom_value2); total += roundToTwo(invoice.custom_value2);
} }
invoice.total_amount = roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance)); invoice.total_amount = roundToTwo(roundToTwo(total) - (roundToTwo(invoice.amount) - roundToTwo(invoice.balance)));
invoice.discount_amount = discount; invoice.discount_amount = discount;
invoice.tax_amount1 = taxAmount1; invoice.tax_amount1 = taxAmount1;
invoice.tax_amount2 = taxAmount2; invoice.tax_amount2 = taxAmount2;

View File

@ -220,6 +220,11 @@
} }
$('#plan_term, #plan').change(updatePlanModal); $('#plan_term, #plan').change(updatePlanModal);
updatePlanModal(); updatePlanModal();
if(window.location.hash) {
var hash = window.location.hash;
$(hash).modal('toggle');
}
}); });
</script> </script>
@stop @stop

View File

@ -190,14 +190,12 @@
fbq('track', 'AddPaymentInfo'); fbq('track', 'AddPaymentInfo');
trackEvent('/account', '/submit_pro_plan/' + NINJA.proPlanFeature); trackEvent('/account', '/submit_pro_plan/' + NINJA.proPlanFeature);
if (NINJA.isRegistered) { if (NINJA.isRegistered) {
$.ajax({ if (window.showChangePlan) {
type: 'POST', $('#proPlanModal').modal('hide');
url: '{{ URL::to('account/go_pro') }}', showChangePlan();
success: function(result) { } else {
NINJA.formIsChanged = false; window.location = '/settings/account_management#changePlanModel';
window.location = '/payment/' + result;
} }
});
} else { } else {
$('#proPlanModal').modal('hide'); $('#proPlanModal').modal('hide');
$('#go_pro').val('true'); $('#go_pro').val('true');

View File

@ -20,11 +20,15 @@
address_zip: $('#postal_code').val(), address_zip: $('#postal_code').val(),
address_country: $("#country_id option:selected").text(), address_country: $("#country_id option:selected").text(),
number: $('#card_number').val(), number: $('#card_number').val(),
cvc: $('#cvv').val(),
exp_month: $('#expiration_month').val(), exp_month: $('#expiration_month').val(),
exp_year: $('#expiration_year').val() exp_year: $('#expiration_year').val()
}; };
// allow space until there's a setting to disable
if ($('#cvv').val() != ' ') {
data.cvc = $('#cvv').val();
}
// Validate the card details // Validate the card details
if (!Stripe.card.validateCardNumber(data.number)) { if (!Stripe.card.validateCardNumber(data.number)) {
$('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn(); $('#js-error-message').html('{{ trans('texts.invalid_card_number') }}').fadeIn();
@ -34,7 +38,8 @@
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn(); $('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
return false; return false;
} }
if (!Stripe.card.validateCVC(data.cvc)) {
if (data.hasOwnProperty('cvc') && !Stripe.card.validateCVC(data.cvc)) {
$('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn(); $('#js-error-message').html('{{ trans('texts.invalid_cvv') }}').fadeIn();
return false; return false;
} }