Work on products, first completed broadband
This commit is contained in:
@@ -7,25 +7,22 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\NextKey;
|
||||
|
||||
/**
|
||||
* Class Account
|
||||
* Service Accounts
|
||||
*
|
||||
* Attributes for accounts:
|
||||
* + lid: : Local ID for account
|
||||
* + sid: : System ID for account
|
||||
* + name: : Account Name
|
||||
* + lid : Local ID for account
|
||||
* + sid : System ID for account
|
||||
* + name : Account Name
|
||||
* + taxes : Taxes Applicable to this account
|
||||
*
|
||||
* @package App\Models
|
||||
*/
|
||||
class Account extends Model implements IDs
|
||||
{
|
||||
use HasFactory,NextKey,ScopeActive;
|
||||
|
||||
const RECORD_ID = 'account';
|
||||
public $incrementing = FALSE;
|
||||
use HasFactory,ScopeActive;
|
||||
|
||||
const CREATED_AT = 'date_orig';
|
||||
const UPDATED_AT = 'date_last';
|
||||
@@ -67,6 +64,11 @@ class Account extends Model implements IDs
|
||||
return $this->belongsToMany(External\Integrations::class,'external_account',NULL,'external_integration_id');
|
||||
}
|
||||
|
||||
public function group()
|
||||
{
|
||||
return $this->hasOneThrough(Group::class,AccountGroup::class,'account_id','id','id','group_id');
|
||||
}
|
||||
|
||||
public function invoices()
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
@@ -89,6 +91,11 @@ class Account extends Model implements IDs
|
||||
return $active ? $query->active() : $query;
|
||||
}
|
||||
|
||||
public function taxes()
|
||||
{
|
||||
return $this->hasMany(Tax::class,'country_id','country_id');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
|
11
app/Models/AccountGroup.php
Normal file
11
app/Models/AccountGroup.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AccountGroup extends Model
|
||||
{
|
||||
protected $table = 'ab_account_group';
|
||||
public $timestamps = FALSE;
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Traits\OrderServiceOptions;
|
||||
|
||||
class AdslPlan extends Model
|
||||
{
|
||||
use OrderServiceOptions;
|
||||
|
||||
protected $table = 'ab_adsl_plan';
|
||||
|
||||
protected $order_attributes = [
|
||||
'options.address'=>[
|
||||
'request'=>'options.address',
|
||||
'key'=>'service_address',
|
||||
'validation'=>'required|string:10|unique:ab_service__adsl,service_address',
|
||||
'validation_message'=>'Address is a required field.',
|
||||
],
|
||||
'options.notes'=>[
|
||||
'request'=>'options.notes',
|
||||
'key'=>'order_info.notes',
|
||||
'validation'=>'present',
|
||||
'validation_message'=>'Special Instructions here.',
|
||||
],
|
||||
];
|
||||
|
||||
protected $order_model = Service\Adsl::class;
|
||||
|
||||
public function product()
|
||||
{
|
||||
return $this->hasOne(AdslSupplierPlan::class,'id','adsl_supplier_plan_id');
|
||||
}
|
||||
}
|
@@ -7,6 +7,9 @@ use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class AdslSupplier extends Model
|
||||
{
|
||||
protected $table = 'ab_adsl_supplier';
|
||||
|
@@ -4,11 +4,9 @@ namespace App\Models\Base;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Models\Product;
|
||||
|
||||
//@todo column prod_plugin_file should no longer be required
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
abstract class ProductType extends Model
|
||||
{
|
||||
public $timestamps = FALSE;
|
||||
public $dateFormat = 'U';
|
||||
}
|
@@ -45,6 +45,46 @@ class Invoice extends Model implements IDs
|
||||
protected $dates = ['date_orig','due_date'];
|
||||
public $dateFormat = 'U';
|
||||
|
||||
/* Our available billing periods */
|
||||
public const billing_periods = [
|
||||
0 => [
|
||||
'name' => 'Weekly',
|
||||
'interval' => 0.25,
|
||||
],
|
||||
1 => [
|
||||
'name' => 'Monthly',
|
||||
'interval' => 1,
|
||||
],
|
||||
2 => [
|
||||
'name' => 'Quarterly',
|
||||
'interval' => 3,
|
||||
],
|
||||
3 => [
|
||||
'name' => 'Semi-Annually',
|
||||
'interval' => 6,
|
||||
],
|
||||
4 => [
|
||||
'name' => 'Annually',
|
||||
'interval' => 12,
|
||||
],
|
||||
5 => [
|
||||
'name' => 'Two years',
|
||||
'interval' => 24,
|
||||
],
|
||||
6 => [
|
||||
'name' => 'Three Years',
|
||||
'interval' => 36,
|
||||
],
|
||||
7 => [
|
||||
'name' => 'Four Years',
|
||||
'interval' => 48,
|
||||
],
|
||||
8 => [
|
||||
'name' => 'Five Years',
|
||||
'interval' => 60,
|
||||
],
|
||||
];
|
||||
|
||||
// Array of items that can be updated with PushNew
|
||||
protected $pushable = ['items'];
|
||||
|
||||
@@ -61,6 +101,58 @@ class Invoice extends Model implements IDs
|
||||
private int $_total = 0;
|
||||
private int $_total_tax = 0;
|
||||
|
||||
/* STATIC */
|
||||
|
||||
/**
|
||||
* This works out what multiplier to use to change billing periods
|
||||
*
|
||||
* @param int $source
|
||||
* @param int $target
|
||||
* @return float
|
||||
*/
|
||||
public static function billing_change(int $source,int $target): float
|
||||
{
|
||||
return Arr::get(self::billing_periods,$target.'.interval')/Arr::get(self::billing_periods,$source.'.interval');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name for the billing interval
|
||||
*
|
||||
* @param int $interval
|
||||
* @return string
|
||||
*/
|
||||
public static function billing_name(int $interval): string
|
||||
{
|
||||
$interval = collect(self::billing_periods)->get($interval);
|
||||
|
||||
return Arr::get($interval,'name','Unknown');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of months in the billing interval
|
||||
*
|
||||
* @param int $interval
|
||||
* @return int
|
||||
*/
|
||||
public static function billing_period(int $interval): int
|
||||
{
|
||||
$interval = collect(self::billing_periods)->get($interval);
|
||||
|
||||
return Arr::get($interval,'interval',0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a contract in months, this will calculate the number of billing intervals required
|
||||
*
|
||||
* @param int $contract_term
|
||||
* @param int $source
|
||||
* @return int
|
||||
*/
|
||||
public static function billing_term(int $contract_term,int $source): int
|
||||
{
|
||||
return ceil(($contract_term ?: 1)/(Arr::get(self::billing_periods,$source.'.interval') ?: 1));
|
||||
}
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function account()
|
||||
@@ -312,7 +404,7 @@ class Invoice extends Model implements IDs
|
||||
|
||||
$lo = $this->account->user->language;
|
||||
return $return->sortBy(function ($item) use ($lo) {
|
||||
return $item->name($lo);
|
||||
return $item->name;
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Traits\OrderServiceOptions;
|
||||
|
||||
class PlanVoip extends Model
|
||||
{
|
||||
use OrderServiceOptions;
|
||||
|
||||
protected $order_attributes = [
|
||||
'options.phonenumber'=>[
|
||||
'request'=>'options.phonenumber',
|
||||
'key'=>'service_number',
|
||||
'validation'=>'nullable|size:10|unique:ab_service__voip,service_number',
|
||||
'validation_message'=>'Phone Number is a required field.',
|
||||
],
|
||||
'options.supplier'=>[
|
||||
'request'=>'options.supplier',
|
||||
'key'=>'order_info.supplier',
|
||||
'validation'=>'required_with:options.phonenumber',
|
||||
'validation_message'=>'Phone Supplier is a required field.',
|
||||
],
|
||||
'options.supplieraccnum'=>[
|
||||
'request'=>'options.supplieraccnum',
|
||||
'key'=>'order_info.supplieraccnum',
|
||||
'validation'=>'required_with:options.phonenumber',
|
||||
'validation_message'=>'Phone Supplier Account Number is a required field.',
|
||||
],
|
||||
'options.notes'=>[
|
||||
'request'=>'options.notes',
|
||||
'key'=>'order_info.notes',
|
||||
'validation'=>'required_if:options.phonenumber,null',
|
||||
'validation_message'=>'Special Instructions here.',
|
||||
],
|
||||
];
|
||||
|
||||
protected $order_model = Service\Voip::class;
|
||||
}
|
@@ -5,48 +5,84 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\NextKey;
|
||||
use App\Traits\{ProductDetails,SiteID};
|
||||
|
||||
/**
|
||||
* Class Product
|
||||
* Products that are available to sale, and appear on invoices
|
||||
* 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.
|
||||
*
|
||||
* Attributes for products:
|
||||
* + lid : Local ID for product (part number)
|
||||
* + 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 ]
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @todo doesnt appear that price_type is used - but could be used to have different offering types billed differently
|
||||
* @package App\Models
|
||||
*/
|
||||
class Product extends Model implements IDs
|
||||
{
|
||||
use HasFactory,NextKey;
|
||||
|
||||
const RECORD_ID = 'product';
|
||||
public $incrementing = FALSE;
|
||||
|
||||
const CREATED_AT = 'date_orig';
|
||||
const UPDATED_AT = 'date_last';
|
||||
|
||||
public $dateFormat = 'U';
|
||||
protected $table = 'ab_product';
|
||||
use HasFactory,SiteID,ProductDetails,ScopeActive;
|
||||
|
||||
protected $casts = [
|
||||
// @todo convert existing data to a json array
|
||||
// 'price_group'=>'array',
|
||||
'pricing'=>'collection',
|
||||
];
|
||||
|
||||
protected $with = ['descriptions'];
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function descriptions()
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
return $this->hasMany(ProductTranslate::class);
|
||||
return $this->hasOne(ProductTranslate::class)
|
||||
->where('language_id',(Auth::user() && Auth::user()->language_id) ? Auth::user()->language_id : config('site')->language_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Which services are configured with this product
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function services()
|
||||
{
|
||||
return $this->hasMany(Service::class);
|
||||
@@ -59,144 +95,203 @@ class Product extends Model implements IDs
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return $this->morphTo(null,'model','prod_plugin_data');
|
||||
return $this->morphTo(null,'model','model_id');
|
||||
}
|
||||
|
||||
/* INTERFACES */
|
||||
|
||||
public function getLIDAttribute(): string
|
||||
{
|
||||
return sprintf('%04s',$this->id);
|
||||
}
|
||||
|
||||
public function getSIDAttribute(): string
|
||||
{
|
||||
return sprintf('%02s-%s',$this->site_id,$this->getLIDattribute());
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
||||
/**
|
||||
* Get the service category (from the product)
|
||||
* The amount we invoice each time period for this service
|
||||
*
|
||||
* @return string
|
||||
* @param int|NULL $timeperiod
|
||||
* @param Group|NULL $go
|
||||
* @return float
|
||||
*/
|
||||
public function getCategoryAttribute()
|
||||
public function getBaseChargeAttribute(int $timeperiod=NULL,Group $go=NULL): float
|
||||
{
|
||||
return $this->prod_plugin_file ?: 'Other';
|
||||
}
|
||||
|
||||
public function getContractTermAttribute()
|
||||
{
|
||||
switch ($this->prod_plugin_file) {
|
||||
case 'ADSL': return $this->plugin()->contract_term;
|
||||
// @todo Incorporate into DB
|
||||
case 'VOIP': return 12;
|
||||
|
||||
// @todo Change this after contracts implemented.
|
||||
default:
|
||||
return 'TBA';
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefaultBillingAttribute()
|
||||
{
|
||||
return Arr::get($this->PricePeriods(),$this->price_recurr_default);
|
||||
}
|
||||
|
||||
public function getDefaultCostAttribute()
|
||||
{
|
||||
// @todo Integrate this into a Tax::class
|
||||
return Arr::get($this->price_array,sprintf('%s.1.price_base',$this->price_recurr_default))*1.1;
|
||||
}
|
||||
|
||||
private function getDefaultLanguage()
|
||||
{
|
||||
return config('site')->language;
|
||||
}
|
||||
|
||||
public function getDescriptionAttribute()
|
||||
{
|
||||
// @todo If the user has selected a specific language.
|
||||
return $this->description($this->getDefaultLanguage());
|
||||
return $this->getCharge('base',$timeperiod,$go);
|
||||
}
|
||||
|
||||
/**
|
||||
* Product Local ID
|
||||
* The amount we invoice each time period for this service, including taxes
|
||||
*
|
||||
* @return string
|
||||
* @param int|null $timeperiod
|
||||
* @param Group|null $go
|
||||
* @param Collection|NULL $taxes
|
||||
* @return float
|
||||
*/
|
||||
public function getLIDattribute(): string
|
||||
public function getBaseChargeTaxableAttribute(int $timeperiod=NULL,Group $go=NULL,Collection $taxes=NULL): float
|
||||
{
|
||||
return sprintf('%04s',$this->id);
|
||||
}
|
||||
|
||||
public function getMinimumCostAttribute()
|
||||
{
|
||||
$table = [
|
||||
0=>4,
|
||||
1=>1,
|
||||
2=>1/3,
|
||||
3=>1/6,
|
||||
4=>1/12,
|
||||
5=>1/24,
|
||||
6=>1/36,
|
||||
7=>1/48,
|
||||
8=>1/60,
|
||||
];
|
||||
|
||||
return $this->setup_cost + ( $this->default_cost * Arr::get($table,$this->price_recurr_default) * $this->contract_term);
|
||||
}
|
||||
|
||||
public function getNameAttribute(Language $lo=NULL)
|
||||
{
|
||||
if (is_null($lo))
|
||||
$lo = $this->getDefaultLanguage();
|
||||
|
||||
return $this->descriptions->where('language_id',$lo->id)->first()->description_short;
|
||||
}
|
||||
|
||||
public function getNameShortAttribute(Language $lo=NULL)
|
||||
{
|
||||
if (is_null($lo))
|
||||
$lo = $this->getDefaultLanguage();
|
||||
|
||||
return $this->descriptions->where('language_id',$lo->id)->first()->name;
|
||||
}
|
||||
|
||||
public function getProductTypeAttribute()
|
||||
{
|
||||
return $this->plugin()->product->name;
|
||||
}
|
||||
|
||||
public function getPriceArrayAttribute()
|
||||
{
|
||||
try {
|
||||
return unserialize($this->attributes['price_group']);
|
||||
} catch (\Exception $e) {
|
||||
Log::debug('Problem with Price array in product ',['pid'=>$this->id]);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function getPriceTypeAttribute()
|
||||
{
|
||||
$table = [
|
||||
0=>_('One-time Charge'),
|
||||
1=>_('Recurring Membership/Subscription'),
|
||||
2=>_('Trial for Membership/Subscription'),
|
||||
];
|
||||
}
|
||||
|
||||
public function getProductIdAttribute()
|
||||
{
|
||||
return sprintf('#%04s',$this->id);
|
||||
}
|
||||
|
||||
public function getSetupCostAttribute()
|
||||
{
|
||||
// @todo Integrate this into a Tax::class
|
||||
return Arr::get($this->price_array,sprintf('%s.1.price_setup',$this->price_recurr_default))*1.1;
|
||||
return Tax::tax_calc($this->getBaseChargeAttribute($timeperiod,$go),$taxes ?: config('site')->taxes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Product System ID
|
||||
* The base cost of this product at the appropriate billing interval
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getBaseCostAttribute(): float
|
||||
{
|
||||
return round($this->type->supplied->base_cost*Invoice::billing_change($this->type->supplied->getBillingIntervalAttribute(),$this->getBillingIntervalAttribute()) ?: 0,2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
return Tax::tax_calc($this->getBaseCostAttribute(),$taxes ?: config('site')->taxes);;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our default billing interval
|
||||
* Its the max of what we define, or what the supplier bills us at
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getBillingIntervalAttribute(): int
|
||||
{
|
||||
return max($this->price_recur_default,$this->type->supplied->getBillingIntervalAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* How long must this product be purchased for as a service.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return $this->type->getContractTermAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
return $this->getSetupChargeAttribute($timeperiod,$go)+$this->getBaseChargeAttribute($timeperiod,$go)*Invoice::billing_term($this->getContractTermAttribute(),$this->getBillingIntervalAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
return Tax::tax_calc($this->getMinChargeAttribute($timeperiod,$go),$taxes ?: config('site')->taxes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Our products short descriptive name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSIDattribute(): string
|
||||
public function getNameAttribute(): string
|
||||
{
|
||||
return sprintf('%02s-%s',$this->site_id,$this->getLIDattribute());
|
||||
return $this->description ? $this->description->description_short : 'Unknown PRODUCT';
|
||||
}
|
||||
|
||||
/**
|
||||
* Our products PID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNameShortAttribute(): string
|
||||
{
|
||||
return $this->description ? $this->description->name : 'Unknown PID';
|
||||
}
|
||||
|
||||
/**
|
||||
* This product full description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNameLongAttribute(): string
|
||||
{
|
||||
return $this->description->description_full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our product type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProductTypeAttribute(): string
|
||||
{
|
||||
return ($this->type && $this->type->supplied) ? $this->type->supplied->getTypeAttribute() : 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* The charge to setup this service
|
||||
*
|
||||
* @param int|null $timeperiod
|
||||
* @param Group|null $go
|
||||
* @return float
|
||||
*/
|
||||
public function getSetupChargeAttribute(int $timeperiod=NULL,Group $go=NULL): float
|
||||
{
|
||||
return $this->getCharge('setup',$timeperiod,$go);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
return Tax::tax_calc($this->getSetupChargeAttribute($timeperiod,$go),$taxes ?: config('site')->taxes);
|
||||
}
|
||||
|
||||
/**
|
||||
* The charge to setup this service
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSetupCostAttribute(): float
|
||||
{
|
||||
return $this->type->supplied->setup_cost ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The charge to setup this service
|
||||
*
|
||||
* @param Collection|null $taxes
|
||||
* @return float
|
||||
*/
|
||||
public function getSetupCostTaxableAttribute(Collection $taxes=NULL): float
|
||||
{
|
||||
return Tax::tax_calc($this->getSetupCostAttribute(),$taxes ?: config('site')->taxes);;
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
* Return if this product captures usage data
|
||||
*
|
||||
@@ -204,81 +299,49 @@ class Product extends Model implements IDs
|
||||
*/
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
// @todo This should be configured in the DB
|
||||
return in_array($this->model, ['App\Models\Product\Adsl']);
|
||||
}
|
||||
|
||||
public function scopeActive()
|
||||
{
|
||||
return $this->where('active',TRUE);
|
||||
}
|
||||
|
||||
public function description(Language $lo=NULL)
|
||||
{
|
||||
if (is_null($lo))
|
||||
$lo = $this->getDefaultLanguage();
|
||||
|
||||
return $this->descriptions->where('language_id',$lo->id)->first()->description_full;
|
||||
}
|
||||
|
||||
public function orderValidation(Request $request)
|
||||
{
|
||||
return $this->plugin()->orderValidation($request);
|
||||
}
|
||||
|
||||
private function plugin()
|
||||
{
|
||||
switch ($this->prod_plugin_file) {
|
||||
case 'ADSL':
|
||||
return AdslPlan::findOrFail($this->prod_plugin_data);
|
||||
case 'VOIP':
|
||||
return new PlanVoip;
|
||||
}
|
||||
return $this->type->supplied->hasUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price for this product based on the period being requested.
|
||||
* Get a charge value from the pricing array
|
||||
*
|
||||
* If the price period doesnt exist, we'll take the default period (0) which should.
|
||||
* @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
|
||||
{
|
||||
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.
|
||||
if (! $price=Arr::get($this->pricing,sprintf('%d.%d.%s',$timeperiod,$go->id,$type)))
|
||||
$price = Arr::get($this->pricing,sprintf('%d.%d.%s',$timeperiod,0,$type));
|
||||
|
||||
// @todo - if price doesnt exist for the time period, reduce down to timeperiod 1 and multiply appropriately.
|
||||
if (is_null($price))
|
||||
abort(500,sprintf('Price is NULL, we need to find it timeperiod[%s] group[%s]',$timeperiod,$go->id));
|
||||
|
||||
return round($price,2);
|
||||
}
|
||||
|
||||
/**
|
||||
* When receiving an order, validate that we have all the required information for the product type
|
||||
*
|
||||
* @param int $period
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function price(int $period,string $key='price_base')
|
||||
public function orderValidation(Request $request): ?Model
|
||||
{
|
||||
return Arr::get(
|
||||
$this->price_array,
|
||||
sprintf('%s.1.%s',$period,$key),
|
||||
Arr::get($this->price_array,sprintf('%s.0.%s',$period,$key))
|
||||
);
|
||||
}
|
||||
|
||||
public function PricePeriods()
|
||||
{
|
||||
return [
|
||||
0=>_('Weekly'),
|
||||
1=>_('Monthly'),
|
||||
2=>_('Quarterly'),
|
||||
3=>_('Semi-Annually'),
|
||||
4=>_('Annually'),
|
||||
5=>_('Two years'),
|
||||
6=>_('Three Years'),
|
||||
7=>_('Four Years'),
|
||||
8=>_('Five Years'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product name
|
||||
*
|
||||
* @param Language $lo
|
||||
* @return string Product Name
|
||||
*/
|
||||
public function name(Language $lo=NULL)
|
||||
{
|
||||
if (is_null($lo))
|
||||
$lo = $this->getDefaultLanguage();
|
||||
|
||||
return $this->descriptions->where('language_id',$lo->id)->first()->name;
|
||||
return $this->type->orderValidation($request);
|
||||
}
|
||||
}
|
@@ -1,159 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Interfaces\ProductSupplier;
|
||||
use App\Models\Base\ProductType;
|
||||
use App\Models\AdslSupplier;
|
||||
use App\Models\AdslSupplierPlan;
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class Adsl extends ProductType implements ProductSupplier
|
||||
{
|
||||
use NextKey;
|
||||
const RECORD_ID = 'adsl_plan';
|
||||
|
||||
protected $table = 'ab_adsl_plan';
|
||||
|
||||
public static $map = [
|
||||
'base_up_offpeak'=>'extra_up_offpeak',
|
||||
'base_down_offpeak'=>'extra_down_offpeak',
|
||||
'base_up_peak'=>'extra_up_peak',
|
||||
'base_down_peak'=>'extra_down_peak',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map upstream metrics into traffic allowance metrics
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $metrics = [
|
||||
'down_peak'=>'base_down_peak',
|
||||
'down_offpeak'=>'base_down_offpeak',
|
||||
'up_peak'=>'base_up_peak',
|
||||
'up_offpeak'=>'base_up_offpeak',
|
||||
'peer'=>'base_down_peak',
|
||||
'internal'=>'base_down_offpeak',
|
||||
];
|
||||
|
||||
/**
|
||||
* The suppliers product
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
return $this->hasOne(AdslSupplierPlan::class,'id','adsl_supplier_plan_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* The supplier
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
|
||||
*/
|
||||
public function supplier()
|
||||
{
|
||||
return $this->hasOneThrough(AdslSupplier::class,AdslSupplierPlan::class,'id','id','adsl_supplier_plan_id','supplier_id');
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
switch($key) {
|
||||
case 'speed':
|
||||
return $this->product->speed;
|
||||
}
|
||||
|
||||
// If we dont have a specific key, we'll resolve it normally
|
||||
return parent::__get($key);
|
||||
}
|
||||
|
||||
/** ATTRIBUTES **/
|
||||
|
||||
/**
|
||||
* Calculate the allowance array or traffic used array
|
||||
*
|
||||
* @param array Traffic Used in each metric.
|
||||
* @param bool $ceil Round the numbers to integers
|
||||
* @return array|string
|
||||
*/
|
||||
public function allowance(array $data=[],bool $ceil=TRUE): Collection
|
||||
{
|
||||
$config = collect();
|
||||
|
||||
// Base Config
|
||||
foreach (array_keys(static::$map) as $k) {
|
||||
$config->put($k,$this->{$k});
|
||||
}
|
||||
|
||||
// Excess Config
|
||||
foreach (array_values(static::$map) as $k) {
|
||||
$config->put($k,$this->{$k});
|
||||
}
|
||||
|
||||
// Shaped or Charge
|
||||
$config->put('shaped',$this->extra_shaped);
|
||||
$config->put('charged',$this->extra_charged);
|
||||
|
||||
// Metric - used to round down data in $data.
|
||||
$config->put('metric',$this->metric);
|
||||
|
||||
return $this->product->allowance($config,$data,$ceil);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the suppliers cost for this service
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function allowance_cost(): float
|
||||
{
|
||||
$result = 0;
|
||||
foreach ($this->product->allowance(NULL,$this->allowance([])->toArray()) as $k=>$v) {
|
||||
$result += -$v*$this->product->{static::$map[$k]};
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the allowance as a string
|
||||
* eg: 50/100
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function allowance_string(): string
|
||||
{
|
||||
$result = '';
|
||||
$data = $this->allowance();
|
||||
|
||||
foreach ([
|
||||
'base_down_peak',
|
||||
'base_up_peak',
|
||||
'base_down_offpeak',
|
||||
'base_up_offpeak',
|
||||
] as $k)
|
||||
{
|
||||
if ($data->has($k)) {
|
||||
if ($result)
|
||||
$result .= '/';
|
||||
|
||||
$result .= $data->get($k);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getCostAttribute(): float
|
||||
{
|
||||
// @todo Tax shouldnt be hard coded
|
||||
return ($this->product->base_cost+$this->allowance_cost())*1.1;
|
||||
}
|
||||
|
||||
public function getSupplierAttribute()
|
||||
{
|
||||
return $this->getRelationValue('supplier');
|
||||
}
|
||||
}
|
178
app/Models/Product/Broadband.php
Normal file
178
app/Models/Product/Broadband.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Interfaces\ProductSupplier;
|
||||
use App\Models\Base\ProductType;
|
||||
use App\Models\{Product,Supplier};
|
||||
use App\Models\Service\Broadband as ServiceBroadband;
|
||||
use App\Models\Supplier\Broadband as SupplierBroadband;
|
||||
use App\Traits\{OrderServiceOptions,SiteID};
|
||||
|
||||
class Broadband extends ProductType implements ProductSupplier
|
||||
{
|
||||
use SiteID;
|
||||
use OrderServiceOptions;
|
||||
|
||||
protected $table = 'product_broadband';
|
||||
|
||||
// Information required during the order process
|
||||
private array $order_attributes = [
|
||||
'options.address'=>[
|
||||
'request'=>'options.address',
|
||||
'key'=>'service_address',
|
||||
'validation'=>'required|string:10|unique:ab_service__adsl,service_address',
|
||||
'validation_message'=>'Address is a required field.',
|
||||
],
|
||||
'options.notes'=>[
|
||||
'request'=>'options.notes',
|
||||
'key'=>'order_info.notes',
|
||||
'validation'=>'present',
|
||||
'validation_message'=>'Special Instructions here.',
|
||||
],
|
||||
];
|
||||
|
||||
protected string $order_model = ServiceBroadband::class;
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
/**
|
||||
* The product that sells this type
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
return $this->morphOne(Product::class, null,'model','model_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* The offering supplied with this product
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function supplied()
|
||||
{
|
||||
return $this->hasOne(SupplierBroadband::class,'id','supplier_broadband_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* The supplier
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
|
||||
*/
|
||||
// @todo To check
|
||||
public function supplier()
|
||||
{
|
||||
return $this->hasOneThrough(Supplier::class,SupplierBroadband::class,'id','id','adsl_supplier_plan_id','supplier_id');
|
||||
}
|
||||
|
||||
/* INTERFACES */
|
||||
|
||||
/**
|
||||
* Calculate the allowance array or traffic used array
|
||||
*
|
||||
* @param array Traffic Used in each metric.
|
||||
* @param bool $ceil Round the numbers to integers
|
||||
* @return array|string
|
||||
*/
|
||||
public function allowance(array $data=[],bool $ceil=TRUE): Collection
|
||||
{
|
||||
$config = collect();
|
||||
|
||||
foreach (array_keys(Supplier\Broadband::traffic_map) as $k => $v) {
|
||||
// Base Config
|
||||
$config->put($k,$this->{$k});
|
||||
// Excess Config
|
||||
$config->put($v,$this->{$v});
|
||||
}
|
||||
|
||||
// Shaped or Charge
|
||||
$config->put('shaped',$this->extra_shaped);
|
||||
$config->put('charged',$this->extra_charged);
|
||||
|
||||
// Metric - used to round down data in $data.
|
||||
$config->put('metric',$this->metric);
|
||||
|
||||
return $this->supplied->allowance($config,$data,$ceil);
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
||||
/**
|
||||
* Return the suppliers cost for this service
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
// @todo To check
|
||||
public function allowance_cost(): float
|
||||
{
|
||||
$result = 0;
|
||||
foreach ($this->supplied->allowance(NULL,$this->allowance([])->toArray()) as $k=>$v) {
|
||||
$result += -$v*$this->supplied->{Supplier\Broadband::traffic_map[$k]};
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the allowance as a string
|
||||
* eg: 50/100
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function allowance_string(): string
|
||||
{
|
||||
$result = '';
|
||||
$data = $this->allowance();
|
||||
|
||||
foreach ([
|
||||
'base_down_peak',
|
||||
'base_up_peak',
|
||||
'base_down_offpeak',
|
||||
'base_up_offpeak',
|
||||
] as $k)
|
||||
{
|
||||
if ($data->has($k)) {
|
||||
if ($result)
|
||||
$result .= '/';
|
||||
|
||||
$result .= $data->get($k);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The product contract term is the highest of
|
||||
* + This defined contract_term
|
||||
* + The suppliers contract_term
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return max($this->attributes['contract_term'],$this->supplied->getContractTermAttribute());
|
||||
}
|
||||
|
||||
public function getCostAttribute(): float
|
||||
{
|
||||
abort(500,'deprecated');
|
||||
// @todo Tax shouldnt be hard coded
|
||||
return ($this->supplied->base_cost+$this->allowance_cost())*1.1;
|
||||
}
|
||||
|
||||
public function getSupplierAttribute()
|
||||
{
|
||||
abort(500,'deprecated');
|
||||
return $this->getRelationValue('supplier');
|
||||
}
|
||||
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
@@ -25,6 +25,11 @@ class Domain extends ProductType implements ProductSupplier
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
public function getCostAttribute(): float
|
||||
{
|
||||
// N/A
|
||||
@@ -35,4 +40,14 @@ class Domain extends ProductType implements ProductSupplier
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getTypeAttribute()
|
||||
{
|
||||
return 'Domain Name';
|
||||
}
|
||||
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@@ -6,4 +6,18 @@ use App\Models\Base\ProductType;
|
||||
|
||||
class Generic extends ProductType
|
||||
{
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getTypeAttribute()
|
||||
{
|
||||
return 'Generic';
|
||||
}
|
||||
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@@ -9,4 +9,19 @@ class Host extends ProductType
|
||||
{
|
||||
use NextKey;
|
||||
const RECORD_ID = '';
|
||||
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
public function getTypeAttribute()
|
||||
{
|
||||
return 'Hosting';
|
||||
}
|
||||
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@@ -27,6 +27,11 @@ class SSL extends ProductType implements ProductSupplier
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
public function getCostAttribute(): float
|
||||
{
|
||||
// N/A
|
||||
@@ -51,4 +56,14 @@ class SSL extends ProductType implements ProductSupplier
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
public function getTypeAttribute()
|
||||
{
|
||||
return 'SSL Certificate';
|
||||
}
|
||||
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
@@ -4,9 +4,56 @@ namespace App\Models\Product;
|
||||
|
||||
use App\Models\Base\ProductType;
|
||||
use App\Traits\NextKey;
|
||||
use App\Traits\OrderServiceOptions;
|
||||
|
||||
class Voip extends ProductType
|
||||
{
|
||||
use NextKey;
|
||||
const RECORD_ID = '';
|
||||
|
||||
use OrderServiceOptions;
|
||||
|
||||
protected $order_attributes = [
|
||||
'options.phonenumber'=>[
|
||||
'request'=>'options.phonenumber',
|
||||
'key'=>'service_number',
|
||||
'validation'=>'nullable|size:10|unique:ab_service__voip,service_number',
|
||||
'validation_message'=>'Phone Number is a required field.',
|
||||
],
|
||||
'options.supplier'=>[
|
||||
'request'=>'options.supplier',
|
||||
'key'=>'order_info.supplier',
|
||||
'validation'=>'required_with:options.phonenumber',
|
||||
'validation_message'=>'Phone Supplier is a required field.',
|
||||
],
|
||||
'options.supplieraccnum'=>[
|
||||
'request'=>'options.supplieraccnum',
|
||||
'key'=>'order_info.supplieraccnum',
|
||||
'validation'=>'required_with:options.phonenumber',
|
||||
'validation_message'=>'Phone Supplier Account Number is a required field.',
|
||||
],
|
||||
'options.notes'=>[
|
||||
'request'=>'options.notes',
|
||||
'key'=>'order_info.notes',
|
||||
'validation'=>'required_if:options.phonenumber,null',
|
||||
'validation_message'=>'Special Instructions here.',
|
||||
],
|
||||
];
|
||||
|
||||
protected $order_model = \App\Models\Service\Voip::class;
|
||||
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
public function getTypeAttribute()
|
||||
{
|
||||
return 'VOIP';
|
||||
}
|
||||
|
||||
public function hasUsage(): bool
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
@@ -18,14 +18,15 @@ use Leenooks\Carbon;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\NextKey;
|
||||
|
||||
/**
|
||||
* Class Service
|
||||
* Services that belong to an account
|
||||
*
|
||||
* Attributes for services:
|
||||
* + billing_period : The period that this service is billed for by default
|
||||
* + additional_cost : Pending additional charges for this service (excluding setup)
|
||||
* + billing_cost : Charge for this service each invoice period
|
||||
* + billing_interval : The period that this service is billed for by default
|
||||
* + name : Service short name with service address
|
||||
* + name_short : Service Product short name, eg: phone number, domain name, certificate CN
|
||||
* + name_detail : Service Detail, eg: service_address
|
||||
@@ -33,9 +34,10 @@ use App\Traits\NextKey;
|
||||
*
|
||||
* @package App\Models
|
||||
*/
|
||||
// @todo All the methods/attributes in this file need to be checked.
|
||||
class Service extends Model implements IDs
|
||||
{
|
||||
use NextKey,HasFactory;
|
||||
use HasFactory;
|
||||
|
||||
const RECORD_ID = 'service';
|
||||
public $incrementing = FALSE;
|
||||
@@ -85,6 +87,7 @@ class Service extends Model implements IDs
|
||||
'status',
|
||||
];
|
||||
|
||||
/*
|
||||
protected $with = [
|
||||
'account.language',
|
||||
'charges',
|
||||
@@ -92,6 +95,7 @@ class Service extends Model implements IDs
|
||||
'product',
|
||||
'type',
|
||||
];
|
||||
*/
|
||||
|
||||
// @todo Change to self::INACTIVE_STATUS
|
||||
private $inactive_status = [
|
||||
@@ -370,7 +374,6 @@ class Service extends Model implements IDs
|
||||
* Product of the service
|
||||
*
|
||||
* @return BelongsTo
|
||||
* @deprecated use type->product
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
@@ -491,10 +494,10 @@ class Service extends Model implements IDs
|
||||
public function getBillingPriceAttribute(): float
|
||||
{
|
||||
// @todo Temporary for services that dont have recur_schedule set.
|
||||
if (is_null($this->recur_schedule) OR is_null($this->product->price($this->recur_schedule)))
|
||||
if (is_null($this->recur_schedule) OR is_null($this->product->getBaseChargeAttribute($this->recur_schedule,$this->account->group)))
|
||||
$this->price=0;
|
||||
|
||||
return $this->addTax(is_null($this->price) ? $this->product->price($this->recur_schedule) : $this->price);
|
||||
return $this->addTax(is_null($this->price) ? $this->product->getBaseChargeAttribute($this->recur_schedule,$this->account->group) : $this->price);
|
||||
}
|
||||
|
||||
public function getBillingMonthlyPriceAttribute(): float
|
||||
@@ -516,11 +519,32 @@ class Service extends Model implements IDs
|
||||
/**
|
||||
* Return the service billing period
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getBillingIntervalAttribute(): int
|
||||
{
|
||||
return $this->recur_schedule ?: $this->product->getBillingIntervalAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human friendly name for the billing interval
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBillingPeriodAttribute(): string
|
||||
public function getBillingIntervalStringAttribute(): string
|
||||
{
|
||||
return Arr::get($this->product->PricePeriods(),$this->recur_schedule,'Unknown');
|
||||
return Invoice::billing_name($this->getBillingIntervalAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will determine the minimum contract term for a service, which is the maximum of
|
||||
* this::type->contract_term, or the product->type->contract_term();
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
abort(500,'To implement (Dec 2021)');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,42 +585,42 @@ class Service extends Model implements IDs
|
||||
{
|
||||
switch ($this->recur_schedule) {
|
||||
// Weekly
|
||||
case 0: $date = $this->product->price_recurr_strict
|
||||
case 0: $date = $this->product->price_recur_strict
|
||||
? $this->getInvoiceNextAttribute()->endOfWeek()
|
||||
: $this->getInvoiceNextAttribute()->addWeek()->subDay();
|
||||
break;
|
||||
|
||||
// Monthly
|
||||
case 1:
|
||||
$date = $this->product->price_recurr_strict
|
||||
$date = $this->product->price_recur_strict
|
||||
? $this->getInvoiceNextAttribute()->endOfMonth()
|
||||
: $this->getInvoiceNextAttribute()->addMonth()->subDay();
|
||||
break;
|
||||
|
||||
// Quarterly
|
||||
case 2:
|
||||
$date = $this->product->price_recurr_strict
|
||||
$date = $this->product->price_recur_strict
|
||||
? $this->getInvoiceNextAttribute()->endOfQuarter()
|
||||
: $this->getInvoiceNextAttribute()->addQuarter()->subDay();
|
||||
break;
|
||||
|
||||
// Half Yearly
|
||||
case 3:
|
||||
$date = $this->product->price_recurr_strict
|
||||
$date = $this->product->price_recur_strict
|
||||
? $this->getInvoiceNextAttribute()->endOfHalf()
|
||||
: $this->getInvoiceNextAttribute()->addQuarter(2)->subDay();
|
||||
break;
|
||||
|
||||
// Yearly
|
||||
case 4:
|
||||
$date = $this->product->price_recurr_strict
|
||||
$date = $this->product->price_recur_strict
|
||||
? $this->getInvoiceNextAttribute()->endOfYear()
|
||||
: $this->getInvoiceNextAttribute()->addYear()->subDay();
|
||||
break;
|
||||
|
||||
// Two Yearly
|
||||
case 5:
|
||||
if (!$this->product->price_recurr_strict)
|
||||
if (!$this->product->price_recur_strict)
|
||||
$date = $this->getInvoiceNextAttribute()->addYear(2)->subDay();
|
||||
else {
|
||||
$date = $this->getInvoiceNextAttribute()->addYear(2)->subDay()->endOfYear();
|
||||
@@ -608,7 +632,7 @@ class Service extends Model implements IDs
|
||||
break;
|
||||
|
||||
// Three Yearly
|
||||
// NOTE: price_recurr_strict ignored
|
||||
// NOTE: price_recur_strict ignored
|
||||
case 6: $date = $this->getInvoiceNextAttribute()->addYear(3)->subDay(); break;
|
||||
|
||||
default: throw new Exception('Unknown recur_schedule');
|
||||
@@ -624,7 +648,7 @@ class Service extends Model implements IDs
|
||||
public function getInvoiceNextQuantityAttribute()
|
||||
{
|
||||
// If we are not rounding to the first day of the cycle, then it is always a full cycle
|
||||
if (! $this->product->price_recurr_strict)
|
||||
if (! $this->product->price_recur_strict)
|
||||
return 1;
|
||||
|
||||
$n = $this->invoice_next->diff($this->invoice_next_end)->days+1;
|
||||
@@ -759,11 +783,12 @@ class Service extends Model implements IDs
|
||||
/**
|
||||
* Get the Product's Category for this service
|
||||
*
|
||||
* @deprecated use product->category directly
|
||||
* @deprecated use product->getProductTypeAttribute() directly
|
||||
*/
|
||||
public function getProductCategoryAttribute(): string
|
||||
{
|
||||
return $this->product->category;
|
||||
abort(500,'deprecated');
|
||||
return $this->product->getProductTypeAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -774,6 +799,7 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function getProductNameAttribute(): string
|
||||
{
|
||||
abort(500,'deprecated');
|
||||
return $this->product->name($this->account->language);
|
||||
}
|
||||
|
||||
@@ -860,7 +886,7 @@ class Service extends Model implements IDs
|
||||
public function getSTypeAttribute(): string
|
||||
{
|
||||
switch($this->product->model) {
|
||||
case 'App\Models\Product\Adsl': return 'broadband';
|
||||
case 'App\Models\Product\Broadband': return 'broadband';
|
||||
default: return $this->type->type;
|
||||
}
|
||||
}
|
||||
@@ -918,6 +944,18 @@ class Service extends Model implements IDs
|
||||
: $this->status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the type of service is provided.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getServiceTypeAttribute(): string
|
||||
{
|
||||
// @todo This is temporary, while we clean the database.
|
||||
return ($this->product->type && $this->product->type->supplied) ? $this->product->type->supplied->getTypeAttribute() : '** TBA **';
|
||||
}
|
||||
|
||||
/**
|
||||
* URL used by an admin to administer the record
|
||||
*
|
||||
@@ -1275,7 +1313,7 @@ class Service extends Model implements IDs
|
||||
// Connection charges are only charged once
|
||||
if ((! $this->invoice_items->filter(function($item) { return $item->item_type==4; })->sum('total'))
|
||||
AND ($this->isPending() OR is_null($this->invoice_to))
|
||||
AND $this->product->price($this->recur_schedule,'price_setup'))
|
||||
AND $this->product->getSetupChargeAttribute($this->recur_schedule,$this->account->group))
|
||||
{
|
||||
$o = new InvoiceItem;
|
||||
|
||||
@@ -1283,7 +1321,7 @@ class Service extends Model implements IDs
|
||||
$o->service_id = $this->id;
|
||||
$o->product_id = $this->product_id;
|
||||
$o->item_type = 4; // @todo change to const or something
|
||||
$o->price_base = $this->product->price($this->recur_schedule,'price_setup'); // @todo change to a method in this class
|
||||
$o->price_base = $this->product->getSetupChargeAttribute($this->recur_schedule,$this->account->group);
|
||||
//$o->recurring_schedule = $this->recur_schedule;
|
||||
$o->date_start = $this->invoice_next;
|
||||
$o->date_stop = $this->invoice_next;
|
||||
@@ -1309,7 +1347,7 @@ class Service extends Model implements IDs
|
||||
$o->product_id = $this->product_id;
|
||||
$o->item_type = 0;
|
||||
$o->price_base = is_null($this->price)
|
||||
? (is_null($this->price_override) ? $this->product->price($this->recur_schedule) : $this->price_override)
|
||||
? (is_null($this->price_override) ? $this->product->getBaseChargeAttribute($this->recur_schedule,$this->account->group) : $this->price_override)
|
||||
: $this->price; // @todo change to a method in this class
|
||||
$o->recurring_schedule = $this->recur_schedule;
|
||||
$o->date_start = $this->invoice_next;
|
||||
@@ -1354,6 +1392,7 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
private function ServicePlugin()
|
||||
{
|
||||
abort(500,'deprecated');
|
||||
// @todo: All services should be linked to a product. This might require data cleaning for old services not linked to a product.
|
||||
if (! is_object($this->product))
|
||||
return NULL;
|
||||
|
@@ -3,17 +3,15 @@
|
||||
namespace App\Models\Service;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Leenooks\Carbon;
|
||||
|
||||
use App\Interfaces\{ServiceItem,ServiceUsage};
|
||||
use App\Models\AdslSupplierPlan;
|
||||
use App\Models\Base\ServiceType;
|
||||
use App\Models\Supplier\Broadband as SupplierBroadband;
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class Adsl extends ServiceType implements ServiceItem,ServiceUsage
|
||||
class Broadband extends ServiceType implements ServiceItem,ServiceUsage
|
||||
{
|
||||
private const LOGKEY = 'MSA';
|
||||
|
||||
@@ -26,21 +24,7 @@ class Adsl extends ServiceType implements ServiceItem,ServiceUsage
|
||||
];
|
||||
protected $table = 'ab_service__adsl';
|
||||
|
||||
/** RELATIONSHIPS **/
|
||||
|
||||
/**
|
||||
* The suppliers product
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
return $this
|
||||
->hasOne(AdslSupplierPlan::class,'id','adsl_supplier_plan_id')
|
||||
->withDefault(function() {
|
||||
$o = new AdslSupplierPlan;
|
||||
});
|
||||
}
|
||||
/* RELATIONS */
|
||||
|
||||
/**
|
||||
* The accounts that this user manages
|
||||
@@ -53,7 +37,7 @@ class Adsl extends ServiceType implements ServiceItem,ServiceUsage
|
||||
return $this->hasMany(AdslTraffic::class,'ab_service_adsl_id');
|
||||
}
|
||||
|
||||
/** SCOPES */
|
||||
/* SCOPES */
|
||||
|
||||
/**
|
||||
* Search for a record
|
||||
@@ -71,12 +55,13 @@ class Adsl extends ServiceType implements ServiceItem,ServiceUsage
|
||||
->orWhere('ipaddress','like','%'.$term.'%');
|
||||
}
|
||||
|
||||
/** ATTRIBUTES **/
|
||||
|
||||
/**
|
||||
* @deprecated use $o->service_name;
|
||||
* @return mixed|string
|
||||
*/
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
||||
public function getNameAttribute()
|
||||
{
|
||||
return $this->service_number ?: $this->service_address;
|
||||
@@ -107,6 +92,8 @@ class Adsl extends ServiceType implements ServiceItem,ServiceUsage
|
||||
return $this->service_number ?: $this->service_address;
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
* Is this service currently in a contract
|
||||
*
|
||||
@@ -117,6 +104,18 @@ class Adsl extends ServiceType implements ServiceItem,ServiceUsage
|
||||
return $this->service_contract_date AND $this->service_contract_date->addMonths($this->contract_term)->isFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the suppliers offering that this service is providing
|
||||
*
|
||||
* @return SupplierBroadband
|
||||
*/
|
||||
public function supplied(): SupplierBroadband
|
||||
{
|
||||
return $this->provided_adsl_plan_id
|
||||
? SupplierBroadband::findOrFail($this->provided_adsl_plan_id)
|
||||
: $this->service->product->type->supplied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return service usage data
|
||||
*
|
@@ -67,7 +67,7 @@ class SSL extends ServiceType implements ServiceItem
|
||||
{
|
||||
return $this->cert
|
||||
? Arr::get($this->crt_parse,'subject.CN')
|
||||
: Arr::get(openssl_csr_get_subject($this->csr),'CN');
|
||||
: Arr::get(openssl_csr_get_subject($this->csr),'CN','');
|
||||
}
|
||||
|
||||
public function inContract(): bool
|
||||
|
@@ -58,6 +58,11 @@ class Site extends Model
|
||||
return $this->belongsTo(Language::class);
|
||||
}
|
||||
|
||||
public function taxes()
|
||||
{
|
||||
return $this->hasMany(Tax::class,'country_id','country_id');
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
||||
/**
|
||||
|
@@ -3,18 +3,93 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
use App\Models\Supplier\{Broadband,Ethernet,HSPA};
|
||||
|
||||
class Supplier extends Model
|
||||
{
|
||||
use ScopeActive;
|
||||
|
||||
public $timestamps = FALSE;
|
||||
|
||||
/* The offerings we provide */
|
||||
public const offering_types = [
|
||||
'broadband' => [
|
||||
'name' => 'Broadband',
|
||||
'class' => Broadband::class,
|
||||
],
|
||||
'hspa' => [
|
||||
'name' => 'Mobile Broadband',
|
||||
'class' => HSPA::class,
|
||||
],
|
||||
'ethernet' => [
|
||||
'name' => 'Ethernet Broadband',
|
||||
'class' => Ethernet::class,
|
||||
],
|
||||
'domainname' => [
|
||||
'name' => 'Domain Name',
|
||||
//'class' => Domain::class,
|
||||
],
|
||||
'generic' => [
|
||||
'name' => 'Generic',
|
||||
//'class' => Generic::class,
|
||||
],
|
||||
'hosting' => [
|
||||
'name' => 'Hosting',
|
||||
//'class' => Host::class,
|
||||
],
|
||||
'voip' => [
|
||||
'name' => 'VOIP Telephone',
|
||||
//'class' => Voip::class,
|
||||
],
|
||||
];
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function detail()
|
||||
{
|
||||
return $this->hasOne(SupplierDetail::class);
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
* Return the offerings that this supplier provides
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function offeringTypes(): Collection
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
// See if we have any configurations
|
||||
foreach (self::offering_types as $key => $type) {
|
||||
if (! $class = Arr::get($type,'class'))
|
||||
continue;
|
||||
|
||||
if (Arr::get($this->detail->connections,$key)) {
|
||||
$result->put($key,(object)[
|
||||
'type' => Arr::get($type,'name'),
|
||||
'items' => (new $class)->where('supplier_detail_id',$this->detail->id),
|
||||
]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// See if we have any products defined
|
||||
$o = new $class;
|
||||
$o->where('supplier_detail_id',$this->detail->id);
|
||||
|
||||
if ($o->count())
|
||||
$result->put($key,(object)[
|
||||
'type' => Arr::get($type,'name'),
|
||||
'items' => (new $class)->where('supplier_detail_id',$this->detail->id),
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@@ -1,14 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
namespace App\Models\Supplier;
|
||||
|
||||
use App\Traits\ProductDetails;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
class AdslSupplierPlan extends Model
|
||||
use App\Interfaces\SupplierItem;
|
||||
use App\Models\{Invoice,Supplier,SupplierDetail,Tax};
|
||||
use App\Models\Product\Broadband as ProductBroadband;
|
||||
use App\Traits\SiteID;
|
||||
|
||||
class Broadband extends Model implements SupplierItem
|
||||
{
|
||||
protected $table = 'ab_adsl_supplier_plan';
|
||||
use SiteID,ScopeActive,ProductDetails;
|
||||
|
||||
protected $casts = [
|
||||
'offpeak_start' => 'datetime:H:i',
|
||||
'offpeak_end' => 'datetime:H:i',
|
||||
];
|
||||
|
||||
protected $table = 'supplier_broadband';
|
||||
|
||||
// Map the table fields, with the extra fields
|
||||
public const traffic_map = [
|
||||
'base_up_offpeak' => 'extra_up_offpeak',
|
||||
'base_down_offpeak' => 'extra_down_offpeak',
|
||||
'base_up_peak' => 'extra_up_peak',
|
||||
'base_down_peak' => 'extra_down_peak',
|
||||
];
|
||||
|
||||
// Map the NULL relationships - and where traffic gets applied if NULL
|
||||
public const traffic_merge = [
|
||||
'extra_up_offpeak' => 'base_down_offpeak',
|
||||
'extra_down_offpeak' => 'base_down_peak',
|
||||
'extra_up_peak' => 'base_down_peak',
|
||||
'extra_down_peak' => 'base_down_peak',
|
||||
];
|
||||
|
||||
/* INTERFACES */
|
||||
|
||||
public function supplier_detail(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SupplierDetail::class);
|
||||
}
|
||||
|
||||
public function types(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(ProductBroadband::class,'supplier_broadband','id','id','id','supplier_broadband_id');
|
||||
}
|
||||
|
||||
public function getBaseCostTaxableAttribute(): float
|
||||
{
|
||||
return Tax::tax_calc($this->attributes['base_cost'],config('site')->taxes);
|
||||
}
|
||||
|
||||
public function getBillingIntervalAttribute(): int
|
||||
{
|
||||
return 1; // Monthly
|
||||
}
|
||||
|
||||
/**
|
||||
* This contract term is the highest of
|
||||
* + The defined contract_term
|
||||
* + The default months in a billing interval
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getContractTermAttribute(): int
|
||||
{
|
||||
return max(Invoice::billing_period(self::getBillingIntervalAttribute()),Arr::get($this->attributes,'contract_term'));
|
||||
}
|
||||
|
||||
public function getMinCostAttribute(): float
|
||||
{
|
||||
return $this->attributes['setup_cost']+$this->attributes['base_cost']*Invoice::billing_term($this->getContractTermAttribute(),$this->getBillingIntervalAttribute());
|
||||
}
|
||||
|
||||
public function getMinCostTaxableAttribute(): float
|
||||
{
|
||||
return Tax::tax_calc($this->getMinCostAttribute(),config('site')->taxes);
|
||||
}
|
||||
|
||||
public function getNameAttribute(): string
|
||||
{
|
||||
return $this->product_id ?: 'Supplier PID Unknown';
|
||||
}
|
||||
|
||||
public function getNameLongAttribute(): string
|
||||
{
|
||||
return $this->product_desc ?: 'Supplier NAME Unknown';
|
||||
}
|
||||
|
||||
public function getSetupCostTaxableAttribute(): float
|
||||
{
|
||||
return Tax::tax_calc($this->attributes['setup_cost'],config('site')->taxes);
|
||||
}
|
||||
|
||||
public function getTypeAttribute(): string
|
||||
{
|
||||
return Arr::get(collect(Supplier::offering_types)->firstWhere('class',get_class($this)),'name','Unknown');
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
* Determine how traffic is counted for Broadband links.
|
||||
@@ -17,7 +115,7 @@ class AdslSupplierPlan extends Model
|
||||
* + down_peak, when not NULL, traffic is included to value of this metric, extra traffic is charged per extra_peak
|
||||
* + down_offpeak, when not NULL, traffic is included to the value of metric, extra traffic is charged per extra_offpeak
|
||||
*
|
||||
* If:
|
||||
* If:
|
||||
* + UPLOADS are charged and there are no PEAK/OFFPEAK periods (therefore all
|
||||
* traffic is charged), the allowance will be shown as 1 metric - TRAFFIC.
|
||||
* + UPLOADS are charged and there are PEAK/OFFPEAK periods the allowance
|
||||
@@ -39,29 +137,15 @@ class AdslSupplierPlan extends Model
|
||||
* + If extra_up_peak is NULL add traffic_up_peak to traffic_down_peak
|
||||
* + If extra_up_offpeak is NULL add traffic_up_offpeak to traffic_down_offpeak
|
||||
*
|
||||
* @param array $config The configuration of the link, if NULL assume the supplieres configuration
|
||||
* @param array $data The traffic used on this link, determine whats left or over
|
||||
* @param bool $format @deprecate
|
||||
* @param bool $over @deprecate
|
||||
* @param bool $ceil Round the numbers to integers
|
||||
* @param Collection|null $config The configuration of the link, if NULL assume the supplieres configuration
|
||||
* @param array $data The traffic used on this link, determine whats left or over
|
||||
* @param bool $ceil Round the numbers to integers
|
||||
* @return array|string
|
||||
*/
|
||||
public function allowance(Collection $config=NULL,array $data=[],$ceil=TRUE) {
|
||||
// Map the table fields, with the extra fields
|
||||
$map = collect([
|
||||
'base_up_offpeak'=>'extra_up_offpeak',
|
||||
'base_down_offpeak'=>'extra_down_offpeak',
|
||||
'base_up_peak'=>'extra_up_peak',
|
||||
'base_down_peak'=>'extra_down_peak',
|
||||
]);
|
||||
|
||||
// Map the NULL relationships - and where traffic gets applied if NULL
|
||||
$merge = collect([
|
||||
'extra_up_offpeak'=>'base_down_offpeak',
|
||||
'extra_down_offpeak'=>'base_down_peak',
|
||||
'extra_up_peak'=>'base_down_peak',
|
||||
'extra_down_peak'=>'base_down_peak',
|
||||
]);
|
||||
public function allowance(Collection $config=NULL,array $data=[],bool $ceil=TRUE)
|
||||
{
|
||||
$map = collect(self::traffic_map);
|
||||
$merge = collect(self::traffic_merge);
|
||||
|
||||
if (is_null($config))
|
||||
$config = collect($config);
|
||||
@@ -69,14 +153,12 @@ class AdslSupplierPlan extends Model
|
||||
// If config is null, use the configuration from this Model
|
||||
if (! $config->count()) {
|
||||
// Base Config
|
||||
foreach ($map->keys() as $k) {
|
||||
foreach ($map->keys() as $k)
|
||||
$config->put($k,$this->{$k});
|
||||
}
|
||||
|
||||
// Excess Config
|
||||
foreach ($map->values() as $k) {
|
||||
foreach ($map->values() as $k)
|
||||
$config->put($k,$this->{$k});
|
||||
}
|
||||
|
||||
// Shaped or Charge
|
||||
$config->put('shaped',$this->extra_shaped);
|
||||
@@ -89,7 +171,7 @@ class AdslSupplierPlan extends Model
|
||||
$result = collect();
|
||||
|
||||
// If data is empty, we'll report on allowance, otherwise we'll report on consumption
|
||||
$report = $data ? FALSE : TRUE;
|
||||
$report = ! $data;
|
||||
|
||||
// Work out if we charge each period
|
||||
foreach ($map as $k => $v) {
|
||||
@@ -136,7 +218,12 @@ class AdslSupplierPlan extends Model
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getNameAttribute()
|
||||
/**
|
||||
* Return the Broadband Speed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function speed(): string
|
||||
{
|
||||
return $this->speed;
|
||||
}
|
7
app/Models/Supplier/Ethernet.php
Normal file
7
app/Models/Supplier/Ethernet.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Supplier;
|
||||
|
||||
class Ethernet extends Broadband
|
||||
{
|
||||
}
|
7
app/Models/Supplier/HSPA.php
Normal file
7
app/Models/Supplier/HSPA.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Supplier;
|
||||
|
||||
class HSPA extends Broadband
|
||||
{
|
||||
}
|
@@ -10,6 +10,8 @@ class SupplierDetail extends Model
|
||||
{
|
||||
use SiteID;
|
||||
|
||||
protected $casts = [ 'connections'=>'collection' ];
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function supplier()
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Tax extends Model
|
||||
@@ -14,4 +15,30 @@ class Tax extends Model
|
||||
{
|
||||
return $this->belongsTo(Country::class);
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
* Calculate Tax on a value
|
||||
*
|
||||
* @param float $value
|
||||
* @param Collection $taxes
|
||||
* @return void
|
||||
*/
|
||||
public static function tax_calc(?float $value,Collection $taxes): float
|
||||
{
|
||||
if (! $value)
|
||||
$value = 0;
|
||||
$tax = 0;
|
||||
|
||||
foreach ($taxes as $o) {
|
||||
// Quick sanity check
|
||||
if (! $o instanceof self)
|
||||
abort(500,'Invalid object for tax calculation');
|
||||
|
||||
$tax += round($value*$o->rate,2);
|
||||
}
|
||||
|
||||
return round($value+$tax,2);
|
||||
}
|
||||
}
|
@@ -495,7 +495,7 @@ class User extends Authenticatable
|
||||
}
|
||||
|
||||
$result->load([
|
||||
'product.descriptions',
|
||||
'product.description',
|
||||
'service.type',
|
||||
]);
|
||||
|
||||
|
Reference in New Issue
Block a user