diff --git a/app/Http/Controllers/ChartController.php b/app/Http/Controllers/ChartController.php index 651ded493fbb..85b387d30202 100644 --- a/app/Http/Controllers/ChartController.php +++ b/app/Http/Controllers/ChartController.php @@ -22,45 +22,6 @@ class ChartController extends BaseController } /** - * @OA\Post( - * path="/api/v1/charts/totals", - * operationId="getChartTotals", - * tags={"charts"}, - * summary="Get chart data", - * description="Get chart data", - * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Parameter(ref="#/components/parameters/index"), - * @OA\Parameter( - * name="rows", - * in="query", - * description="The number of activities to return", - * example="50", - * required=false, - * @OA\Schema( - * type="number", - * format="integer", - * ), - * ), - * @OA\Response( - * response=200, - * description="json dataset of chart data", - * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) * @param ShowChartRequest $request */ public function totals(ShowChartRequest $request) @@ -81,4 +42,28 @@ class ChartController extends BaseController return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200); } + + /** + * @param ShowChartRequest $request + */ + public function totalsV2(ShowChartRequest $request) + { + /** @var \App\Models\User auth()->user() */ + $user = auth()->user(); + $cs = new ChartService($user->company(), $user, $user->isAdmin()); + + return response()->json($cs->totals($request->input('start_date'), $request->input('end_date')), 200); + } + + public function chart_summaryV2(ShowChartRequest $request) + { + + /** @var \App\Models\User auth()->user() */ + $user = auth()->user(); + $cs = new ChartService($user->company(), $user, $user->isAdmin()); + + return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200); + } + + } diff --git a/app/Services/Chart/ChartQueriesLegacy.php b/app/Services/Chart/ChartQueriesLegacy.php new file mode 100644 index 000000000000..2e03bca4abee --- /dev/null +++ b/app/Services/Chart/ChartQueriesLegacy.php @@ -0,0 +1,169 @@ + $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date]); + } + + public function getExpenseChartQuery($start_date, $end_date, $currency_id) + { + return DB::select(DB::raw(' + SELECT + sum(expenses.amount) as total, + expenses.date, + IFNULL(expenses.currency_id, :company_currency) AS currency_id + FROM expenses + WHERE (expenses.date BETWEEN :start_date AND :end_date) + AND expenses.company_id = :company_id + AND expenses.is_deleted = 0 + GROUP BY expenses.date + HAVING currency_id = :currency_id + '), [ + 'company_currency' => $this->company->settings->currency_id, + 'currency_id' => $currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } + + /** + * Payments + */ + public function getPaymentQuery($start_date, $end_date) + { + return DB::select(DB::raw(' + SELECT sum(payments.amount) as amount, + IFNULL(payments.currency_id, :company_currency) as currency_id + FROM payments + WHERE payments.is_deleted = 0 + AND payments.company_id = :company_id + AND (payments.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + '), [ + 'company_currency' => $this->company->settings->currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } + + public function getPaymentChartQuery($start_date, $end_date, $currency_id) + { + return DB::select(DB::raw(' + SELECT + sum(payments.amount - payments.refunded) as total, + payments.date, + IFNULL(payments.currency_id, :company_currency) AS currency_id + FROM payments + WHERE payments.status_id IN (4,5,6) + AND (payments.date BETWEEN :start_date AND :end_date) + AND payments.company_id = :company_id + AND payments.is_deleted = 0 + GROUP BY payments.date + HAVING currency_id = :currency_id + '), [ + 'company_currency' => $this->company->settings->currency_id, + 'currency_id' => $currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } + + /** + * Invoices + */ + public function getOutstandingQuery($start_date, $end_date) + { + return DB::select(DB::raw(" + SELECT + sum(invoices.balance) as amount, + IFNULL(JSON_EXTRACT( settings, '$.currency_id' ), :company_currency) AS currency_id + FROM clients + JOIN invoices + on invoices.client_id = clients.id + WHERE invoices.status_id IN (2,3) + AND invoices.company_id = :company_id + AND invoices.balance > 0 + AND clients.is_deleted = 0 + AND invoices.is_deleted = 0 + AND (invoices.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date]); + } + + public function getRevenueQuery($start_date, $end_date) + { + return DB::select(DB::raw(" + SELECT + sum(invoices.paid_to_date) as paid_to_date, + IFNULL(JSON_EXTRACT( settings, '$.currency_id' ), :company_currency) AS currency_id + FROM clients + JOIN invoices + on invoices.client_id = clients.id + WHERE invoices.status_id IN (3,4) + AND invoices.company_id = :company_id + AND invoices.amount > 0 + AND clients.is_deleted = 0 + AND invoices.is_deleted = 0 + AND (invoices.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date]); + } + + public function getInvoiceChartQuery($start_date, $end_date, $currency_id) + { + return DB::select(DB::raw(" + SELECT + sum(invoices.amount) as total, + invoices.date, + IFNULL(CAST(JSON_EXTRACT( settings, '$.currency_id' ) AS SIGNED), :company_currency) AS currency_id + FROM clients + JOIN invoices + on invoices.client_id = clients.id + WHERE invoices.status_id IN (2,3,4) + AND (invoices.date BETWEEN :start_date AND :end_date) + AND invoices.company_id = :company_id + AND clients.is_deleted = 0 + AND invoices.is_deleted = 0 + GROUP BY invoices.date + HAVING currency_id = :currency_id + "), [ + 'company_currency' => (int) $this->company->settings->currency_id, + 'currency_id' => $currency_id, + 'company_id' => $this->company->id, + 'start_date' => $start_date, + 'end_date' => $end_date, + ]); + } +} diff --git a/app/Services/Chart/ChartServiceLegacy.php b/app/Services/Chart/ChartServiceLegacy.php new file mode 100644 index 000000000000..84e53cec935c --- /dev/null +++ b/app/Services/Chart/ChartServiceLegacy.php @@ -0,0 +1,162 @@ +company = $company; + } + + /** + * Returns an array of currencies that have + * transacted with a company + */ + public function getCurrencyCodes() :array + { + /* Get all the distinct client currencies */ + $currencies = Client::withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted', 0) + ->distinct() + ->pluck('settings->currency_id as id'); + + /* Push the company currency on also */ + $currencies->push((int) $this->company->settings->currency_id); + + /* Add our expense currencies*/ + $expense_currencies = Expense::withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted', 0) + ->distinct() + ->pluck('currency_id as id'); + + /* Merge and filter by unique */ + $currencies = $currencies->merge($expense_currencies)->unique(); + + $cache_currencies = Cache::get('currencies'); + + $filtered_currencies = $cache_currencies->whereIn('id', $currencies)->all(); + + $final_currencies = []; + + foreach ($filtered_currencies as $c_currency) { + $final_currencies[$c_currency['id']] = $c_currency['code']; + } + + return $final_currencies; + } + + /* Chart Data */ + public function chart_summary($start_date, $end_date) :array + { + $currencies = $this->getCurrencyCodes(); + + $data = []; + + foreach ($currencies as $key => $value) { + $data[$key]['invoices'] = $this->getInvoiceChartQuery($start_date, $end_date, $key); + $data[$key]['payments'] = $this->getPaymentChartQuery($start_date, $end_date, $key); + $data[$key]['expenses'] = $this->getExpenseChartQuery($start_date, $end_date, $key); + } + + return $data; + } + + /* Chart Data */ + + /* Totals */ + + public function totals($start_date, $end_date) :array + { + $data = []; + + $data['currencies'] = $this->getCurrencyCodes(); + + foreach ($data['currencies'] as $key => $value) { + $revenue = $this->getRevenue($start_date, $end_date); + $outstanding = $this->getOutstanding($start_date, $end_date); + $expenses = $this->getExpenses($start_date, $end_date); + + $data[$key]['revenue'] = count($revenue) > 0 ? $revenue[array_search($key, array_column($revenue, 'currency_id'))] : new \stdClass; + $data[$key]['outstanding'] = count($outstanding) > 0 ? $outstanding[array_search($key, array_column($outstanding, 'currency_id'))] : new \stdClass; + $data[$key]['expenses'] = count($expenses) > 0 ? $expenses[array_search($key, array_column($expenses, 'currency_id'))] : new \stdClass; + } + + return $data; + } + + public function getRevenue($start_date, $end_date) :array + { + $revenue = $this->getRevenueQuery($start_date, $end_date); + $revenue = $this->addCurrencyCodes($revenue); + + return $revenue; + } + + public function getOutstanding($start_date, $end_date) :array + { + $outstanding = $this->getOutstandingQuery($start_date, $end_date); + $outstanding = $this->addCurrencyCodes($outstanding); + + return $outstanding; + } + + public function getExpenses($start_date, $end_date) :array + { + $expenses = $this->getExpenseQuery($start_date, $end_date); + $expenses = $this->addCurrencyCodes($expenses); + + return $expenses; + } + + /* Totals */ + + /* Helpers */ + + private function addCurrencyCodes($data_set) :array + { + $currencies = Cache::get('currencies'); + + foreach ($data_set as $key => $value) { + $data_set[$key]->currency_id = str_replace('"', '', $value->currency_id); + $data_set[$key]->code = $this->getCode($currencies, $data_set[$key]->currency_id); + } + + return $data_set; + } + + private function getCode($currencies, $currency_id) :string + { + $currency_id = str_replace('"', '', $currency_id); + + $currency = $currencies->filter(function ($item) use ($currency_id) { + return $item->id == $currency_id; + })->first(); + + if ($currency) { + return $currency->code; + } + + return ''; + } +} diff --git a/config/liap.php b/config/liap.php index cbc569b294b0..6a57acf72901 100644 --- a/config/liap.php +++ b/config/liap.php @@ -1,8 +1,17 @@ class_exists(\Modules\Admin\Listeners\Subscription\AppleAutoRenew::class) ? [\Modules\Admin\Listeners\Subscription\AppleAutoRenew::class] : [], SubscriptionRenewed::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleAutoRenew::class) ? [\Modules\Admin\Listeners\Subscription\GoogleAutoRenew::class] : [], - + DidChangeRenewalStatus::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleChangeRenewalStaus::class) ? [\Modules\Admin\Listeners\Subscription\GoogleChangeRenewalStaus::class] : [], + DidFailToRenew::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleFailedToRenew::class) ? [\Modules\Admin\Listeners\Subscription\GoogleFailedToRenew::class] : [], + Refund::class => class_exists(\Modules\Admin\Listeners\Subscription\AppleRefund::class) ? [\Modules\Admin\Listeners\Subscription\AppleRefund::class] : [], + SubscriptionRecovered::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRecovered::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRecovered::class] : [], + SubscriptionCanceled::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionCanceled::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionCanceled::class] : [], + SubscriptionPurchased::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPurchased::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPurchased::class] : [], + SubscriptionRestarted::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRestarted::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRestarted::class] : [], + SubscriptionPaused::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPaused::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionPaused::class] : [], + SubscriptionRevoked::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRevoked::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionRevoked::class] : [], + SubscriptionExpired::class => class_exists(\Modules\Admin\Listeners\Subscription\GoogleSubscriptionExpired::class) ? [\Modules\Admin\Listeners\Subscription\GoogleSubscriptionExpired::class] : [], ], /* diff --git a/routes/api.php b/routes/api.php index 0806d44c6ac5..1ce3e7136379 100644 --- a/routes/api.php +++ b/routes/api.php @@ -142,6 +142,9 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('charts/totals', [ChartController::class, 'totals'])->name('chart.totals'); Route::post('charts/chart_summary', [ChartController::class, 'chart_summary'])->name('chart.chart_summary'); + Route::post('charts/totals_v2', [ChartController::class, 'totalsV2'])->name('chart.totals_v2'); + Route::post('charts/chart_summary_v2', [ChartController::class, 'chart_summaryV2'])->name('chart.chart_summary_v2'); + Route::post('claim_license', [LicenseController::class, 'index'])->name('license.index'); Route::resource('clients', ClientController::class); // name = (clients. index / create / show / update / destroy / edit