More Service display
This commit is contained in:
parent
6103b61265
commit
59a8ef2476
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
use App\Models\{Invoice,Service};
|
use App\Models\{Invoice,Service};
|
||||||
use App\User;
|
use App\User;
|
||||||
use PDF;
|
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 $table = 'ab_invoice';
|
||||||
protected $dates = ['date_orig','due_date'];
|
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 = [
|
protected $appends = [
|
||||||
'date_due',
|
'date_due',
|
||||||
|
@ -3,14 +3,26 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use App\Traits\NextKey;
|
||||||
|
|
||||||
class Product extends Model
|
class Product extends Model
|
||||||
{
|
{
|
||||||
|
use NextKey;
|
||||||
|
|
||||||
|
const RECORD_ID = 'product';
|
||||||
|
|
||||||
protected $table = 'ab_product';
|
protected $table = 'ab_product';
|
||||||
protected $with = ['descriptions'];
|
protected $with = ['descriptions'];
|
||||||
|
|
||||||
|
const CREATED_AT = 'date_orig';
|
||||||
|
const UPDATED_AT = 'date_last';
|
||||||
|
public $incrementing = FALSE;
|
||||||
|
public $dateFormat = 'U';
|
||||||
|
|
||||||
public function descriptions()
|
public function descriptions()
|
||||||
{
|
{
|
||||||
return $this->hasMany(ProductTranslate::class);
|
return $this->hasMany(ProductTranslate::class);
|
||||||
@ -21,6 +33,16 @@ class Product extends Model
|
|||||||
return $this->hasMany(Service::class);
|
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)
|
* 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;
|
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()
|
public function getProductTypeAttribute()
|
||||||
{
|
{
|
||||||
return $this->plugin()->product->name;
|
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()
|
public function PricePeriods()
|
||||||
{
|
{
|
||||||
return [
|
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;
|
namespace App\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
@ -20,9 +19,9 @@ class Service extends Model
|
|||||||
public $incrementing = FALSE;
|
public $incrementing = FALSE;
|
||||||
|
|
||||||
protected $table = 'ab_service';
|
protected $table = 'ab_service';
|
||||||
protected $with = ['product.descriptions','account.language'];
|
|
||||||
protected $dates = ['date_last_invoice','date_next_invoice'];
|
protected $dates = ['date_last_invoice','date_next_invoice'];
|
||||||
public $dateFormat = 'U';
|
public $dateFormat = 'U';
|
||||||
|
protected $with = ['product.descriptions','account.language','type'];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'order_info'=>'array',
|
'order_info'=>'array',
|
||||||
@ -97,9 +96,23 @@ class Service extends Model
|
|||||||
return $this->belongsTo(Account::class);
|
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)
|
public function invoice_items($active=TRUE)
|
||||||
{
|
{
|
||||||
$query = $this->hasMany(InvoiceItem::class)
|
$query = $this->hasMany(InvoiceItem::class)
|
||||||
|
->where('item_type','=',0)
|
||||||
->orderBy('date_orig');
|
->orderBy('date_orig');
|
||||||
|
|
||||||
if ($active)
|
if ($active)
|
||||||
@ -131,6 +144,7 @@ class Service extends Model
|
|||||||
/**
|
/**
|
||||||
* Account that ordered the service
|
* Account that ordered the service
|
||||||
*
|
*
|
||||||
|
* @todo changed to orderedby
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
*/
|
*/
|
||||||
public function orderby()
|
public function orderby()
|
||||||
@ -215,11 +229,7 @@ class Service extends Model
|
|||||||
|
|
||||||
public function getBillingPriceAttribute(): float
|
public function getBillingPriceAttribute(): float
|
||||||
{
|
{
|
||||||
if ($this->price)
|
return $this->addTax($this->price ?: $this->product->price($this->recur_schedule));
|
||||||
return $this->addtax($this->price);
|
|
||||||
|
|
||||||
dd($this->product->price_group,$this);
|
|
||||||
return $this->cost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,11 +258,36 @@ class Service extends Model
|
|||||||
* @todo This function negates the need for date_next_invoice
|
* @todo This function negates the need for date_next_invoice
|
||||||
* @return null
|
* @return null
|
||||||
*/
|
*/
|
||||||
public function getInvoiceNextAttribute(): Carbon
|
public function getInvoiceNextAttribute()
|
||||||
{
|
{
|
||||||
$last = $this->getInvoiceToAttribute();
|
$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;
|
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.
|
* Return the short name for the service.
|
||||||
*
|
*
|
||||||
@ -448,6 +488,8 @@ class Service extends Model
|
|||||||
|
|
||||||
public function invoices_due(): Collection
|
public function invoices_due(): Collection
|
||||||
{
|
{
|
||||||
|
$this->load('invoice_items.invoice');
|
||||||
|
|
||||||
return $this->invoice_items->filter(function($item) {
|
return $this->invoice_items->filter(function($item) {
|
||||||
return $item->invoice->due > 0;
|
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));
|
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
|
* @todo
|
||||||
* @param string $status
|
* @param string $status
|
||||||
|
@ -17,7 +17,6 @@ class User extends Authenticatable
|
|||||||
use HasApiTokens,Notifiable,UserSwitch,HasQuickBooksToken;
|
use HasApiTokens,Notifiable,UserSwitch,HasQuickBooksToken;
|
||||||
|
|
||||||
protected $dates = ['created_at','updated_at','last_access'];
|
protected $dates = ['created_at','updated_at','last_access'];
|
||||||
protected $with = ['accounts.services'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* 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">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Speed</th>
|
<th>Speed</th>
|
||||||
<td>TBA</td>
|
<td>{{ $o->product->type->speed }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@if ($o->product->type->base_down_peak)
|
||||||
<tr>
|
<tr>
|
||||||
<th>Peak Included Downloads</th>
|
<th>Peak Included Downloads</th>
|
||||||
<td>TBA</td>
|
<td>{{ number_format($o->product->type->base_down_peak,0) }}GB</td>
|
||||||
</tr>
|
</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>
|
<tr>
|
||||||
<th>Traffic Last Month</th>
|
<th>Traffic Last Month</th>
|
||||||
<td>TBA</td>
|
<td>TBA</td>
|
||||||
@ -111,7 +119,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane" id="tab-invoice">
|
<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>
|
||||||
|
|
||||||
<div class="tab-pane" id="tab-email">
|
<div class="tab-pane" id="tab-email">
|
||||||
|
Loading…
Reference in New Issue
Block a user