From c1080481ecd1789d4c45b97594b013d1f6c92276 Mon Sep 17 00:00:00 2001 From: Deon George Date: Sun, 12 Jun 2022 18:32:54 +1000 Subject: [PATCH] Enabled changing broadband services and adjusting invoices --- app/Http/Controllers/ServiceController.php | 102 +++++++++- app/Http/Requests/ServiceChangeRequest.php | 38 ++++ app/Models/Charge.php | 7 +- app/Models/InvoiceItem.php | 2 + app/Models/Service.php | 14 +- .../a/charge/service_change.blade.php | 33 ++++ .../a/service/change_pending.blade.php | 180 ++++++++++++++++++ routes/web.php | 8 + 8 files changed, 379 insertions(+), 5 deletions(-) create mode 100644 app/Http/Requests/ServiceChangeRequest.php create mode 100644 resources/views/theme/backend/adminlte/a/charge/service_change.blade.php create mode 100644 resources/views/theme/backend/adminlte/a/service/change_pending.blade.php diff --git a/app/Http/Controllers/ServiceController.php b/app/Http/Controllers/ServiceController.php index 8db3f39..191be79 100644 --- a/app/Http/Controllers/ServiceController.php +++ b/app/Http/Controllers/ServiceController.php @@ -6,14 +6,16 @@ use Carbon\Carbon; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Illuminate\View\View; use Symfony\Component\HttpKernel\Exception\HttpException; +use App\Http\Requests\ServiceChangeRequest; use App\Mail\{CancelRequest,ChangeRequest}; -use App\Models\Service; +use App\Models\{Charge,Product,Service}; class ServiceController extends Controller { @@ -69,6 +71,23 @@ class ServiceController extends Controller /* OTHER METHODS */ + public function change_pending(ServiceChangeRequest $request,Service $o) + { + if ($request->post()) { + foreach ($this->service_change_charges($request,$o) as $co) + $co->save(); + + $o->product_id = Arr::get($request->broadband,'product_id'); + $o->order_status = 'ACTIVE'; + $o->save(); + + return redirect()->to(url('u/service',[$o->id])); + } + + return view('a.service.change_pending') + ->with('o',$o); + } + /** * Process a request to cancel a service * @@ -283,6 +302,87 @@ class ServiceController extends Controller ->with('o',$o); } + private function service_change_charges(Request $request,Service $o): Collection + { + $charges = collect(); + $po = Product::findOrFail(Arr::get($request->broadband,'product_id')); + $start_at = Carbon::create(Arr::get($request->broadband,'start_at')); + + // Get the invoiced items covering the start_at date + foreach ($o->invoice_items->filter(function($item) use ($start_at) { + return ($item->start_at < $start_at) && ($item->stop_at > $start_at) && ($item->item_type === 0); + }) as $iio) + { + // Reverse the original charge + $co = new Charge; + $co->active = TRUE; + $co->service_id = $o->id; + $co->account_id = $o->account_id; + $co->sweep_type = 6; + $co->product_id = $iio->product_id; + $co->description = 'Plan Upgrade Adjustment'; + $co->user_id = Auth::id(); + $co->type = $iio->item_type; + $co->start_at = $start_at; + $co->stop_at = $iio->stop_at; + $co->amount = $iio->price_base; + $co->taxable = TRUE; // @todo this should be determined + $co->quantity = -1*$start_at->diff($iio->stop_at)->days/$iio->start_at->diff($iio->stop_at)->days; + $charges->push($co); + + // Add the new charge + $co = new Charge; + $co->active = TRUE; + $co->service_id = $o->id; + $co->account_id = $o->account_id; + $co->sweep_type = 6; + $co->product_id = $po->id; + $co->description = 'Plan Upgrade Adjustment'; + $co->user_id = Auth::id(); + $co->type = $iio->item_type; + $co->start_at = $start_at; + $co->stop_at = $iio->stop_at; + $co->amount = $po->base_charge; + $co->taxable = TRUE; // @todo this should be determined + $co->quantity = $start_at->diff($iio->stop_at)->days/$iio->start_at->diff($iio->stop_at)->days; + $charges->push($co); + } + + // Add any fee + if (Arr::get($request->broadband,'change_fee')) { + $co = new Charge; + $co->active = TRUE; + $co->service_id = $o->id; + $co->account_id = $o->account_id; + $co->sweep_type = 6; + $co->product_id = $po->id; + $co->description = 'Plan Upgrade Fee'; + $co->user_id = Auth::id(); + $co->type = 3; + $co->start_at = $start_at; + $co->stop_at = $start_at; + $co->amount = Arr::get($request->broadband,'change_fee'); + $co->taxable = TRUE; // @todo this should be determined + $co->quantity = 1; + $charges->push($co); + } + + return $charges; + } + + /** + * This is an API method, that works with service change - to return the new charges as a result of changing a service + * + * @param Request $request + * @param Service $o + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function service_change_charges_display(Request $request,Service $o) + { + return view('a.charge.service_change') + ->with('charges',$this->service_change_charges($request,$o)); + } + /** * Update details about a service * diff --git a/app/Http/Requests/ServiceChangeRequest.php b/app/Http/Requests/ServiceChangeRequest.php new file mode 100644 index 0000000..a313fb7 --- /dev/null +++ b/app/Http/Requests/ServiceChangeRequest.php @@ -0,0 +1,38 @@ +route('o')->serviceUserAuthorised(Auth::user()); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + * @todo This is specific to broadband - this needs to be more generic. + */ + public function rules(Request $request) + { + if (! $request->isMethod('post')) + return []; + + return [ + 'broadband.product_id' => 'required|exists:products,id', + 'broadband.change_fee' => 'nullable|numeric', + 'broadband.start_at' => 'required|date', // @todo Check that it is not more than 1 billing cycle ago, and not future. + ]; + } +} \ No newline at end of file diff --git a/app/Models/Charge.php b/app/Models/Charge.php index 09c92d6..5ad4311 100644 --- a/app/Models/Charge.php +++ b/app/Models/Charge.php @@ -12,6 +12,7 @@ use App\Traits\SiteID; * + Charge Date should not be null * + Attributes should be a collection array * + type should not be null + * + It would be useful, given an array of Charges to call a function that renders them into invoice format. This may provide consistence and be the single view of how charges do look on an invoice. */ class Charge extends Model { @@ -22,7 +23,9 @@ class Charge extends Model ]; protected $dates = [ - 'charged_at', + 'start_at', + 'stop_at', + 'charged_at', // @todo Dont remember what this attribute is for ]; public const sweep = [ @@ -75,6 +78,6 @@ class Charge extends Model public function getTypeNameAttribute(): string { - return Arr::get(InvoiceItem::type,$this->attribute('type')); + return Arr::get(InvoiceItem::type,$this->type); } } \ No newline at end of file diff --git a/app/Models/InvoiceItem.php b/app/Models/InvoiceItem.php index 02c2589..ed3ff91 100644 --- a/app/Models/InvoiceItem.php +++ b/app/Models/InvoiceItem.php @@ -30,7 +30,9 @@ class InvoiceItem extends Model // Array of items that can be updated with PushNew protected $pushable = ['taxes']; + // @todo Change these to CONSTS so it's easier to reference through out the code public const type = [ + 0 => 'Service Charge', 1 => 'Hardware', // * 2 => 'Service Relocation Fee', // * Must have corresponding SERVICE_ID 3 => 'Service Change', // * Must have corresponding SERVICE_ID diff --git a/app/Models/Service.php b/app/Models/Service.php index cdf26b1..adc7b1c 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -31,6 +31,7 @@ use App\Traits\SiteID; * + billing_charge : Charge for this service each invoice period // @todo change to "charge" * + billing_interval : The period that this service is billed for by default * + billing_interval_string : The period that this service is billed for by default as a name + * + billed_to : When this service has been billed to // @todo rename all references to invoice_to * + category : The type of service this is, eg: broadband, phone * + contract_term : The term that this service must be active * + contract_end : The date that the contract ends for this service @@ -100,6 +101,7 @@ class Service extends Model implements IDs * to see if it can proceed further if not, it'll wait here for user/admin intervention * * @var array + * @todo This needs an overhaul, its not implemented correctly. */ private const ACTION_PROGRESS = [ // Order Submitted @todo redo @@ -256,6 +258,14 @@ class Service extends Model implements IDs 'enter_method'=>'action_request_enter_redirect', 'title'=>'Change Service', ], + + 'CHANGE-PENDING' => [ + 'next'=>[ + 'ACTIVE'=>['wholesaler'], + ], + 'enter_method'=>'action_request_enter_redirect', + 'title'=>'Activate Change', + ], ]; /* INTERFACES */ @@ -1221,8 +1231,8 @@ class Service extends Model implements IDs $o->quantity = $oo->quantity; $o->item_type = $oo->type; $o->price_base = $oo->amount; - $o->start_at = $oo->date_charge; - $o->stop_at = $oo->date_charge; + $o->start_at = $oo->start_at; + $o->stop_at = $oo->stop_at; $o->module_id = 30; // @todo This shouldnt be hard coded $o->module_ref = $oo->id; $o->site_id = 1; // @todo diff --git a/resources/views/theme/backend/adminlte/a/charge/service_change.blade.php b/resources/views/theme/backend/adminlte/a/charge/service_change.blade.php new file mode 100644 index 0000000..9dab783 --- /dev/null +++ b/resources/views/theme/backend/adminlte/a/charge/service_change.blade.php @@ -0,0 +1,33 @@ +
+
+
+
+

Pending Transactions

+
+ +
+ + + + + + + + + + + + @foreach ($charges as $co) + + + + + + + @endforeach + +
Service ChargeQuantityRateAmount
{{ $co->type_name }}{{ number_format($co->quantity,2) }}${{ number_format($co->amount,2) }}${{ number_format($co->amount*$co->quantity,2) }}
+
+
+
+
\ No newline at end of file diff --git a/resources/views/theme/backend/adminlte/a/service/change_pending.blade.php b/resources/views/theme/backend/adminlte/a/service/change_pending.blade.php new file mode 100644 index 0000000..87a3c05 --- /dev/null +++ b/resources/views/theme/backend/adminlte/a/service/change_pending.blade.php @@ -0,0 +1,180 @@ +@extends('adminlte::layouts.app') + +@section('htmlheader_title') + Change Service #{{ $o->id }} +@endsection + +@section('contentheader_title') + Change Service #{{ $o->id }} - WARNING - this is only for Broadband for now +@endsection +@section('contentheader_description') + {{ $o->sid }} +@endsection + + +@section('main-content') +
+
+
+
+ {{ csrf_field() }} + +
+

Service Information

+
+ +
+
+
+ +
+ + +
+
+ +
+ +
+
+
+
+ +
+
+ +
+ +
+
+ +
+ + + @error('broadband.product_id') + {{ $message }} + @else + Type is required. + @enderror + +
+ Product - {{ $o->product->category_name }}. +
+
+
+ +
+
+ +
+ +
+
+ +
+ + + @error('broadband.start_at') + {{ $message }} + @else + Type is required. + @enderror + +
+
+
+
+ +
+
+ +
+ +
+
+ +
+ + + @error('broadband.change_fee') + {{ $message }} + @enderror + +
+
+
+
+ +
+
+ Cancel + @can('wholesaler') + + @endcan +
+
+
+
+
+
+
+ @include('a.service.widgets.internal') +
+
+
+@endsection + +@section('page-scripts') + @css(select2) + @js(select2,autofocus) + + +@append \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 32f02a4..4b2ad75 100644 --- a/routes/web.php +++ b/routes/web.php @@ -87,6 +87,10 @@ Route::group(['middleware'=>['theme:adminlte-be','auth','role:reseller'],'prefix // Charges on an account Route::get('charges/{o}',[AdminController::class,'charge_pending_account']) ->where('o','[0-9]+'); + + // Reseller API calls + Route::post('service_change_charges/{o}',[ServiceController::class,'service_change_charges_display']) + ->where('o','[0-9]+'); }); // Our User Routes @@ -118,6 +122,10 @@ Route::group(['middleware'=>['theme:adminlte-be','auth'],'prefix'=>'u'],function Route::match(['get','post'],'service/{o}/change-request',[ServiceController::class,'change_request']) ->where('o','[0-9]+') ->middleware('can:progress,o,"change-request"'); + // @todo This shouldnt be a user privilege. + Route::match(['get','post'],'service/{o}/change-pending',[ServiceController::class,'change_pending']) + ->where('o','[0-9]+') + ->middleware('can:progress,o,"change-pending"'); Route::get('service/{o}/change/{status}',[ServiceController::class,'change']) ->where('o','[0-9]+') ->middleware('can:progress,o,status');