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
-