Reimplmement service changes going to the service__change table

This commit is contained in:
Deon George 2023-05-06 13:53:14 +10:00
parent 691180b3f0
commit 013bb632d3
9 changed files with 181 additions and 26 deletions

View File

@ -67,7 +67,11 @@ class ServiceController extends Controller
if (! $o->order_info) if (! $o->order_info)
$o->order_info = collect(); $o->order_info = collect();
$o->order_info->put('change_cancel',Carbon::now()->format('Y-m-d H:i:s')); // @todo add some validation if this doesnt return a result
$np = $o->changes()->where('service__change.active',TRUE)->where('complete',FALSE)->get()->pop();
$np->pivot->active = FALSE;
$np->pivot->save();
$o->order_status = 'ACTIVE'; $o->order_status = 'ACTIVE';
return $o->save(); return $o->save();
@ -91,6 +95,9 @@ class ServiceController extends Controller
public function change_pending(ServiceChangeRequest $request,Service $o) public function change_pending(ServiceChangeRequest $request,Service $o)
{ {
// @todo add some validation if this doesnt return a result
$np = $o->changes()->where('service__change.active',TRUE)->where('complete',FALSE)->get()->pop();
if ($request->post()) { if ($request->post()) {
foreach ($this->service_change_charges($request,$o) as $co) foreach ($this->service_change_charges($request,$o) as $co)
$co->save(); $co->save();
@ -100,12 +107,17 @@ class ServiceController extends Controller
$o->order_status = 'ACTIVE'; $o->order_status = 'ACTIVE';
$o->save(); $o->save();
$np->pivot->complete = TRUE;
$np->pivot->effective_at = Carbon::now();
$np->pivot->save();
return redirect()->to(url('u/service',[$o->id])); return redirect()->to(url('u/service',[$o->id]));
} }
return view('service.change_pending') return view('service.change_pending')
->with('breadcrumb',collect()->merge($o->account->breadcrumb)) ->with('breadcrumb',collect()->merge($o->account->breadcrumb))
->with('o',$o); ->with('o',$o)
->with('np',$np);
} }
/** /**
@ -218,16 +230,22 @@ class ServiceController extends Controller
{ {
if ($request->post()) { if ($request->post()) {
$request->validate([ $request->validate([
'product_id'=>'required|exists:products,id',
'change_date'=>'required|date', 'change_date'=>'required|date',
'notes'=>'required|min:10', 'notes'=>'nullable|min:10',
]); ]);
if (! $o->order_info) $o->changes()->attach([$o->id=>[
$o->order_info = collect(); 'site_id'=> $o->site_id,
'ordered_by' => Auth::id(),
'ordered_at' => Carbon::now(),
'effective_at' => $request->change_date,
'product_id' => $request->product_id,
'notes' => $request->notes,
'active' => TRUE,
'complete' => FALSE,
]]);
$o->order_info->put('change_note',$request->notes);
$o->order_info->put('change_date',$request->change_date);
$o->order_info->put('change_product_id',$request->product_id);
$o->order_status = 'CHANGE-REQUEST'; $o->order_status = 'CHANGE-REQUEST';
$o->save(); $o->save();

View File

@ -26,7 +26,7 @@ class ChangeRequest extends Mailable
public function __construct(Service $o,string $notes='') public function __construct(Service $o,string $notes='')
{ {
$this->service = $o; $this->service = $o;
$this->notes = $notes; $this->notes = $notes ?? '';
} }
/** /**

View File

@ -28,6 +28,11 @@ use App\Traits\SiteID;
* Class Service * Class Service
* Services that belong to an account * Services that belong to an account
* *
* So each service attribute has:
* - Offering, what product we supply (we make offerings from supplier's supplied products) - in the DB these are products/*
* - Supplied, our supplier's product that is providing the service - in the DB these are supplier/*
* - Type, what service we are providing, made up of a product we supply - in the DB these are service/*
*
* Attributes for services: * Attributes for services:
* + additional_cost : Pending additional charges for this service (excluding setup) //@todo check all these are still valid * + additional_cost : Pending additional charges for this service (excluding setup) //@todo check all these are still valid
* + billing_charge : Charge for this service each invoice period // @todo change to "charge" * + billing_charge : Charge for this service each invoice period // @todo change to "charge"
@ -46,14 +51,10 @@ use App\Traits\SiteID;
* + sid : System ID for service * + sid : System ID for service
* + supplied : The model of the supplier's product used for this service. * + supplied : The model of the supplier's product used for this service.
* *
* = Terminology:
* - Offering, what product we supply (we make offerings from supplier's supplied products) - in the DB these are products/*
* - Supplied, our supplier's product that is providing the service - in the DB these are supplier/*
* - Type, what service we are providing, made up of a product we supply - in the DB these are service/*
*
* @package App\Models * @package App\Models
* @todo "Billing Start Date" = "connection date" for sub types?? * @todo "Billing Start Date" = "connection date" for sub types??
* @todo Add min_charge * @todo Add min_charge
* @todo deprecate price_override, and if price < what would be billed, show it striked through, otherwise show it as if it was the price
*/ */
class Service extends Model implements IDs class Service extends Model implements IDs
{ {
@ -356,6 +357,14 @@ class Service extends Model implements IDs
->orderBy('created_at'); ->orderBy('created_at');
} }
public function changes()
{
return $this->belongsToMany(Product::class,'service__change','service_id','product_id','id','id')
->where('service__change.site_id',$this->site_id)
->withPivot(['ordered_at','effective_at','ordered_by','active','complete','notes'])
->withTimestamps();
}
// @todo changed to invoiced_items // @todo changed to invoiced_items
public function invoice_items($active=TRUE) public function invoice_items($active=TRUE)
{ {

View File

@ -12,5 +12,4 @@ use Illuminate\Database\Eloquent\Model;
class ServiceChange extends Model class ServiceChange extends Model
{ {
protected $table = 'service__change'; protected $table = 'service__change';
public $timestamps = FALSE;
} }

View File

@ -0,0 +1,100 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
DB::statement('ALTER TABLE service__change RENAME COLUMN ordered_at TO ordered_at_old');
DB::statement('ALTER TABLE service__change RENAME COLUMN effective_at TO effective_at_old');
Schema::table('service__change', function (Blueprint $table) {
$table->dateTime('created_at')->nullable()->after('id');
$table->dateTime('updated_at')->nullable()->after('created_at');
$table->date('ordered_at')->nullable()->after('ordered_by');
$table->date('effective_at')->nullable()->after('ordered_at');
$table->text('notes')->nullable();
});
DB::statement('ALTER TABLE service__change MODIFY service_id int unsigned NOT NULL');
DB::statement('ALTER TABLE service__change MODIFY product_id int unsigned NOT NULL');
DB::statement('ALTER TABLE service__change MODIFY ordered_by int unsigned NOT NULL');
DB::statement('ALTER TABLE service__change MODIFY active tinyint(1) NOT NULL');
DB::statement('ALTER TABLE service__change MODIFY complete tinyint(1) NOT NULL');
// Convert out dates
foreach (\App\Models\ServiceChange::withoutGlobalScope(\App\Models\Scopes\SiteScope::class)->cursor() as $o) {
// If we are running again
if ($o->created_at)
continue;
if ($o->ordered_at_old)
$o->created_at = \Carbon\Carbon::create(substr($o->ordered_at_old,0,4),substr($o->ordered_at_old,4,2),substr($o->ordered_at_old,6,2));
$o->updated_at = $o->created_at;
$o->ordered_at = $o->created_at;
if ($o->effective_at_old)
$o->effective_at = \Carbon\Carbon::create(substr($o->effective_at_old,0,4),substr($o->effective_at_old,4,2),substr($o->effective_at_old,6,2));
$o->save();
}
Schema::table('service__change', function (Blueprint $table) {
$table->dropColumn(['ordered_at_old','effective_at_old']);
});
DB::statement('ALTER TABLE service__change MODIFY ordered_at date NOT NULL');
foreach (\App\Models\Service::where('order_info','LIKE','%change%')->withoutGlobalScope(\App\Models\Scopes\SiteScope::class)->cursor() as $o) {
if ($o->order_info->only(['change_note','change_product_id','change_date'])->count() !== 3)
continue;
$o->changes()->attach([$o->id => [
'site_id'=> $o->site_id,
'ordered_by' => 1,
'ordered_at' => $x=\Carbon\Carbon::createFromDate(\Illuminate\Support\Arr::get($o->order_info,'change_date')),
'effective_at' => $x,
'product_id' => \Illuminate\Support\Arr::get($o->order_info,'change_product_id'),
'notes' => \Illuminate\Support\Arr::get($o->order_info,'change_note'),
'active' => true,
'complete' => true,
]]);
$o->order_info->forget(['change_note','change_product_id','change_date']);
$o->save();
}
// Additional cleanup
foreach (\App\Models\Service::whereNotNull('order_info')->withoutGlobalScope(\App\Models\Scopes\SiteScope::class)->cursor() as $o) {
foreach (['notes','provision_notes','cancel_note'] as $key) {
if ($o->order_info && ((is_array($o->order_info) && array_key_exists($key,$o->order_info)) || ($o->order_info->has($key))) && is_null(\Illuminate\Support\Arr::get($o->order_info,$key)))
$o->order_info->forget($key);
}
$o->save();
}
// Final cleanup
DB::statement("UPDATE services set order_info=null WHERE order_info='[]'");
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
abort(500,'cant go back');
}
};

