2016-08-15 05:46:47 +02:00
< ? php namespace App\Ninja\Repositories ;
2016-09-11 16:30:23 +02:00
use stdClass ;
2016-08-15 05:46:47 +02:00
use DB ;
use App\Models\Activity ;
2016-09-11 16:30:23 +02:00
use App\Models\Invoice ;
use App\Models\Task ;
use DateInterval ;
use DatePeriod ;
2016-08-15 05:46:47 +02:00
class DashboardRepository
{
2016-09-11 16:30:23 +02:00
/**
* @ param $groupBy
* @ param $startDate
* @ param $endDate
* @ return array
*/
public function chartData ( $account , $groupBy , $startDate , $endDate , $currencyId , $includeExpenses )
{
$accountId = $account -> id ;
$startDate = date_create ( $startDate );
$endDate = date_create ( $endDate );
$groupBy = strtoupper ( $groupBy );
if ( $groupBy == 'DAY' ) {
$groupBy = 'DAYOFYEAR' ;
}
$datasets = [];
$labels = [];
$totals = new stdClass ;
$entitTypes = [ ENTITY_INVOICE , ENTITY_PAYMENT ];
if ( $includeExpenses ) {
$entitTypes [] = ENTITY_EXPENSE ;
}
foreach ( $entitTypes as $entityType ) {
$data = [];
$count = 0 ;
2016-11-02 08:43:57 +01:00
$balance = 0 ;
2016-09-11 16:30:23 +02:00
$records = $this -> rawChartData ( $entityType , $account , $groupBy , $startDate , $endDate , $currencyId );
2016-11-02 08:43:57 +01:00
array_map ( function ( $item ) use ( & $data , & $count , & $balance , $groupBy ) {
2016-09-11 16:30:23 +02:00
$data [ $item -> $groupBy ] = $item -> total ;
$count += $item -> count ;
2016-11-02 08:43:57 +01:00
$balance += isset ( $item -> balance ) ? $item -> balance : 0 ;
}, $records );
2016-11-16 11:41:32 +01:00
2016-09-11 16:30:23 +02:00
$padding = $groupBy == 'DAYOFYEAR' ? 'day' : ( $groupBy == 'WEEK' ? 'week' : 'month' );
$endDate -> modify ( '+1 ' . $padding );
$interval = new DateInterval ( 'P1' . substr ( $groupBy , 0 , 1 ));
$period = new DatePeriod ( $startDate , $interval , $endDate );
$endDate -> modify ( '-1 ' . $padding );
$records = [];
foreach ( $period as $d ) {
$dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ( $groupBy == 'WEEK' ? 'W' : 'n' );
// MySQL returns 1-366 for DAYOFYEAR, whereas PHP returns 0-365
$date = $groupBy == 'DAYOFYEAR' ? $d -> format ( 'Y' ) . ( $d -> format ( $dateFormat ) + 1 ) : $d -> format ( 'Y' . $dateFormat );
$records [] = isset ( $data [ $date ]) ? $data [ $date ] : 0 ;
if ( $entityType == ENTITY_INVOICE ) {
$labels [] = $d -> format ( 'r' );
}
}
if ( $entityType == ENTITY_INVOICE ) {
$color = '51,122,183' ;
} elseif ( $entityType == ENTITY_PAYMENT ) {
$color = '54,193,87' ;
} elseif ( $entityType == ENTITY_EXPENSE ) {
$color = '128,128,128' ;
}
$record = new stdClass ;
$record -> data = $records ;
$record -> label = trans ( " texts. { $entityType } s " );
$record -> lineTension = 0 ;
$record -> borderWidth = 4 ;
$record -> borderColor = " rgba( { $color } , 1) " ;
$record -> backgroundColor = " rgba( { $color } , 0.05) " ;
$datasets [] = $record ;
if ( $entityType == ENTITY_INVOICE ) {
$totals -> invoices = array_sum ( $data );
$totals -> average = $count ? round ( $totals -> invoices / $count , 2 ) : 0 ;
2016-11-02 08:43:57 +01:00
$totals -> balance = $balance ;
2016-09-11 16:30:23 +02:00
} elseif ( $entityType == ENTITY_PAYMENT ) {
$totals -> revenue = array_sum ( $data );
} elseif ( $entityType == ENTITY_EXPENSE ) {
//$totals->profit = $totals->revenue - array_sum($data);
$totals -> expenses = array_sum ( $data );
}
}
$data = new stdClass ;
$data -> labels = $labels ;
$data -> datasets = $datasets ;
$response = new stdClass ;
$response -> data = $data ;
$response -> totals = $totals ;
return $response ;
}
private function rawChartData ( $entityType , $account , $groupBy , $startDate , $endDate , $currencyId )
{
2016-11-02 08:43:57 +01:00
if ( ! in_array ( $groupBy , [ 'DAYOFYEAR' , 'WEEK' , 'MONTH' ])) {
return [];
}
2016-09-11 16:30:23 +02:00
$accountId = $account -> id ;
$currencyId = intval ( $currencyId );
$timeframe = 'concat(YEAR(' . $entityType . '_date), ' . $groupBy . '(' . $entityType . '_date))' ;
$records = DB :: table ( $entityType . 's' )
2016-09-11 19:05:59 +02:00
-> leftJoin ( 'clients' , 'clients.id' , '=' , $entityType . 's.client_id' )
-> whereRaw ( '(clients.id IS NULL OR clients.is_deleted = 0)' )
2016-09-11 16:30:23 +02:00
-> where ( $entityType . 's.account_id' , '=' , $accountId )
-> where ( $entityType . 's.is_deleted' , '=' , false )
-> where ( $entityType . 's.' . $entityType . '_date' , '>=' , $startDate -> format ( 'Y-m-d' ))
-> where ( $entityType . 's.' . $entityType . '_date' , '<=' , $endDate -> format ( 'Y-m-d' ))
-> groupBy ( $groupBy );
if ( $entityType == ENTITY_EXPENSE ) {
$records -> where ( 'expenses.expense_currency_id' , '=' , $currencyId );
} elseif ( $currencyId == $account -> getCurrencyId ()) {
$records -> whereRaw ( " (clients.currency_id = { $currencyId } or coalesce(clients.currency_id, 0) = 0) " );
} else {
$records -> where ( 'clients.currency_id' , '=' , $currencyId );
}
if ( $entityType == ENTITY_INVOICE ) {
2016-11-02 08:43:57 +01:00
$records -> select ( DB :: raw ( 'sum(invoices.amount) as total, sum(invoices.balance) as balance, count(invoices.id) as count, ' . $timeframe . ' as ' . $groupBy ))
2016-09-11 16:30:23 +02:00
-> where ( 'invoice_type_id' , '=' , INVOICE_TYPE_STANDARD )
-> where ( 'is_recurring' , '=' , false );
} elseif ( $entityType == ENTITY_PAYMENT ) {
$records -> select ( DB :: raw ( 'sum(payments.amount - payments.refunded) as total, count(payments.id) as count, ' . $timeframe . ' as ' . $groupBy ))
-> join ( 'invoices' , 'invoices.id' , '=' , 'payments.invoice_id' )
-> where ( 'invoices.is_deleted' , '=' , false )
-> whereNotIn ( 'payment_status_id' , [ PAYMENT_STATUS_VOIDED , PAYMENT_STATUS_FAILED ]);
} elseif ( $entityType == ENTITY_EXPENSE ) {
2016-10-27 16:26:42 +02:00
$records -> select ( DB :: raw ( 'sum(expenses.amount + (expenses.amount * expenses.tax_rate1 / 100) + (expenses.amount * expenses.tax_rate2 / 100)) as total, count(expenses.id) as count, ' . $timeframe . ' as ' . $groupBy ));
2016-09-11 16:30:23 +02:00
}
2016-11-02 08:43:57 +01:00
return $records -> get ();
2016-09-11 16:30:23 +02:00
}
2016-08-15 05:46:47 +02:00
public function totals ( $accountId , $userId , $viewAll )
{
// total_income, billed_clients, invoice_sent and active_clients
$select = DB :: raw (
'COUNT(DISTINCT CASE WHEN ' . DB :: getQueryGrammar () -> wrap ( 'invoices.id' , true ) . ' IS NOT NULL THEN ' . DB :: getQueryGrammar () -> wrap ( 'clients.id' , true ) . ' ELSE null END ) billed_clients ,
SUM ( CASE WHEN '.DB::getQueryGrammar()->wrap(' invoices . invoice_status_id ', true).' >= '.INVOICE_STATUS_SENT.' THEN 1 ELSE 0 END ) invoices_sent ,
COUNT ( DISTINCT '.DB::getQueryGrammar()->wrap(' clients . id ', true).' ) active_clients '
);
$metrics = DB :: table ( 'accounts' )
-> select ( $select )
-> leftJoin ( 'clients' , 'accounts.id' , '=' , 'clients.account_id' )
-> leftJoin ( 'invoices' , 'clients.id' , '=' , 'invoices.client_id' )
-> where ( 'accounts.id' , '=' , $accountId )
-> where ( 'clients.is_deleted' , '=' , false )
-> where ( 'invoices.is_deleted' , '=' , false )
-> where ( 'invoices.is_recurring' , '=' , false )
-> where ( 'invoices.invoice_type_id' , '=' , INVOICE_TYPE_STANDARD );
if ( ! $viewAll ){
$metrics = $metrics -> where ( function ( $query ) use ( $userId ){
$query -> where ( 'invoices.user_id' , '=' , $userId );
$query -> orwhere ( function ( $query ) use ( $userId ){
$query -> where ( 'invoices.user_id' , '=' , null );
$query -> where ( 'clients.user_id' , '=' , $userId );
});
});
}
return $metrics -> groupBy ( 'accounts.id' ) -> first ();
}
2016-11-16 11:41:32 +01:00
public function paidToDate ( $account , $userId , $viewAll , $startDate = false )
2016-08-15 05:46:47 +02:00
{
2016-10-31 10:11:30 +01:00
$accountId = $account -> id ;
2016-08-15 05:46:47 +02:00
$select = DB :: raw (
2016-10-31 10:11:30 +01:00
'SUM(' . DB :: getQueryGrammar () -> wrap ( 'payments.amount' , true ) . ' - ' . DB :: getQueryGrammar () -> wrap ( 'payments.refunded' , true ) . ') as value,'
2016-08-15 05:46:47 +02:00
. DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' as currency_id'
);
2016-10-31 10:11:30 +01:00
$paidToDate = DB :: table ( 'payments' )
2016-08-15 05:46:47 +02:00
-> select ( $select )
2016-10-31 10:11:30 +01:00
-> leftJoin ( 'invoices' , 'invoices.id' , '=' , 'payments.invoice_id' )
-> leftJoin ( 'clients' , 'clients.id' , '=' , 'invoices.client_id' )
-> where ( 'payments.account_id' , '=' , $accountId )
-> where ( 'clients.is_deleted' , '=' , false )
-> where ( 'invoices.is_deleted' , '=' , false )
2016-11-16 11:41:32 +01:00
-> where ( 'payments.is_deleted' , '=' , false )
2016-10-31 10:11:30 +01:00
-> whereNotIn ( 'payments.payment_status_id' , [ PAYMENT_STATUS_VOIDED , PAYMENT_STATUS_FAILED ]);
2016-08-15 05:46:47 +02:00
if ( ! $viewAll ){
2016-10-31 10:11:30 +01:00
$paidToDate -> where ( 'invoices.user_id' , '=' , $userId );
2016-08-15 05:46:47 +02:00
}
2016-11-16 11:41:32 +01:00
if ( $startDate ) {
$paidToDate -> where ( 'payments.payment_date' , '>=' , $startDate );
} elseif ( $account -> financial_year_start ) {
2017-02-02 13:55:51 +01:00
$paidToDate -> where ( 'payments.payment_date' , '>=' , $account -> financialYearStart ());
2016-10-31 10:11:30 +01:00
}
return $paidToDate -> groupBy ( 'payments.account_id' )
-> groupBy ( DB :: raw ( 'CASE WHEN ' . DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' IS NULL THEN ' . ( $account -> currency_id ? : DEFAULT_CURRENCY ) . ' ELSE ' . DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' END' ))
2016-08-15 05:46:47 +02:00
-> get ();
}
2016-10-31 10:11:30 +01:00
public function averages ( $account , $userId , $viewAll )
2016-08-15 05:46:47 +02:00
{
2016-10-31 10:11:30 +01:00
$accountId = $account -> id ;
2016-08-15 05:46:47 +02:00
$select = DB :: raw (
'AVG(' . DB :: getQueryGrammar () -> wrap ( 'invoices.amount' , true ) . ') as invoice_avg, '
. DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' as currency_id'
);
$averageInvoice = DB :: table ( 'accounts' )
-> select ( $select )
-> leftJoin ( 'clients' , 'accounts.id' , '=' , 'clients.account_id' )
-> leftJoin ( 'invoices' , 'clients.id' , '=' , 'invoices.client_id' )
-> where ( 'accounts.id' , '=' , $accountId )
-> where ( 'clients.is_deleted' , '=' , false )
-> where ( 'invoices.is_deleted' , '=' , false )
-> where ( 'invoices.invoice_type_id' , '=' , INVOICE_TYPE_STANDARD )
-> where ( 'invoices.is_recurring' , '=' , false );
if ( ! $viewAll ){
2016-10-31 10:11:30 +01:00
$averageInvoice -> where ( 'invoices.user_id' , '=' , $userId );
}
if ( $account -> financial_year_start ) {
2017-02-02 13:55:51 +01:00
$averageInvoice -> where ( 'invoices.invoice_date' , '>=' , $account -> financialYearStart ());
2016-08-15 05:46:47 +02:00
}
return $averageInvoice -> groupBy ( 'accounts.id' )
-> groupBy ( DB :: raw ( 'CASE WHEN ' . DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' IS NULL THEN CASE WHEN ' . DB :: getQueryGrammar () -> wrap ( 'accounts.currency_id' , true ) . ' IS NULL THEN 1 ELSE ' . DB :: getQueryGrammar () -> wrap ( 'accounts.currency_id' , true ) . ' END ELSE ' . DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' END' ))
-> get ();
}
public function balances ( $accountId , $userId , $viewAll )
{
$select = DB :: raw (
'SUM(' . DB :: getQueryGrammar () -> wrap ( 'clients.balance' , true ) . ') as value, '
. DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' as currency_id'
);
$balances = DB :: table ( 'accounts' )
-> select ( $select )
-> leftJoin ( 'clients' , 'accounts.id' , '=' , 'clients.account_id' )
-> where ( 'accounts.id' , '=' , $accountId )
-> where ( 'clients.is_deleted' , '=' , false )
-> groupBy ( 'accounts.id' )
-> groupBy ( DB :: raw ( 'CASE WHEN ' . DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' IS NULL THEN CASE WHEN ' . DB :: getQueryGrammar () -> wrap ( 'accounts.currency_id' , true ) . ' IS NULL THEN 1 ELSE ' . DB :: getQueryGrammar () -> wrap ( 'accounts.currency_id' , true ) . ' END ELSE ' . DB :: getQueryGrammar () -> wrap ( 'clients.currency_id' , true ) . ' END' ));
if ( ! $viewAll ) {
$balances -> where ( 'clients.user_id' , '=' , $userId );
}
return $balances -> get ();
}
public function activities ( $accountId , $userId , $viewAll )
{
$activities = Activity :: where ( 'activities.account_id' , '=' , $accountId )
-> where ( 'activities.activity_type_id' , '>' , 0 );
if ( ! $viewAll ){
$activities = $activities -> where ( 'activities.user_id' , '=' , $userId );
}
return $activities -> orderBy ( 'activities.created_at' , 'desc' )
2016-10-30 07:50:58 +01:00
-> with ( 'client.contacts' , 'user' , 'invoice' , 'payment' , 'credit' , 'account' , 'task' , 'expense' , 'contact' )
2016-08-15 05:46:47 +02:00
-> take ( 50 )
-> get ();
}
public function pastDue ( $accountId , $userId , $viewAll )
{
$pastDue = DB :: table ( 'invoices' )
-> leftJoin ( 'clients' , 'clients.id' , '=' , 'invoices.client_id' )
-> leftJoin ( 'contacts' , 'contacts.client_id' , '=' , 'clients.id' )
-> where ( 'invoices.account_id' , '=' , $accountId )
-> where ( 'clients.deleted_at' , '=' , null )
-> where ( 'contacts.deleted_at' , '=' , null )
-> where ( 'invoices.is_recurring' , '=' , false )
-> where ( 'invoices.quote_invoice_id' , '=' , null )
-> where ( 'invoices.balance' , '>' , 0 )
-> where ( 'invoices.is_deleted' , '=' , false )
-> where ( 'invoices.deleted_at' , '=' , null )
-> where ( 'contacts.is_primary' , '=' , true )
-> where ( 'invoices.due_date' , '<' , date ( 'Y-m-d' ));
if ( ! $viewAll ){
$pastDue = $pastDue -> where ( 'invoices.user_id' , '=' , $userId );
}
return $pastDue -> select ([ 'invoices.due_date' , 'invoices.balance' , 'invoices.public_id' , 'invoices.invoice_number' , 'clients.name as client_name' , 'contacts.email' , 'contacts.first_name' , 'contacts.last_name' , 'clients.currency_id' , 'clients.public_id as client_public_id' , 'clients.user_id as client_user_id' , 'invoice_type_id' ])
-> orderBy ( 'invoices.due_date' , 'asc' )
-> take ( 50 )
-> get ();
}
public function upcoming ( $accountId , $userId , $viewAll )
{
$upcoming = DB :: table ( 'invoices' )
-> leftJoin ( 'clients' , 'clients.id' , '=' , 'invoices.client_id' )
-> leftJoin ( 'contacts' , 'contacts.client_id' , '=' , 'clients.id' )
-> where ( 'invoices.account_id' , '=' , $accountId )
-> where ( 'clients.deleted_at' , '=' , null )
-> where ( 'contacts.deleted_at' , '=' , null )
-> where ( 'invoices.deleted_at' , '=' , null )
-> where ( 'invoices.is_recurring' , '=' , false )
-> where ( 'invoices.quote_invoice_id' , '=' , null )
-> where ( 'invoices.balance' , '>' , 0 )
-> where ( 'invoices.is_deleted' , '=' , false )
-> where ( 'contacts.is_primary' , '=' , true )
-> where ( 'invoices.due_date' , '>=' , date ( 'Y-m-d' ))
-> orderBy ( 'invoices.due_date' , 'asc' );
if ( ! $viewAll ){
$upcoming = $upcoming -> where ( 'invoices.user_id' , '=' , $userId );
}
return $upcoming -> take ( 50 )
-> select ([ 'invoices.due_date' , 'invoices.balance' , 'invoices.public_id' , 'invoices.invoice_number' , 'clients.name as client_name' , 'contacts.email' , 'contacts.first_name' , 'contacts.last_name' , 'clients.currency_id' , 'clients.public_id as client_public_id' , 'clients.user_id as client_user_id' , 'invoice_type_id' ])
-> get ();
}
public function payments ( $accountId , $userId , $viewAll )
{
$payments = DB :: table ( 'payments' )
-> leftJoin ( 'clients' , 'clients.id' , '=' , 'payments.client_id' )
-> leftJoin ( 'contacts' , 'contacts.client_id' , '=' , 'clients.id' )
-> leftJoin ( 'invoices' , 'invoices.id' , '=' , 'payments.invoice_id' )
-> where ( 'payments.account_id' , '=' , $accountId )
-> where ( 'payments.is_deleted' , '=' , false )
-> where ( 'invoices.is_deleted' , '=' , false )
-> where ( 'clients.is_deleted' , '=' , false )
-> where ( 'contacts.deleted_at' , '=' , null )
2016-09-11 19:05:59 +02:00
-> where ( 'contacts.is_primary' , '=' , true )
-> whereNotIn ( 'payments.payment_status_id' , [ PAYMENT_STATUS_VOIDED , PAYMENT_STATUS_FAILED ]);
2016-08-15 05:46:47 +02:00
if ( ! $viewAll ){
$payments = $payments -> where ( 'payments.user_id' , '=' , $userId );
}
2016-09-11 19:05:59 +02:00
return $payments -> select ([ 'payments.payment_date' , DB :: raw ( '(payments.amount - payments.refunded) as amount' ), 'invoices.public_id' , 'invoices.invoice_number' , 'clients.name as client_name' , 'contacts.email' , 'contacts.first_name' , 'contacts.last_name' , 'clients.currency_id' , 'clients.public_id as client_public_id' , 'clients.user_id as client_user_id' ])
2016-08-15 05:46:47 +02:00
-> orderBy ( 'payments.payment_date' , 'desc' )
-> take ( 50 )
-> get ();
}
2016-09-11 16:30:23 +02:00
public function expenses ( $accountId , $userId , $viewAll )
{
2016-10-27 16:26:42 +02:00
$amountField = DB :: getQueryGrammar () -> wrap ( 'expenses.amount' , true );
$taxRate1Field = DB :: getQueryGrammar () -> wrap ( 'expenses.tax_rate1' , true );
$taxRate2Field = DB :: getQueryGrammar () -> wrap ( 'expenses.tax_rate2' , true );
2016-09-11 16:30:23 +02:00
$select = DB :: raw (
2016-10-27 16:26:42 +02:00
" SUM( { $amountField } + ( { $amountField } * { $taxRate1Field } / 100) + ( { $amountField } * { $taxRate2Field } / 100)) as value, "
2016-09-11 16:30:23 +02:00
. DB :: getQueryGrammar () -> wrap ( 'expenses.expense_currency_id' , true ) . ' as currency_id'
);
$paidToDate = DB :: table ( 'accounts' )
-> select ( $select )
-> leftJoin ( 'expenses' , 'accounts.id' , '=' , 'expenses.account_id' )
-> where ( 'accounts.id' , '=' , $accountId )
-> where ( 'expenses.is_deleted' , '=' , false );
if ( ! $viewAll ){
$paidToDate = $paidToDate -> where ( 'expenses.user_id' , '=' , $userId );
}
return $paidToDate -> groupBy ( 'accounts.id' )
-> groupBy ( 'expenses.expense_currency_id' )
-> get ();
}
public function tasks ( $accountId , $userId , $viewAll )
{
return Task :: scope ()
-> withArchived ()
-> whereIsRunning ( true )
-> get ();
}
2016-08-15 05:46:47 +02:00
}