From 5297ae8a6255d99cd4990d6854025eedc8ec9f40 Mon Sep 17 00:00:00 2001 From: Deon George Date: Thu, 30 Jun 2022 23:51:20 +1000 Subject: [PATCH] Enable editing of supplier products and listing services connected to them --- app/Http/Controllers/SupplierController.php | 108 +++++- app/Http/Requests/SupplierProductAddEdit.php | 60 ++++ app/Interfaces/SupplierItem.php | 4 +- app/Models/Product.php | 6 +- app/Models/Product/Broadband.php | 2 - app/Models/Product/Domain.php | 2 - app/Models/Product/Email.php | 2 - app/Models/Product/Generic.php | 2 - app/Models/Product/Host.php | 2 - app/Models/Product/Phone.php | 2 - app/Models/Product/SSL.php | 2 - app/Models/Product/Type.php | 4 + app/Models/Supplier.php | 157 +++++---- app/Models/Supplier/Broadband.php | 22 +- app/Models/Supplier/Domain.php | 6 +- app/Models/Supplier/Email.php | 6 +- app/Models/Supplier/Ethernet.php | 1 + app/Models/Supplier/Generic.php | 12 +- app/Models/Supplier/HSPA.php | 1 + app/Models/Supplier/Host.php | 12 +- app/Models/Supplier/Phone.php | 12 +- app/Models/Supplier/SSL.php | 12 +- app/Models/Supplier/Type.php | 21 ++ app/Models/SupplierDetail.php | 44 +++ .../supplier/product/addedit.blade.php | 219 +++++++++++++ .../product/widget/broadband.blade.php | 308 ++++++++++++++++++ .../supplier/widget/connections.blade.php | 2 +- .../adminlte/supplier/widget/costs.blade.php | 2 +- .../supplier/widget/offerings.blade.php | 16 +- .../supplier/widget/products.blade.php | 14 +- .../widgets/broadband/details.blade.php | 2 +- .../layouts/partials/sidebarmenu.blade.php | 67 ++-- routes/web.php | 13 + 33 files changed, 963 insertions(+), 182 deletions(-) create mode 100644 app/Http/Requests/SupplierProductAddEdit.php create mode 100644 resources/views/theme/backend/adminlte/supplier/product/addedit.blade.php create mode 100644 resources/views/theme/backend/adminlte/supplier/product/widget/broadband.blade.php diff --git a/app/Http/Controllers/SupplierController.php b/app/Http/Controllers/SupplierController.php index a34caac..57ff68d 100644 --- a/app/Http/Controllers/SupplierController.php +++ b/app/Http/Controllers/SupplierController.php @@ -2,7 +2,8 @@ namespace App\Http\Controllers; -use App\Http\Requests\SupplierAddEdit; +use Illuminate\Http\Request; +use App\Http\Requests\{SupplierAddEdit,SupplierProductAddEdit}; use App\Models\{Cost,Supplier,SupplierDetail}; class SupplierController extends Controller @@ -67,6 +68,111 @@ class SupplierController extends Controller return view('supplier.cost',['o'=>$o]); } + /** + * New Product from a supplier + * + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function product_add() + { + return view('supplier.product.addedit') + ->with('o',new Supplier) + ->with('oo',NULL); + } + + public function product_addedit(SupplierProductAddEdit $request,Supplier $o,int $id,string $type) + { + // Quick validation + if ($type !== $request->offering_type) + abort(500,'Type and offering type do not match'); + if ($o->exists && ($o->detail->id !== (int)$request->supplier_detail_id)) + abort(500,sprintf('Supplier [%d] and supplier_detail_id [%d] do not match',$o->detail->id,$request->supplier_detail_id)); + + switch ($request->offering_type) { + case 'broadband': + $oo = Supplier\Broadband::findOrNew($id); + + // @todo these are broadband requirements - get them from the broadband class. + foreach ($request->only([ + 'supplier_detail_id', + 'product_id'. + 'product_desc', + 'base_cost', + 'setup_cost', + 'contract_term', + 'metric', + 'speed', + 'technology', + 'offpeak_start', + 'offpeak_end', + 'base_down_peak', + 'base_up_peak', + 'base_down_offpeak', + 'base_up_offpeak', + 'extra_down_peak', + 'extra_up_peak', + 'extra_down_offpeak', + 'extra_up_offpeak', + ]) as $key => $value) + $oo->$key = $value; + + // Our boolean values + foreach ($request->only(['active','extra_shaped','extra_charged']) as $key => $value) + $oo->$key = ($value == 'on' ? 1 : 0); + + break; + + default: + throw new \Exception('Unknown offering type:'.$request->offering_type); + } + + $oo->save(); + + return redirect()->back() + ->with('success','Saved'); + } + + /** + * Edit a supplier product + * + * @param Supplier $o + * @param int $id + * @param string $type + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function product_view(Supplier $o,int $id,string $type) + { + $oo = $o->detail->find($type,$id); + $oo->load(['products.product.services.product.type']); + + return view('supplier.product.addedit') + ->with('o',$o) + ->with('oo',$oo); + } + + /** + * Return the form for a specific product type + * + * @param Request $request + * @param string $type + * @param int|null $id + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function product_view_type(Request $request,string $type,int $id=NULL) + { + $o = $id ? Supplier::offeringTypeClass($type)->findOrFail($id) : NULL; + + if ($request->old) + $request->session()->flashInput($request->old); + + if ($o) + $o->load(['products.product.services']); + + return view('supplier.product.widget.'.$type) + ->with('o',$id ? $o : NULL) + ->withErrors($request->errors); + } + /** * View a supplier. * diff --git a/app/Http/Requests/SupplierProductAddEdit.php b/app/Http/Requests/SupplierProductAddEdit.php new file mode 100644 index 0000000..87e456a --- /dev/null +++ b/app/Http/Requests/SupplierProductAddEdit.php @@ -0,0 +1,60 @@ +isWholesaler(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(Request $request) + { + // @todo these are broadband requirements - perhaps move them to the broadband class. + // @todo Enhance the validation so that extra_* values are not accepted if base_* values are not included. + return [ + 'id' => 'required|nullable', + 'offering_type' => ['required',Rule::in(Supplier::offeringTypeKeys()->toArray())], + 'supplier_detail_id' => 'required|exists:supplier_details,id', + 'active' => 'sometimes|accepted', + 'extra_shaped' => 'sometimes|accepted', + 'extra_charged' => 'sometimes|accepted', + 'product_id' => 'required|string|min:2', + 'product_desc' => 'required|string|min:2', + 'base_cost' => 'required|numeric|min:.01', + 'setup_cost' => 'nullable|numeric', + 'contract_term' => 'nullable|numeric|min:1', + 'metric' => 'nullable|numeric|min:1', + 'speed' => 'nullable|string|max:64', + 'technology' => 'nullable|string|max:255', + 'offpeak_start' => 'nullable|date_format:H:i', + 'offpeak_end' => 'nullable|date_format:H:i', + 'base_down_peak' => 'nullable|numeric', + 'base_up_peak' => 'nullable|numeric', + 'base_down_offpeak' => 'nullable|numeric', + 'base_up_offpeak' => 'nullable|numeric', + 'extra_down_peak' => 'nullable|numeric', + 'extra_up_peak' => 'nullable|numeric', + 'extra_down_offpeak' => 'nullable|numeric', + 'extra_up_offpeak' => 'nullable|numeric', + ]; + } +} diff --git a/app/Interfaces/SupplierItem.php b/app/Interfaces/SupplierItem.php index 096a43f..8c1b651 100644 --- a/app/Interfaces/SupplierItem.php +++ b/app/Interfaces/SupplierItem.php @@ -16,9 +16,9 @@ interface SupplierItem /** * Available products created from this supplier offering * - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ - public function types(); + public function products(); /* ATTRIBUTES */ diff --git a/app/Models/Product.php b/app/Models/Product.php index 702acf3..66fdc98 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -183,11 +183,10 @@ class Product extends Model implements IDs * Return the type of service is provided. eg: Broadband, Phone. * * @return string - * @todo Does type need to be a mandatory attribute on a model - then we can remove this condition */ public function getCategoryAttribute(): string { - return $this->type ? $this->type->getCategoryAttribute() : 'generic'; + return $this->supplied->getCategoryAttribute(); } /** @@ -195,11 +194,10 @@ class Product extends Model implements IDs * other logic of these types. * * @return string - * @todo Does type need to be a mandatory attribute on a model - then we can remove this condition */ public function getCategoryNameAttribute(): string { - return $this->type ? $this->type->getCategoryNameAttribute() : 'Generic'; + return $this->supplied->getCategoryNameAttribute(); } /** diff --git a/app/Models/Product/Broadband.php b/app/Models/Product/Broadband.php index 25514ed..46e444b 100644 --- a/app/Models/Product/Broadband.php +++ b/app/Models/Product/Broadband.php @@ -15,8 +15,6 @@ final class Broadband extends Type implements ProductItem protected $table = 'product_broadband'; - protected const category_name = 'Broadband'; - // Information required during the order process protected array $order_attributes = [ 'options.address'=>[ diff --git a/app/Models/Product/Domain.php b/app/Models/Product/Domain.php index 97d46af..c66c86d 100644 --- a/app/Models/Product/Domain.php +++ b/app/Models/Product/Domain.php @@ -12,8 +12,6 @@ final class Domain extends Type implements ProductItem { protected $table = 'product_domain'; - protected const category_name = 'Domain Name'; - // The model that is referenced when this product is ordered protected string $order_model = ServiceDomain::class; diff --git a/app/Models/Product/Email.php b/app/Models/Product/Email.php index e6d5cd7..bd98f00 100644 --- a/app/Models/Product/Email.php +++ b/app/Models/Product/Email.php @@ -12,8 +12,6 @@ final class Email extends Type implements ProductItem { protected $table = 'product_email'; - protected const category_name = 'Email Hosting'; - // The model that is referenced when this product is ordered protected string $order_model = ServiceEmail::class; diff --git a/app/Models/Product/Generic.php b/app/Models/Product/Generic.php index dcd62a0..7173b97 100644 --- a/app/Models/Product/Generic.php +++ b/app/Models/Product/Generic.php @@ -12,8 +12,6 @@ final class Generic extends Type implements ProductItem { protected $table = 'product_generic'; - protected const category_name = 'Generic'; - // The model that is referenced when this product is ordered protected string $order_model = ServiceGeneric::class; diff --git a/app/Models/Product/Host.php b/app/Models/Product/Host.php index e42397f..5351513 100644 --- a/app/Models/Product/Host.php +++ b/app/Models/Product/Host.php @@ -12,8 +12,6 @@ final class Host extends Type implements ProductItem { protected $table = 'product_host'; - protected const category_name = 'Web Hosting'; - // The model that is referenced when this product is ordered protected string $order_model = ServiceHost::class; diff --git a/app/Models/Product/Phone.php b/app/Models/Product/Phone.php index af17f53..8f43d99 100644 --- a/app/Models/Product/Phone.php +++ b/app/Models/Product/Phone.php @@ -12,8 +12,6 @@ final class Phone extends Type implements ProductItem { protected $table = 'product_phone'; - protected const category_name = 'Telephone'; - protected array $order_attributes = [ 'options.phonenumber'=>[ 'request'=>'options.phonenumber', diff --git a/app/Models/Product/SSL.php b/app/Models/Product/SSL.php index 64e260e..3f8dcbe 100644 --- a/app/Models/Product/SSL.php +++ b/app/Models/Product/SSL.php @@ -12,8 +12,6 @@ final class SSL extends Type implements ProductItem { protected $table = 'product_ssl'; - protected const category_name = 'SSL Certificate'; - // The model that is referenced when this product is ordered protected string $order_model = ServiceSSL::class; diff --git a/app/Models/Product/Type.php b/app/Models/Product/Type.php index 125afa4..b583aa1 100644 --- a/app/Models/Product/Type.php +++ b/app/Models/Product/Type.php @@ -32,9 +32,11 @@ abstract class Type extends Model * other logic of these types. * * @return string + * @deprecated - can this be replaced with product->supplied->category? */ final public function getCategoryAttribute(): string { + abort(500,'use product->supplied->category_name'); return strtolower((new \ReflectionClass($this))->getShortName()); } @@ -42,9 +44,11 @@ abstract class Type extends Model * Return a friendly name for this product, used for display * * @return string + * @deprecated - can this be replaced with product->supplied->category_name */ final public function getCategoryNameAttribute(): string { + abort(500,'use product->supplied->category_name'); return static::category_name; } } \ No newline at end of file diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index e21350f..64d38cc 100644 --- a/app/Models/Supplier.php +++ b/app/Models/Supplier.php @@ -8,57 +8,93 @@ use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Leenooks\Traits\ScopeActive; -use App\Models\Supplier\{Broadband,Domain,Email,Ethernet,Generic,Host,HSPA,Phone,SSL}; +use App\Models\Supplier\{Broadband,Domain,Email,Ethernet,Generic,Host,HSPA,Phone,SSL,Type}; class Supplier extends Model { + /** + * The offerings types we provide + */ + private const offering_types = [ + 'broadband' => Broadband::class, + 'hspa' => HSPA::class, + 'ethernet' => Ethernet::class, + 'domainname' => Domain::class, + 'email' => Email::class, + 'generic' => Generic::class, + 'hosting' => Host::class, + 'phone' => Phone::class, + 'ssl' => SSL::class, + ]; + use ScopeActive; public $timestamps = FALSE; + /* STATIC METHODS */ + /** - * The offerings we provide - * @todo Use the product/* category instead of this const. The assumption is the supplier/* type is the same as the product/* type. - * @deprecated - use the product/* category instead. + * Return the offerings that this supplier provides + * + * @param Supplier|null $so + * @return Collection */ - public const offering_types = [ - 'broadband' => [ - 'name' => 'Broadband', - 'class' => Broadband::class, - ], - 'hspa' => [ - 'name' => 'Mobile Broadband', - 'class' => HSPA::class, - ], - 'ethernet' => [ - 'name' => 'Ethernet Broadband', - 'class' => Ethernet::class, - ], - 'domainname' => [ - 'name' => 'Domain Name', - 'class' => Domain::class, - ], - 'email' => [ - 'name' => 'Email Hosting', - 'class' => Email::class, - ], - 'generic' => [ - 'name' => 'Generic', - 'class' => Generic::class, - ], - 'hosting' => [ - 'name' => 'Hosting', - 'class' => Host::class, - ], - 'phone' => [ - 'name' => 'Phone', - 'class' => Phone::class, - ], - 'ssl' => [ - 'name' => 'SSL', - 'class' => SSL::class, - ], - ]; + public static function offeringTypes(self $so=NULL): Collection + { + $result = collect(); + + foreach (self::offering_types as $type) { + $class = new $type; + + if ($so) { + // If we have a connections configuration for that supplier, then build the child relationships + if (Arr::get($so->detail->connections,$class->category)) { + $result->put($class->category,(object)[ + 'type' => $class->category_name, + 'items' => $class->where('supplier_detail_id',$so->detail->id), + ]); + + continue; + } + + // Even if we dont have any connections, see if we have any products defined + $o = new $class; + $o->where('supplier_detail_id',$so->detail->id); + + if ($o->count()) + $result->put($class->category,(object)[ + 'type' => $class->category_name, + 'items' => $class->where('supplier_detail_id',$so->detail->id), + ]); + + } else { + $result->put($class->category_name,$class); + } + } + + return $result; + } + + /** + * Return a new model object for the offering type + * + * @param string $type + * @return Type + */ + public static function offeringTypeClass(string $type): Type + { + return ($class=collect(self::offering_types)->get($type)) ? new $class : new Generic; + } + + /** + * Return our supported offering type keys + * + * @return Collection + */ + public static function offeringTypeKeys(): Collection + { + return collect(self::offering_types)->keys(); + } /* RELATIONS */ @@ -73,43 +109,6 @@ class Supplier extends Model /* METHODS */ - /** - * Return the offerings that this supplier provides - * - * @return void - */ - public function offeringTypes(): Collection - { - $result = collect(); - - // See if we have any configurations - foreach (self::offering_types as $key => $type) { - if (! ($class=Arr::get($type,'class'))) - continue; - - if (Arr::get($this->detail->connections,$key)) { - $result->put($key,(object)[ - 'type' => Arr::get($type,'name'), - 'items' => (new $class)->where('supplier_detail_id',$this->detail->id), - ]); - - continue; - } - - // See if we have any products defined - $o = new $class; - $o->where('supplier_detail_id',$this->detail->id); - - if ($o->count()) - $result->put($key,(object)[ - 'type' => Arr::get($type,'name'), - 'items' => (new $class)->where('supplier_detail_id',$this->detail->id), - ]); - } - - return $result; - } - /** * Return the traffic records, that were not matched to a service. * diff --git a/app/Models/Supplier/Broadband.php b/app/Models/Supplier/Broadband.php index ab00b91..511527d 100644 --- a/app/Models/Supplier/Broadband.php +++ b/app/Models/Supplier/Broadband.php @@ -10,6 +10,8 @@ use App\Models\Product\Broadband as ProductBroadband; class Broadband extends Type implements SupplierItem { + protected const category_name = 'Broadband'; + protected $casts = [ 'offpeak_start' => 'datetime:H:i', 'offpeak_end' => 'datetime:H:i', @@ -35,16 +37,16 @@ class Broadband extends Type implements SupplierItem /* INTERFACES */ - public function types() - { - return $this->belongsToMany(ProductBroadband::class,$this->table,'id','id','id','supplier_item_id'); - } - public function getBillingIntervalAttribute(): int { return 1; // Monthly } + public function products() + { + return $this->hasMany(ProductBroadband::class,'supplier_item_id','id'); + } + /* METHODS */ /** @@ -156,14 +158,4 @@ class Broadband extends Type implements SupplierItem return $result; } - - /** - * Return the Broadband Speed - * - * @return string - */ - public function speed(): string - { - return $this->speed; - } } \ No newline at end of file diff --git a/app/Models/Supplier/Domain.php b/app/Models/Supplier/Domain.php index 999b5c4..5e57c60 100644 --- a/app/Models/Supplier/Domain.php +++ b/app/Models/Supplier/Domain.php @@ -8,6 +8,8 @@ use App\Models\TLD; final class Domain extends Type implements SupplierItem { + protected const category_name = 'Domain Name'; + protected $table = 'supplier_domain'; /* INTERFACES */ @@ -22,9 +24,9 @@ final class Domain extends Type implements SupplierItem return sprintf('%s: %s',$this->product_id,$this->tld->name); } - public function types() + public function products() { - return $this->belongsToMany(ProductDomain::class,$this->table,'id','id','id','supplier_item_id'); + return $this->hasMany(ProductDomain::class,'supplier_item_id','id'); } /* RELATIONS */ diff --git a/app/Models/Supplier/Email.php b/app/Models/Supplier/Email.php index c9ffc7a..a31bd72 100644 --- a/app/Models/Supplier/Email.php +++ b/app/Models/Supplier/Email.php @@ -7,6 +7,8 @@ use App\Models\Product\Email as ProductEmail; final class Email extends Type implements SupplierItem { + protected const category_name = 'Email Hosting'; + protected $table = 'supplier_email'; /* INTERFACES */ @@ -16,8 +18,8 @@ final class Email extends Type implements SupplierItem return 4; // Yearly } - public function types() + public function products() { - return $this->belongsToMany(ProductEmail::class,$this->table,'id','id','id','supplier_item_id'); + return $this->hasMany(ProductEmail::class,$this->table,'supplier_item_id','id'); } } \ No newline at end of file diff --git a/app/Models/Supplier/Ethernet.php b/app/Models/Supplier/Ethernet.php index 7db3244..6da759a 100644 --- a/app/Models/Supplier/Ethernet.php +++ b/app/Models/Supplier/Ethernet.php @@ -4,4 +4,5 @@ namespace App\Models\Supplier; class Ethernet extends Broadband { + protected const category_name = 'Broadband Ethernet'; } \ No newline at end of file diff --git a/app/Models/Supplier/Generic.php b/app/Models/Supplier/Generic.php index 706611b..f095f88 100644 --- a/app/Models/Supplier/Generic.php +++ b/app/Models/Supplier/Generic.php @@ -7,17 +7,19 @@ use App\Models\Product\Generic as ProductGeneric; final class Generic extends Type implements SupplierItem { + protected const category_name = 'Generic'; + protected $table = 'supplier_generic'; /* INTERFACES */ - public function types() - { - return $this->belongsToMany(ProductGeneric::class,$this->table,'id','id','id','supplier_item_id'); - } - public function getBillingIntervalAttribute(): int { return 1; // Monthly } + + public function products() + { + return $this->hasMany(ProductGeneric::class,'supplier_item_id','id'); + } } \ No newline at end of file diff --git a/app/Models/Supplier/HSPA.php b/app/Models/Supplier/HSPA.php index 2a78416..86e1070 100644 --- a/app/Models/Supplier/HSPA.php +++ b/app/Models/Supplier/HSPA.php @@ -4,4 +4,5 @@ namespace App\Models\Supplier; class HSPA extends Broadband { + protected const category_name = 'Mobile Broadband'; } \ No newline at end of file diff --git a/app/Models/Supplier/Host.php b/app/Models/Supplier/Host.php index ea70067..ef73cd1 100644 --- a/app/Models/Supplier/Host.php +++ b/app/Models/Supplier/Host.php @@ -7,17 +7,19 @@ use App\Models\Product\Host as ProductHost; final class Host extends Type implements SupplierItem { + protected const category_name = 'Web Hosting'; + protected $table = 'supplier_host'; /* INTERFACES */ - public function types() - { - return $this->belongsToMany(ProductHost::class,$this->table,'id','id','id','supplier_item_id'); - } - public function getBillingIntervalAttribute(): int { return 4; // Yearly } + + public function products() + { + return $this->belongsToMany(ProductHost::class,'supplier_item_id','id'); + } } \ No newline at end of file diff --git a/app/Models/Supplier/Phone.php b/app/Models/Supplier/Phone.php index e4b5424..d9b7b3b 100644 --- a/app/Models/Supplier/Phone.php +++ b/app/Models/Supplier/Phone.php @@ -7,17 +7,19 @@ use App\Models\Product\Phone as ProductVoip; final class Phone extends Type implements SupplierItem { + protected const category_name = 'Telephone'; + protected $table = 'supplier_phone'; /* INTERFACES */ - public function types() - { - return $this->belongsToMany(ProductVoip::class,$this->table,'id','id','id','supplier_item_id'); - } - public function getBillingIntervalAttribute(): int { return 1; // Monthly } + + public function products() + { + return $this->hasMany(ProductVoip::class,'supplier_item_id','id'); + } } \ No newline at end of file diff --git a/app/Models/Supplier/SSL.php b/app/Models/Supplier/SSL.php index 1072530..209d062 100644 --- a/app/Models/Supplier/SSL.php +++ b/app/Models/Supplier/SSL.php @@ -7,17 +7,19 @@ use App\Models\Product\SSL as ProductSSL; final class SSL extends Type implements SupplierItem { + protected const category_name = 'SSL Certificate'; + protected $table = 'supplier_ssl'; /* INTERFACES */ - public function types() - { - return $this->belongsToMany(ProductSSL::class,$this->table,'id','id','id','supplier_item_id'); - } - public function getBillingIntervalAttribute(): int { return 4; // Yearly } + + public function products() + { + return $this->belongsToMany(ProductSSL::class,'supplier_item_id','id'); + } } \ No newline at end of file diff --git a/app/Models/Supplier/Type.php b/app/Models/Supplier/Type.php index 5ee41d8..98070c2 100644 --- a/app/Models/Supplier/Type.php +++ b/app/Models/Supplier/Type.php @@ -27,6 +27,27 @@ abstract class Type extends Model return Tax::tax_calc($this->attributes['base_cost'],config('site')->taxes); } + /** + * This will return the category of the product (eg: domain, hosting, etc) which is the basis for all + * other logic of these types. + * + * @return string + */ + final public function getCategoryAttribute(): string + { + return strtolower((new \ReflectionClass($this))->getShortName()); + } + + /** + * Return a friendly name for this product, used for display + * + * @return string + */ + final public function getCategoryNameAttribute(): string + { + return static::category_name; + } + /** * This contract term is the highest of * + The defined contract_term diff --git a/app/Models/SupplierDetail.php b/app/Models/SupplierDetail.php index c09e73f..19887a7 100644 --- a/app/Models/SupplierDetail.php +++ b/app/Models/SupplierDetail.php @@ -3,7 +3,9 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\ModelNotFoundException; +use App\Models\Supplier\{Broadband,Generic,Phone,Type}; use App\Traits\SiteID; class SupplierDetail extends Model @@ -14,8 +16,50 @@ class SupplierDetail extends Model /* RELATIONS */ + public function broadbands() + { + return $this->hasMany(Broadband::class); + } + + public function generics() + { + return $this->hasMany(Generic::class); + } + + public function phones() + { + return $this->hasMany(Phone::class); + } + public function supplier() { return $this->belongsTo(Supplier::class); } + + /* METHODS */ + + /** + * Find a supplier product of a particular type + * + * @param string $type + * @param int $id + * @return Type + * @throws \Exception + */ + public function find(string $type,int $id): Type + { + switch ($type) { + case 'broadband': + $item = $this->broadbands->where('id',$id); + break; + + default: + throw new \Exception('Unknown type: '.$type); + } + + if ($item->count() !== 1) + throw new ModelNotFoundException(sprintf('Unknown Model of type [%s] with id [%d]',$type,$id)); + + return $item->pop(); + } } \ No newline at end of file diff --git a/resources/views/theme/backend/adminlte/supplier/product/addedit.blade.php b/resources/views/theme/backend/adminlte/supplier/product/addedit.blade.php new file mode 100644 index 0000000..e9017df --- /dev/null +++ b/resources/views/theme/backend/adminlte/supplier/product/addedit.blade.php @@ -0,0 +1,219 @@ + + +@extends('adminlte::layouts.app') + +@section('htmlheader_title') + Supplier - @if ($oo && $oo->exists)Edit @else New @endif Product +@endsection +@section('page_title') + @if ($oo && $oo->exists)Edit @else New @endif Product +@endsection + +@section('contentheader_title') + Supplier - @if ($oo && $oo->exists)Edit @else New @endif Product +@endsection +@section('contentheader_description') +@endsection + +@section('main-content') +
+
+
+
+

Supplier Product

+ @if(session()->has('success')) + {{ session()->get('success') }} + @endif +
+ +
+
+ @csrf + + +
+
+ +
+ + + + @error('supplier_detail_id') + {{ $message }} + @else + Date is required. + @enderror + + Suppliers Name +
+
+ +
+ +
+ + + + @error('offering_type') + {{ $message }} + @else + Offering Type is Required + @enderror + + Offering Type +
+
+
+ +
+ +
+
+ Cancel + @can('wholesaler') + + @endcan +
+
+
+
+
+
+ + @if($oo && $oo->exists) +
+
+
+
+
+

Offering Products

+
+ +
+ + + + + + + + + + + + @foreach ($oo->products as $pto) + + + + + + + @endforeach + +
IDNameServicesSold
{{ $pto->id }}{{ $pto->product->name }}{{ $pto->product->services->where('active',true)->count() }}{{ $pto->product->services->count() }}
+
+
+
+
+ +
+
+
+
+

Services Using this Supplier Product

+
+ +
+ + + + + + + + + + + + @foreach ($oo->products as $pto) + @foreach ($pto->product->services as $so) + + + + + + + @endforeach + @endforeach + +
IDProductNameStatus
{{ $so->lid }}{{ $so->product->type->id }}{{ $so->name }}{{ $so->active ? 'Active' : 'Not Active' }}
+
+
+
+
+
+ @endif +
+@endsection + +@section('page-scripts') + @css(datatables,bootstrap4|rowgroup) + @js(datatables,bootstrap4|responsive|rowgroup) + + +@endsection \ No newline at end of file diff --git a/resources/views/theme/backend/adminlte/supplier/product/widget/broadband.blade.php b/resources/views/theme/backend/adminlte/supplier/product/widget/broadband.blade.php new file mode 100644 index 0000000..9db24e4 --- /dev/null +++ b/resources/views/theme/backend/adminlte/supplier/product/widget/broadband.blade.php @@ -0,0 +1,308 @@ + +
+ +
+
+ + + + @error('product_id') + {{ $message }} + @else + Supplier's Product ID + @enderror + + Supplier's Product ID +
+
+ + +
+
+ + + + @error('product_desc') + {{ $message }} + @else + Product Description is required + @enderror + + Supplier's Product Description as it appears on Invoices +
+
+
+ +
+ +
+
+
+ active) ? 'checked' : '' }}> + +
+
+
+ + +
+
+ + + + @error('base_cost') + {{ $message }} + @enderror + + Monthly Cost +
+
+ + +
+
+ + + + @error('setup_cost') + {{ $message }} + @enderror + + Setup Cost +
+
+ + +
+
+ + + + @error('contract_term') + {{ $message }} + @enderror + + Term (mths) +
+
+ + +
+
+ + + + @error('speed') + {{ $message }} + @enderror + + Speed +
+
+ + +
+
+ + + + @error('technology') + {{ $message }} + @enderror + + Technology +
+
+
+ +
+ + +
+
+
+ extra_charged) ? 'checked' : '' }}> + +
+
+
+ +
+
+ + + + @error('offpeak_start') + {{ $message }} + @enderror + + Offpeak Start +
+
+ +
+
+ + + + @error('offpeak_end') + {{ $message }} + @enderror + + Offpeak Ends +
+
+
+ +
+ +
+
+
+ extra_shaped) ? 'checked' : '' }}> + +
+
+
+ +
+

