More Service display
This commit is contained in:
parent
6103b61265
commit
59a8ef2476
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use App\Models\{Invoice,Service};
|
||||
use App\User;
|
||||
use PDF;
|
||||
|
14
app/Models/Base/ProductType.php
Normal file
14
app/Models/Base/ProductType.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Base;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Models\Product;
|
||||
|
||||
//@todo column prod_plugin_file should no longer be required
|
||||
abstract class ProductType extends Model
|
||||
{
|
||||
public $timestamps = FALSE;
|
||||
public $dateFormat = 'U';
|
||||
}
|
@ -8,7 +8,7 @@ class Invoice extends Model
|
||||
{
|
||||
protected $table = 'ab_invoice';
|
||||
protected $dates = ['date_orig','due_date'];
|
||||
protected $with = ['account.country.currency','items.product','items.service','items.taxes','paymentitems'];
|
||||
protected $with = ['account.country.currency','items','paymentitems'];
|
||||
|
||||
protected $appends = [
|
||||
'date_due',
|
||||
|
@ -3,14 +3,26 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class Product extends Model
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = 'product';
|
||||
|
||||
protected $table = 'ab_product';
|
||||
protected $with = ['descriptions'];
|
||||
|
||||
const CREATED_AT = 'date_orig';
|
||||
const UPDATED_AT = 'date_last';
|
||||
public $incrementing = FALSE;
|
||||
public $dateFormat = 'U';
|
||||
|
||||
public function descriptions()
|
||||
{
|
||||
return $this->hasMany(ProductTranslate::class);
|
||||
@ -21,6 +33,16 @@ class Product extends Model
|
||||
return $this->hasMany(Service::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a child model with details of the service
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return $this->morphTo(null,'model','prod_plugin_data');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service category (from the product)
|
||||
*
|
||||
@ -91,6 +113,14 @@ class Product extends Model
|
||||
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;
|
||||
@ -149,6 +179,11 @@ class Product extends Model
|
||||
}
|
||||
}
|
||||
|
||||
public function price(int $period)
|
||||
{
|
||||
return Arr::get($this->price_array,sprintf('%s.1.price_base',$period));
|
||||
}
|
||||
|
||||
public function PricePeriods()
|
||||
{
|
||||
return [
|
||||
|
36
app/Models/Product/Adsl.php
Normal file
36
app/Models/Product/Adsl.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
use App\Models\AdslSupplierPlan;
|
||||
|
||||
class Adsl extends \App\Models\Base\ProductType
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = 'adsl_plan';
|
||||
|
||||
protected $table = 'ab_adsl_plan';
|
||||
|
||||
public function product()
|
||||
{
|
||||
return $this->hasOne(AdslSupplierPlan::class,'id','adsl_supplier_plan_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'];
|
||||
case 'speed':
|
||||
return $this->product->speed;
|
||||
}
|
||||
|
||||
// If we dont have a specific key, we'll resolve it normally
|
||||
return parent::__get($key);
|
||||
}
|
||||
}
|
12
app/Models/Product/Domain.php
Normal file
12
app/Models/Product/Domain.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class Domain extends \App\Models\Base\ProductType
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = '';
|
||||
}
|
12
app/Models/Product/Host.php
Normal file
12
app/Models/Product/Host.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class Host extends \App\Models\Base\ProductType
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = '';
|
||||
}
|
14
app/Models/Product/SSL.php
Normal file
14
app/Models/Product/SSL.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class SSL extends \App\Models\Base\ProductType
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = 'ssl';
|
||||
|
||||
protected $table = 'ab_ssl';
|
||||
}
|
12
app/Models/Product/Voip.php
Normal file
12
app/Models/Product/Voip.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Product;
|
||||
|
||||
use App\Traits\NextKey;
|
||||
|
||||
class Voip extends \App\Models\Base\ProductType
|
||||
{
|
||||
use NextKey;
|
||||
|
||||
const RECORD_ID = '';
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
@ -20,9 +19,9 @@ class Service extends Model
|
||||
public $incrementing = FALSE;
|
||||
|
||||
protected $table = 'ab_service';
|
||||
protected $with = ['product.descriptions','account.language'];
|
||||
protected $dates = ['date_last_invoice','date_next_invoice'];
|
||||
public $dateFormat = 'U';
|
||||
protected $with = ['product.descriptions','account.language','type'];
|
||||
|
||||
protected $casts = [
|
||||
'order_info'=>'array',
|
||||
@ -97,9 +96,23 @@ class Service extends Model
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Charges associated with this Service
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function charges()
|
||||
{
|
||||
return $this->hasMany(Charge::class)
|
||||
->where('active','=',TRUE)
|
||||
->orderBy('date_orig');
|
||||
}
|
||||
|
||||
// @todo changed to invoiced_items
|
||||
public function invoice_items($active=TRUE)
|
||||
{
|
||||
$query = $this->hasMany(InvoiceItem::class)
|
||||
->where('item_type','=',0)
|
||||
->orderBy('date_orig');
|
||||
|
||||
if ($active)
|
||||
@ -131,6 +144,7 @@ class Service extends Model
|
||||
/**
|
||||
* Account that ordered the service
|
||||
*
|
||||
* @todo changed to orderedby
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function orderby()
|
||||
@ -215,11 +229,7 @@ class Service extends Model
|
||||
|
||||
public function getBillingPriceAttribute(): float
|
||||
{
|
||||
if ($this->price)
|
||||
return $this->addtax($this->price);
|
||||
|
||||
dd($this->product->price_group,$this);
|
||||
return $this->cost;
|
||||
return $this->addTax($this->price ?: $this->product->price($this->recur_schedule));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,11 +258,36 @@ class Service extends Model
|
||||
* @todo This function negates the need for date_next_invoice
|
||||
* @return null
|
||||
*/
|
||||
public function getInvoiceNextAttribute(): Carbon
|
||||
public function getInvoiceNextAttribute()
|
||||
{
|
||||
$last = $this->getInvoiceToAttribute();
|
||||
$date = $last ? $last->addDay() : now();
|
||||
|
||||
return $last ? $last->addDay() : now();
|
||||
return request()->wantsJson() ? $date->format('Y-m-d') : $date;
|
||||
}
|
||||
|
||||
public function getInvoiceNextEndAttribute()
|
||||
{
|
||||
switch ($this->recur_schedule)
|
||||
{
|
||||
// Weekly
|
||||
case 0: $date = $this->getInvoiceNextAttribute()->addWeek(); break;
|
||||
// Monthly
|
||||
case 1: $date = $this->getInvoiceNextAttribute()->addMonth(); break;
|
||||
// Quarterly
|
||||
case 2: $date = $this->getInvoiceNextAttribute()->addQuarter(); break;
|
||||
// Half Yearly
|
||||
case 3: $date = $this->getInvoiceNextAttribute()->addQuarter(2); break;
|
||||
// Yearly
|
||||
case 4: $date = $this->getInvoiceNextAttribute()->addYear(); break;
|
||||
// Two Yearly
|
||||
case 5: $date = $this->getInvoiceNextAttribute()->addYear(2); break;
|
||||
// Three Yearly
|
||||
case 6: $date = $this->getInvoiceNextAttribute()->addYear(3); break;
|
||||
default: throw new \Exception('Unknown recur_schedule');
|
||||
}
|
||||
|
||||
return $date->subDay();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,6 +298,11 @@ class Service extends Model
|
||||
return $this->invoice_items->count() ? $this->invoice_items->last()->date_stop : NULL;
|
||||
}
|
||||
|
||||
public function getNameAttribute(): string
|
||||
{
|
||||
return $this->product->name_short.': '.$this->getNameShortAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the short name for the service.
|
||||
*
|
||||
@ -448,6 +488,8 @@ class Service extends Model
|
||||
|
||||
public function invoices_due(): Collection
|
||||
{
|
||||
$this->load('invoice_items.invoice');
|
||||
|
||||
return $this->invoice_items->filter(function($item) {
|
||||
return $item->invoice->due > 0;
|
||||
});
|
||||
@ -464,6 +506,28 @@ class Service extends Model
|
||||
return $this->active OR ($this->order_status AND ! in_array($this->order_status,$this->inactive_status));
|
||||
}
|
||||
|
||||
public function next_invoice_items()
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
$result->push([
|
||||
'item'=>0,
|
||||
'desc'=>sprintf('Product/Service [%s->%s]',
|
||||
$this->invoice_next->format('Y-m-d'),
|
||||
$this->invoice_next_end->format('Y-m-d')),
|
||||
'amt'=>$this->getBillingPriceAttribute()]);
|
||||
|
||||
foreach ($this->charges->filter(function($item) { return ! $item->processed; }) as $o)
|
||||
{
|
||||
$result->push([
|
||||
'item'=>5, // @tod Charges
|
||||
'desc'=>sprintf('%d@%3.2f - %s',$o->quantity,$this->addTax($o->amount),$o->name),
|
||||
'amt'=>$this->addTax($o->amount*$o->quantity)]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
* @param string $status
|
||||
|
@ -17,7 +17,6 @@ class User extends Authenticatable
|
||||
use HasApiTokens,Notifiable,UserSwitch,HasQuickBooksToken;
|
||||
|
||||
protected $dates = ['created_at','updated_at','last_access'];
|
||||
protected $with = ['accounts.services'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddModelToProduct extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('ab_product', function (Blueprint $table) {
|
||||
$table->string('model')->nullable();
|
||||
});
|
||||
|
||||
// Go through our services and add the relation
|
||||
foreach (\App\Models\Product\Adsl::all() as $o)
|
||||
$this->update($o,'ADSL');
|
||||
/*
|
||||
foreach (\App\Models\Product\Domain::all() as $o)
|
||||
$this->update($o);
|
||||
foreach (\App\Models\Product\Host::all() as $o)
|
||||
$this->update($o);
|
||||
*/
|
||||
foreach (\App\Models\Product\SSL::all() as $o)
|
||||
$this->update($o,'SSL');
|
||||
/*
|
||||
foreach (\App\Models\Product\Voip::all() as $o)
|
||||
$this->update($o);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('ab_product', function (Blueprint $table) {
|
||||
$table->dropColumn('model');
|
||||
});
|
||||
}
|
||||
|
||||
function update(\Illuminate\Database\Eloquent\Model $o,$file)
|
||||
{
|
||||
foreach (\App\Models\Product::where('prod_plugin_file',$file)->where('prod_plugin_data',$o->id)->get() as $oo)
|
||||
{
|
||||
$oo->model = get_class($o);
|
||||
$oo->save();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<strong>Next Invoice Details</strong>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<th colspan="3">{{ $o->name }}</th><th class="text-right">{{ number_format($o->next_invoice_items()->sum('amt'),2) }}</th>
|
||||
</tr>
|
||||
@foreach ($o->next_invoice_items() as $oo)
|
||||
<tr>
|
||||
<td class="pt-0 pb-1"> </td><td class="pt-0 pb-1">{{ $oo['desc'] }}</td><td class="text-right pt-0 pb-1">{{ number_format($oo['amt'],2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@ -84,12 +84,20 @@
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<th>Speed</th>
|
||||
<td>TBA</td>
|
||||
<td>{{ $o->product->type->speed }}</td>
|
||||
</tr>
|
||||
@if ($o->product->type->base_down_peak)
|
||||
<tr>
|
||||
<th>Peak Included Downloads</th>
|
||||
<td>TBA</td>
|
||||
<td>{{ number_format($o->product->type->base_down_peak,0) }}GB</td>
|
||||
</tr>
|
||||
@endif
|
||||
@if ($o->product->type->base_down_offpeak)
|
||||
<tr>
|
||||
<th>Peak Included Downloads</th>
|
||||
<td>{{ number_format($o->product->type->base_down_offpeak,0) }}MB</td>
|
||||
</tr>
|
||||
@endif
|
||||
<tr>
|
||||
<th>Traffic Last Month</th>
|
||||
<td>TBA</td>
|
||||
@ -111,7 +119,22 @@
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab-invoice">
|
||||
@include('common.invoice.widget.list')
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
@include('common.service.widget.invoice')
|
||||
|
||||
{{-- @todo show list of outstanding invoices --}}
|
||||
</div>
|
||||
|
||||
{{-- Workaround since col-offset-x is not in our CSS? --}}
|
||||
<div class="col-2">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
@include('common.invoice.widget.list')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab-email">
|
||||
|
Loading…
Reference in New Issue
Block a user