From 326b1dcfc5a78073adc07b0462265babbb11611d Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 5 Jul 2024 22:56:02 +1000 Subject: [PATCH] User optimisation and code cleanup --- app/Models/Policies/AccountPolicy.php | 8 +- app/Models/Policies/InvoicePolicy.php | 9 +- app/Models/Policies/ServicePolicy.php | 8 +- app/Models/Policies/UserPolicy.php | 11 +- app/Models/Rtm.php | 17 ++ app/Models/Service.php | 12 + app/Models/User.php | 272 ++---------------- app/Traits/ScopeServiceUserAuthorised.php | 2 +- .../account/widget/summary_boxes.blade.php | 17 +- .../theme/backend/adminlte/home.blade.php | 6 +- .../adminlte/r/account/widgets/list.blade.php | 6 +- .../r/service/widget/movement.blade.php | 6 +- 12 files changed, 80 insertions(+), 294 deletions(-) diff --git a/app/Models/Policies/AccountPolicy.php b/app/Models/Policies/AccountPolicy.php index 8a00a62..283a594 100644 --- a/app/Models/Policies/AccountPolicy.php +++ b/app/Models/Policies/AccountPolicy.php @@ -20,13 +20,7 @@ class AccountPolicy public function view(User $uo,Account $ao): bool { // If this is a service for an account managed by a user. - return ($uo->accounts->pluck('id')->search($ao->id) !== FALSE) - - // The user is the wholesaler - OR $uo->isWholesaler() - - // The user has this as one of their accounts - OR $uo->accounts->pluck('id')->contains($ao->id); + return $uo->accounts_all->pluck('id')->contains($ao->id) || $uo->isWholesaler(); } /** diff --git a/app/Models/Policies/InvoicePolicy.php b/app/Models/Policies/InvoicePolicy.php index 923f5e8..3c8d498 100644 --- a/app/Models/Policies/InvoicePolicy.php +++ b/app/Models/Policies/InvoicePolicy.php @@ -19,14 +19,7 @@ class InvoicePolicy */ public function view(User $uo,Invoice $io): bool { - // If this is a service for an account managed by a user. - return ($uo->invoices->pluck('id')->search($io->id) !== FALSE) - - // The user is the wholesaler - OR $uo->isWholesaler() - - // The user has this as one of their accounts - OR $uo->accounts->pluck('id')->contains($io->account_id); + return $uo->accounts_all->pluck('id')->contains($io->account_id) || $uo->isWholesaler(); } /** diff --git a/app/Models/Policies/ServicePolicy.php b/app/Models/Policies/ServicePolicy.php index 0aa7cfe..511ba25 100644 --- a/app/Models/Policies/ServicePolicy.php +++ b/app/Models/Policies/ServicePolicy.php @@ -20,13 +20,7 @@ class ServicePolicy public function view(User $uo, Service $so): bool { // If this is a service for an account managed by a user. - return ($uo->services->pluck('id')->search($so->id) !== FALSE) - - // The user is the wholesaler - OR $uo->isWholesaler() - - // The user has this as one of their accounts - OR $uo->accounts->pluck('id')->contains($so->account_id); + return $uo->accounts_all->pluck('id')->contains($so->account_id) || $uo->isWholesaler(); } /** diff --git a/app/Models/Policies/UserPolicy.php b/app/Models/Policies/UserPolicy.php index c15de04..4ed4166 100644 --- a/app/Models/Policies/UserPolicy.php +++ b/app/Models/Policies/UserPolicy.php @@ -15,11 +15,11 @@ class UserPolicy * * @param User $uo * @param string $ability - * @return bool|null + * @return null|bool */ - public function before(User $uo,string $ability): bool + public function before(User $uo,string $ability): bool|NULL { - return $uo->isWholesaler() ?: FALSE; + return $uo->isWholesaler() ?: NULL; } /** @@ -44,9 +44,6 @@ class UserPolicy public function view(User $uo,User $o): bool { // If this is a service for an account managed by a user. - return ($uo->id == $o->id) - - // The user has this as one of their accounts - OR $uo->accounts->pluck('user')->pluck('id')->unique()->contains($o->id); + return ($uo->id == $o->id) || $uo->accounts_all->pluck('user_id')->contains($o->id) || $uo->isWholesaler(); } } \ No newline at end of file diff --git a/app/Models/Rtm.php b/app/Models/Rtm.php index eead218..56739c9 100644 --- a/app/Models/Rtm.php +++ b/app/Models/Rtm.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; class Rtm extends Model { @@ -23,4 +24,20 @@ class Rtm extends Model { return $this->hasMany(self::class,'parent_id'); } + + /** + * Return all the children RTM records that this record is parent of + * + * @return Collection + */ + public function children_all(): Collection + { + $result = collect(); + $result->push($this->withoutRelations()); + + foreach ($this->children as $o) + $result = $result->merge($o->children_all()); + + return $result; + } } \ No newline at end of file diff --git a/app/Models/Service.php b/app/Models/Service.php index 96ba74c..9e88073 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -317,6 +317,18 @@ class Service extends Model implements IDs return sprintf('%02s-%04s.%s',$this->site_id,$this->account_id,$this->getLIDattribute()); } + /* STATIC */ + + public static function movements(User $uo): Collection + { + return (new self) + ->active() + ->serviceUserAuthorised($uo) + ->where('order_status','!=','ACTIVE') + ->with(['account','product']) + ->get(); + } + /* RELATIONS */ /** diff --git a/app/Models/User.php b/app/Models/User.php index 6f07b7d..adf6e1f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -7,17 +7,12 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Support\Collection; use Illuminate\Database\Eloquent\Collection as DatabaseCollection; -use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Session; use Laravel\Passport\HasApiTokens; use Leenooks\Traits\ScopeActive; use Leenooks\Traits\UserSwitch; use App\Interfaces\IDs; -use App\Models\Scopes\SiteScope; use App\Notifications\ResetPassword as ResetPasswordNotification; -use App\Traits\SiteID; /** * Class User @@ -27,14 +22,12 @@ use App\Traits\SiteID; */ class User extends Authenticatable implements IDs { - use HasFactory,HasApiTokens,Notifiable,UserSwitch,SiteID,ScopeActive; + use HasFactory,HasApiTokens,Notifiable,UserSwitch,ScopeActive; private const CACHE_TIME = 3600; - protected $dates = [ - 'created_at', - 'updated_at', - 'last_access' + protected $casts = [ + 'last_access' => 'datetime:Y-m-d H:i:s', ]; /** @@ -96,27 +89,25 @@ class User extends Authenticatable implements IDs /* RELATIONS */ /** - * The accounts that this user manages - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - * @note This cannot be loaded with "with"? + * This user's accounts */ public function accounts() { return $this->hasMany(Account::class) - ->orWhereIn('id',$this->rtm_accounts()->pluck('id')) ->active(); } /** - * This users invoices + * The accounts that this user manages * - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough - * @deprecated Accounts have invoices, not users + * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @note This cannot be preloaded with load() or with() - $this is a blank object */ - public function invoices() + public function accounts_all() { - return $this->hasManyThrough(Invoice::class,Account::class) + return $this->hasMany(Account::class) + ->when((! is_null($this->rtm) && $this->id), + fn($query)=>$query->orWhereIn('rtm_id',$this->rtm?->children_all()->pluck('id'))) ->active(); } @@ -132,37 +123,20 @@ class User extends Authenticatable implements IDs /** * Return the routes to market account for this user - * - * @return \Illuminate\Database\Eloquent\Relations\HasOneThrough */ public function rtm() { return $this->hasOneThrough(Rtm::class,Account::class); } - /** - * The services this user has - * - * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough - * @deprecated Accounts have services, not users - */ - public function services() - { - return $this->hasManyThrough(Service::class,Account::class) - ->with(['product.type']) - ->active(); - } - /** * Supplier configuration for this user * - * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany - * @deprecated Move to account->suppliers() + * @deprecated To move to account->suppliers() */ public function suppliers() { return $this->belongsToMany(Supplier::class) - ->where('supplier_user.site_id',$this->site_id) ->withPivot('id','created_at'); } @@ -179,7 +153,7 @@ class User extends Authenticatable implements IDs } /** - * Logged in users full name + * Logged in user's full name * * @return string */ @@ -188,25 +162,9 @@ class User extends Authenticatable implements IDs return sprintf('%s %s',$this->firstname,$this->lastname); } - /** - * Return my accounts, but only those accounts with the same group_id - * - * @note Users can only manage accounts with the same group ID, thereby ensuring they dont see different - * pricing options - since prices can be controlled by groups - * @return Collection - */ - public function getMyAccountsAttribute(): Collection - { - $result = $this->accounts->where('user_id',$this->id); - - if (! $result->count()) - return $result; - - return $this->isReseller() ? $result : $result->groupBy('group.id')->first(); - } - /** * Return a friendly string of this persons role + * * @return string */ public function getRoleAttribute(): string @@ -259,22 +217,6 @@ class User extends Authenticatable implements IDs /* METHODS */ - /** - * Show this user's clients with service movements - * - * A service movement, is an active service where the status is not ACTIVE - * - * @return DatabaseCollection - */ - public function client_service_movements(): DatabaseCollection - { - return Service::active() - ->serviceUserAuthorised($this) - ->where('order_status','!=','ACTIVE') - ->with(['account','product']) - ->get(); - } - /** * Determine if the user is an admin of the user with $id * @@ -283,7 +225,11 @@ class User extends Authenticatable implements IDs */ public function isAdmin(User $user=NULL): bool { - return $user->exists AND $this->isReseller() AND $this->accounts->pluck('user_id')->contains($user->id); + return $user->exists + && $this->isReseller() + && $this->accounts_all + ->pluck('user_id') + ->contains($user->id); } /** @@ -311,9 +257,11 @@ class User extends Authenticatable implements IDs * * @param bool $future * @return DatabaseCollection + * @deprecated This should be done in accounts */ - public function next_invoice_items(bool $future=FALSE): DatabaseCollection + public function next_invoice_items(bool $future=FALSE): Collection { + return collect(); $result = new DatabaseCollection; $this->services->load(['invoice_items.taxes']); @@ -339,117 +287,6 @@ class User extends Authenticatable implements IDs return $result; } - /** - * Return an SQL query that will return a list of invoices - * - * @return \Illuminate\Database\Query\Builder - */ - private function query_invoice_items() - { - return DB::table('invoice_items') - ->select([ - 'invoice_id', - DB::raw('invoice_items.id AS invoice_item_id'), - DB::raw('COALESCE(invoice_items.discount_amt,0) AS discount'), - DB::raw('quantity*price_base AS base'), - DB::raw('invoice_item_taxes.amount AS tax'), - ]) - ->leftjoin('invoice_item_taxes',['invoice_item_taxes.invoice_item_id'=>'invoice_items.id']) - ->where('invoice_items.active',TRUE); - } - - /** - * Return an SQL query that will return payment summaries by invoices. - * - * @return \Illuminate\Database\Query\Builder - */ - private function query_payment_items() - { - return DB::table('payment_items') - ->select([ - 'payment_id', - 'invoice_id', - DB::raw('SUM(amount) AS allocate'), - ]) - ->where('amount','>',0) - ->groupBy(['invoice_id','payment_id']); - } - - /** - * Return an SQL query that will summarise invoices with payments - * - * @return \Illuminate\Database\Query\Builder - * @todo change this to just return outstanding invoices as a collection. - * @deprecated Use account->invoiceSummary - */ - public function query_invoice_summary() - { - $invoices = (new Invoice) - ->select([ - 'invoice_id', - DB::raw('SUM(discount) AS discount'), - DB::raw('SUM(base) AS base'), - DB::raw('SUM(tax) AS tax'), - DB::raw('base+tax-discount AS total'), - DB::raw('0 AS payments'), - DB::raw('0 AS payment_fees'), - ]) - ->from($this->query_invoice_items(),'II') - ->join('invoices',['invoices.id'=>'II.invoice_id']) - ->whereIN('account_id',$this->accounts->pluck('id')) - ->where('invoices.active',TRUE) - ->groupBy(['invoice_id','base','tax','discount']); - - $payments = (new Payment) - ->select([ - 'invoice_id', - DB::raw('0 AS discount'), - DB::raw('0 AS base'), - DB::raw('0 AS tax'), - DB::raw('0 AS total'), - DB::raw('SUM(allocate) AS payments'), - DB::raw('SUM(fees_amt) AS payment_fees'), - ]) - ->from($this->query_payment_items(),'PI') - ->join('payments',['payments.id'=>'PI.payment_id']) - ->whereIN('account_id',$this->accounts->pluck('id')) - //->where('payments.active',TRUE) // @todo To implement - ->groupBy(['invoice_id']); - - $summary = (new Invoice) - ->withoutGlobalScope(SiteScope::class) - ->select([ - 'invoice_id', - DB::raw('SUM(discount) AS discount'), - DB::raw('SUM(base) AS invoice_base'), - DB::raw('SUM(tax) AS invoice_tax'), - DB::raw('SUM(total) AS invoice_total'), - DB::raw('SUM(payments) AS payments'), - DB::raw('SUM(payment_fees) AS payment_fees'), - ]) - ->from($invoices->unionAll($payments),'invoices') - ->groupBy(['invoice_id']); - - return (new Invoice) - ->select([ - 'account_id', - 'id', - 'due_at', - 'created_at', - 'discount', - 'invoice_base', - 'invoice_tax', - 'invoice_total', - 'payments', - 'payment_fees', - DB::raw('invoice_total-payments AS balance'), - ]) - ->join('invoices',['invoices.id'=>'invoice_id']) - ->with(['items.taxes']) - ->from($summary,'summary') - ->groupBy(['id','account_id','discount','invoice_base','invoice_tax','invoice_total','payments','payment_fees']); - } - /** * Determine what the logged in user's role is * + Wholesaler - aka Super User @@ -460,67 +297,8 @@ class User extends Authenticatable implements IDs */ public function role() { - // Cache our role for this session - $cache_key = sprintf('%s:%s:%s',$this->id,__METHOD__,Session::getId()); - - return Cache::remember($cache_key,self::CACHE_TIME,function() { - // Get the RTM for our accounts - $rtms = Rtm::whereIn('account_id',$this->accounts->pluck('id'))->get(); - - // If I have no parent, I am the wholesaler - if ($rtms->whereNull('parent_id')->count()) - return 'wholesaler'; - - // If I exist in the RTM table, I'm a reseller - else if ($rtms->count()) - return 'reseller'; - - // Otherwise a client - else - return 'customer'; - }); - } - - /** - * Return the accounts that this user can manage - * This method is a helper to User::accounts() - use $user->accounts instead - * - * @return Collection - */ - private function rtm_accounts(): Collection - { - return Account::whereIn('rtm_id',$this->rtm_list()->pluck('id')) - ->get(); - } - - /** - * Return the RTM hierarchy that this user can manage - * - * @param Rtm|null $rtm - * @return Collection - */ - public function rtm_list(Rtm $rtm=NULL): Collection - { - // If this user doesnt manage any accounts - if (! $this->exists || ! $this->rtm) - return collect(); - - $list = collect(); - - // Add this RTM to the list - if (! $rtm) { - $list->push($this->rtm); - $children = $this->rtm->children; - - } else { - $list->push($rtm); - $children =$rtm->children; - } - - // Capture any children - foreach ($children as $child) - $list->push($this->rtm_list($child)); - - return $rtm ? $list : $list->flatten()->unique(function($item) { return $item->id; }); + return $this->rtm + ? ($this->rtm->parent_id ? 'reseller' : 'wholesaler') + : 'customer'; } } \ No newline at end of file diff --git a/app/Traits/ScopeServiceUserAuthorised.php b/app/Traits/ScopeServiceUserAuthorised.php index b9d3d91..e419af4 100644 --- a/app/Traits/ScopeServiceUserAuthorised.php +++ b/app/Traits/ScopeServiceUserAuthorised.php @@ -16,6 +16,6 @@ trait ScopeServiceUserAuthorised public function scopeServiceUserAuthorised($query,User $uo) { return $query - ->whereIN('services.account_id',$uo->accounts->pluck('id')); + ->whereIN('services.account_id',$uo->accounts_all->pluck('id')); } } \ No newline at end of file diff --git a/resources/views/theme/backend/adminlte/account/widget/summary_boxes.blade.php b/resources/views/theme/backend/adminlte/account/widget/summary_boxes.blade.php index 955e32c..6c4111d 100644 --- a/resources/views/theme/backend/adminlte/account/widget/summary_boxes.blade.php +++ b/resources/views/theme/backend/adminlte/account/widget/summary_boxes.blade.php @@ -1,27 +1,25 @@ -@if(Auth::user()->isReseller() && $o->my_accounts->count() <= 2 && $o->my_accounts->pluck('providers')->flatten()->count()) +@if($user->isReseller() && ($o->accounts->count() <= 2) && ($x=$o->accounts->pluck('providers')->flatten())->count())
Accounting - @foreach ($o->my_accounts as $ao) - @foreach($ao->providers as $po) - {{ ucfirst($po->name) }} - @endforeach + @foreach($x as $po) + {{ ucfirst($po->name) }} @endforeach
@endif -@if ($o->accounts->count() > 1) +@if ($o->accounts_all->count() > 1)
Linked Accounts - {{ number_format($o->my_accounts->count()) }} + {{ number_format($o->accounts_all->count()) }}
@@ -33,8 +31,7 @@
Active Services - - {{ $o->services->count() }} /{{ $o->services->count() }} + {{ $o->accounts_all->map(fn($item)=>$item->services->where('active',TRUE)->count())->sum() }} /{{ $o->accounts_all->map(fn($item)=>$item->services->count())->sum() }}
@@ -45,7 +42,7 @@
Account Balance - $ {{ number_format(($x=$o->accounts->map(fn($item)=>$item->invoiceSummaryDue()->get()->pluck('_balance'))->flatten())->sum(),2) }} + $ {{ number_format(($x=$o->accounts_all->map(fn($item)=>$item->invoiceSummaryDue()->get()->pluck('_balance'))->flatten())->sum(),2) }}
diff --git a/resources/views/theme/backend/adminlte/home.blade.php b/resources/views/theme/backend/adminlte/home.blade.php index 2f07a66..a228e61 100644 --- a/resources/views/theme/backend/adminlte/home.blade.php +++ b/resources/views/theme/backend/adminlte/home.blade.php @@ -24,7 +24,7 @@