Work on product costing (broadband) and reporting
This commit is contained in:
10
app/Models/AdslSupplier.php
Normal file
10
app/Models/AdslSupplier.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AdslSupplier extends Model
|
||||
{
|
||||
protected $table = 'ab_adsl_supplier';
|
||||
}
|
@@ -3,11 +3,139 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class AdslSupplierPlan extends Model
|
||||
{
|
||||
protected $table = 'ab_adsl_supplier_plan';
|
||||
|
||||
/**
|
||||
* Determine how traffic is counted for Broadband links.
|
||||
*
|
||||
* Configuration allows for traffic to fit into the following categories:
|
||||
* + 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:
|
||||
* + 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
|
||||
* will be shown as 2 metrics - PEAK/OFFPEAK.
|
||||
* + UPLOADS are NOT charged and there are no PEAK/OFFPEAK periods the allowance
|
||||
* will be shown as 1 metrics - TRAFFIC.
|
||||
* + UPLOADS are NOT charged and there are PEAK/OFFPEAK periods the allowance
|
||||
* will be shown as 2 metrics - PEAK/OFFPEAK.
|
||||
*
|
||||
* Thus:
|
||||
* (x = up/down, Y=peak/offpeak)
|
||||
*
|
||||
* + If base_x_Y is NULL, all Y traffic is FREE (ignore respective extra_x_Y setting).
|
||||
* + If base_x_Y is a number, all Y traffic is FREE up to the number (evaluate extra_x_Y setting).
|
||||
* + If extra_x_Y is a number, charge this amount for traffic over base_x_Y.
|
||||
*
|
||||
* + If extra_down_peak is NULL this is invalid, treat base_down_peak as NULL
|
||||
* + If extra_down_offpeak is NULL add traffic_down_offpeak to traffic_down_peak
|
||||
* + 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
|
||||
* @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',
|
||||
]);
|
||||
|
||||
if (is_null($config))
|
||||
$config = collect($config);
|
||||
|
||||
// If config is null, use the configuration from this Model
|
||||
if (! $config->count()) {
|
||||
// Base Config
|
||||
foreach ($map->keys() as $k) {
|
||||
$config->put($k,$this->{$k});
|
||||
}
|
||||
|
||||
// Excess Config
|
||||
foreach ($map->values() 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);
|
||||
}
|
||||
|
||||
$result = collect();
|
||||
|
||||
// If data is empty, we'll report on allowance, otherwise we'll report on consumption
|
||||
$report = $data ? FALSE : TRUE;
|
||||
|
||||
// Work out if we charge each period
|
||||
foreach ($map as $k => $v) {
|
||||
// Anything NULL is not counted
|
||||
if (is_null($config->get($k)))
|
||||
continue;
|
||||
|
||||
$x = $report ? $config->get($k) : ($config->get($k)-Arr::get($data,$k,0));
|
||||
|
||||
if ($ceil)
|
||||
$x = (int)ceil($x);
|
||||
|
||||
// Non-NULL entries are counted as is
|
||||
if (! is_null($config->get($v))) {
|
||||
// Existing value for this item to be added
|
||||
$value = $result->has($k) ? $result->get($k) : 0;
|
||||
$result->put($k,$value+$x);
|
||||
|
||||
// NULL entries are merged into another key
|
||||
} else {
|
||||
// New Key for this item
|
||||
$key = $merge->get($v);
|
||||
|
||||
// Existing value for this item to be added
|
||||
$value = $result->has($key) ? $result->get($key) : 0;
|
||||
|
||||
// Any value in the existing key, add it too.
|
||||
if ($k !== $key AND $result->has($k)) {
|
||||
$value += $result->get($k);
|
||||
$result->forget($k);
|
||||
}
|
||||
|
||||
$result->put($key,$value+$x);
|
||||
}
|
||||
}
|
||||
|
||||
if ($config->has('metric') AND $config->get('metric'))
|
||||
$result->transform(function($item) use ($config,$ceil) {
|
||||
return $ceil
|
||||
? (int)ceil($item/$config->get('metric'))
|
||||
: $item/$config->get('metric');
|
||||
});
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getNameAttribute()
|
||||
{
|
||||
return $this->speed;
|
||||
|
@@ -7,6 +7,7 @@ use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class Product extends Model
|
||||
{
|
||||
@@ -132,7 +133,12 @@ class Product extends Model
|
||||
|
||||
public function getPriceArrayAttribute()
|
||||
{
|
||||
return unserialize($this->attributes['price_group']);
|
||||
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()
|
||||
|
@@ -2,30 +2,65 @@
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
use App\Models\AdslSupplierPlan;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class Adsl extends \App\Models\Base\ProductType
|
||||
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 'base_down_peak':
|
||||
return $this->attributes['base_down_peak']/$this->attributes['metric'];
|
||||
case 'base_down_peak':
|
||||
return $this->attributes['base_down_offpeak']/$this->attributes['metric'];
|
||||
switch($key) {
|
||||
case 'speed':
|
||||
return $this->product->speed;
|
||||
}
|
||||
@@ -33,4 +68,87 @@ class Adsl extends \App\Models\Base\ProductType
|
||||
// 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;
|
||||
}
|
||||
}
|
@@ -2,13 +2,34 @@
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use App\Interfaces\ProductSupplier;
|
||||
use App\Models\Base\ProductType;
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class SSL extends \App\Models\Base\ProductType
|
||||
class SSL extends ProductType implements ProductSupplier
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = 'ssl';
|
||||
|
||||
protected $table = 'ab_ssl';
|
||||
|
||||
public function allowance(): Collection
|
||||
{
|
||||
// N/A
|
||||
return collect();
|
||||
}
|
||||
|
||||
public function allowance_string(): string
|
||||
{
|
||||
// N/A
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getCostAttribute(): float
|
||||
{
|
||||
// N/A
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -301,6 +301,22 @@ class Service extends Model
|
||||
return $this->addTax(is_null($this->price) ? $this->product->price($this->recur_schedule) : $this->price);
|
||||
}
|
||||
|
||||
public function getBillingMonthlyPriceAttribute(): float
|
||||
{
|
||||
$d = 0;
|
||||
switch ($this->recur_schedule) {
|
||||
case 0: $d = 12/52; break;
|
||||
case 1: $d = 1; break;
|
||||
case 2: $d = 3; break;
|
||||
case 3: $d = 6; break;
|
||||
case 4: $d = 12; break;
|
||||
case 5: $d = 24; break;
|
||||
case 6: $d = 36; break;
|
||||
}
|
||||
|
||||
return number_format($this->getBillingPriceAttribute()/$d,2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service billing period
|
||||
*
|
||||
|
Reference in New Issue
Block a user