hasMany(Account::class); } /** * The agents that this users manages * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function agents() { return $this->hasMany(static::class,'parent_id','id')->with('agents'); } /** * The clients that this user has * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function clients() { return $this ->hasMany(static::class,'parent_id','id') ->with('clients'); } /** * This users language configuration * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function language() { return $this->belongsTo(Language::class); } /** * This users invoices * * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough */ public function invoices() { return $this->hasManyThrough(Invoice::class,Account::class) ->active(); } /** * The payments this user has made * * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough */ public function payments() { return $this->hasManyThrough(Payment::class,Account::class); } /** * THe services this user has * * @return \Illuminate\Database\Eloquent\Relations\HasManyThrough */ public function services() { return $this->hasManyThrough(Service::class,Account::class) ->active(); } /** * The site this user is configured to access * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function site() { return $this->belongsTo(Site::class); } /** * This users supplier/reseller * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ protected function supplier() { return $this->belongsTo(static::class,'parent_id','id'); } /** * Who this user supplies to * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ protected function suppliers() { return $this->hasMany(static::class,'parent_id','id'); } /* ATTRIBUTES */ public function getActiveDisplayAttribute($value) { return sprintf('%s',$this->active ? 'primary' : 'danger',$this->active ? 'Active' : 'Inactive'); } /** * Logged in users full name * * @return string */ public function getFullNameAttribute(): string { return sprintf('%s %s',$this->firstname,$this->lastname); } /** * A list of all invoices currently unpaid * * @return mixed */ public function getInvoicesDueAttribute() { return $this->invoices ->where('active',TRUE) ->sortBy('id') ->transform(function ($item) { if ($item->due > 0) return $item; }) ->reverse() ->filter(); } /** * Return a Carbon Date if it has a value. * * @param $value * @return Carbon * @throws \Exception * @todo This attribute is not in the schema */ public function getLastAccessAttribute($value) { if (! is_null($value)) return new Carbon($value); } /** * @deprecated Use static::getFullNameAttribute() * @return mixed */ public function getNameAttribute() { return $this->full_name; } /** * Return a list of the payments that the user has made * * @return mixed * @todo Merge this with payments() */ public function getPaymentHistoryAttribute() { return $this->payments ->sortBy('payment_date') ->reverse(); } public function getServicesCountHtmlAttribute() { return sprintf('%s /%s',$this->services->where('active',TRUE)->count(),$this->services->count()); } public function getSurFirstNameAttribute() { return sprintf('%s, %s',$this->lastname,$this->firstname); } public function getSwitchUrlAttribute() { return sprintf('',$this->id); } public function getUserIdAttribute() { return sprintf('%02s-%04s',$this->site_id,$this->id); } public function getUserIdUrlAttribute() { return sprintf('%s',$this->id,$this->user_id); } /* METHODS */ /** * Users password reset email notification * * @param string $token */ public function sendPasswordResetNotification($token) { $this->notify((new ResetPasswordNotification($token))->onQueue('high')); } /* SCOPES */ // @todo use trait public function scopeActive() { return $this->where('active',TRUE); } /** * Search for a record * * @param $query * @param string $term * @return */ public function scopeSearch($query,string $term) { // Build our where clause // First Name, Last name if (preg_match('/\ /',$term)) { [$fn,$ln] = explode(' ',$term,2); $query->where(function($query1) use ($fn,$ln,$term) { $query1->where(function($query2) use ($fn,$ln) { return $query2 ->where('firstname','like','%'.$fn.'%') ->where('lastname','like','%'.$ln.'%'); }); }); } elseif (is_numeric($term)) { $query->where('id','like','%'.$term.'%'); } elseif (preg_match('/\@/',$term)) { $query->where('email','like','%'.$term.'%'); } else { $query ->Where('firstname','like','%'.$term.'%') ->orWhere('lastname','like','%'.$term.'%'); } return $query; } /* GENERAL METHODS */ /** * Determine if the user is an admin of the user with $id * * @param $id * @return bool */ public function isAdmin($id): bool { return $id AND $this->isReseller() AND in_array($id,$this->all_accounts()->pluck('user_id')->toArray()); } /** * Get a list of accounts for the clients of this user * * @return DatabaseCollection */ public function all_accounts(): DatabaseCollection { $result = new DatabaseCollection(); $clients = $this->all_clients(); foreach ($clients->pluck('accounts') as $accounts) { foreach ($accounts as $o) { if (! $o->active) continue; $result->push($o); } } // Include my accounts foreach ($this->accounts as $o) { if (! $o->active) continue; $result->push($o); } $result->load('user.accounts'); return $result; } /** * Get a list of clients that this user is responsible for. * * @param int $level * @return Collection */ public function all_clients($level=0,DatabaseCollection $clients=NULL): DatabaseCollection { $result = is_null($clients) ? $this->clients : $clients; $result ->filter(function($item) { return $item->active; }) ->transform(function($item) use ($level) { $item->level = $level; return $item; }); foreach ($result->pluck('clients') as $clients) { foreach ($this->all_clients($level+1,$clients) as $o) { if (! $o->active) continue; $result->push($o); } } return $result; } public function all_client_service_inactive() { $s = Service::InActive(); $aa = $this->all_accounts()->pluck('id')->unique()->toArray(); return $s->get()->filter(function($item) use ($aa) { return in_array($item->account_id,$aa); }); } /** * List of all this users agents, recursively * * @param int $level * @return Collection */ public function all_agents($level=0) { $result = collect(); foreach ($this->agents as $o) { if (! $o->active OR ! $o->agents->count()) continue; $o->level = $level; $result->push($o); // Include agents of agents $result->push($o->all_agents($level+1)); } return $result->flatten(); } /** * 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() ->authorised($this) ->where('order_status','!=','ACTIVE') ->with(['account','product']) ->get(); } /** * Determine if the logged in user is a reseller or wholesaler * * @return bool */ public function isReseller(): bool { return in_array($this->role(),['wholesaler','reseller']); } /** * Determine if the logged in user is a wholesaler * * @return bool */ public function isWholesaler(): bool { return in_array($this->role(),['wholesaler']); } /** * Get all the items for the next invoice * * @param bool $future * @return DatabaseCollection */ public function next_invoice_items(bool $future=FALSE): DatabaseCollection { $result = new DatabaseCollection; foreach ($this->services as $o) { if ($future) { if ($o->invoice_next->subDays(config('app.invoice_inadvance'))->isPast()) continue; } else { if ($o->invoice_next->subDays(config('app.invoice_inadvance'))->isFuture()) continue; } foreach ($o->next_invoice_items($future) as $oo) $result->push($oo); } $result->load([ 'product.descriptions', 'service.type', ]); 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('ab_invoice_item') ->select([ 'invoice_id', DB::raw('ab_invoice_item.id AS invoice_item_id'), DB::raw('IFNULL(ab_invoice_item.discount_amt,0) AS discount'), DB::raw('ROUND(CAST(quantity*price_base AS decimal(8,2)),2) AS base'), DB::raw('ROUND(ab_invoice_item_tax.amount,2) AS tax'), ]) ->leftjoin('ab_invoice_item_tax',['ab_invoice_item_tax.invoice_item_id'=>'ab_invoice_item.id']) ->where('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(alloc_amt) AS allocate'), ]) ->where('alloc_amt','>',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. */ 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('ROUND(SUM(base)+SUM(tax)-SUM(discount),2) AS total'), DB::raw('false AS payments'), DB::raw('false AS payment_fees'), ]) ->from($this->query_invoice_items(),'II') ->join('ab_invoice',['ab_invoice.id'=>'II.invoice_id']) ->whereIN('account_id',$this->all_accounts()->pluck('id')->unique()->toArray()) ->where('ab_invoice.active',TRUE) ->groupBy(['invoice_id']); $payments = (new Payment) ->select([ 'invoice_id', DB::raw('false AS discount'), DB::raw('false AS base'), DB::raw('false AS tax'), DB::raw('false 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->all_accounts()->pluck('id')->unique()->toArray()) //->where('payments.active',TRUE) // @todo To implement ->groupBy(['invoice_id']); $summary = (new Invoice) ->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_date', 'date_orig', 'discount', 'invoice_base', 'invoice_tax', 'invoice_total', 'payments', 'payment_fees', DB::raw('ROUND(invoice_total-payments,2) AS balance'), ]) ->join('ab_invoice',['ab_invoice.id'=>'invoice_id']) ->from($summary,'summary'); } public function query_payment_summary() { $payment = (new Payment) ->select([ DB::raw('payment_id AS id'), DB::raw('SUM(allocate) AS allocate'), ]) ->from($this->query_payment_items(),'PI') //->where('payments.active',TRUE) // @todo To implement ->groupBy(['payment_id']); return (new Payment) ->select([ DB::raw('payments.id AS id'), 'date_orig', 'payment_date', 'total_amt', //'fees_amt', DB::raw('total_amt-allocate AS balance'), ]) ->rightJoin('payments',['payments.id'=>'summary.id']) //->where('payments.active',TRUE) // @todo To implement ->whereIN('account_id',$this->all_accounts()->pluck('id')->unique()->toArray()) ->from($payment,'summary'); } /** * Determine what the logged in user's role is * + Wholesaler - aka Super User * + Reseller - services accounts on behalf of their customers * + Customer - end user customer * * @return string */ public function role() { // If I have agents and no parent, I am the wholesaler if (is_null($this->parent_id) AND ($this->all_agents()->count() OR $this->all_clients()->count())) return 'wholesaler'; // If I have agents and a parent, I am a reseller elseif ($this->parent_id AND ($this->all_agents()->count() OR $this->all_clients()->count())) return 'reseller'; // If I have no agents and a parent, I am a customer elseif (! $this->all_agents()->count() AND ! $this->all_clients()->count()) return 'customer'; } }