From 2a4dd7e5935d842b3cba30a77aba361b8db8bbb1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 5 Aug 2024 18:52:34 +1000 Subject: [PATCH] Updates for chart queries --- .../ClientPortal/NinjaPlanController.php | 4 +- app/Http/ViewComposers/PortalComposer.php | 6 +- app/Models/Task.php | 63 ++++++++-- app/Services/Chart/ChartCalculations.php | 116 ++++++++++++++++++ app/Services/Chart/ChartService.php | 14 +-- .../portal/ninja2020/plan/trial.blade.php | 46 +++++-- .../ninja2020/plan/trial_confirmed.blade.php | 2 +- 7 files changed, 218 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/NinjaPlanController.php b/app/Http/Controllers/ClientPortal/NinjaPlanController.php index 7d7d3d5ba1b2..3b3c19580488 100644 --- a/app/Http/Controllers/ClientPortal/NinjaPlanController.php +++ b/app/Http/Controllers/ClientPortal/NinjaPlanController.php @@ -80,7 +80,7 @@ class NinjaPlanController extends Controller $data['intent'] = $setupIntent; $data['client'] = Auth::guard('contact')->user()->client; - + return $this->render('plan.trial', $data); } @@ -88,6 +88,8 @@ class NinjaPlanController extends Controller { $trial_started = "Trial Started @ ".now()->format('Y-m-d H:i:s'); + auth()->guard('contact')->user()->fill($request->only(['first_name','last_name']))->save(); + $client = auth()->guard('contact')->user()->client; $client->private_notes = $trial_started; $client->fill($request->all()); diff --git a/app/Http/ViewComposers/PortalComposer.php b/app/Http/ViewComposers/PortalComposer.php index 6b0790a0f4ff..9799260c5a17 100644 --- a/app/Http/ViewComposers/PortalComposer.php +++ b/app/Http/ViewComposers/PortalComposer.php @@ -136,11 +136,11 @@ class PortalComposer $data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity']; - if (Ninja::isHosted() && auth()->guard('contact')->user()->company->id == config('ninja.ninja_default_company_id')) { + // if (Ninja::isHosted() && auth()->guard('contact')->user()->company->id == config('ninja.ninja_default_company_id')) { $data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card']; - } else { + // } else { $data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar']; - } + // } if (auth()->guard('contact')->user()->client->getSetting('client_initiated_payments')) { $data[] = ['title' => ctrans('texts.pre_payment'), 'url' => 'client.pre_payments.index', 'icon' => 'dollar-sign']; diff --git a/app/Models/Task.php b/app/Models/Task.php index 0df362e4d529..89b21ffa81ac 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -16,6 +16,7 @@ use App\Models\CompanyUser; use Illuminate\Support\Carbon; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\SoftDeletes; +use App\Libraries\Currency\Conversion\CurrencyApi; /** * App\Models\Task @@ -159,27 +160,55 @@ class Task extends BaseModel return $this->morphMany(Document::class, 'documentable'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ public function assigned_user() { return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed(); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ public function user() { return $this->belongsTo(User::class)->withTrashed(); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ public function client() { return $this->belongsTo(Client::class)->withTrashed(); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ public function status() { return $this->belongsTo(TaskStatus::class)->withTrashed(); } - public function stringStatus() + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function invoice() + { + return $this->belongsTo(Invoice::class)->withTrashed(); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function project() + { + return $this->belongsTo(Project::class)->withTrashed(); + } + + public function stringStatus(): string { if($this->invoice_id) { return '
'.ctrans('texts.invoiced').'
'; @@ -193,16 +222,6 @@ class Task extends BaseModel } - public function invoice() - { - return $this->belongsTo(Invoice::class)->withTrashed(); - } - - public function project() - { - return $this->belongsTo(Project::class)->withTrashed(); - } - public function calcStartTime() { $parts = json_decode($this->time_log) ?: []; @@ -230,7 +249,7 @@ class Task extends BaseModel public function calcDuration($start_time_cutoff = 0, $end_time_cutoff = 0) { $duration = 0; - $parts = json_decode($this->time_log) ?: []; + $parts = json_decode($this->time_log ?? '{}') ?: []; foreach ($parts as $part) { $start_time = $part[0]; @@ -272,6 +291,26 @@ class Task extends BaseModel return $this->company->settings->default_task_rate ?? 0; } + public function taskCompanyValue(): float + { + $client_currency = $this->client->getSetting('currency_id'); + $company_currency = $this->company->getSetting('currency_id'); + + if($client_currency != $company_currency) + { + $converter = new CurrencyApi(); + return $converter->convert($this->taskValue(), $client_currency, $company_currency); + } + + return $this->taskValue(); + + } + + public function taskValue(): float + { + return round(($this->calcDuration() / 3600) * $this->getRate(),2); + } + public function processLogs() { diff --git a/app/Services/Chart/ChartCalculations.php b/app/Services/Chart/ChartCalculations.php index 92cbbd93c1fc..f8af770d90ff 100644 --- a/app/Services/Chart/ChartCalculations.php +++ b/app/Services/Chart/ChartCalculations.php @@ -14,6 +14,7 @@ namespace App\Services\Chart; use App\Models\Invoice; use App\Models\Payment; use App\Models\Quote; +use App\Models\Task; /** * Class ChartCalculations. @@ -170,4 +171,119 @@ trait ChartCalculations return $result; } + + public function getLoggedTasks($data): int|float + { + //tasks with at least 1 timelog entry. + + $result = 0; + $calculated = collect(); + + $q = Task::query() + ->withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted',0); + + if(in_array($data['period'], ['current,previous'])) { + $q->whereBetween('calculated_start_date', [$data['start_date'], $data['end_date']]); + } + + if($data['calculation'] != 'count' && $data['format'] == 'money') + { + if($data['currency_id'] != '999') + { + + $q->whereHas('client', function ($query) use ($data){ + $query->where('settings->currency_id', $data['currency_id']); + }); + + } + + $calculated = $this->taskMoneyCalculator($q, $data); + + } + + if($data['calculation'] != 'count' && $data['format'] == 'time') + { + $calculated = $q->get()->map(function ($t){ + return $t->calcDuration(); + }); + } + + match ($data['calculation']) { + 'sum' => $result = $calculated->sum(), + 'avg' => $result = $calculated->avg(), + 'count' => $result = $q->count(), + default => $result = 0, + }; + + return $result; + + } + + private function taskMoneyCalculator($query, $data) + { + + return $query->get() + ->when($data['currency_id'] == '999', function ($collection) { + $collection->map(function ($t) { + return $t->taskCompanyValue(); + }); + }) + ->when($data['currency_id'] != '999', function ($collection) { + + $collection->map(function ($t) { + return $t->taskValue(); + }); + + }); + + } + + public function getInvoicedTasks($data): int|float + { + + $result = 0; + $calculated = collect(); + + $q = Task::query() + ->withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted', 0) + ->whereHas('invoice'); + + if(in_array($data['period'], ['current,previous'])) { + $q->whereBetween('calculated_start_date', [$data['start_date'], $data['end_date']]); + } + + if($data['calculation'] != 'count' && $data['format'] == 'money') { + + if($data['currency_id'] != '999') { + + $q->whereHas('client', function ($query) use ($data) { + $query->where('settings->currency_id', $data['currency_id']); + }); + + } + + $calculated = $this->taskMoneyCalculator($q, $data); + + } + + if($data['calculation'] != 'count' && $data['format'] == 'time') { + $calculated = $q->get()->map(function ($t) { + return $t->calcDuration(); + }); + } + + match ($data['calculation']) { + 'sum' => $result = $calculated->sum(), + 'avg' => $result = $calculated->avg(), + 'count' => $result = $q->count(), + default => $result = 0, + }; + + return $result; + + } } \ No newline at end of file diff --git a/app/Services/Chart/ChartService.php b/app/Services/Chart/ChartService.php index 4bf080f950e6..f22183f255aa 100644 --- a/app/Services/Chart/ChartService.php +++ b/app/Services/Chart/ChartService.php @@ -234,13 +234,13 @@ class ChartService match($data['field']){ 'active_invoices' => $results = $this->getActiveInvoices($data), - 'outstanding_invoices' => $results = 0, - 'completed_payments' => $results = 0, - 'refunded_payments' => $results = 0, - 'active_quotes' => $results = 0, - 'unapproved_quotes' => $results = 0, - 'logged_tasks' => $results = 0, - 'invoiced_tasks' => $results = 0, + 'outstanding_invoices' => $results = $this->getOutstandingInvoices($data), + 'completed_payments' => $results = $this->getCompletedPayments($data), + 'refunded_payments' => $results = $this->getRefundedPayments($data), + 'active_quotes' => $results = $this->getActiveQuotes($data), + 'unapproved_quotes' => $results = $this->getUnapprovedQuotes($data), + 'logged_tasks' => $results = $this->getLoggedTasks($data), + 'invoiced_tasks' => $results = $this->getInvoicedTasks($data), 'paid_tasks' => $results = 0, 'logged_expenses' => $results = 0, 'pending_expenses' => $results = 0, diff --git a/resources/views/portal/ninja2020/plan/trial.blade.php b/resources/views/portal/ninja2020/plan/trial.blade.php index 4be765d1bc3a..f905d182ab20 100644 --- a/resources/views/portal/ninja2020/plan/trial.blade.php +++ b/resources/views/portal/ninja2020/plan/trial.blade.php @@ -1451,16 +1451,31 @@ Ensure the default browser behavior of the `hidden` attribute. @csrf -
+
+ +
+
+
+ +
+
present()->email() }}', address: { line1: document.querySelector('input[name=address1]').content, diff --git a/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php b/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php index 338db31d485d..b8519f3df7c9 100644 --- a/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php +++ b/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php @@ -1450,7 +1450,7 @@ Ensure the default browser behavior of the `hidden` attribute. type="button" class="mx-[auto] max-w-[212px] bg-primary-blue hover:opacity-80 button button-primary bg-primary rounded-sm text-sm transition duration-300 ease-in md:mx-[0]" > - Account Login + {{ ctrans('texts.return_to_app') }}