mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-06 03:02:34 +01:00
323 lines
9.8 KiB
PHP
323 lines
9.8 KiB
PHP
<?php
|
|
/**
|
|
* Invoice Ninja (https://invoiceninja.com).
|
|
*
|
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
*
|
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
|
*
|
|
* @license https://opensource.org/licenses/AAL
|
|
*/
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Models\Presenters\AccountPresenter;
|
|
use App\Utils\Ninja;
|
|
use App\Utils\Traits\MakesHash;
|
|
use DateTime;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Laracasts\Presenter\PresentableTrait;
|
|
|
|
class Account extends BaseModel
|
|
{
|
|
use PresentableTrait;
|
|
use MakesHash;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $presenter = AccountPresenter::class;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $fillable = [
|
|
'plan',
|
|
'plan_term',
|
|
'plan_price',
|
|
'plan_paid',
|
|
'plan_started',
|
|
'plan_expires',
|
|
'utm_source',
|
|
'utm_medium',
|
|
'utm_campaign',
|
|
'utm_term',
|
|
'utm_content',
|
|
'user_agent',
|
|
];
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $dates = [
|
|
'deleted_at',
|
|
'promo_expires',
|
|
'discount_expires',
|
|
];
|
|
|
|
const PLAN_FREE = 'free';
|
|
const PLAN_PRO = 'pro';
|
|
const PLAN_ENTERPRISE = 'enterprise';
|
|
const PLAN_WHITE_LABEL = 'white_label';
|
|
const PLAN_TERM_MONTHLY = 'month';
|
|
const PLAN_TERM_YEARLY = 'year';
|
|
|
|
const FEATURE_TASKS = 'tasks';
|
|
const FEATURE_EXPENSES = 'expenses';
|
|
const FEATURE_QUOTES = 'quotes';
|
|
const FEATURE_CUSTOMIZE_INVOICE_DESIGN = 'custom_designs';
|
|
const FEATURE_DIFFERENT_DESIGNS = 'different_designs';
|
|
const FEATURE_EMAIL_TEMPLATES_REMINDERS = 'template_reminders';
|
|
const FEATURE_INVOICE_SETTINGS = 'invoice_settings';
|
|
const FEATURE_CUSTOM_EMAILS = 'custom_emails';
|
|
const FEATURE_PDF_ATTACHMENT = 'pdf_attachments';
|
|
const FEATURE_MORE_INVOICE_DESIGNS = 'more_invoice_designs';
|
|
const FEATURE_REPORTS = 'reports';
|
|
const FEATURE_BUY_NOW_BUTTONS = 'buy_now_buttons';
|
|
const FEATURE_API = 'api';
|
|
const FEATURE_CLIENT_PORTAL_PASSWORD = 'client_portal_password';
|
|
const FEATURE_CUSTOM_URL = 'custom_url';
|
|
const FEATURE_MORE_CLIENTS = 'more_clients';
|
|
const FEATURE_WHITE_LABEL = 'white_label';
|
|
const FEATURE_REMOVE_CREATED_BY = 'remove_created_by';
|
|
const FEATURE_USERS = 'users'; // Grandfathered for old Pro users
|
|
const FEATURE_DOCUMENTS = 'documents';
|
|
const FEATURE_USER_PERMISSIONS = 'permissions';
|
|
|
|
const RESULT_FAILURE = 'failure';
|
|
const RESULT_SUCCESS = 'success';
|
|
|
|
public function getEntityType()
|
|
{
|
|
return self::class;
|
|
}
|
|
|
|
public function users()
|
|
{
|
|
return $this->hasMany(User::class)->withTrashed();
|
|
}
|
|
|
|
public function default_company()
|
|
{
|
|
return $this->hasOne(Company::class, 'id', 'default_company_id');
|
|
}
|
|
|
|
/**
|
|
* @return BelongsTo
|
|
*/
|
|
public function payment()
|
|
{
|
|
return $this->belongsTo(Payment::class)->withTrashed();
|
|
}
|
|
|
|
public function companies()
|
|
{
|
|
return $this->hasMany(Company::class);
|
|
}
|
|
|
|
public function company_users()
|
|
{
|
|
return $this->hasMany(CompanyUser::class);
|
|
}
|
|
|
|
public function getPlan()
|
|
{
|
|
return $this->plan ?: '';
|
|
}
|
|
|
|
public function hasFeature($feature)
|
|
{
|
|
$plan_details = $this->getPlanDetails();
|
|
$self_host = ! Ninja::isNinja();
|
|
|
|
switch ($feature) {
|
|
|
|
case self::FEATURE_TASKS:
|
|
case self::FEATURE_EXPENSES:
|
|
case self::FEATURE_QUOTES:
|
|
return true;
|
|
|
|
case self::FEATURE_CUSTOMIZE_INVOICE_DESIGN:
|
|
case self::FEATURE_DIFFERENT_DESIGNS:
|
|
case self::FEATURE_EMAIL_TEMPLATES_REMINDERS:
|
|
case self::FEATURE_INVOICE_SETTINGS:
|
|
case self::FEATURE_CUSTOM_EMAILS:
|
|
case self::FEATURE_PDF_ATTACHMENT:
|
|
case self::FEATURE_MORE_INVOICE_DESIGNS:
|
|
case self::FEATURE_REPORTS:
|
|
case self::FEATURE_BUY_NOW_BUTTONS:
|
|
case self::FEATURE_API:
|
|
case self::FEATURE_CLIENT_PORTAL_PASSWORD:
|
|
case self::FEATURE_CUSTOM_URL:
|
|
return $self_host || ! empty($plan_details);
|
|
|
|
// Pro; No trial allowed, unless they're trialing enterprise with an active pro plan
|
|
case self::FEATURE_MORE_CLIENTS:
|
|
return $self_host || ! empty($plan_details) && (! $plan_details['trial'] || ! empty($this->getPlanDetails(false, false)));
|
|
|
|
// White Label
|
|
case self::FEATURE_WHITE_LABEL:
|
|
if (! $self_host && $plan_details && ! $plan_details['expires']) {
|
|
return false;
|
|
}
|
|
// Fallthrough
|
|
// no break
|
|
case self::FEATURE_REMOVE_CREATED_BY:
|
|
return ! empty($plan_details); // A plan is required even for self-hosted users
|
|
|
|
// Enterprise; No Trial allowed; grandfathered for old pro users
|
|
case self::FEATURE_USERS:// Grandfathered for old Pro users
|
|
if ($planDetails && $planDetails['trial']) {
|
|
// Do they have a non-trial plan?
|
|
$planDetails = $this->getPlanDetails(false, false);
|
|
}
|
|
|
|
return $self_host || ! empty($planDetails) && ($planDetails['plan'] == self::PLAN_ENTERPRISE);
|
|
|
|
// Enterprise; No Trial allowed
|
|
case self::FEATURE_DOCUMENTS:
|
|
case self::FEATURE_USER_PERMISSIONS:
|
|
return $self_host || ! empty($planDetails) && $planDetails['plan'] == self::PLAN_ENTERPRISE && ! $planDetails['trial'];
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function isPaid()
|
|
{
|
|
return Ninja::isNinja() ? ($this->isPaidHostedClient() && ! $this->isTrial()) : $this->hasFeature(self::FEATURE_WHITE_LABEL);
|
|
}
|
|
|
|
public function isPaidHostedClient()
|
|
{
|
|
if (! Ninja::isNinja()) {
|
|
return false;
|
|
}
|
|
|
|
return $this->plan == 'pro' || $this->plan == 'enterprise';
|
|
}
|
|
|
|
public function isFreeHostedClient()
|
|
{
|
|
if (! Ninja::isNinja()) {
|
|
return false;
|
|
}
|
|
|
|
return $this->plan == 'free';
|
|
}
|
|
|
|
public function isEnterpriseClient()
|
|
{
|
|
if (! Ninja::isNinja()) {
|
|
return false;
|
|
}
|
|
|
|
return $this->plan == 'enterprise';
|
|
}
|
|
|
|
public function isTrial()
|
|
{
|
|
if (! Ninja::isNinja()) {
|
|
return false;
|
|
}
|
|
|
|
$plan_details = $this->getPlanDetails();
|
|
|
|
return $plan_details && $plan_details['trial'];
|
|
}
|
|
|
|
public function getPlanDetails($include_inactive = false, $include_trial = true)
|
|
{
|
|
$plan = $this->plan;
|
|
$price = $this->plan_price;
|
|
$trial_plan = $this->trial_plan;
|
|
|
|
if ((! $plan || $plan == self::PLAN_FREE) && (! $trial_plan || ! $include_trial)) {
|
|
return null;
|
|
}
|
|
|
|
$trial_active = false;
|
|
if ($trial_plan && $include_trial) {
|
|
$trial_started = DateTime::createFromFormat('Y-m-d', $this->trial_started);
|
|
$trial_expires = clone $trial_started;
|
|
$trial_expires->modify('+2 weeks');
|
|
|
|
if ($trial_expires >= date_create()) {
|
|
$trial_active = true;
|
|
}
|
|
}
|
|
|
|
$plan_active = false;
|
|
if ($plan) {
|
|
if ($this->plan_expires == null) {
|
|
$plan_active = true;
|
|
$plan_expires = false;
|
|
} else {
|
|
$plan_expires = DateTime::createFromFormat('Y-m-d', $this->plan_expires);
|
|
if ($plan_expires >= date_create()) {
|
|
$plan_active = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! $include_inactive && ! $plan_active && ! $trial_active) {
|
|
return null;
|
|
}
|
|
|
|
// Should we show plan details or trial details?
|
|
if (($plan && ! $trial_plan) || ! $include_trial) {
|
|
$use_plan = true;
|
|
} elseif (! $plan && $trial_plan) {
|
|
$use_plan = false;
|
|
} else {
|
|
// There is both a plan and a trial
|
|
if (! empty($plan_active) && empty($trial_active)) {
|
|
$use_plan = true;
|
|
} elseif (empty($plan_active) && ! empty($trial_active)) {
|
|
$use_plan = false;
|
|
} elseif (! empty($plan_active) && ! empty($trial_active)) {
|
|
// Both are active; use whichever is a better plan
|
|
if ($plan == self::PLAN_ENTERPRISE) {
|
|
$use_plan = true;
|
|
} elseif ($trial_plan == self::PLAN_ENTERPRISE) {
|
|
$use_plan = false;
|
|
} else {
|
|
// They're both the same; show the plan
|
|
$use_plan = true;
|
|
}
|
|
} else {
|
|
// Neither are active; use whichever expired most recently
|
|
$use_plan = $plan_expires >= $trial_expires;
|
|
}
|
|
}
|
|
|
|
if ($use_plan) {
|
|
return [
|
|
'account_id' => $this->id,
|
|
'num_users' => $this->num_users,
|
|
'plan_price' => $price,
|
|
'trial' => false,
|
|
'plan' => $plan,
|
|
'started' => DateTime::createFromFormat('Y-m-d', $this->plan_started),
|
|
'expires' => $plan_expires,
|
|
'paid' => DateTime::createFromFormat('Y-m-d', $this->plan_paid),
|
|
'term' => $this->plan_term,
|
|
'active' => $plan_active,
|
|
];
|
|
} else {
|
|
return [
|
|
'account_id' => $this->id,
|
|
'num_users' => 1,
|
|
'plan_price' => 0,
|
|
'trial' => true,
|
|
'plan' => $trial_plan,
|
|
'started' => $trial_started,
|
|
'expires' => $trial_expires,
|
|
'active' => $trial_active,
|
|
];
|
|
}
|
|
}
|
|
}
|