View File

@ -14,7 +14,7 @@
{{ $o->sid }} {{ $o->sid }}
@endsection @endsection
<!-- $o = App\Models\Service::class --> <!-- $o = Service::class, $np = Product::class -->
@section('main-content') @section('main-content')
<div class="row"> <div class="row">
<div class="col-12 col-lg-4"> <div class="col-12 col-lg-4">
@ -56,7 +56,7 @@
<!-- @todo TO DO LIMIT THIS TO OF THE SAME OFFERING TYPE AND SORT BY NAME --> <!-- @todo TO DO LIMIT THIS TO OF THE SAME OFFERING TYPE AND SORT BY NAME -->
@foreach (\App\Models\Product::get() as $po) @foreach (\App\Models\Product::get() as $po)
@if (! $po->category || ($po->category !== $o->product->category)) @continue @endif @if (! $po->category || ($po->category !== $o->product->category)) @continue @endif
<option value="{{ $po->id }}" {{ $po->id == old('broadband.product_id',$po->exists ? Arr::get($o->order_info,'change_product_id') : NULL) ? 'selected' : '' }}>{{ $po->name }}</option> <option value="{{ $po->id }}" {{ $po->id == old('broadband.product_id',$np->id) ? 'selected' : '' }}>{{ $po->name }}</option>
@endforeach @endforeach
</select> </select>
<span class="invalid-feedback" role="alert"> <span class="invalid-feedback" role="alert">
@ -81,7 +81,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div> </div>
<input type="date" class="form-control @error('broadband.start_at') is-invalid @enderror" id="start_at" name="broadband[start_at]" value="{{ \Illuminate\Support\Arr::get($o->order_info,'change_date') ?? '' }}" required> <input type="date" class="form-control @error('broadband.start_at') is-invalid @enderror" id="start_at" name="broadband[start_at]" value="{{ $np->pivot->effective_at ?? $np->pivot->ordered_at }}" required>
<span class="invalid-feedback" role="alert"> <span class="invalid-feedback" role="alert">
@error('broadband.start_at') @error('broadband.start_at')
{{ $message }} {{ $message }}
@ -153,7 +153,7 @@
</div> </div>
<div class="card-body"> <div class="card-body">
@include('service.widget.internal',['o'=>$o,'p'=>\App\Models\Product::where('id',Arr::get($o->order_info,'change_product_id'))->singleOrFail()]) @include('service.widget.internal',['o'=>$o,'p'=>$np])
</div> </div>
</div> </div>
</div> </div>
@ -170,13 +170,14 @@
var pid = $('#product_id').val(); var pid = $('#product_id').val();
var start = $('#start_at').val(); var start = $('#start_at').val();
var fee = $('#change_fee').val(); var fee = $('#change_fee').val();
var price = $('#price').val();
$("div[id=transactions]").empty(); $("div[id=transactions]").empty();
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
dataType: 'html', dataType: 'html',
data: {broadband: {product_id: pid,start_at: start,change_fee: fee}}, data: {broadband: {product_id: pid,start_at: start,change_fee: fee,price: price}},
cache: false, cache: false,
url: '{{ url('r/service_change_charges',[$o->id]) }}', url: '{{ url('r/service_change_charges',[$o->id]) }}',
timeout: 2000, timeout: 2000,
@ -205,6 +206,10 @@
pendingtrans(); pendingtrans();
}); });
$('#price').on('change',function() {
pendingtrans();
});
pendingtrans(); pendingtrans();
}); });
</script> </script>

