mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Support pro plan discounts
This commit is contained in:
parent
8015b88c31
commit
cc8959626f
@ -212,7 +212,7 @@ class AccountController extends BaseController
|
||||
|
||||
$days_total = $planDetails['paid']->diff($planDetails['expires'])->days;
|
||||
$percent_used = $days_used / $days_total;
|
||||
$credit = $planDetails['plan_price'] * (1 - $percent_used);
|
||||
$credit = floatval($company->payment->amount) * (1 - $percent_used);
|
||||
}
|
||||
|
||||
if ($newPlan['price'] > $credit) {
|
||||
@ -224,7 +224,9 @@ class AccountController extends BaseController
|
||||
}
|
||||
} else {
|
||||
|
||||
if ($plan != PLAN_FREE) {
|
||||
if ($plan == PLAN_FREE) {
|
||||
$company->discount = 0;
|
||||
} else {
|
||||
$company->plan_term = $term;
|
||||
$company->plan_price = $newPlan['price'];
|
||||
$company->num_users = $numUsers;
|
||||
|
@ -1,7 +1,10 @@
|
||||
<?php namespace App\Models;
|
||||
|
||||
use Carbon;
|
||||
use Utils;
|
||||
use Eloquent;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
||||
/**
|
||||
* Class Company
|
||||
@ -9,11 +12,21 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
class Company extends Eloquent
|
||||
{
|
||||
use SoftDeletes;
|
||||
use PresentableTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $presenter = 'App\Ninja\Presenters\CompanyPresenter';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['deleted_at'];
|
||||
protected $dates = [
|
||||
'deleted_at',
|
||||
'promo_expires',
|
||||
'discount_expires',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
@ -30,4 +43,64 @@ class Company extends Eloquent
|
||||
{
|
||||
return $this->belongsTo('App\Models\Payment');
|
||||
}
|
||||
|
||||
public function hasActivePromo()
|
||||
{
|
||||
if ($this->discount_expires) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->promo_expires && $this->promo_expires->gte(Carbon::today());
|
||||
}
|
||||
|
||||
// handle promos and discounts
|
||||
public function hasActiveDiscount()
|
||||
{
|
||||
if ( ! $this->discount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->discount_expires && $this->discount_expires->gt(Carbon::today());
|
||||
}
|
||||
|
||||
public function discountedPrice($price)
|
||||
{
|
||||
if ( ! $this->hasActivePromo() && ! $this->hasActiveDiscount()) {
|
||||
return $price;
|
||||
}
|
||||
|
||||
return $price - ($price * $this->discount);
|
||||
}
|
||||
|
||||
public function hasEarnedPromo()
|
||||
{
|
||||
if ( ! Utils::isNinjaProd() || Utils::isPro()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if they've already had a discount or a promotion is active return false
|
||||
if ($this->discount_expires || $this->hasActivePromo()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// after 52 weeks, offer a 50% discount for 3 days
|
||||
$discounts = [
|
||||
52 => [.5, 3],
|
||||
16 => [.5, 3],
|
||||
10 => [.25, 5],
|
||||
];
|
||||
|
||||
foreach ($discounts as $weeks => $promo) {
|
||||
list($discount, $validFor) = $promo;
|
||||
$difference = $this->created_at->diffInWeeks();
|
||||
if ($difference >= $weeks) {
|
||||
$this->discount = $discount;
|
||||
$this->promo_expires = date_create()->modify($validFor . ' days')->format('Y-m-d');
|
||||
$this->save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -667,6 +667,11 @@ class BasePaymentDriver
|
||||
$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 ($company->hasActivePromo()) {
|
||||
$company->discount_expires = date_create()->modify('1 year')->format('Y-m-d');
|
||||
$company->promo_expires = null;
|
||||
}
|
||||
|
||||
$company->save();
|
||||
}
|
||||
}
|
||||
|
29
app/Ninja/Presenters/CompanyPresenter.php
Normal file
29
app/Ninja/Presenters/CompanyPresenter.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php namespace App\Ninja\Presenters;
|
||||
|
||||
class CompanyPresenter extends EntityPresenter
|
||||
{
|
||||
public function promoMessage()
|
||||
{
|
||||
if ( ! $this->entity->hasActivePromo()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trans('texts.promo_message', [
|
||||
'expires' => $this->entity->promo_expires->format('M dS, Y'),
|
||||
'amount' => (int)($this->discount * 100)
|
||||
]);
|
||||
}
|
||||
|
||||
public function discountMessage()
|
||||
{
|
||||
if ( ! $this->entity->hasActiveDiscount()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trans('texts.discount_message', [
|
||||
'expires' => $this->entity->discount_expires->format('M dS, Y'),
|
||||
'amount' => (int)($this->discount * 100)
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -289,6 +289,16 @@ class AccountRepository
|
||||
$invoice->invoice_date = $clientAccount->getRenewalDate();
|
||||
$invoice->amount = $invoice->balance = $plan_cost - $credit;
|
||||
$invoice->invoice_type_id = INVOICE_TYPE_STANDARD;
|
||||
|
||||
// check for promo/discount
|
||||
$clientCompany = $clientAccount->company;
|
||||
if ($clientCompany->hasActivePromo() || $clientCompany->hasActiveDiscount()) {
|
||||
$discount = $invoice->amount * $clientCompany->discount;
|
||||
$invoice->discount = $clientCompany->discount * 100;
|
||||
$invoice->amount -= $discount;
|
||||
$invoice->balance -= $discount;
|
||||
}
|
||||
|
||||
$invoice->save();
|
||||
|
||||
if ($credit) {
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddProPlanDiscount extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('companies', function ($table)
|
||||
{
|
||||
$table->float('discount');
|
||||
$table->date('discount_expires')->nullable();
|
||||
$table->date('promo_expires')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('companies', function ($table)
|
||||
{
|
||||
$table->dropColumn('discount');
|
||||
$table->dropColumn('discount_expires');
|
||||
$table->dropColumn('promo_expires');
|
||||
});
|
||||
}
|
||||
}
|
@ -2267,6 +2267,8 @@ $LANG = array(
|
||||
'contact_us' => 'Contact Us',
|
||||
'support_forum' => 'Support Forum',
|
||||
'user_guide' => 'User Guide',
|
||||
'promo_message' => 'Upgrade before :expires and get :amount% OFF your first year of our Pro or Enterprise packages.',
|
||||
'discount_message' => ':amount% off expires :expires',
|
||||
|
||||
);
|
||||
|
||||
|
@ -50,6 +50,12 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($account->company->hasActiveDiscount())
|
||||
{!! Former::plaintext('discount')
|
||||
->value($account->company->present()->discountMessage) !!}
|
||||
@endif
|
||||
|
||||
@if (Utils::isNinjaProd())
|
||||
{!! Former::actions( Button::info(trans('texts.plan_change'))->large()->withAttributes(['onclick' => 'showChangePlan()'])->appendIcon(Icon::create('edit'))) !!}
|
||||
@endif
|
||||
@ -120,6 +126,9 @@
|
||||
->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'])])) !!}
|
||||
|
||||
{!! Former::plaintext(' ')
|
||||
->inlineHelp($account->company->present()->promoMessage) !!}
|
||||
|
||||
</div>
|
||||
<div class="modal-footer" style="margin-top: 0px">
|
||||
@if (Utils::isPro())
|
||||
@ -272,18 +281,18 @@
|
||||
$('#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_MONTHLY * 10])) !!});
|
||||
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>$account->company->discountedPrice(PLAN_PRICE_PRO_MONTHLY)])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>$account->company->discountedPrice(PLAN_PRICE_PRO_MONTHLY) * 10])) !!});
|
||||
} else if(plan=='{{PLAN_ENTERPRISE}}') {
|
||||
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])) !!});
|
||||
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>$account->company->discountedPrice(PLAN_PRICE_ENTERPRISE_MONTHLY_2)])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>$account->company->discountedPrice(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])) !!});
|
||||
$('#plan_term option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>$account->company->discountedPrice(PLAN_PRICE_ENTERPRISE_MONTHLY_5)])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>$account->company->discountedPrice(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 option[value=month]').text({!! json_encode(trans('texts.plan_price_monthly', ['price'=>$account->company->discountedPrice(PLAN_PRICE_ENTERPRISE_MONTHLY_10)])) !!});
|
||||
$('#plan_term option[value=year]').text({!! json_encode(trans('texts.plan_price_yearly', ['price'=>$account->company->discountedPrice(PLAN_PRICE_ENTERPRISE_MONTHLY_10) * 10])) !!});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,9 +194,13 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@if($showBlueVinePromo)
|
||||
|
||||
@if ($account->company->hasEarnedPromo())
|
||||
@include('partials/discount_promo')
|
||||
@elseif ($showBlueVinePromo)
|
||||
@include('partials/bluevine_promo')
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
|
@ -374,7 +374,11 @@
|
||||
@if (!Auth::user()->registered)
|
||||
{!! Button::success(trans('texts.sign_up'))->withAttributes(array('id' => 'signUpButton', 'data-toggle'=>'modal', 'data-target'=>'#signUpModal', 'style' => 'max-width:100px;;overflow:hidden'))->small() !!}
|
||||
@elseif (Utils::isNinjaProd() && (!Auth::user()->isPro() || Auth::user()->isTrial()))
|
||||
{!! Button::success(trans('texts.plan_upgrade'))->withAttributes(array('onclick' => 'showUpgradeModal()', 'style' => 'max-width:100px;overflow:hidden'))->small() !!}
|
||||
@if (Auth::user()->account->company->hasActivePromo())
|
||||
{!! Button::warning(trans('texts.plan_upgrade'))->withAttributes(array('onclick' => 'showUpgradeModal()', 'style' => 'max-width:100px;overflow:hidden'))->small() !!}
|
||||
@else
|
||||
{!! Button::success(trans('texts.plan_upgrade'))->withAttributes(array('onclick' => 'showUpgradeModal()', 'style' => 'max-width:100px;overflow:hidden'))->small() !!}
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
|
||||
|
14
resources/views/partials/discount_promo.blade.php
Normal file
14
resources/views/partials/discount_promo.blade.php
Normal file
@ -0,0 +1,14 @@
|
||||
<div class="alert alert-info" id="discountPromo">
|
||||
{{ $account->company->present()->promoMessage }}
|
||||
<a href="#" onclick="showUpgradeModal()" class="btn btn-primary btn-sm">{{ trans('texts.plan_upgrade') }}</a>
|
||||
<a href="#" onclick="hideDiscountMessage()" class="pull-right">{{ trans('texts.hide') }}</a>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function hideDiscountMessage() {
|
||||
jQuery('#discountPromo').fadeOut();
|
||||
return false;
|
||||
}
|
||||
|
||||
</script>
|
@ -49,6 +49,10 @@
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
#upgrade-modal h4 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#upgrade-modal ul {
|
||||
list-style: none;
|
||||
color: #fff;
|
||||
@ -135,12 +139,15 @@
|
||||
<label for="plan_term_year" class="radio-inline">
|
||||
<input value="year" id="plan_term_year" type="radio" name="plan_term">Annually</label>
|
||||
</h4>
|
||||
@if (Auth::user()->account->company->hasActivePromo())
|
||||
<h4>{{ Auth::user()->account->company->present()->promoMessage }}</h4><br/>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-2 text-center">
|
||||
<h2>{{ trans('texts.pro_upgrade_title') }}</h2>
|
||||
<p class="subhead">{{ trans('texts.pay_annually_discount') }}</p>
|
||||
<img width="65" src="{{ asset('images/pro_plan/border.png') }}"/>
|
||||
<h3>$<span id="upgrade_pro_price">8</span> <span class="upgrade_frequency">/ {{ trans('texts.plan_term_month') }}</span></h3>
|
||||
<h3>$<span id="upgrade_pro_price">{{ PLAN_PRICE_PRO_MONTHLY }}</span> <span class="upgrade_frequency">/ {{ trans('texts.plan_term_month') }}</span></h3>
|
||||
<select style="visibility:hidden">
|
||||
</select>
|
||||
<p> </p>
|
||||
@ -155,7 +162,7 @@
|
||||
<h2>{{ trans('texts.plan_enterprise') }}</h2>
|
||||
<p class="subhead">{{ trans('texts.pay_annually_discount') }}</p>
|
||||
<img width="65" src="{{ asset('images/pro_plan/border.png') }}"/>
|
||||
<h3>$<span id="upgrade_enterprise_price">12</span> <span class="upgrade_frequency">/ {{ trans('texts.plan_term_month') }}</span></h3>
|
||||
<h3>$<span id="upgrade_enterprise_price">{{ PLAN_PRICE_ENTERPRISE_MONTHLY_2 }}</span> <span class="upgrade_frequency">/ {{ trans('texts.plan_term_month') }}</span></h3>
|
||||
<select name="num_users" id="upgrade_num_users" onchange="updateUpgradePrices()">
|
||||
<option value="2">1 to 2 {{ trans('texts.users') }}</option>
|
||||
<option value="5">3 to 5 {{ trans('texts.users') }}</option>
|
||||
@ -214,6 +221,10 @@
|
||||
}
|
||||
var label = "{{ trans('texts.freq_annually') }}";
|
||||
}
|
||||
@if (Auth::user()->account->company->hasActivePromo())
|
||||
proPrice = proPrice - (proPrice * {{ Auth::user()->account->company->discount }});
|
||||
enterprisePrice = enterprisePrice - (enterprisePrice * {{ Auth::user()->account->company->discount }});
|
||||
@endif
|
||||
$('#upgrade_pro_price').text(proPrice);
|
||||
$('#upgrade_enterprise_price').text(enterprisePrice);
|
||||
$('span.upgrade_frequency').text(label);
|
||||
@ -225,6 +236,11 @@
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
@if (Auth::user()->account->company->hasActivePromo())
|
||||
updateUpgradePrices();
|
||||
@endif
|
||||
|
||||
$(document).keyup(function(e) {
|
||||
if (e.keyCode == 27) { // escape key maps to keycode `27`
|
||||
hideUpgradeModal();
|
||||
|
Loading…
x
Reference in New Issue
Block a user