mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Updates for country selector
This commit is contained in:
parent
028eb24fd9
commit
dd1fc3da82
@ -11,8 +11,9 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Chart\ShowChartRequest;
|
||||
use App\Services\Chart\ChartService;
|
||||
use App\Http\Requests\Chart\ShowChartRequest;
|
||||
use App\Http\Requests\Chart\ShowCalculatedFieldRequest;
|
||||
|
||||
class ChartController extends BaseController
|
||||
{
|
||||
@ -65,5 +66,15 @@ class ChartController extends BaseController
|
||||
return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200);
|
||||
}
|
||||
|
||||
public function calculatedField(ShowCalculatedFieldRequest $request)
|
||||
{
|
||||
|
||||
/** @var \App\Models\User auth()->user() */
|
||||
$user = auth()->user();
|
||||
$cs = new ChartService($user->company(), $user, $user->isAdmin());
|
||||
$result = $cs->getCalculatedField($request->all());
|
||||
|
||||
return response()->json($result, 200);
|
||||
|
||||
}
|
||||
}
|
||||
|
78
app/Http/Requests/Chart/ShowCalculatedFieldRequest.php
Normal file
78
app/Http/Requests/Chart/ShowCalculatedFieldRequest.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Chart;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
|
||||
class ShowCalculatedFieldRequest extends Request
|
||||
{
|
||||
use MakesDates;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
/**@var \App\Models\User auth()->user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->isAdmin() || $user->hasPermission('view_dashboard');
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom',
|
||||
'start_date' => 'bail|sometimes|date',
|
||||
'end_date' => 'bail|sometimes|date',
|
||||
'field' => 'required|bail|in:active_invoices, outstanding_invoices, completed_payments, refunded_payments, active_quotes, unapproved_quotes, logged_tasks, invoiced_tasks, paid_tasks, logged_expenses, pending_expenses, invoiced_expenses, invoice_paid_expenses',
|
||||
'calculation' => 'required|bail|in:sum,avg,count',
|
||||
'period' => 'required|bail|in:current,previous,total',
|
||||
'format' => 'sometimes|bail|in:time,money',
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
|
||||
/**@var \App\Models\User auth()->user */
|
||||
$user = auth()->user();
|
||||
|
||||
$input = $this->all();
|
||||
|
||||
if(isset($input['date_range'])) {
|
||||
$dates = $this->calculateStartAndEndDates($input, $user->company());
|
||||
$input['start_date'] = $dates[0];
|
||||
$input['end_date'] = $dates[1];
|
||||
}
|
||||
|
||||
if (! isset($input['start_date'])) {
|
||||
$input['start_date'] = now()->subDays(20)->format('Y-m-d');
|
||||
}
|
||||
|
||||
if (! isset($input['end_date'])) {
|
||||
$input['end_date'] = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
if(isset($input['period']) && $input['period'] == 'previous')
|
||||
{
|
||||
$dates = $this->calculatePreviousPeriodStartAndEndDates($input, $user->company());
|
||||
$input['start_date'] = $dates[0];
|
||||
$input['end_date'] = $dates[1];
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
173
app/Services/Chart/ChartCalculations.php
Normal file
173
app/Services/Chart/ChartCalculations.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Chart;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Quote;
|
||||
|
||||
/**
|
||||
* Class ChartCalculations.
|
||||
*/
|
||||
trait ChartCalculations
|
||||
{
|
||||
|
||||
public function getActiveInvoices($data): int|float
|
||||
{
|
||||
$result = 0;
|
||||
|
||||
$q = Invoice::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [2,3,4]);
|
||||
|
||||
if(in_array($data['period'],['current,previous']))
|
||||
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $q->sum('amount'),
|
||||
'avg' => $result = $q->avg('amount'),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function getOutstandingInvoices($data): int|float
|
||||
{
|
||||
$result = 0;
|
||||
|
||||
$q = Invoice::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [2,3]);
|
||||
|
||||
if(in_array($data['period'],['current,previous']))
|
||||
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $q->sum('balance'),
|
||||
'avg' => $result = $q->avg('balance'),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function getCompletedPayments($data): int|float
|
||||
{
|
||||
$result = 0;
|
||||
|
||||
$q = Payment::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->where('status_id', 4);
|
||||
|
||||
if(in_array($data['period'],['current,previous']))
|
||||
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $q->sum('amount'),
|
||||
'avg' => $result = $q->avg('amount'),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function getRefundedPayments($data): int|float
|
||||
{
|
||||
$result = 0;
|
||||
|
||||
$q = Payment::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [5,6]);
|
||||
|
||||
if(in_array($data['period'],['current,previous']))
|
||||
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $q->sum('refunded'),
|
||||
'avg' => $result = $q->avg('refunded'),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function getActiveQuotes($data): int|float
|
||||
{
|
||||
$result = 0;
|
||||
|
||||
$q = Quote::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [2,3])
|
||||
->where(function ($qq){
|
||||
$qq->where('due_date', '>=', now()->toDateString())->orWhereNull('due_date');
|
||||
});
|
||||
|
||||
if(in_array($data['period'],['current,previous']))
|
||||
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $q->sum('refunded'),
|
||||
'avg' => $result = $q->avg('refunded'),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function getUnapprovedQuotes($data): int|float
|
||||
{
|
||||
$result = 0;
|
||||
|
||||
$q = Quote::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [2])
|
||||
->where(function ($qq){
|
||||
$qq->where('due_date', '>=', now()->toDateString())->orWhereNull('due_date');
|
||||
});
|
||||
|
||||
if(in_array($data['period'],['current,previous']))
|
||||
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $q->sum('refunded'),
|
||||
'avg' => $result = $q->avg('refunded'),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
}
|
@ -14,12 +14,17 @@ namespace App\Services\Chart;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ChartService
|
||||
{
|
||||
use ChartQueries;
|
||||
use ChartCalculations;
|
||||
|
||||
public function __construct(public Company $company, private User $user, private bool $is_admin)
|
||||
{
|
||||
@ -71,7 +76,7 @@ class ChartService
|
||||
|
||||
return $final_currencies;
|
||||
}
|
||||
|
||||
|
||||
/* Chart Data */
|
||||
public function chart_summary($start_date, $end_date): array
|
||||
{
|
||||
@ -207,4 +212,44 @@ class ChartService
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* calculatedField
|
||||
*
|
||||
* @param array $data -
|
||||
*
|
||||
* field - list of fields for calculation
|
||||
* period - current/previous
|
||||
* calculation - sum/count/avg
|
||||
*
|
||||
* date_range - this_month
|
||||
* or
|
||||
* start_date - end_date
|
||||
*/
|
||||
public function getCalculatedField(array $data)
|
||||
{
|
||||
$results = 0;
|
||||
|
||||
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,
|
||||
'paid_tasks' => $results = 0,
|
||||
'logged_expenses' => $results = 0,
|
||||
'pending_expenses' => $results = 0,
|
||||
'invoiced_expenses' => $results = 0,
|
||||
'invoice_paid_expenses' => $results = 0,
|
||||
default => $results = 0,
|
||||
};
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ class SystemHealth
|
||||
'file_permissions' => (string) self::checkFileSystem(),
|
||||
'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(),
|
||||
'api_version' => (string) config('ninja.app_version'),
|
||||
'is_docker' => (bool) config('ninja.is_docker'),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,6 @@ trait MakesDates
|
||||
|
||||
}
|
||||
|
||||
|
||||
return match ($data['date_range']) {
|
||||
EmailStatement::LAST7 => [now()->startOfDay()->subDays(7)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST30 => [now()->startOfDay()->subDays(30)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
@ -162,4 +161,37 @@ trait MakesDates
|
||||
};
|
||||
}
|
||||
|
||||
public function calculatePreviousPeriodStartAndEndDates(array $data, ?Company $company = null): array
|
||||
{
|
||||
|
||||
//override for financial years
|
||||
if($data['date_range'] == 'this_year') {
|
||||
|
||||
$first_month_of_year = $company ? $company?->first_month_of_year : 1;
|
||||
$fin_year_start = now()->createFromDate(now()->year, $first_month_of_year, 1);
|
||||
|
||||
$fin_year_start->subYearNoOverflow();
|
||||
|
||||
if(now()->subYear()->lt($fin_year_start)) {
|
||||
$fin_year_start->subYearNoOverflow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return match ($data['date_range']) {
|
||||
EmailStatement::LAST7 => [now()->startOfDay()->subDays(14)->format('Y-m-d'), now()->subDays(7)->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST30 => [now()->startOfDay()->subDays(60)->format('Y-m-d'), now()->subDays(30)->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST365 => [now()->startOfDay()->subDays(739)->format('Y-m-d'), now()->subDays(365)->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::THIS_MONTH => [now()->startOfDay()->subMonthNoOverflow()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->subMonthNoOverflow()->lastOfMonth()->format('Y-m-d')],
|
||||
EmailStatement::LAST_MONTH => [now()->startOfDay()->subMonthsNoOverflow(2)->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->subMonthNoOverflow()->lastOfMonth()->format('Y-m-d')],
|
||||
EmailStatement::THIS_QUARTER => [now()->startOfDay()->subQuarterNoOverflow()->startOfQuarter()->format('Y-m-d'), now()->startOfDay()->subQuarterNoOverflow()->endOfQuarter()->format('Y-m-d')],
|
||||
EmailStatement::LAST_QUARTER => [now()->startOfDay()->subQuartersNoOverflow(2)->startOfQuarter()->format('Y-m-d'), now()->startOfDay()->subQuartersNoOverflow(2)->endOfQuarter()->format('Y-m-d')],
|
||||
EmailStatement::THIS_YEAR => [$fin_year_start->subYear()->format('Y-m-d'), $fin_year_start->copy()->subDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST_YEAR => [$fin_year_start->subYear(2)->format('Y-m-d'), $fin_year_start->copy()->subYear()->subDay()->format('Y-m-d')],
|
||||
EmailStatement::CUSTOM_RANGE => [$data['start_date'], $data['end_date']],
|
||||
default => [now()->startOfDay()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->lastOfMonth()->format('Y-m-d')],
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -81,7 +81,7 @@
|
||||
name="country_id">
|
||||
<option value="none"></option>
|
||||
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
|
||||
<option
|
||||
<option value="{{ $country->id }}">
|
||||
{{ $country->iso_3166_2 }}
|
||||
({{ $country->name }})
|
||||
</option>
|
||||
|
Loading…
Reference in New Issue
Block a user