View File

@ -89,7 +89,11 @@
</div> </div>
@can('wholesaler') @can('wholesaler')
<div class="tab-pane fade" id="internal" role="tabpanel"> <div class="tab-pane fade" id="internal" role="tabpanel">
@if(($x=$o->changes()->where('service__change.active',TRUE)->where('complete',FALSE)->get()->pop()))
@include('service.widget.internal',['o'=>$o,'p'=>$x])
@else
@include('service.widget.internal',['o'=>$o,'p'=>new \App\Models\Product()]) @include('service.widget.internal',['o'=>$o,'p'=>new \App\Models\Product()])
@endif
</div> </div>
<div class="tab-pane fade {{ session()->pull('service_update') ? 'active show' : '' }}" id="update" role="tabpanel"> <div class="tab-pane fade {{ session()->pull('service_update') ? 'active show' : '' }}" id="update" role="tabpanel">

View File

@ -49,7 +49,7 @@
<!-- @todo --> <!-- @todo -->
<tr> <tr>
<th>Traffic</th> <th>Traffic</th>
<td>{{ $o->service->offering->allowance_string() }} GB @if(FALSE)(YY GB used month)@endif</td> <td>{{ $o->service->offering->allowance_string() }} GB</td>
</tr> </tr>
<tr> <tr>
<th>IP4 Address</th> <th>IP4 Address</th>
@ -73,6 +73,23 @@
<th>Cancel Notice</th> <th>Cancel Notice</th>
<td>1 month @if($o->inContract())<small>(after {{ $o->service_expire->subMonth()->format('Y-m-d') }})</small>@endif</td> <td>1 month @if($o->inContract())<small>(after {{ $o->service_expire->subMonth()->format('Y-m-d') }})</small>@endif</td>
</tr> </tr>
@if(($x=$o->service->changes()->where('service__change.active',TRUE)->where('complete',FALSE)->get()->pop()))
<tr>
<th>Pending Plan Change</th>
<td>{{ $x->name }}</td>
</tr>
<tr>
<th>Pending Submitted</th>
<td>{{ $x->pivot->ordered_at }}</td>
</tr>
@if($x->pivot->effective_at)
<tr>
<th>Pending Active</th>
<td>{{ $x->pivot->effective_at }}</td>
</tr>
@endif
@endif
</table> </table>
</div> </div>
</div> </div>

