mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 09:07:32 -04: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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user