2018-05-20 12:53:14 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
2021-06-30 04:00:41 +00:00
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
2018-05-20 12:53:14 +00:00
|
|
|
use Illuminate\Database\Eloquent\Model;
|
2019-07-02 05:28:27 +00:00
|
|
|
use Illuminate\Support\Arr;
|
2021-12-24 01:14:01 +00:00
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
use Illuminate\Support\Facades\Auth;
|
2018-08-11 05:09:41 +00:00
|
|
|
use Illuminate\Http\Request;
|
2022-02-01 05:40:46 +00:00
|
|
|
use Illuminate\Support\Facades\Log;
|
2021-12-24 01:14:01 +00:00
|
|
|
use Leenooks\Traits\ScopeActive;
|
2018-05-20 12:53:14 +00:00
|
|
|
|
2021-06-29 06:36:34 +00:00
|
|
|
use App\Interfaces\IDs;
|
2021-12-24 01:14:01 +00:00
|
|
|
use App\Traits\{ProductDetails,SiteID};
|
2019-07-02 05:28:27 +00:00
|
|
|
|
2021-06-29 06:36:34 +00:00
|
|
|
/**
|
|
|
|
* Class Product
|
2021-12-24 01:14:01 +00:00
|
|
|
* Products that are available to sale, and appear on invoices.
|
|
|
|
*
|
|
|
|
* Products have one Type (Product/*), made of an Offering (Supplier/*) from a Supplier.
|
|
|
|
* Conversely, Suppliers provide Offerings (Supplier/*) which belong to a Type (Product/*) of a Product.
|
2021-06-29 06:36:34 +00:00
|
|
|
*
|
|
|
|
* Attributes for products:
|
|
|
|
* + lid : Local ID for product (part number)
|
2021-12-24 01:14:01 +00:00
|
|
|
* + supplied : Supplier product provided for this offering
|
|
|
|
* + supplier : Supplier for this offering
|
|
|
|
* + name : Brief Name for our product
|
|
|
|
* + name_short : Product ID for our Product
|
|
|
|
* + name_long : Long Name for our product
|
|
|
|
* + billing_interval : Default Billing Interval
|
|
|
|
* + billing_interval_string: Default Billing Interval in human-readable form
|
|
|
|
* + setup_charge : Charge to setup this product
|
|
|
|
* + setup_charge_taxable : Charge to setup this product including taxes
|
|
|
|
* + base_charge : Default billing amount
|
|
|
|
* + base_charge_taxable : Default billing amount including taxes
|
|
|
|
* + min_charge : Minimum cost taking into account billing interval and setup costs
|
|
|
|
* + min_charge_taxable : Minimum cost taking into account billing interval and setup costs including taxes
|
|
|
|
*
|
|
|
|
* Attributes for product types (type - Product/*)
|
|
|
|
* + name : Short Name for our Product
|
|
|
|
* + name_long : Long Name for our Product
|
|
|
|
* + description : Description of offering (Broadband=speed)
|
|
|
|
*
|
|
|
|
* Attributes for supplier's offerings (type->supplied - Supplier/*)
|
|
|
|
* + name : Short Name for suppliers offering
|
|
|
|
* + name_long : Long Name for suppliers offering
|
|
|
|
* + description : Description of offering (Broadband=speed)
|
|
|
|
*
|
|
|
|
* Product Pricing self::pricing is an array of:
|
|
|
|
* [
|
|
|
|
* timeperiod => [
|
|
|
|
* show => true|false (show this time period to the user for ordering)
|
|
|
|
* group => [ pricing/setup ]
|
|
|
|
* ]
|
|
|
|
* ]
|
2021-06-29 06:36:34 +00:00
|
|
|
*
|
2021-12-24 01:14:01 +00:00
|
|
|
* @todo doesnt appear that price_type is used - but could be used to have different offering types billed differently
|
2021-06-29 06:36:34 +00:00
|
|
|
* @package App\Models
|
|
|
|
*/
|
|
|
|
class Product extends Model implements IDs
|
2018-05-20 12:53:14 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
use HasFactory,SiteID,ProductDetails,ScopeActive;
|
2020-02-12 10:32:57 +00:00
|
|
|
|
|
|
|
protected $casts = [
|
2021-12-24 01:14:01 +00:00
|
|
|
'pricing'=>'collection',
|
2020-02-12 10:32:57 +00:00
|
|
|
];
|
|
|
|
|
2021-02-17 13:22:50 +00:00
|
|
|
/* RELATIONS */
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Get the product name in the users language, and if the user isnt logged in, the sites language
|
|
|
|
*
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
|
|
|
*/
|
|
|
|
public function description()
|
2018-05-20 12:53:14 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->hasOne(ProductTranslate::class)
|
|
|
|
->where('language_id',(Auth::user() && Auth::user()->language_id) ? Auth::user()->language_id : config('site')->language_id);
|
2018-05-20 12:53:14 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Which services are configured with this product
|
|
|
|
*
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
|
|
*/
|
2018-05-20 12:53:14 +00:00
|
|
|
public function services()
|
|
|
|
{
|
|
|
|
return $this->hasMany(Service::class);
|
|
|
|
}
|
|
|
|
|
2019-07-02 05:28:27 +00:00
|
|
|
/**
|
|
|
|
* Return a child model with details of the service
|
|
|
|
*
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
|
|
|
*/
|
|
|
|
public function type()
|
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->morphTo(null,'model','model_id');
|
2019-07-02 05:28:27 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/* INTERFACES */
|
2018-08-09 14:10:51 +00:00
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
public function getLIDAttribute(): string
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return sprintf('%04s',$this->id);
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
public function getSIDAttribute(): string
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return sprintf('%02s-%s',$this->site_id,$this->getLIDattribute());
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/* ATTRIBUTES */
|
2018-08-11 05:09:41 +00:00
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* The amount we invoice each time period for this service
|
|
|
|
*
|
|
|
|
* @param int|NULL $timeperiod
|
|
|
|
* @param Group|NULL $go
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getBaseChargeAttribute(int $timeperiod=NULL,Group $go=NULL): float
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->getCharge('base',$timeperiod,$go);
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* The amount we invoice each time period for this service, including taxes
|
|
|
|
*
|
|
|
|
* @param int|null $timeperiod
|
|
|
|
* @param Group|null $go
|
|
|
|
* @param Collection|NULL $taxes
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getBaseChargeTaxableAttribute(int $timeperiod=NULL,Group $go=NULL,Collection $taxes=NULL): float
|
2018-08-09 14:10:51 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return Tax::tax_calc($this->getBaseChargeAttribute($timeperiod,$go),$taxes ?: config('site')->taxes);
|
2018-08-09 14:10:51 +00:00
|
|
|
}
|
|
|
|
|
2021-06-29 06:36:34 +00:00
|
|
|
/**
|
2021-12-24 01:14:01 +00:00
|
|
|
* The base cost of this product at the appropriate billing interval
|
2021-06-29 06:36:34 +00:00
|
|
|
*
|
2021-12-24 01:14:01 +00:00
|
|
|
* @return float
|
2021-06-29 06:36:34 +00:00
|
|
|
*/
|
2021-12-24 01:14:01 +00:00
|
|
|
public function getBaseCostAttribute(): float
|
2021-06-29 06:36:34 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return round($this->type->supplied->base_cost*Invoice::billing_change($this->type->supplied->getBillingIntervalAttribute(),$this->getBillingIntervalAttribute()) ?: 0,2);
|
2021-06-29 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* The base cost of this product at the appropriate billing interval including taxes
|
|
|
|
*
|
|
|
|
* @param Collection|NULL $taxes
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getBaseCostTaxableAttribute(Collection $taxes=NULL): float
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return Tax::tax_calc($this->getBaseCostAttribute(),$taxes ?: config('site')->taxes);;
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Our default billing interval
|
|
|
|
* Its the max of what we define, or what the supplier bills us at
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getBillingIntervalAttribute(): int
|
2018-08-09 14:10:51 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return max($this->price_recur_default,$this->type->supplied->getBillingIntervalAttribute());
|
2018-08-09 14:10:51 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* How long must this product be purchased for as a service.
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getContractTermAttribute(): int
|
2019-07-02 05:28:27 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->type->getContractTermAttribute();
|
2019-07-02 05:28:27 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Get the minimum cost of this product
|
|
|
|
*
|
|
|
|
* @param int|null $timeperiod
|
|
|
|
* @param Group|null $go
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getMinChargeAttribute(int $timeperiod=NULL,Group $go=NULL): float
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->getSetupChargeAttribute($timeperiod,$go)+$this->getBaseChargeAttribute($timeperiod,$go)*Invoice::billing_term($this->getContractTermAttribute(),$this->getBillingIntervalAttribute());
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Get the minimum cost of this product with taxes
|
|
|
|
*
|
|
|
|
* @param int|null $timeperiod
|
|
|
|
* @param Group|null $go
|
|
|
|
* @param Collection|NULL $taxes
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getMinChargeTaxableAttribute(int $timeperiod=NULL,Group $go=NULL,Collection $taxes=NULL): float
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return Tax::tax_calc($this->getMinChargeAttribute($timeperiod,$go),$taxes ?: config('site')->taxes);
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Our products short descriptive name
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getNameAttribute(): string
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->description ? $this->description->description_short : 'Unknown PRODUCT';
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Our products PID
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getNameShortAttribute(): string
|
2018-08-01 07:09:38 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->description ? $this->description->name : 'Unknown PID';
|
2018-08-01 07:09:38 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* This product full description
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getNameLongAttribute(): string
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->description->description_full;
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-29 06:36:34 +00:00
|
|
|
/**
|
2021-12-24 01:14:01 +00:00
|
|
|
* Get our product type
|
2021-06-29 06:36:34 +00:00
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
2021-12-24 01:14:01 +00:00
|
|
|
public function getProductTypeAttribute(): string
|
2021-06-29 06:36:34 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return ($this->type && $this->type->supplied) ? $this->type->supplied->getTypeAttribute() : 'Unknown';
|
2021-06-29 06:36:34 +00:00
|
|
|
}
|
|
|
|
|
2021-02-17 13:22:50 +00:00
|
|
|
/**
|
2021-12-24 01:14:01 +00:00
|
|
|
* The charge to setup this service
|
2021-02-17 13:22:50 +00:00
|
|
|
*
|
2021-12-24 01:14:01 +00:00
|
|
|
* @param int|null $timeperiod
|
|
|
|
* @param Group|null $go
|
|
|
|
* @return float
|
2021-02-17 13:22:50 +00:00
|
|
|
*/
|
2021-12-24 01:14:01 +00:00
|
|
|
public function getSetupChargeAttribute(int $timeperiod=NULL,Group $go=NULL): float
|
2021-02-17 13:22:50 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->getCharge('setup',$timeperiod,$go);
|
2021-02-17 13:22:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* The charge to setup this service including taxes
|
|
|
|
*
|
|
|
|
* @param int|null $timeperiod
|
|
|
|
* @param Group|null $go
|
|
|
|
* @param Collection|null $taxes
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getSetupChargeTaxableAttribute(int $timeperiod=NULL,Group $go=NULL,Collection $taxes=NULL): float
|
2018-08-09 14:10:51 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return Tax::tax_calc($this->getSetupChargeAttribute($timeperiod,$go),$taxes ?: config('site')->taxes);
|
2018-08-09 14:10:51 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* The charge to setup this service
|
|
|
|
*
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getSetupCostAttribute(): float
|
2018-08-09 14:10:51 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->type->supplied->setup_cost ?: 0;
|
2018-08-09 14:10:51 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* The charge to setup this service
|
|
|
|
*
|
|
|
|
* @param Collection|null $taxes
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
public function getSetupCostTaxableAttribute(Collection $taxes=NULL): float
|
2018-08-09 14:10:51 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return Tax::tax_calc($this->getSetupCostAttribute(),$taxes ?: config('site')->taxes);;
|
2018-08-11 05:09:41 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/* METHODS */
|
2018-08-11 05:09:41 +00:00
|
|
|
|
2019-07-04 04:55:05 +00:00
|
|
|
/**
|
2021-12-24 01:14:01 +00:00
|
|
|
* Return if this product captures usage data
|
2019-07-04 04:55:05 +00:00
|
|
|
*
|
2021-12-24 01:14:01 +00:00
|
|
|
* @return bool
|
2019-07-04 04:55:05 +00:00
|
|
|
*/
|
2021-12-24 01:14:01 +00:00
|
|
|
public function hasUsage(): bool
|
2019-07-02 05:28:27 +00:00
|
|
|
{
|
2022-02-01 05:40:46 +00:00
|
|
|
return $this->type->hasUsage();
|
2019-07-02 05:28:27 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:14:01 +00:00
|
|
|
/**
|
|
|
|
* Get a charge value from the pricing array
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @param int|NULL $timeperiod
|
|
|
|
* @param Group|NULL $go
|
|
|
|
* @return float
|
|
|
|
*/
|
|
|
|
private function getCharge(string $type,int $timeperiod=NULL,Group $go=NULL): float
|
2018-08-11 05:09:41 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
static $default = NULL;
|
|
|
|
if (! $go) {
|
|
|
|
if (is_null($default))
|
|
|
|
$default = Group::findOrFail(0); // All public users
|
|
|
|
|
|
|
|
$go = $default;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_null($timeperiod))
|
|
|
|
$timeperiod = $this->getBillingIntervalAttribute();
|
|
|
|
|
|
|
|
// If the price doesnt exist for $go->id, use $go->id = 0 which is all users.
|
2022-02-01 05:40:46 +00:00
|
|
|
if (! $price=Arr::get($this->pricing,sprintf('%d.%d.%s',$timeperiod,$go->id,$type))) {
|
|
|
|
$alt_tp = $timeperiod;
|
|
|
|
|
|
|
|
while (is_null($price=Arr::get($this->pricing,sprintf('%d.%d.%s',$alt_tp,0,$type))) && ($alt_tp >= 0)) {
|
|
|
|
$alt_tp--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! is_null($price) && $alt_tp !== $timeperiod) {
|
|
|
|
$price = $price*Invoice::billing_change($alt_tp,$timeperiod);
|
|
|
|
}
|
|
|
|
}
|
2021-12-24 01:14:01 +00:00
|
|
|
|
|
|
|
// @todo - if price doesnt exist for the time period, reduce down to timeperiod 1 and multiply appropriately.
|
2022-02-01 05:40:46 +00:00
|
|
|
if (is_null($price)) {
|
|
|
|
Log::error(sprintf('Price is still null for [%d] timeperiod [%d] group [%d]',$this->id,$timeperiod,$go->id));
|
|
|
|
|
|
|
|
$price = 0;
|
|
|
|
}
|
2021-12-24 01:14:01 +00:00
|
|
|
|
|
|
|
return round($price,2);
|
2018-08-09 14:10:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-20 12:53:14 +00:00
|
|
|
/**
|
2021-12-24 01:14:01 +00:00
|
|
|
* When receiving an order, validate that we have all the required information for the product type
|
2018-05-20 12:53:14 +00:00
|
|
|
*
|
2021-12-24 01:14:01 +00:00
|
|
|
* @param Request $request
|
|
|
|
* @return mixed
|
2018-05-20 12:53:14 +00:00
|
|
|
*/
|
2021-12-24 01:14:01 +00:00
|
|
|
public function orderValidation(Request $request): ?Model
|
2018-05-20 12:53:14 +00:00
|
|
|
{
|
2021-12-24 01:14:01 +00:00
|
|
|
return $this->type->orderValidation($request);
|
2018-05-20 12:53:14 +00:00
|
|
|
}
|
|
|
|
}
|