View File

@ -11,12 +11,14 @@
@if ($o->setup_charge) @if ($o->setup_charge)
<tr> <tr>
<th>Setup Charges <sup>*</sup></th> <th>Setup Charges <sup>*</sup></th>
<td class="text-right">${{ number_format($user->exists ? $user->taxed($o->setup_charge) : Config::get('site')->taxed($o->setup_charge),2) }}</td> {{-- @todo this should use account::taxed() when the user is known --}}
<td class="text-right">${{ number_format($user->exists ? Config::get('site')->taxed($o->setup_charge) : Config::get('site')->taxed($o->setup_charge),2) }}</td>
</tr> </tr>
@endif @endif
<tr> <tr>
<th>Cost <sup>+</sup></th> <th>Cost <sup>+</sup></th>
<td class="text-right">${{ number_format($user->exists ? $user->taxed($o->base_charge) : Config::get('site')->taxed($o->base_charge),2) }}</td> {{-- @todo this should use account::taxed() when the user is known --}}
<td class="text-right">${{ number_format($user->exists ? Config::get('site')->taxed($o->base_charge) : Config::get('site')->taxed($o->base_charge),2) }}</td>
</tr> </tr>
<tr> <tr>
<th>Default Billing</th> <th>Default Billing</th>
@ -28,7 +30,8 @@
</tr> </tr>
<tr> <tr>
<th>Minimum Costs <sup>+*</sup></th> <th>Minimum Costs <sup>+*</sup></th>
<td class="text-right">${{ number_format($user->exists ? $user->taxed($o->min_charge) : Config::get('site')->taxed($o->min_charge),2) }}</td> {{-- @todo this should use account::taxed() when the user is known --}}
<td class="text-right">${{ number_format($user->exists ? Config::get('site')->taxed($o->min_charge) : Config::get('site')->taxed($o->min_charge),2) }}</td>
</tr> </tr>
<tfoot> <tfoot>