Add cost/price/supplierid to service, update service report to show supplerid and overridden costs (if any)
This commit is contained in:
parent
8d72c1ceb5
commit
b33c052995
@ -412,7 +412,9 @@ class ServiceController extends Controller
|
|||||||
'suspend_billing' => 'nullable|in:on',
|
'suspend_billing' => 'nullable|in:on',
|
||||||
'recur_schedule' => ['required',Rule::in(collect(Invoice::billing_periods)->keys())],
|
'recur_schedule' => ['required',Rule::in(collect(Invoice::billing_periods)->keys())],
|
||||||
'invoice_next_at' => 'nullable|date',
|
'invoice_next_at' => 'nullable|date',
|
||||||
'price' => 'nullable|numeric',
|
'price' => 'nullable|numeric|min:0', // Price we charge the client, if we dont charge supplied/price
|
||||||
|
'cost' => 'nullable|numeric|min:0', // Price we are charged by supplier, if we arent charged supplier/price
|
||||||
|
'supplierid' => 'nullable|string|min:1', // As used on invoices
|
||||||
$o->product->category => 'array|min:1',
|
$o->product->category => 'array|min:1',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -456,6 +458,8 @@ class ServiceController extends Controller
|
|||||||
$o->suspend_billing = ($validated->get('suspend_billing') == 'on');
|
$o->suspend_billing = ($validated->get('suspend_billing') == 'on');
|
||||||
$o->external_billing = ($validated->get('external_billing') == 'on');
|
$o->external_billing = ($validated->get('external_billing') == 'on');
|
||||||
$o->price = $validated->get('price');
|
$o->price = $validated->get('price');
|
||||||
|
$o->cost = $validated->get('cost');
|
||||||
|
$o->supplierid = $validated->get('supplierid');
|
||||||
|
|
||||||
// Also update our service start_at date.
|
// Also update our service start_at date.
|
||||||
// @todo We may want to make start_at/stop_at dynamic values calculated by the type records
|
// @todo We may want to make start_at/stop_at dynamic values calculated by the type records
|
||||||
|
@ -955,13 +955,14 @@ class Service extends Model implements IDs
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount we are charged for the client to have this service
|
* The amount we are charged for the client to have this service
|
||||||
|
*
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public function billing_cost(): float
|
public function billing_cost(): float
|
||||||
{
|
{
|
||||||
return is_null($this->cost)
|
return is_null($this->cost)
|
||||||
? $this->product->getBaseCostAttribute()
|
? $this->product->getBaseCostAttribute()
|
||||||
: $this->cost;
|
: $this->cost*Invoice::billing_change($this->product->type->billing_interval,$this->product->billing_interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1127,7 +1128,7 @@ class Service extends Model implements IDs
|
|||||||
|
|
||||||
public function isContract(): bool
|
public function isContract(): bool
|
||||||
{
|
{
|
||||||
return $this->getContractEndAttribute()->greaterThan(Carbon::now());
|
return $this->getContractEndAttribute() && $this->getContractEndAttribute()->greaterThan(Carbon::now());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,16 +57,6 @@ class Broadband extends Type implements ServiceUsage
|
|||||||
|
|
||||||
/* ATTRIBUTES */
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use $o->service_name;
|
|
||||||
* @return mixed|string
|
|
||||||
*/
|
|
||||||
public function getNameAttribute()
|
|
||||||
{
|
|
||||||
abort(500,'deprecated - use $o->service_name');
|
|
||||||
return $this->service_number ?: $this->service_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of technology used to provide this Internet Service
|
* The type of technology used to provide this Internet Service
|
||||||
*
|
*
|
||||||
@ -104,15 +94,14 @@ class Broadband extends Type implements ServiceUsage
|
|||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the suppliers offering that this service is providing
|
* Return the supplier's offering that this service is providing
|
||||||
*
|
*
|
||||||
* @return SupplierType
|
* @return SupplierType
|
||||||
* @todo This column provided_adsl_plan_id should either be deprecated or renamed.
|
|
||||||
*/
|
*/
|
||||||
public function supplied(): SupplierType
|
public function supplied(): SupplierType
|
||||||
{
|
{
|
||||||
return $this->provided_adsl_plan_id
|
return $this->provided_supplier_broadband_id
|
||||||
? SupplierBroadband::findOrFail($this->provided_adsl_plan_id)
|
? SupplierBroadband::findOrFail($this->provided_supplier_broadband_id)
|
||||||
: $this->service->offering->supplied;
|
: $this->service->offering->supplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
30
database/migrations/2025_05_16_135847_add_cost.php
Normal file
30
database/migrations/2025_05_16_135847_add_cost.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('services', function (Blueprint $table) {
|
||||||
|
$table->float('cost')->nullable();
|
||||||
|
$table->string('supplierid')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_broadband', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('cost');
|
||||||
|
$table->dropColumn('supplierid');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -25,6 +25,7 @@
|
|||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Service</th>
|
<th>Service</th>
|
||||||
<th>Product</th>
|
<th>Product</th>
|
||||||
|
<th>Supplier ID</th>
|
||||||
<th class="text-right">Monthly</th>
|
<th class="text-right">Monthly</th>
|
||||||
<th class="text-right">Cost</th>
|
<th class="text-right">Cost</th>
|
||||||
<th class="text-right">Usage</th>
|
<th class="text-right">Usage</th>
|
||||||
@ -38,8 +39,9 @@
|
|||||||
<td><a href="{{ url('u/service',[$o->id]) }}">{{ $o->id }}</a></td>
|
<td><a href="{{ url('u/service',[$o->id]) }}">{{ $o->id }}</a></td>
|
||||||
<td>{{ $o->name }}</td>
|
<td>{{ $o->name }}</td>
|
||||||
<td>{{ $o->product->name }}</td>
|
<td>{{ $o->product->name }}</td>
|
||||||
|
<td>{{ $o->supplierid }}</td>
|
||||||
<td class="text-right">{{ number_format($o->billing_charge_normalised,2) }}</td>
|
<td class="text-right">{{ number_format($o->billing_charge_normalised,2) }}</td>
|
||||||
<td class="text-right">{{ number_format($o->product->cost_normalized(),2) }}</td>
|
<td class="text-right">{{ number_format($o->billing_cost_normalised,2) }}</td>
|
||||||
<td class="text-right">{{ $o->product->hasUsage() ? number_format($o->type->usage_summary(0)->sum()/1000,1) : '-' }}</td>
|
<td class="text-right">{{ $o->product->hasUsage() ? number_format($o->type->usage_summary(0)->sum()/1000,1) : '-' }}</td>
|
||||||
<td>{{ $o->product->supplier->name }}</td>
|
<td>{{ $o->product->supplier->name }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -49,6 +51,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th>TOTAL:</th>
|
<th>TOTAL:</th>
|
||||||
|
<td></td>
|
||||||
<td class="text-right"></td>
|
<td class="text-right"></td>
|
||||||
<td class="text-right"></td>
|
<td class="text-right"></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@ -101,22 +104,23 @@
|
|||||||
// Total over all pages
|
// Total over all pages
|
||||||
let month = rows
|
let month = rows
|
||||||
.data()
|
.data()
|
||||||
.pluck(3)
|
.pluck(4)
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
let cost = rows
|
let cost = rows
|
||||||
.data()
|
.data()
|
||||||
.pluck(4)
|
.pluck(5)
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
let usage = rows
|
let usage = rows
|
||||||
.data()
|
.data()
|
||||||
.pluck(5)
|
.pluck(6)
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
return $('<tr/>')
|
return $('<tr/>')
|
||||||
.append('<td/>')
|
.append('<td/>')
|
||||||
.append('<td><strong>SUB-TOTAL:</strong></td>')
|
.append('<td><strong>SUB-TOTAL:</strong></td>')
|
||||||
|
.append('<td/>')
|
||||||
.append('<td class="text-right"><strong>'+month.toFixed(2)+'</strong></td>')
|
.append('<td class="text-right"><strong>'+month.toFixed(2)+'</strong></td>')
|
||||||
.append('<td class="text-right"><strong>'+cost.toFixed(2)+'</strong></td>')
|
.append('<td class="text-right"><strong>'+cost.toFixed(2)+'</strong></td>')
|
||||||
.append('<td class="text-right"><strong>'+usage.toFixed(2)+'</strong></td>')
|
.append('<td class="text-right"><strong>'+usage.toFixed(2)+'</strong></td>')
|
||||||
@ -129,7 +133,7 @@
|
|||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
targets: [0,1,3,4,5],
|
targets: [0,1,4,5,6],
|
||||||
searchPanes: {
|
searchPanes: {
|
||||||
show: false,
|
show: false,
|
||||||
}
|
}
|
||||||
@ -163,31 +167,31 @@
|
|||||||
|
|
||||||
// Total over all pages
|
// Total over all pages
|
||||||
month = api
|
month = api
|
||||||
.column(3,{ search: 'applied' })
|
|
||||||
.data()
|
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
|
||||||
|
|
||||||
// Total over this page
|
|
||||||
monthTotal = api
|
|
||||||
.column(3, { page: 'current' })
|
|
||||||
.data()
|
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
|
||||||
|
|
||||||
// Total over all pages
|
|
||||||
cost = api
|
|
||||||
.column(4,{ search: 'applied' })
|
.column(4,{ search: 'applied' })
|
||||||
.data()
|
.data()
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
// Total over this page
|
// Total over this page
|
||||||
costTotal = api
|
monthTotal = api
|
||||||
.column(4, { page: 'current' })
|
.column(4, { page: 'current' })
|
||||||
.data()
|
.data()
|
||||||
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
|
// Total over all pages
|
||||||
|
cost = api
|
||||||
|
.column(5,{ search: 'applied' })
|
||||||
|
.data()
|
||||||
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
|
// Total over this page
|
||||||
|
costTotal = api
|
||||||
|
.column(5, { page: 'current' })
|
||||||
|
.data()
|
||||||
|
.reduce((a, b) => intVal(a) + intVal(b), 0);
|
||||||
|
|
||||||
// Update footer
|
// Update footer
|
||||||
api.column(3).footer().innerHTML = monthTotal.toFixed(2)+'<br><small>('+month.toFixed(2)+')</small>';
|
api.column(4).footer().innerHTML = monthTotal.toFixed(2)+'<br><small>('+month.toFixed(2)+')</small>';
|
||||||
api.column(4).footer().innerHTML = costTotal.toFixed(2)+'<br><small>('+cost.toFixed(2)+')</small>';
|
api.column(5).footer().innerHTML = costTotal.toFixed(2)+'<br><small>('+cost.toFixed(2)+')</small>';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col-12">
|
||||||
Connection Type
|
Connection Type
|
||||||
<x-leenooks::form.toggle id="pppoe" name="broadband[pppoe]" label="PPPoE" old="broadband.pppoe" :value="$o->pppoe"/>
|
<x-leenooks::form.toggle id="pppoe" name="broadband[pppoe]" label="PPPoE" old="broadband.pppoe" :value="$o->pppoe"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!-- $o=Service::class,$p=Product::class -->
|
<!-- $o=Service::class,$p=Product::class -->
|
||||||
|
|
||||||
@use(App\Models\Invoice)
|
|
||||||
@use(Carbon\CarbonInterface)
|
@use(Carbon\CarbonInterface)
|
||||||
|
@use(App\Models\Invoice)
|
||||||
|
|
||||||
@php($c=$o->product)
|
@php($c=$o->product)
|
||||||
|
|
||||||
@ -68,7 +68,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Billing Price</th>
|
<th>Billing Price</th>
|
||||||
<td @if($o->isChargeOverridden())class="text-danger"@endif>${{ number_format($b=$o->billing_charge,2) }}</td>
|
<td @if($o->isChargeOverridden())class="text-danger"@endif>${{ number_format($b=$o->billing_charge,2) }}</td>
|
||||||
<td>${{ number_format($a=$o->account->taxed($c->base_cost),2) }}</td>
|
<td @if($o->isCostOverridden())class="text-danger"@endif>${{ number_format($a=$o->billing_cost,2) }}</td>
|
||||||
<td>{!! markup($a,$b) !!}</td>
|
<td>{!! markup($a,$b) !!}</td>
|
||||||
@if($p->exists)
|
@if($p->exists)
|
||||||
<td @if($o->isChargeOverridden())class="text-danger"@endif>${{ number_format($b=$o->account->taxed($p->base_charge),2) }}</td>
|
<td @if($o->isChargeOverridden())class="text-danger"@endif>${{ number_format($b=$o->account->taxed($p->base_charge),2) }}</td>
|
||||||
@ -90,7 +90,7 @@
|
|||||||
@if($x)
|
@if($x)
|
||||||
<abbr title="${{ number_format($a=$o->account->taxed($c->base_cost)*Invoice::billing_change($o->billing_interval,Invoice::BILL_MONTHLY),2) }}">${{ number_format($b=$o->billing_cost_normalised,2) }}
|
<abbr title="${{ number_format($a=$o->account->taxed($c->base_cost)*Invoice::billing_change($o->billing_interval,Invoice::BILL_MONTHLY),2) }}">${{ number_format($b=$o->billing_cost_normalised,2) }}
|
||||||
@else
|
@else
|
||||||
${{ number_format($o->billing_cost_normalised,2) }}
|
${{ number_format($a=$o->billing_cost_normalised,2) }}
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>{!! markup($a,$b) !!}</td>
|
<td>{!! markup($a,$b) !!}</td>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h4>Update Service details <x-leenooks::button.success class="float-right"/></h4>
|
<h4>Update Service Details <x-leenooks::button.success class="float-right"/></h4>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<form method="POST" action="{{ url('a/service/update',[$o->id]) }}">
|
<form method="POST" action="{{ url('a/service/update',[$o->id]) }}">
|
||||||
@ -29,11 +29,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-3"></div>
|
<div class="col-12 col-sm-3">
|
||||||
|
<x-leenooks::form.text name="supplierid" icon="fa-hashtag" label="Supplier ID" :value="$o->supplierid"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-9 col-md-6 col-xl-5">
|
<div class="col-12 col-sm-9 col-md-6 col-xl-5">
|
||||||
<x-leenooks::form.select id="recur_schedule" name="recur_schedule" icon="fa-redo" label="Renew Term" :value="$o->recur_schedule" :options="collect(Invoice::billing_periods)->map(fn($item,$key)=>['id'=>$key,'value'=>$item['name']])"/>
|
<x-leenooks::form.select id="recur_schedule" name="recur_schedule" icon="fa-redo" label="Renew Term" :value="$o->recur_schedule" :options="collect(Invoice::billing_periods)->map(fn($item,$key)=>['id'=>$key,'value'=>$item['name']])"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Cost -->
|
||||||
|
<div class="col-12 col-sm-4 col-xl-3">
|
||||||
|
<x-leenooks::form.text name="cost" icon="fa-dollar-sign" label="Cost (Monthly)" :value="$o->cost"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user