1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-23 01:41:34 +02:00

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->save();
$this->mailer->sendInvoice($invoice);
$this->info("Sent invoice to {$client->getDisplayName()}");
if ($term == PLAN_TERM_YEARLY) {
$this->mailer->sendInvoice($invoice);
$this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}");
} else {
$this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}");
}
}
$this->info('Done');

View File

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

View File

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

View File

@ -142,7 +142,9 @@ class StartupCheck
} elseif ($productId == PRODUCT_WHITE_LABEL) {
if ($data == 'valid') {
$company = Auth::user()->account->company;
$company->plan_term = PLAN_TERM_YEARLY;
$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->save();

View File

@ -554,6 +554,7 @@ if (!defined('CONTACT_EMAIL')) {
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
define('NINJA_APP_URL', 'https://app.invoiceninja.com');
define('NINJA_DATE', '2000-01-01');
define('NINJA_VERSION', '2.5.1.3');
define('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja');

View File

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

View File

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

View File

@ -98,9 +98,13 @@ class PaymentService extends BaseService
'number' => isset($input['card_number']) ? $input['card_number'] : null,
'expiryMonth' => isset($input['expiration_month']) ? $input['expiration_month'] : 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'])) {
$country = Country::find($input['country_id']);

View File

@ -13,36 +13,45 @@ class EnterprisePlan extends Migration
* @return void
*/
public function up() {
$timeout = ini_get('max_execution_time');
if ($timeout == 0) {
$timeout = 600;
}
$timeout = max($timeout - 10, $timeout * .9);
$startTime = time();
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');
});
if (!Schema::hasTable('companies')) {
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();
});
}
if (!Schema::hasColumn('accounts', 'company_id')) {
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) {
@ -52,34 +61,54 @@ class EnterprisePlan extends Migration
$join->orOn('user_accounts.user_id4', '=', 'users.id');
$join->orOn('user_accounts.user_id5', '=', 'users.id');
})
->leftJoin('accounts', 'accounts.id', '=', 'users.account_id')
->whereNull('user_accounts.id')
->whereNull('accounts.company_id')
->where(function ($query) {
$query->whereNull('users.public_id');
$query->orWhere('users.public_id', '=', 0);
})
->lists('users.account_id');
if (count($single_account_ids)) {
foreach (Account::find($single_account_ids) as $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');
foreach (Account::find($single_account_ids) as $account) {
$this->upAccounts($account);
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) {
$this->upAccounts(null, Account::find(get_object_vars($group_account)));
$this->checkTimeout($timeout, $startTime);
}
}
foreach ($group_accounts as $group_account) {
$this->upAccounts(null, Account::find(get_object_vars($group_account)));
if (Schema::hasColumn('accounts', 'pro_plan_paid')) {
Schema::table('accounts', function($table)
{
$table->dropColumn('pro_plan_paid');
$table->dropColumn('pro_plan_trial');
});
}
Schema::table('accounts', function($table)
{
$table->dropColumn('pro_plan_paid');
$table->dropColumn('pro_plan_trial');
});
}
private function upAccounts($primaryAccount, $otherAccounts = array()) {
@ -87,6 +116,10 @@ LEFT JOIN users u5 ON (u5.public_id IS NULL OR u5.public_id = 0) AND user_accoun
$primaryAccount = $otherAccounts->first();
}
if (empty($primaryAccount)) {
return;
}
$company = Company::create();
if ($primaryAccount->pro_plan_paid && $primaryAccount->pro_plan_paid != '0000-00-00') {
$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_paid = $primaryAccount->pro_plan_paid;
$expires = DateTime::createFromFormat('Y-m-d', $primaryAccount->pro_plan_paid);
$expires->modify('+1 year');
$expires = $expires->format('Y-m-d');
// check for self host white label licenses
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 ($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.
*
@ -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()
{
Schema::table('accounts', function($table)
{
$table->date('pro_plan_paid')->nullable();
$table->date('pro_plan_trial')->nullable();
});
$timeout = ini_get('max_execution_time');
if ($timeout == 0) {
$timeout = 600;
}
$timeout = max($timeout - 10, $timeout * .9);
$startTime = time();
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();
if (!Schema::hasColumn('accounts', 'pro_plan_paid')) {
Schema::table('accounts', function($table)
{
$table->date('pro_plan_paid')->nullable();
$table->date('pro_plan_trial')->nullable();
});
}
$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) {
$account->pro_plan_paid = $company->plan_paid;
$account->pro_plan_trial = $company->trial_started;
$account->save();
}
$this->checkTimeout($timeout, $startTime);
}
}
Schema::table('accounts', function($table)
{
$table->dropForeign('accounts_company_id_foreign');
$table->dropColumn('company_id');
});
if (Schema::hasColumn('accounts', 'company_id')) {
Schema::table('accounts', function($table)
{
$table->dropForeign('accounts_company_id_foreign');
$table->dropColumn('company_id');
});
}
Schema::dropIfExists('companies');
}

View File

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

File diff suppressed because one or more lines are too long

View File

@ -597,25 +597,18 @@ NINJA.invoiceDetails = function(invoice) {
])
}
var isPartial = NINJA.parseFloat(invoice.partial);
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
data.push([
{text: invoiceLabels.balance_due},
{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: invoiceLabels.balance_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.total_amount, invoice), style: ['invoiceDetailBalanceDue']}
])
if (NINJA.parseFloat(invoice.partial)) {
data.push([
{text: invoiceLabels.partial_due, style: ['invoiceDetailBalanceDueLabel']},
{text: formatMoneyInvoice(invoice.balance_amount, invoice), style: ['invoiceDetailBalanceDue']}
])
}
return NINJA.prepareDataPairs(data, 'invoiceDetails');
}

View File

@ -710,7 +710,7 @@ function calculateAmounts(invoice) {
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.tax_amount1 = taxAmount1;
invoice.tax_amount2 = taxAmount2;

View File

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

View File

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

View File

@ -20,10 +20,14 @@
address_zip: $('#postal_code').val(),
address_country: $("#country_id option:selected").text(),
number: $('#card_number').val(),
cvc: $('#cvv').val(),
exp_month: $('#expiration_month').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
if (!Stripe.card.validateCardNumber(data.number)) {
@ -34,11 +38,12 @@
$('#js-error-message').html('{{ trans('texts.invalid_expiry') }}').fadeIn();
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();
return false;
}
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
$('#js-error-message').hide();