From e98264707a5d107f89cbc9ab48f1be8a01d888ac Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 20 Jan 2022 20:09:08 +1100 Subject: [PATCH] Charts for react --- app/Services/Chart/ChartQueries.php | 80 ++++++++++++ app/Services/Chart/ChartService.php | 167 +++++++++++++++++++++++-- tests/Unit/Chart/ChartCurrencyTest.php | 125 ++++++++++++++++++ 3 files changed, 363 insertions(+), 9 deletions(-) create mode 100644 app/Services/Chart/ChartQueries.php create mode 100644 tests/Unit/Chart/ChartCurrencyTest.php diff --git a/app/Services/Chart/ChartQueries.php b/app/Services/Chart/ChartQueries.php new file mode 100644 index 000000000000..5cfab1b692ea --- /dev/null +++ b/app/Services/Chart/ChartQueries.php @@ -0,0 +1,80 @@ + 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_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] ); + + } + + public function getOutstandingQuery($start_date, $end_date) + { + + return DB::select( DB::raw(" + SELECT + sum(invoices.balance) as balance, + JSON_EXTRACT( settings, '$.currency_id' ) 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.due_date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] ); + + } + + public function getExpenseQuery($start_date, $end_date) + { + + return DB::select( DB::raw(" + SELECT sum(expenses.amount) as amount, + expenses.currency_id as currency_id + FROM expenses + WHERE expenses.is_deleted = 0 + AND expenses.company_id = :company_id + AND (expenses.date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] ); + + } + + +} diff --git a/app/Services/Chart/ChartService.php b/app/Services/Chart/ChartService.php index f1cc714eec3b..7f9a61f3e048 100644 --- a/app/Services/Chart/ChartService.php +++ b/app/Services/Chart/ChartService.php @@ -11,12 +11,17 @@ namespace App\Services\Chart; +use App\Models\Client; use App\Models\Company; use App\Models\Expense; use App\Models\Payment; +use App\Services\Chart\ChartQueries; +use Illuminate\Support\Facades\Cache; class ChartService { + use ChartQueries; + public Company $company; public function __construct(Company $company) @@ -24,24 +29,168 @@ class ChartService $this->company = $company; } - public function getCurrencyCodes() + /** + * Returns an array of currencies that have + * transacted with a company + */ + public function getCurrencyCodes() :array { - $currencies = Payment::withTrashed() + // $currencies = Payment::withTrashed() + // ->where('company_id', $this->company->id) + // ->where('is_deleted', 0) + // ->distinct() + // ->get(['currency_id']); + + /* 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() ->get(['currency_id']); - $expense_currencies = Expense::withTrashed() - ->where('company_id', $this->company->id) - ->where('is_deleted', 0) - ->distinct() - ->get(['expense_currency_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; + + } + + + + public function totals($start_date, $end_date) + { + $data = []; + + $data['revenue'] = $this->getRevenue($start_date, $end_date); + $data['outstanding'] = $this->getOutstanding($start_date, $end_date); + $data['expenses'] = $this->getExpenses($start_date, $end_date); + + return $data; + } + + public function oustanding($start_date, $end_date) + { + + $company_currency = (int) $this->company->settings->currency_id; + + $results = \DB::select( \DB::raw(" + SELECT + sum(invoices.balance) as balance, + JSON_EXTRACT( settings, '$.currency_id' ) 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.due_date BETWEEN :start_date AND :end_date) + GROUP BY currency_id + "), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] ); + + //return $results; + + //the output here will most likely contain a currency_id = null value - we need to merge this value with the company currency + + } + + private function getRevenue($start_date, $end_date) + { + $revenue = $this->getRevenueQuery($start_date, $end_date); + $revenue = $this->parseTotals($revenue); + $revenue = $this->addCountryCodes($revenue); + + return $revenue; + } + + private function getOutstanding($start_date, $end_date) + { + $outstanding = $this->getOutstandingQuery($start_date, $end_date); + $outstanding = $this->parseTotals($outstanding); + $outstanding = $this->addCountryCodes($outstanding); + + return $outstanding; + } + + private function getExpenses($start_date, $end_date) + { + $expenses = $this->getExpenseQuery($start_date, $end_date); + $expenses = $this->parseTotals($expenses); + $expenses = $this->addCountryCodes($expenses); + + return $expenses; + } + + private function parseTotals($data_set) + { + /* Find the key where the company currency amount lives*/ + $c_key = array_search($this->company->id , array_column($data_set, 'currency_id')); + + if(!$c_key) + return $data_set; + + /* Find the key where null currency_id lives */ + $key = array_search(null , array_column($data_set, 'currency_id')); + + if(!$key) + return $data_set; + + $null_currency_amount = $data_set[$key]['amount']; + unset($data_set[$key]); + + $data_set[$c_key]['amount'] += $null_currency_amount; + + return $data_set; + + } + + private function addCountryCodes($data_set) + { + + $currencies = Cache::get('currencies'); + + foreach($data_set as $key => $value) + { + $data_set[$key]['code'] = $this->getCode($currencies, $value); + } + + return $data_set; + + } + + private function getCode($currencies, $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/tests/Unit/Chart/ChartCurrencyTest.php b/tests/Unit/Chart/ChartCurrencyTest.php new file mode 100644 index 000000000000..afd3f1ab8dd0 --- /dev/null +++ b/tests/Unit/Chart/ChartCurrencyTest.php @@ -0,0 +1,125 @@ +makeTestData(); + } + + public function testClientServiceDataSetBuild() + { + + $haystack = [ + [ + 'currency_id' => null, + 'amount' => 10 + ], + [ + 'currency_id' => 1, + 'amount' => 11 + ], + [ + 'currency_id' => 2, + 'amount' => 12 + ], + [ + 'currency_id' => 3, + 'amount' => 13 + ], + ]; + + $cs = new ChartService($this->company); + + nlog($cs->totals(now()->subYears(10), now())); + + $this->assertTrue(is_array($cs->totals(now()->subYears(10), now()))); + + } + + /* coalesces the company currency with the null currencies */ + public function testFindNullValueinArray() + { + + $haystack = [ + [ + 'currency_id' => null, + 'amount' => 10 + ], + [ + 'currency_id' => 1, + 'amount' => 11 + ], + [ + 'currency_id' => 2, + 'amount' => 12 + ], + [ + 'currency_id' => 3, + 'amount' => 13 + ], + ]; + + $company_currency_id = 1; + + $c_key = array_search($company_currency_id , array_column($haystack, 'currency_id')); + + $this->assertNotEquals($c_key, 2); + $this->assertEquals($c_key, 1); + + $key = array_search(null , array_column($haystack, 'currency_id')); + + $this->assertNotEquals($key, 39); + $this->assertEquals($key, 0); + + $null_currency_amount = $haystack[$key]['amount']; + + unset($haystack[$key]); + + $haystack[$c_key]['amount'] += $null_currency_amount; + + $this->assertEquals($haystack[$c_key]['amount'], 21); + + } + + + public function testCollectionMerging() + { + $currencies = collect([1,2,3,4,5,6]); + + $expense_currencies = collect([4,5,6,7,8]); + + $currencies = $currencies->merge($expense_currencies); + + $this->assertEquals($currencies->count(), 11); + + $currencies = $currencies->unique(); + + $this->assertEquals($currencies->count(), 8); + + } + + +} \ No newline at end of file