2018-10-12 13:29:34 +02:00
|
|
|
<?php
|
2019-05-11 05:32:07 +02:00
|
|
|
/**
|
2020-09-06 11:38:10 +02:00
|
|
|
* Invoice Ninja (https://invoiceninja.com).
|
2019-05-11 05:32:07 +02:00
|
|
|
*
|
|
|
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
|
|
*
|
2024-04-12 06:15:41 +02:00
|
|
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
2019-05-11 05:32:07 +02:00
|
|
|
*
|
2021-06-16 08:58:16 +02:00
|
|
|
* @license https://www.elastic.co/licensing/elastic-license
|
2019-05-11 05:32:07 +02:00
|
|
|
*/
|
2018-10-12 13:29:34 +02:00
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
2021-08-07 12:56:42 +02:00
|
|
|
use App\Jobs\Mail\NinjaMailerJob;
|
|
|
|
use App\Jobs\Mail\NinjaMailerObject;
|
2021-08-07 13:10:01 +02:00
|
|
|
use App\Mail\Ninja\EmailQuotaExceeded;
|
2022-03-23 22:34:52 +01:00
|
|
|
use App\Mail\Ninja\GmailTokenInvalid;
|
2020-10-28 11:10:49 +01:00
|
|
|
use App\Models\Presenters\AccountPresenter;
|
2021-08-31 13:29:18 +02:00
|
|
|
use App\Notifications\Ninja\EmailQuotaNotification;
|
2022-03-23 22:34:52 +01:00
|
|
|
use App\Notifications\Ninja\GmailCredentialNotification;
|
2020-03-01 11:18:13 +01:00
|
|
|
use App\Utils\Ninja;
|
2018-11-20 05:36:56 +01:00
|
|
|
use App\Utils\Traits\MakesHash;
|
2021-04-10 00:27:02 +02:00
|
|
|
use Carbon\Carbon;
|
2020-10-28 11:10:49 +01:00
|
|
|
use DateTime;
|
2023-01-22 06:34:47 +01:00
|
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
|
2021-08-31 13:29:18 +02:00
|
|
|
use Illuminate\Support\Facades\App;
|
2021-08-07 11:55:18 +02:00
|
|
|
use Illuminate\Support\Facades\Cache;
|
2021-08-07 12:56:42 +02:00
|
|
|
use Laracasts\Presenter\PresentableTrait;
|
2018-10-12 13:29:34 +02:00
|
|
|
|
2023-03-08 08:33:42 +01:00
|
|
|
/**
|
|
|
|
* App\Models\Account
|
|
|
|
*
|
|
|
|
* @property int $id
|
2024-05-20 00:31:28 +02:00
|
|
|
* @property int $email_quota
|
2023-03-08 08:33:42 +01:00
|
|
|
* @property string|null $plan
|
|
|
|
* @property string|null $plan_term
|
|
|
|
* @property string|null $plan_started
|
|
|
|
* @property string|null $plan_paid
|
|
|
|
* @property string|null $plan_expires
|
|
|
|
* @property string|null $user_agent
|
|
|
|
* @property string|null $key
|
|
|
|
* @property int|null $payment_id
|
|
|
|
* @property int $default_company_id
|
|
|
|
* @property string|null $trial_started
|
|
|
|
* @property string|null $trial_plan
|
|
|
|
* @property string|null $plan_price
|
|
|
|
* @property int $num_users
|
|
|
|
* @property string|null $utm_source
|
|
|
|
* @property string|null $utm_medium
|
|
|
|
* @property string|null $utm_campaign
|
|
|
|
* @property string|null $utm_term
|
|
|
|
* @property string|null $utm_content
|
|
|
|
* @property string $latest_version
|
|
|
|
* @property int $report_errors
|
|
|
|
* @property string|null $referral_code
|
|
|
|
* @property int|null $created_at
|
|
|
|
* @property int|null $updated_at
|
2024-10-02 02:05:28 +02:00
|
|
|
* @property bool $is_scheduler_running
|
2023-03-08 08:33:42 +01:00
|
|
|
* @property int|null $trial_duration
|
2024-10-02 02:05:28 +02:00
|
|
|
* @property bool $is_onboarding
|
2023-03-08 08:33:42 +01:00
|
|
|
* @property object|null $onboarding
|
2024-06-16 06:35:56 +02:00
|
|
|
* @property bool $is_migrated
|
2023-03-08 08:33:42 +01:00
|
|
|
* @property string|null $platform
|
|
|
|
* @property int|null $hosted_client_count
|
|
|
|
* @property int|null $hosted_company_count
|
|
|
|
* @property string|null $inapp_transaction_id
|
|
|
|
* @property bool $set_react_as_default_ap
|
2023-05-05 06:15:50 +02:00
|
|
|
* @property bool $is_flagged
|
2024-06-16 02:33:18 +02:00
|
|
|
* @property bool $is_verified_account
|
2023-03-08 08:33:42 +01:00
|
|
|
* @property string|null $account_sms_verification_code
|
|
|
|
* @property string|null $account_sms_verification_number
|
2023-04-29 04:32:57 +02:00
|
|
|
* @property bool $account_sms_verified
|
2023-12-13 16:32:51 +01:00
|
|
|
* @property string|null $bank_integration_account_id
|
2024-06-15 09:34:08 +02:00
|
|
|
* @property bool $is_trial
|
2024-10-08 05:44:50 +02:00
|
|
|
* @property int $e_invoice_quota
|
2023-03-08 08:33:42 +01:00
|
|
|
* @property-read int|null $bank_integrations_count
|
|
|
|
* @property-read int|null $companies_count
|
|
|
|
* @property-read int|null $company_users_count
|
|
|
|
* @property-read \App\Models\Company|null $default_company
|
|
|
|
* @property-read mixed $hashed_id
|
|
|
|
* @property-read \App\Models\Payment|null $payment
|
|
|
|
* @property-read int|null $users_count
|
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
|
|
|
|
* @method static \Database\Factories\AccountFactory factory($count = null, $state = [])
|
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account newModelQuery()
|
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account newQuery()
|
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account query()
|
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope()
|
2023-04-27 00:05:57 +02:00
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account first()
|
2023-04-28 04:10:53 +02:00
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account with()
|
2023-10-26 04:57:44 +02:00
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account count()
|
2023-08-06 09:03:12 +02:00
|
|
|
* @method static \Illuminate\Database\Eloquent\Builder|Account where($query)
|
2023-03-16 05:20:38 +01:00
|
|
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\BankIntegration> $bank_integrations
|
2023-04-13 07:39:12 +02:00
|
|
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Company> $companies
|
|
|
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyUser> $company_users
|
|
|
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $users
|
2023-04-27 00:18:39 +02:00
|
|
|
|
2023-03-08 08:33:42 +01:00
|
|
|
* @mixin \Eloquent
|
|
|
|
*/
|
2018-11-02 11:54:46 +01:00
|
|
|
class Account extends BaseModel
|
2018-10-12 13:29:34 +02:00
|
|
|
{
|
2018-10-22 14:04:37 +02:00
|
|
|
use PresentableTrait;
|
2018-11-20 05:36:56 +01:00
|
|
|
use MakesHash;
|
2018-10-15 14:40:34 +02:00
|
|
|
|
2022-08-19 11:19:17 +02:00
|
|
|
private $free_plan_email_quota = 20;
|
2021-08-08 13:50:13 +02:00
|
|
|
|
2024-03-17 06:04:44 +01:00
|
|
|
private $paid_plan_email_quota = 300;
|
2023-04-26 16:11:31 +02:00
|
|
|
|
2018-10-15 14:40:34 +02:00
|
|
|
/**
|
2018-10-22 14:04:37 +02:00
|
|
|
* @var string
|
2018-10-15 14:40:34 +02:00
|
|
|
*/
|
2020-10-28 11:10:49 +01:00
|
|
|
protected $presenter = AccountPresenter::class;
|
2018-10-15 14:40:34 +02:00
|
|
|
|
2018-10-22 14:04:37 +02:00
|
|
|
protected $fillable = [
|
2023-11-15 07:49:01 +01:00
|
|
|
// 'plan',
|
|
|
|
// 'plan_term',
|
|
|
|
// 'plan_price',
|
|
|
|
// 'plan_paid',
|
|
|
|
// 'plan_started',
|
|
|
|
// 'plan_expires',
|
|
|
|
// 'num_users',
|
2018-10-24 05:50:15 +02:00
|
|
|
'utm_source',
|
|
|
|
'utm_medium',
|
|
|
|
'utm_campaign',
|
|
|
|
'utm_term',
|
|
|
|
'utm_content',
|
2019-10-24 06:46:24 +02:00
|
|
|
'user_agent',
|
2022-01-22 05:22:59 +01:00
|
|
|
'platform',
|
2022-06-16 05:04:05 +02:00
|
|
|
'set_react_as_default_ap',
|
2022-10-07 01:10:10 +02:00
|
|
|
'inapp_transaction_id',
|
2018-10-22 14:04:37 +02:00
|
|
|
];
|
2018-10-15 14:40:34 +02:00
|
|
|
|
2021-11-03 14:18:33 +01:00
|
|
|
protected $casts = [
|
2021-11-05 15:49:50 +01:00
|
|
|
'updated_at' => 'timestamp',
|
|
|
|
'created_at' => 'timestamp',
|
|
|
|
'deleted_at' => 'timestamp',
|
2022-06-16 05:04:05 +02:00
|
|
|
'onboarding' => 'object',
|
2023-08-21 03:31:45 +02:00
|
|
|
'set_react_as_default_ap' => 'bool',
|
|
|
|
'promo_expires' => 'date',
|
|
|
|
'discount_expires' => 'date',
|
2021-11-03 14:18:33 +01:00
|
|
|
];
|
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
public const PLAN_FREE = 'free';
|
|
|
|
public const PLAN_PRO = 'pro';
|
|
|
|
public const PLAN_ENTERPRISE = 'enterprise';
|
|
|
|
public const PLAN_WHITE_LABEL = 'white_label';
|
|
|
|
public const PLAN_TERM_MONTHLY = 'month';
|
|
|
|
public const PLAN_TERM_YEARLY = 'year';
|
|
|
|
|
|
|
|
public const FEATURE_TASKS = 'tasks';
|
|
|
|
public const FEATURE_EXPENSES = 'expenses';
|
|
|
|
public const FEATURE_QUOTES = 'quotes';
|
|
|
|
public const FEATURE_PURCHASE_ORDERS = 'purchase_orders';
|
|
|
|
public const FEATURE_CUSTOMIZE_INVOICE_DESIGN = 'custom_designs';
|
|
|
|
public const FEATURE_DIFFERENT_DESIGNS = 'different_designs';
|
|
|
|
public const FEATURE_EMAIL_TEMPLATES_REMINDERS = 'template_reminders';
|
|
|
|
public const FEATURE_INVOICE_SETTINGS = 'invoice_settings';
|
|
|
|
public const FEATURE_CUSTOM_EMAILS = 'custom_emails';
|
|
|
|
public const FEATURE_PDF_ATTACHMENT = 'pdf_attachments';
|
|
|
|
public const FEATURE_MORE_INVOICE_DESIGNS = 'more_invoice_designs';
|
|
|
|
public const FEATURE_REPORTS = 'reports';
|
|
|
|
public const FEATURE_BUY_NOW_BUTTONS = 'buy_now_buttons';
|
|
|
|
public const FEATURE_API = 'api';
|
|
|
|
public const FEATURE_CLIENT_PORTAL_PASSWORD = 'client_portal_password';
|
|
|
|
public const FEATURE_CUSTOM_URL = 'custom_url';
|
|
|
|
public const FEATURE_MORE_CLIENTS = 'more_clients';
|
|
|
|
public const FEATURE_WHITE_LABEL = 'white_label';
|
|
|
|
public const FEATURE_REMOVE_CREATED_BY = 'remove_created_by';
|
|
|
|
public const FEATURE_USERS = 'users'; // Grandfathered for old Pro users
|
|
|
|
public const FEATURE_DOCUMENTS = 'documents';
|
|
|
|
public const FEATURE_USER_PERMISSIONS = 'permissions';
|
|
|
|
public const FEATURE_SUBSCRIPTIONS = 'subscriptions';
|
|
|
|
|
|
|
|
public const RESULT_FAILURE = 'failure';
|
|
|
|
public const RESULT_SUCCESS = 'success';
|
2020-05-06 13:49:42 +02:00
|
|
|
|
|
|
|
public function getEntityType()
|
|
|
|
{
|
2020-09-06 11:38:10 +02:00
|
|
|
return self::class;
|
2020-05-06 13:49:42 +02:00
|
|
|
}
|
2018-10-15 14:40:34 +02:00
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function users(): \Illuminate\Database\Eloquent\Relations\HasMany
|
2020-03-24 10:15:30 +01:00
|
|
|
{
|
|
|
|
return $this->hasMany(User::class)->withTrashed();
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function default_company(): \Illuminate\Database\Eloquent\Relations\HasOne
|
2018-10-29 04:16:17 +01:00
|
|
|
{
|
2019-03-27 05:50:13 +01:00
|
|
|
return $this->hasOne(Company::class, 'id', 'default_company_id');
|
2018-10-29 04:16:17 +01:00
|
|
|
}
|
2020-09-06 11:38:10 +02:00
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function payment(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
2018-10-15 14:40:34 +02:00
|
|
|
{
|
2019-11-25 10:38:55 +01:00
|
|
|
return $this->belongsTo(Payment::class)->withTrashed();
|
2018-10-15 14:40:34 +02:00
|
|
|
}
|
2019-06-25 05:55:02 +02:00
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function companies(): \Illuminate\Database\Eloquent\Relations\HasMany
|
2019-06-25 05:55:02 +02:00
|
|
|
{
|
|
|
|
return $this->hasMany(Company::class);
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function bank_integrations(): \Illuminate\Database\Eloquent\Relations\HasMany
|
2022-08-12 05:41:55 +02:00
|
|
|
{
|
|
|
|
return $this->hasMany(BankIntegration::class);
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function company_users(): \Illuminate\Database\Eloquent\Relations\HasMany
|
2019-06-25 05:55:02 +02:00
|
|
|
{
|
|
|
|
return $this->hasMany(CompanyUser::class);
|
|
|
|
}
|
2019-09-18 14:43:37 +02:00
|
|
|
|
2023-08-06 09:03:12 +02:00
|
|
|
/**
|
|
|
|
* Returns the owner of the Account - not a HasMany relation
|
|
|
|
* @return \App\Models\User | bool
|
|
|
|
*/
|
2021-07-11 06:54:57 +02:00
|
|
|
public function owner()
|
|
|
|
{
|
|
|
|
return $this->hasMany(CompanyUser::class)->where('is_owner', true)->first() ? $this->hasMany(CompanyUser::class)->where('is_owner', true)->first()->user : false;
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function tokens(): \Illuminate\Database\Eloquent\Relations\HasMany
|
2023-05-05 06:15:50 +02:00
|
|
|
{
|
|
|
|
return $this->hasMany(CompanyToken::class)->withTrashed();
|
|
|
|
}
|
|
|
|
|
2019-09-18 14:43:37 +02:00
|
|
|
public function getPlan()
|
|
|
|
{
|
2023-02-16 02:36:09 +01:00
|
|
|
if (Carbon::parse($this->plan_expires)->lt(now())) {
|
2021-08-12 14:04:50 +02:00
|
|
|
return '';
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2021-08-12 14:04:50 +02:00
|
|
|
|
2019-09-18 14:43:37 +02:00
|
|
|
return $this->plan ?: '';
|
|
|
|
}
|
2020-03-01 11:18:13 +01:00
|
|
|
|
|
|
|
public function hasFeature($feature)
|
|
|
|
{
|
|
|
|
$plan_details = $this->getPlanDetails();
|
2023-12-01 14:30:33 +01:00
|
|
|
$self_host = !Ninja::isNinja();
|
2020-03-01 11:18:13 +01:00
|
|
|
|
|
|
|
switch ($feature) {
|
|
|
|
case self::FEATURE_TASKS:
|
|
|
|
case self::FEATURE_EXPENSES:
|
|
|
|
case self::FEATURE_QUOTES:
|
2022-06-13 11:59:24 +02:00
|
|
|
case self::FEATURE_PURCHASE_ORDERS:
|
2020-03-01 11:18:13 +01:00
|
|
|
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:
|
2023-12-01 14:30:33 +01:00
|
|
|
return $self_host || !empty($plan_details);
|
2020-03-01 11:18:13 +01:00
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
// Pro; No trial allowed, unless they're trialing enterprise with an active pro plan
|
2020-03-02 11:22:37 +01:00
|
|
|
case self::FEATURE_MORE_CLIENTS:
|
2023-12-01 14:30:33 +01:00
|
|
|
return $self_host || !empty($plan_details) && (!$plan_details['trial'] || !empty($this->getPlanDetails(false, false)));
|
2020-03-01 11:18:13 +01:00
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
// White Label
|
2020-03-02 11:22:37 +01:00
|
|
|
case self::FEATURE_WHITE_LABEL:
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!$self_host && $plan_details && !$plan_details['expires']) {
|
2020-03-01 11:18:13 +01:00
|
|
|
return false;
|
|
|
|
}
|
2024-01-14 05:05:00 +01:00
|
|
|
// Fallthrough
|
|
|
|
// no break
|
2020-03-02 11:22:37 +01:00
|
|
|
case self::FEATURE_REMOVE_CREATED_BY:
|
2023-12-01 14:30:33 +01:00
|
|
|
return !empty($plan_details); // A plan is required even for self-hosted users
|
2020-03-01 11:18:13 +01:00
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
// Enterprise; No Trial allowed; grandfathered for old pro users
|
2023-12-01 14:30:33 +01:00
|
|
|
case self::FEATURE_USERS: // Grandfathered for old Pro users
|
2021-03-07 11:14:53 +01:00
|
|
|
if ($plan_details && $plan_details['trial']) {
|
2020-03-01 11:18:13 +01:00
|
|
|
// Do they have a non-trial plan?
|
2021-03-07 11:14:53 +01:00
|
|
|
$plan_details = $this->getPlanDetails(false, false);
|
2020-03-01 11:18:13 +01:00
|
|
|
}
|
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
return $self_host || !empty($plan_details) && ($plan_details['plan'] == self::PLAN_ENTERPRISE);
|
2020-03-01 11:18:13 +01:00
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
// Enterprise; No Trial allowed
|
2020-03-02 11:22:37 +01:00
|
|
|
case self::FEATURE_DOCUMENTS:
|
|
|
|
case self::FEATURE_USER_PERMISSIONS:
|
2023-12-01 14:30:33 +01:00
|
|
|
return $self_host || !empty($plan_details) && $plan_details['plan'] == self::PLAN_ENTERPRISE && !$plan_details['trial'];
|
2020-03-01 11:18:13 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function isPaid(): bool
|
2020-03-01 11:18:13 +01:00
|
|
|
{
|
2024-04-02 05:19:51 +02:00
|
|
|
return Ninja::isNinja() ? $this->isPaidHostedClient() : $this->hasFeature(self::FEATURE_WHITE_LABEL);
|
2020-03-01 11:18:13 +01:00
|
|
|
}
|
|
|
|
|
2024-02-09 06:14:51 +01:00
|
|
|
public function isPremium(): bool
|
|
|
|
{
|
2024-02-13 07:33:00 +01:00
|
|
|
// return true;
|
2024-02-09 06:14:51 +01:00
|
|
|
return Ninja::isHosted() && $this->isPaidHostedClient() && !$this->isTrial() && Carbon::createFromTimestamp($this->created_at)->diffInMonths() > 2;
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function isPaidHostedClient(): bool
|
2020-03-01 11:18:13 +01:00
|
|
|
{
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!Ninja::isNinja()) {
|
2020-03-21 06:37:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-02 11:22:37 +01:00
|
|
|
|
2023-03-09 05:45:37 +01:00
|
|
|
// 09-03-2023 - winds forward expiry checks to ensure we don't cut off users prior to billing cycle being commenced
|
|
|
|
if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()->subHours(12))) {
|
2022-07-22 03:00:29 +02:00
|
|
|
return false;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-07-22 03:00:29 +02:00
|
|
|
|
2020-03-01 11:18:13 +01:00
|
|
|
return $this->plan == 'pro' || $this->plan == 'enterprise';
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function isFreeHostedClient(): bool
|
2020-04-21 07:16:45 +02:00
|
|
|
{
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!Ninja::isNinja()) {
|
2020-04-21 07:16:45 +02:00
|
|
|
return false;
|
2020-09-06 11:38:10 +02:00
|
|
|
}
|
2020-04-21 07:16:45 +02:00
|
|
|
|
2023-03-09 05:45:37 +01:00
|
|
|
if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()->subHours(12))) {
|
2022-07-22 03:00:29 +02:00
|
|
|
return true;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-07-22 03:00:29 +02:00
|
|
|
|
|
|
|
return $this->plan == 'free' || is_null($this->plan) || empty($this->plan);
|
2020-04-21 07:16:45 +02:00
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function isEnterpriseClient(): bool
|
2020-04-23 00:54:10 +02:00
|
|
|
{
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!Ninja::isNinja()) {
|
2020-04-23 00:54:10 +02:00
|
|
|
return false;
|
2020-09-06 11:38:10 +02:00
|
|
|
}
|
2020-04-23 00:54:10 +02:00
|
|
|
|
|
|
|
return $this->plan == 'enterprise';
|
|
|
|
}
|
|
|
|
|
2023-12-20 09:53:03 +01:00
|
|
|
public function isEnterprisePaidClient(): bool
|
|
|
|
{
|
|
|
|
if (! Ninja::isNinja()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->isEnterpriseClient() && $this->isPaid();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function isProClient(): bool
|
|
|
|
{
|
|
|
|
if (! Ninja::isNinja()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->plan == 'pro';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function isProPaidClient(): bool
|
|
|
|
{
|
|
|
|
if (! Ninja::isNinja()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->isProClient() && $this->isPaid();
|
|
|
|
}
|
|
|
|
|
2024-07-30 08:19:07 +02:00
|
|
|
public function isNewHostedAccount()
|
|
|
|
{
|
|
|
|
return Ninja::isHosted() && Carbon::createFromTimestamp($this->created_at)->diffInWeeks() <= 2;
|
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function isTrial(): bool
|
2020-03-01 11:18:13 +01:00
|
|
|
{
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!Ninja::isNinja()) {
|
2020-03-01 11:18:13 +01:00
|
|
|
return false;
|
2020-03-21 06:37:30 +01:00
|
|
|
}
|
2020-03-01 11:18:13 +01:00
|
|
|
|
2024-01-27 05:43:37 +01:00
|
|
|
return !$this->plan_paid && $this->trial_started && Carbon::parse($this->trial_started)->addDays(14)->gte(now()->subHours(12));
|
2024-07-30 08:19:07 +02:00
|
|
|
|
2020-03-01 11:18:13 +01:00
|
|
|
}
|
|
|
|
|
2023-08-04 08:40:44 +02:00
|
|
|
public function startTrial($plan): void
|
2021-06-27 11:20:56 +02:00
|
|
|
{
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!Ninja::isNinja()) {
|
2021-06-27 11:20:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->trial_started && $this->trial_started != '0000-00-00') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->trial_plan = $plan;
|
|
|
|
$this->trial_started = now();
|
|
|
|
$this->save();
|
|
|
|
}
|
|
|
|
|
2020-03-01 11:18:13 +01:00
|
|
|
public function getPlanDetails($include_inactive = false, $include_trial = true)
|
|
|
|
{
|
|
|
|
$plan = $this->plan;
|
|
|
|
$price = $this->plan_price;
|
|
|
|
$trial_plan = $this->trial_plan;
|
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
if ((!$plan || $plan == self::PLAN_FREE) && (!$trial_plan || !$include_trial)) {
|
2020-03-01 11:18:13 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$trial_active = false;
|
2023-04-26 16:11:31 +02:00
|
|
|
$trial_expires = false;
|
|
|
|
$trial_started = false;
|
2021-04-06 06:02:27 +02:00
|
|
|
|
2021-10-04 01:17:26 +02:00
|
|
|
//14 day trial
|
2023-12-01 14:30:33 +01:00
|
|
|
$duration = 60 * 60 * 24 * 14;
|
2021-10-04 01:17:26 +02:00
|
|
|
|
2020-03-01 11:18:13 +01:00
|
|
|
if ($trial_plan && $include_trial) {
|
2021-04-06 05:05:40 +02:00
|
|
|
$trial_started = $this->trial_started;
|
2021-10-04 01:17:26 +02:00
|
|
|
$trial_expires = Carbon::parse($this->trial_started)->addSeconds($duration);
|
2020-03-01 11:18:13 +01:00
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
if ($trial_expires->greaterThan(now())) {
|
2020-03-01 11:18:13 +01:00
|
|
|
$trial_active = true;
|
2023-12-01 14:30:33 +01:00
|
|
|
}
|
2020-03-01 11:18:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$plan_active = false;
|
2023-04-26 16:11:31 +02:00
|
|
|
$plan_expires = false;
|
2023-12-10 08:18:31 +01:00
|
|
|
|
2020-03-01 11:18:13 +01:00
|
|
|
if ($plan) {
|
|
|
|
if ($this->plan_expires == null) {
|
|
|
|
$plan_active = true;
|
|
|
|
} else {
|
2021-04-10 00:27:02 +02:00
|
|
|
$plan_expires = Carbon::parse($this->plan_expires);
|
|
|
|
if ($plan_expires->greaterThan(now())) {
|
2020-03-01 11:18:13 +01:00
|
|
|
$plan_active = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!$include_inactive && !$plan_active && !$trial_active) {
|
2020-03-01 11:18:13 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should we show plan details or trial details?
|
2023-12-01 14:30:33 +01:00
|
|
|
if (($plan && !$trial_plan) || !$include_trial) {
|
2020-03-01 11:18:13 +01:00
|
|
|
$use_plan = true;
|
2023-12-01 14:30:33 +01:00
|
|
|
} elseif (!$plan && $trial_plan) {
|
2020-03-01 11:18:13 +01:00
|
|
|
$use_plan = false;
|
|
|
|
} else {
|
|
|
|
// There is both a plan and a trial
|
2023-12-01 14:30:33 +01:00
|
|
|
if (!empty($plan_active) && empty($trial_active)) {
|
2020-03-01 11:18:13 +01:00
|
|
|
$use_plan = true;
|
2023-12-01 14:30:33 +01:00
|
|
|
} elseif (empty($plan_active) && !empty($trial_active)) {
|
2020-03-01 11:18:13 +01:00
|
|
|
$use_plan = false;
|
2023-12-01 14:30:33 +01:00
|
|
|
} elseif (!empty($plan_active) && !empty($trial_active)) {
|
2020-03-01 11:18:13 +01:00
|
|
|
// 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,
|
2022-09-14 00:54:59 +02:00
|
|
|
'started' => $this->plan_started ? DateTime::createFromFormat('Y-m-d', $this->plan_started) : false,
|
2020-03-01 11:18:13 +01:00
|
|
|
'expires' => $plan_expires,
|
2022-09-14 00:54:59 +02:00
|
|
|
'paid' => $this->plan_paid ? DateTime::createFromFormat('Y-m-d', $this->plan_paid) : false,
|
2020-03-01 11:18:13 +01:00
|
|
|
'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,
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
2021-06-11 00:20:46 +02:00
|
|
|
|
2021-08-07 11:55:18 +02:00
|
|
|
public function getDailyEmailLimit()
|
|
|
|
{
|
2023-02-16 02:36:09 +01:00
|
|
|
if ($this->is_flagged) {
|
2022-06-30 02:11:55 +02:00
|
|
|
return 0;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2021-08-07 11:55:18 +02:00
|
|
|
|
2024-06-14 09:09:44 +02:00
|
|
|
if($this->email_quota) {
|
2024-06-13 02:21:58 +02:00
|
|
|
return (int)$this->email_quota;
|
2024-06-14 09:09:44 +02:00
|
|
|
}
|
|
|
|
|
2024-03-18 02:58:42 +01:00
|
|
|
if (Carbon::createFromTimestamp($this->created_at)->diffInWeeks() <= 1) {
|
2022-06-19 05:01:29 +02:00
|
|
|
return 20;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-06-18 00:54:58 +02:00
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
if (Carbon::createFromTimestamp($this->created_at)->diffInWeeks() <= 2 && !$this->payment_id) {
|
2022-06-29 03:37:40 +02:00
|
|
|
return 20;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-06-29 03:37:40 +02:00
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
if ($this->isPaid()) {
|
2024-03-17 06:06:06 +01:00
|
|
|
$multiplier = $this->plan == 'enterprise' ? 2 : 1.2;
|
|
|
|
|
2021-08-08 13:50:13 +02:00
|
|
|
$limit = $this->paid_plan_email_quota;
|
2024-03-17 06:06:06 +01:00
|
|
|
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * (20 * $multiplier);
|
2023-12-01 14:30:33 +01:00
|
|
|
} else {
|
2021-08-08 13:50:13 +02:00
|
|
|
$limit = $this->free_plan_email_quota;
|
2024-03-17 06:04:44 +01:00
|
|
|
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 1.5;
|
2021-08-08 13:50:13 +02:00
|
|
|
}
|
2021-08-07 11:55:18 +02:00
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
return min($limit, 1000);
|
2021-08-07 11:55:18 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 03:40:58 +02:00
|
|
|
public function emailsSent()
|
|
|
|
{
|
2023-12-10 08:18:31 +01:00
|
|
|
if (is_null(Cache::get("email_quota" . $this->key))) {
|
2021-08-10 03:40:58 +02:00
|
|
|
return 0;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2021-08-10 03:40:58 +02:00
|
|
|
|
2023-12-10 08:18:31 +01:00
|
|
|
return Cache::get("email_quota" . $this->key);
|
2023-12-01 14:30:33 +01:00
|
|
|
}
|
2021-08-07 11:55:18 +02:00
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
public function emailQuotaExceeded(): bool
|
2021-08-07 11:55:18 +02:00
|
|
|
{
|
2023-12-10 08:18:31 +01:00
|
|
|
if (is_null(Cache::get("email_quota" . $this->key))) {
|
2021-08-07 11:55:18 +02:00
|
|
|
return false;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2021-08-07 11:55:18 +02:00
|
|
|
|
2021-08-09 12:33:59 +02:00
|
|
|
try {
|
2023-12-10 08:18:31 +01:00
|
|
|
if (Cache::get("email_quota" . $this->key) > $this->getDailyEmailLimit()) {
|
2023-12-01 14:30:33 +01:00
|
|
|
if (is_null(Cache::get("throttle_notified:{$this->key}"))) {
|
2021-08-31 13:29:18 +02:00
|
|
|
App::forgetInstance('translator');
|
|
|
|
$t = app('translator');
|
|
|
|
$t->replace(Ninja::transformTranslations($this->companies()->first()->settings));
|
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
$nmo = new NinjaMailerObject();
|
2021-08-09 12:33:59 +02:00
|
|
|
$nmo->mailable = new EmailQuotaExceeded($this->companies()->first());
|
|
|
|
$nmo->company = $this->companies()->first();
|
|
|
|
$nmo->settings = $this->companies()->first()->settings;
|
|
|
|
$nmo->to_user = $this->companies()->first()->owner();
|
2022-07-28 02:58:13 +02:00
|
|
|
NinjaMailerJob::dispatch($nmo, true);
|
2021-08-07 12:56:42 +02:00
|
|
|
|
2023-07-19 00:32:10 +02:00
|
|
|
Cache::put("throttle_notified:{$this->key}", true, 60 * 60 * 24);
|
2021-08-31 13:29:18 +02:00
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
if (config('ninja.notification.slack')) {
|
2021-08-31 13:29:18 +02:00
|
|
|
$this->companies()->first()->notification(new EmailQuotaNotification($this))->ninja();
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2021-08-09 12:33:59 +02:00
|
|
|
}
|
2021-08-07 12:56:42 +02:00
|
|
|
|
2021-08-09 12:33:59 +02:00
|
|
|
return true;
|
|
|
|
}
|
2023-12-01 14:30:33 +01:00
|
|
|
} catch (\Exception $e) {
|
2021-10-01 00:55:35 +02:00
|
|
|
\Sentry\captureMessage("I encountered an error with email quotas for account {$this->key} - defaulting to SEND");
|
2021-08-07 12:56:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2021-08-07 11:55:18 +02:00
|
|
|
}
|
|
|
|
|
2023-12-01 14:30:33 +01:00
|
|
|
public function gmailCredentialNotification(): bool
|
2022-03-23 22:34:52 +01:00
|
|
|
{
|
2022-05-03 01:45:40 +02:00
|
|
|
nlog("checking if gmail credential notification has already been sent");
|
2022-03-23 22:34:52 +01:00
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
if (is_null(Cache::get($this->key))) {
|
2022-03-23 22:34:52 +01:00
|
|
|
return false;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-03-23 22:34:52 +01:00
|
|
|
|
2022-05-03 01:45:40 +02:00
|
|
|
nlog("Sending notification");
|
2023-12-01 14:30:33 +01:00
|
|
|
|
2022-03-23 22:34:52 +01:00
|
|
|
try {
|
2023-12-01 14:30:33 +01:00
|
|
|
if (is_null(Cache::get("gmail_credentials_notified:{$this->key}"))) {
|
2022-03-23 22:34:52 +01:00
|
|
|
App::forgetInstance('translator');
|
|
|
|
$t = app('translator');
|
|
|
|
$t->replace(Ninja::transformTranslations($this->companies()->first()->settings));
|
|
|
|
|
2024-01-14 05:05:00 +01:00
|
|
|
$nmo = new NinjaMailerObject();
|
2022-03-23 22:34:52 +01:00
|
|
|
$nmo->mailable = new GmailTokenInvalid($this->companies()->first());
|
|
|
|
$nmo->company = $this->companies()->first();
|
|
|
|
$nmo->settings = $this->companies()->first()->settings;
|
|
|
|
$nmo->to_user = $this->companies()->first()->owner();
|
2022-07-28 02:58:13 +02:00
|
|
|
NinjaMailerJob::dispatch($nmo, true);
|
2022-03-23 22:34:52 +01:00
|
|
|
|
|
|
|
Cache::put("gmail_credentials_notified:{$this->key}", true, 60 * 24);
|
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
if (config('ninja.notification.slack')) {
|
2022-03-23 22:34:52 +01:00
|
|
|
$this->companies()->first()->notification(new GmailCredentialNotification($this))->ninja();
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-03-23 22:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2023-12-01 14:30:33 +01:00
|
|
|
} catch (\Exception $e) {
|
2022-03-23 22:34:52 +01:00
|
|
|
\Sentry\captureMessage("I encountered an error with sending with gmail for account {$this->key}");
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-06-16 07:58:11 +02:00
|
|
|
public function resolveRouteBinding($value, $field = null)
|
|
|
|
{
|
|
|
|
if (is_numeric($value)) {
|
|
|
|
throw new ModelNotFoundException("Record with value {$value} not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this
|
2023-01-22 06:34:47 +01:00
|
|
|
->where('id', $this->decodePrimaryKey($value))
|
|
|
|
->firstOrFail();
|
2022-06-16 07:58:11 +02:00
|
|
|
}
|
|
|
|
|
2022-07-11 02:42:05 +02:00
|
|
|
public function getTrialDays()
|
|
|
|
{
|
2023-06-09 13:17:43 +02:00
|
|
|
if ($this->payment_id || $this->is_migrated) {
|
2022-07-11 02:42:05 +02:00
|
|
|
return 0;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2022-07-11 02:42:05 +02:00
|
|
|
|
|
|
|
$plan_expires = Carbon::parse($this->plan_expires);
|
|
|
|
|
2023-04-26 16:11:31 +02:00
|
|
|
if ($plan_expires->gt(now())) {
|
2024-06-07 01:15:19 +02:00
|
|
|
$diff = intval(abs($plan_expires->diffInDays()));
|
2023-12-01 14:30:33 +01:00
|
|
|
|
2023-10-26 04:57:44 +02:00
|
|
|
if ($diff > 14) {
|
2023-04-26 16:11:31 +02:00
|
|
|
return 0;
|
2023-10-26 04:57:44 +02:00
|
|
|
}
|
2022-07-17 12:33:34 +02:00
|
|
|
|
|
|
|
return $diff;
|
|
|
|
}
|
2022-07-11 02:42:05 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-10-12 13:29:34 +02:00
|
|
|
}
|