mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 21:22:58 +01:00
ba75a44eb8
* Adopt Laravel coding style The Laravel framework adopts the PSR-2 coding style with some additions. Laravel apps *should* adopt this coding style as well. However, Shift allows you to customize the adopted coding style by adding your own [PHP CS Fixer][1] `.php_cs` config to your project. You may use [Shift's .php_cs][2] file as a base. [1]: https://github.com/FriendsOfPHP/PHP-CS-Fixer [2]: https://gist.github.com/laravel-shift/cab527923ed2a109dda047b97d53c200 * Shift bindings PHP 5.5.9+ adds the new static `class` property which provides the fully qualified class name. This is preferred over using class name strings as these references are checked by the parser. * Shift core files * Shift to Throwable * Add laravel/ui dependency * Unindent vendor mail templates * Shift config files * Default config files In an effort to make upgrading the constantly changing config files easier, Shift defaulted them so you can review the commit diff for changes. Moving forward, you should use ENV variables or create a separate config file to allow the core config files to remain automatically upgradeable. * Shift Laravel dependencies * Shift cleanup * Upgrade to Laravel 7 Co-authored-by: Laravel Shift <shift@laravelshift.com>
320 lines
9.7 KiB
PHP
320 lines
9.7 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\Utils\Ninja;
|
|
use App\Utils\Traits\MakesHash;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Laracasts\Presenter\PresentableTrait;
|
|
|
|
class Account extends BaseModel
|
|
{
|
|
use PresentableTrait;
|
|
use MakesHash;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $presenter = \App\Models\Presenters\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 \Illuminate\Database\Eloquent\Relations\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,
|
|
];
|
|
}
|
|
}
|
|
}
|