<?php
namespace App\Models;
use App\Library\Tool;
use Carbon\Carbon;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Translation\Translator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @method static where(string $string, bool $true)
* @method static select(string $string)
* @method static count()
* @method static offset(mixed $start)
* @method static whereLike(string[] $array, mixed $search)
* @method static cursor()
* @method static find(mixed $plan_id)
* @method static create(array $plan)
* @method static whereIn(string $string, array $ids)
* @property mixed frequency_unit
* @property mixed frequency_amount
* @property bool|mixed status
* @property mixed id
* @property mixed name
* @property mixed currency
* @property mixed price
*/
class Plan extends Model
{
// Plan status
const STATUS_INACTIVE = false;
const STATUS_ACTIVE = true;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id',
'name',
'description',
'billing_cycle',
'frequency_amount',
'frequency_unit',
'price',
'currency_id',
'options',
'status',
'is_popular',
'tax_billing_required',
'show_in_customer',
];
protected $casts = [
'status' => 'boolean',
'show_in_customer' => 'boolean',
'is_popular' => 'boolean',
'tax_billing_required' => 'boolean',
];
/**
* Bootstrap any application services.
*/
public static function boot()
{
parent::boot();
// Create uid when creating list.
static::creating(function ($item) {
// Create new uid
$uid = uniqid();
while (self::where('uid', $uid)->count() > 0) {
$uid = uniqid();
}
$item->uid = $uid;
// Update custom order
self::getAll()->increment('custom_order', 1);
$item->custom_order = 0;
});
}
/**
* Active status scope
*
* @param $query
* @param bool $status
*
* @return mixed
*/
public function scopeStatus($query, bool $status)
{
return $query->where('status', $status);
}
/**
* get user
*
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Plan Sending Servers
*
* @return HasMany
*/
public function plansSendingServers(): HasMany
{
return $this->hasMany(PlansSendingServer::class);
}
/**
* Plan Coverage countries
*
* @return HasMany
*/
public function plansCoverageCountries(): HasMany
{
return $this->hasMany(PlansCoverageCountries::class);
}
/**
* Currency
*
* @return BelongsTo
*
*/
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
/**
* get all plans
*
* @return mixed
*/
public static function getAll()
{
return self::select('*');
}
/**
* Find item by uid.
*
* @param $uid
*
* @return object
*/
public static function findByUid($uid): object
{
return self::where('uid', $uid)->first();
}
/**
* Frequency time unit options.
*
* @return array
*/
public static function timeUnitOptions(): array
{
return [
['value' => 'day', 'text' => 'day'],
['value' => 'week', 'text' => 'week'],
['value' => 'month', 'text' => 'month'],
['value' => 'year', 'text' => 'year'],
];
}
/**
* Get sending limit types.
*
* @return array
*/
public static function sendingLimitValues(): array
{
return [
'unlimited' => [
'quota_value' => -1,
'quota_base' => -1,
'quota_unit' => 'day',
],
'100_per_minute' => [
'quota_value' => 100,
'quota_base' => 1,
'quota_unit' => 'minute',
],
'1000_per_hour' => [
'quota_value' => 1000,
'quota_base' => 1,
'quota_unit' => 'hour',
],
'10000_per_hour' => [
'quota_value' => 10000,
'quota_base' => 1,
'quota_unit' => 'hour',
],
'50000_per_hour' => [
'quota_value' => 50000,
'quota_base' => 1,
'quota_unit' => 'hour',
],
'10000_per_day' => [
'quota_value' => 10000,
'quota_base' => 1,
'quota_unit' => 'day',
],
'100000_per_day' => [
'quota_value' => 100000,
'quota_base' => 1,
'quota_unit' => 'day',
],
];
}
/**
* Get billing recurs available values.
*
* @return array
*/
public static function billingCycleValues(): array
{
return [
'daily' => [
'frequency_amount' => 1,
'frequency_unit' => 'day',
],
'monthly' => [
'frequency_amount' => 1,
'frequency_unit' => 'month',
],
'yearly' => [
'frequency_amount' => 1,
'frequency_unit' => 'year',
],
];
}
/**
* Check if plan time is unlimited.
*
* @return bool
*/
public function isTimeUnlimited(): bool
{
return $this->frequency_unit == 'unlimited';
}
/**
* Display frequency time
*
* @return array|Application|Translator|string|null
*/
public function displayFrequencyTime()
{
// unlimited
if ($this->isTimeUnlimited()) {
return __('locale.labels.unlimited');
}
if ($this->frequency_amount == 1) {
return Tool::getPluralParse($this->frequency_unit, $this->frequency_amount);
}
return $this->frequency_amount.' '.Tool::getPluralParse($this->frequency_unit, $this->frequency_amount);
}
/**
* Display total quota
*
* @return Application|array|string|Translator|null
*/
public function displayTotalQuota()
{
if ($this->getOption('sms_max') == -1) {
return __('locale.labels.unlimited');
} else {
return Tool::format_number($this->getOption('sms_max'));
}
}
/**
* Display total quota
*
* @return Application|array|string|Translator|null
*/
public function displayWhatsAppQuota()
{
if ($this->getOption('sms_max') == -1) {
return __('locale.labels.unlimited');
} else {
return Tool::format_number($this->getOption('whatsapp_max'));
}
}
/**
* Display max lists.
*
* @return array|Application|Translator|string|null
*/
public function displayMaxList()
{
if ($this->getOption('list_max') == -1) {
return __('locale.labels.unlimited');
} else {
return Tool::format_number($this->getOption('list_max'));
}
}
/**
* Display max subscribers.
*
* @return array|Application|Translator|string|null
*/
public function displayMaxContact()
{
if ($this->getOption('subscriber_max') == -1) {
return __('locale.labels.unlimited');
} else {
return Tool::format_number($this->getOption('subscriber_max'));
}
}
/**
* Display max subscribers per list
*
* @return array|Application|Translator|string|null
*/
public function displayMaxContactPerList()
{
if ($this->getOption('subscriber_per_list_max') == -1) {
return __('locale.labels.unlimited');
} else {
return Tool::format_number($this->getOption('subscriber_per_list_max'));
}
}
/**
* get single option
*
* @param $name
*
* @return string
*/
public function getOption($name): string
{
return $this->getOptions()[$name];
}
/**
* Get sending limit select options.
*
* @return array
*/
public function getSendingLimitSelectOptions(): array
{
$options = [];
foreach (self::sendingLimitValues() as $key => $data) {
$wording = __('locale.plans.'.$key);
$options[] = ['text' => $wording, 'value' => $key];
}
// exist
if ($this->getOption('sending_limit') == 'custom') {
$wording = __('messages.plan.sending_limit.phrase', [
'quota_value' => Tool::format_number($this->getOption('sending_quota')),
'quota_base' => Tool::format_number($this->getOption('sending_quota_time')),
'quota_unit' => $this->getOption('sending_quota_time_unit'),
]);
$options[] = ['text' => $wording, 'value' => 'other'];
}
// Custom
$options[] = ['text' => 'Custom', 'value' => 'custom'];
return $options;
}
/**
* Get options.
*
* @return array
*/
public function getOptions(): array
{
if (empty($this->options)) {
return self::defaultOptions();
} else {
$default_options = self::defaultOptions();
$saved_options = json_decode($this->options, true);
foreach ($default_options as $x => $group) {
if (isset($saved_options[$x])) {
$default_options[$x] = $saved_options[$x];
}
}
return $default_options;
}
}
/**
* Default options for new plan.
*
* @return array
*/
public static function defaultOptions(): array
{
return [
'sms_max' => '100',
'whatsapp_max' => '100',
'list_max' => '-1',
'subscriber_max' => '-1',
'subscriber_per_list_max' => '-1',
'segment_per_list_max' => '3',
'billing_cycle' => 'monthly',
'sending_limit' => '1000_per_hour',
'sending_quota' => '1000',
'sending_quota_time' => '1',
'sending_quota_time_unit' => 'hour',
'max_process' => '1',
'unsubscribe_url_required' => 'no',
'create_sending_server' => 'no',
'sending_servers_max' => '5',
'list_import' => 'yes',
'list_export' => 'yes',
'api_access' => 'no',
'create_sub_account' => 'yes',
'delete_sms_history' => 'yes',
'add_previous_balance' => 'no',
'sender_id_verification' => 'yes',
'send_spam_message' => 'no',
'cutting_system' => 'no',
'cutting_value' => '0',
'cutting_unit' => 'percentage',
'cutting_logic' => 'random',
'plain_sms' => '1',
'receive_plain_sms' => '0',
'voice_sms' => '2',
'receive_voice_sms' => '0',
'mms_sms' => '3',
'receive_mms_sms' => '0',
'whatsapp_sms' => '1',
'receive_whatsapp_sms' => '0',
'per_unit_price' => '.3',
];
}
/**
* Disable plan.
*
* @return bool
*/
public function disable(): bool
{
$this->status = self::STATUS_INACTIVE;
return $this->save();
}
/**
* Enable plan.
*
* @return bool
*/
public function enable(): bool
{
$this->status = self::STATUS_ACTIVE;
return $this->save();
}
/**
* Get Primary sending server
*
* @return mixed|null
*/
public function primarySendingServer()
{
$pss = $this->plansSendingServers()->first();
return is_object($pss) ? $pss->sendingServer : null;
}
/**
* Get country coverage
*
* @return HasMany|Model|object|null
*/
public function pricingCoverage()
{
$pss = $this->plansCoverageCountries()->first();
return is_object($pss) ? $pss : null;
}
/**
* Check if plan has primary sending server.
*
* @return bool
*/
public function hasPrimarySendingServer(): bool
{
return is_object($this->primarySendingServer());
}
public function hasPricingCoverage(): bool
{
return is_object($this->pricingCoverage());
}
/**
* get active sending server
*
* @return mixed
*/
public function getActiveSendingServer()
{
return SendingServer::status(true)->get();
}
/**
* get available sending server
*
* @param $sending_server_ids
*
* @return mixed
*/
public function availableSendingServer($sending_server_ids)
{
return SendingServer::status(true)->whereNotIn('id', $sending_server_ids)->get();
}
/**
* get plan id
*
* @return string
*/
public function getBillableId(): string
{
return $this->id;
}
/**
* get plan name
*
* @return string
*/
public function getBillableName(): string
{
return $this->name;
}
/**
* get plan interval.
*
* @return string
*/
public function getBillableInterval(): string
{
return $this->frequency_unit;
}
/**
* get plan interval count.
*
* @return string
*/
public function getBillableIntervalCount(): string
{
return $this->frequency_amount;
}
/**
* get currency.
*
* @return string
*/
public function getBillableCurrency(): string
{
return $this->currency->code;
}
/**
* get plan interval count.
*
* @return string
*/
public function getBillableAmount(): string
{
return $this->price;
}
/**
* get plan interval count.
*
* @return string
*/
public function getBillableFormattedPrice(): string
{
return Tool::format_price($this->price, $this->currency->format);
}
/**
* get subscriptions
*
* @return HasMany
*/
public function subscriptions(): HasMany
{
return $this->hasMany(Subscription::class, 'plan_id', 'id')
->where(function ($query) {
$query->whereNull('end_at')
->orWhere('end_at', '>=', Carbon::now());
})
->orderBy('created_at', 'desc');
}
/**
* Customers count.
*
* @return int
*/
public function customersCount(): int
{
return $this->subscriptions()->distinct('user_id')->count('user_id');
}
/**
* check valid
*
* @return bool
*/
public function isValid(): bool
{
// use system sending server but has no primary sending server
if ( ! $this->hasPrimarySendingServer()) {
return false;
}
// else return true
return true;
}
/**
* Check status of sending server
*
* @return void
*/
public function checkStatus()
{
// disable sending server if it is not valid
if ( ! $this->isValid()) {
$this->disable();
}
}
/**
* get route key by uid
*
* @return string
*/
public function getRouteKeyName(): string
{
return 'uid';
}
}