Included Traffic in MB

+
+
+ +
+ +
+
+ + + + @error('base_down_peak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('base_up_peak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('base_down_offpeak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('base_up_offpeak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('metric') + {{ $message }} + @enderror + + Metric +
+
+
+ +
+
+

Values determine the included traffic. A value of 0 means no traffic is included and charged at the Extra Traffic rates. An empty value means the traffic is not counted in this category.

+

Any values charged at extra rates, the rate determines the value charged, and if the value is empty it will roll up to the Peak category or Down Peak if the Peak category is null.

+
+
+ +
+ +
+
+

Extra Traffic in $/mb

+
+
+ +
+ +
+
+ + + + @error('extra_down_peak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('extra_up_peak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('extra_down_offpeak') + {{ $message }} + @enderror + +
+
+ + +
+
+ + + + @error('extra_up_offpeak') + {{ $message }} + @enderror + +
+
+
\ No newline at end of file diff --git a/resources/views/theme/backend/adminlte/supplier/widget/connections.blade.php b/resources/views/theme/backend/adminlte/supplier/widget/connections.blade.php index 3c96bf5..9055813 100644 --- a/resources/views/theme/backend/adminlte/supplier/widget/connections.blade.php +++ b/resources/views/theme/backend/adminlte/supplier/widget/connections.blade.php @@ -14,7 +14,7 @@ - @foreach ($o->offeringTypes() as $key => $offering) + @foreach (\App\Models\Supplier::offeringTypes($o) as $key => $offering) {{ $offering->type }} @if(Arr::get($o->detail->connections,$key)) diff --git a/resources/views/theme/backend/adminlte/supplier/widget/costs.blade.php b/resources/views/theme/backend/adminlte/supplier/widget/costs.blade.php index 569e32d..d068115 100644 --- a/resources/views/theme/backend/adminlte/supplier/widget/costs.blade.php +++ b/resources/views/theme/backend/adminlte/supplier/widget/costs.blade.php @@ -6,7 +6,7 @@ diff --git a/resources/views/theme/backend/adminlte/supplier/widget/offerings.blade.php b/resources/views/theme/backend/adminlte/supplier/widget/offerings.blade.php index 99645fd..6f82f64 100644 --- a/resources/views/theme/backend/adminlte/supplier/widget/offerings.blade.php +++ b/resources/views/theme/backend/adminlte/supplier/widget/offerings.blade.php @@ -2,7 +2,7 @@
@@ -10,7 +10,7 @@
- @foreach($o->offeringTypes() as $key => $offering) + @foreach(\App\Models\Supplier::offeringTypes($o) as $key => $offering)
@@ -33,7 +33,7 @@ - @foreach($xx=$offering->items->with(['types.product.services'])->get() as $oo) + @foreach($xx=$offering->items->with(['products.product.services'])->get() as $oo) @@ -41,9 +41,9 @@ - - - + + + @endforeach @@ -53,7 +53,7 @@ - + @@ -72,7 +72,7 @@
{{ $oo->id }} {{ $oo->name }}{{ $oo->active ? 'YES' : 'NO' }} {{ number_format($oo->setup_cost_taxable,2) }} {{ number_format($oo->base_cost_taxable,2) }}{{ number_format($oo->types->count()) }}{{ number_format($oo->types->pluck('product')->filter()->count()) }}{{ number_format(($x=$oo->types->pluck('product.services')->flatten()->filter())->count()) }}{{ number_format($oo->products->count()) }}{{ number_format($oo->products->pluck('product')->filter()->count()) }}{{ number_format(($x=$oo->products->pluck('product.services')->flatten()->filter())->count()) }} {{ number_format($x->where('active')->count()) }}
TOTALS {{ $xx->where('active',TRUE)->count() }}{{ number_format(($x=$xx->pluck('types')->flatten()->filter())->count()) }}{{ number_format(($x=$xx->pluck('products')->flatten()->filter())->count()) }} {{ number_format($x->pluck('product')->filter()->count()) }} {{ number_format(($xxx=$x->pluck('product.services')->flatten()->filter())->count()) }} {{ number_format($xxx->where('active')->count()) }}