2015-03-17 02:30:56 +01:00
< ? php namespace App\Http\Controllers ;
use Auth ;
2015-06-19 01:28:43 +02:00
use Config ;
2015-03-17 02:30:56 +01:00
use Input ;
2015-04-01 21:57:02 +02:00
use Utils ;
use DB ;
use DateInterval ;
use DatePeriod ;
use Session ;
use View ;
2015-03-17 02:30:56 +01:00
2015-04-01 21:57:02 +02:00
use App\Models\Account ;
class ReportController extends BaseController
2015-03-16 22:45:25 +01:00
{
public function d3 ()
{
$message = '' ;
2015-05-19 21:14:00 +02:00
$fileName = storage_path () . '/dataviz_sample.txt' ;
2015-03-16 22:45:25 +01:00
if ( Auth :: user () -> account -> isPro ()) {
2015-08-11 16:38:36 +02:00
$account = Account :: where ( 'id' , '=' , Auth :: user () -> account -> id )
-> with ([ 'clients.invoices.invoice_items' , 'clients.contacts' ])
-> first ();
2015-03-16 22:45:25 +01:00
$account = $account -> hideFieldsForViz ();
$clients = $account -> clients -> toJson ();
2015-05-19 21:14:00 +02:00
} elseif ( file_exists ( $fileName )) {
$clients = file_get_contents ( $fileName );
2015-03-16 22:45:25 +01:00
$message = trans ( 'texts.sample_data' );
} else {
$clients = '[]' ;
}
$data = [
'feature' => ACCOUNT_DATA_VISUALIZATIONS ,
'clients' => $clients ,
'message' => $message ,
];
return View :: make ( 'reports.d3' , $data );
}
2015-04-30 19:54:19 +02:00
public function showReports ()
2015-03-16 22:45:25 +01:00
{
2015-04-30 19:54:19 +02:00
$action = Input :: get ( 'action' );
2015-03-16 22:45:25 +01:00
if ( Input :: all ()) {
$groupBy = Input :: get ( 'group_by' );
$chartType = Input :: get ( 'chart_type' );
2015-04-30 19:54:19 +02:00
$reportType = Input :: get ( 'report_type' );
2015-03-16 22:45:25 +01:00
$startDate = Utils :: toSqlDate ( Input :: get ( 'start_date' ), false );
$endDate = Utils :: toSqlDate ( Input :: get ( 'end_date' ), false );
2015-04-30 19:54:19 +02:00
$enableReport = Input :: get ( 'enable_report' ) ? true : false ;
$enableChart = Input :: get ( 'enable_chart' ) ? true : false ;
2015-03-16 22:45:25 +01:00
} else {
$groupBy = 'MONTH' ;
$chartType = 'Bar' ;
2015-04-30 19:54:19 +02:00
$reportType = '' ;
2015-03-16 22:45:25 +01:00
$startDate = Utils :: today ( false ) -> modify ( '-3 month' );
$endDate = Utils :: today ( false );
2015-04-30 19:54:19 +02:00
$enableReport = true ;
$enableChart = true ;
2015-03-16 22:45:25 +01:00
}
$datasets = [];
$labels = [];
$maxTotals = 0 ;
$width = 10 ;
2015-04-30 19:54:19 +02:00
$displayData = [];
$exportData = [];
$reportTotals = [
'amount' => [],
'balance' => [],
'paid' => []
];
2015-05-10 10:45:03 +02:00
if ( $reportType ) {
$columns = [ 'client' , 'amount' , 'paid' , 'balance' ];
} else {
$columns = [ 'client' , 'invoice_number' , 'invoice_date' , 'amount' , 'paid' , 'balance' ];
}
2015-04-30 19:54:19 +02:00
2015-03-16 22:45:25 +01:00
if ( Auth :: user () -> account -> isPro ()) {
2015-04-30 19:54:19 +02:00
if ( $enableReport ) {
$query = DB :: table ( 'invoices' )
-> join ( 'clients' , 'clients.id' , '=' , 'invoices.client_id' )
-> join ( 'contacts' , 'contacts.client_id' , '=' , 'clients.id' )
-> where ( 'invoices.account_id' , '=' , Auth :: user () -> account_id )
-> where ( 'invoices.is_deleted' , '=' , false )
-> where ( 'clients.is_deleted' , '=' , false )
-> where ( 'contacts.deleted_at' , '=' , null )
-> where ( 'invoices.invoice_date' , '>=' , $startDate -> format ( 'Y-m-d' ))
-> where ( 'invoices.invoice_date' , '<=' , $endDate -> format ( 'Y-m-d' ))
-> where ( 'invoices.is_quote' , '=' , false )
-> where ( 'invoices.is_recurring' , '=' , false )
-> where ( 'contacts.is_primary' , '=' , true );
$select = [ 'clients.currency_id' , 'contacts.first_name' , 'contacts.last_name' , 'contacts.email' , 'clients.name as client_name' , 'clients.public_id as client_public_id' , 'invoices.public_id as invoice_public_id' ];
if ( $reportType ) {
$query -> groupBy ( 'clients.id' );
array_push ( $select , DB :: raw ( 'sum(invoices.amount) amount' ), DB :: raw ( 'sum(invoices.balance) balance' ), DB :: raw ( 'sum(invoices.amount - invoices.balance) paid' ));
} else {
array_push ( $select , 'invoices.invoice_number' , 'invoices.amount' , 'invoices.balance' , 'invoices.invoice_date' , DB :: raw ( '(invoices.amount - invoices.balance) paid' ));
$query -> orderBy ( 'invoices.id' );
2015-03-16 22:45:25 +01:00
}
2015-04-30 19:54:19 +02:00
$query -> select ( $select );
$data = $query -> get ();
2015-03-16 22:45:25 +01:00
2015-04-30 19:54:19 +02:00
foreach ( $data as $record ) {
// web display data
$displayRow = [ link_to ( '/clients/' . $record -> client_public_id , Utils :: getClientDisplayName ( $record ))];
if ( ! $reportType ) {
array_push ( $displayRow ,
link_to ( '/invoices/' . $record -> invoice_public_id , $record -> invoice_number ),
Utils :: fromSqlDate ( $record -> invoice_date , true )
);
}
array_push ( $displayRow ,
Utils :: formatMoney ( $record -> amount , $record -> currency_id ),
Utils :: formatMoney ( $record -> paid , $record -> currency_id ),
Utils :: formatMoney ( $record -> balance , $record -> currency_id )
);
2015-03-16 22:45:25 +01:00
2015-04-30 19:54:19 +02:00
// export data
$exportRow = [ trans ( 'texts.client' ) => Utils :: getClientDisplayName ( $record )];
if ( ! $reportType ) {
$exportRow [ trans ( 'texts.invoice_number' )] = $record -> invoice_number ;
$exportRow [ trans ( 'texts.invoice_date' )] = Utils :: fromSqlDate ( $record -> invoice_date , true );
}
$exportRow [ trans ( 'texts.amount' )] = Utils :: formatMoney ( $record -> amount , $record -> currency_id );
$exportRow [ trans ( 'texts.paid' )] = Utils :: formatMoney ( $record -> paid , $record -> currency_id );
$exportRow [ trans ( 'texts.balance' )] = Utils :: formatMoney ( $record -> balance , $record -> currency_id );
2015-03-16 22:45:25 +01:00
2015-04-30 19:54:19 +02:00
$displayData [] = $displayRow ;
$exportData [] = $exportRow ;
2015-03-16 22:45:25 +01:00
2015-04-30 19:54:19 +02:00
$accountCurrencyId = Auth :: user () -> account -> currency_id ;
$currencyId = $record -> currency_id ? $record -> currency_id : ( $accountCurrencyId ? $accountCurrencyId : DEFAULT_CURRENCY );
if ( ! isset ( $reportTotals [ 'amount' ][ $currencyId ])) {
$reportTotals [ 'amount' ][ $currencyId ] = 0 ;
$reportTotals [ 'balance' ][ $currencyId ] = 0 ;
$reportTotals [ 'paid' ][ $currencyId ] = 0 ;
}
$reportTotals [ 'amount' ][ $currencyId ] += $record -> amount ;
$reportTotals [ 'paid' ][ $currencyId ] += $record -> paid ;
$reportTotals [ 'balance' ][ $currencyId ] += $record -> balance ;
}
2015-06-19 01:28:43 +02:00
if ( $action == 'export' )
{
2015-04-30 19:54:19 +02:00
self :: export ( $exportData , $reportTotals );
}
}
2015-06-19 01:28:43 +02:00
if ( $enableChart )
{
foreach ([ ENTITY_INVOICE , ENTITY_PAYMENT , ENTITY_CREDIT ] as $entityType )
{
// SQLite does not support the YEAR(), MONTH(), WEEK() and similar functions.
// Let's see if SQLite is being used.
if ( Config :: get ( 'database.connections.' . Config :: get ( 'database.default' ) . '.driver' ) == 'sqlite' )
{
// Replace the unsupported function with it's date format counterpart
switch ( $groupBy )
{
case 'MONTH' :
$dateFormat = '%m' ; // returns 01-12
break ;
case 'WEEK' :
$dateFormat = '%W' ; // returns 00-53
break ;
case 'DAYOFYEAR' :
$dateFormat = '%j' ; // returns 001-366
break ;
default :
$dateFormat = '%m' ; // MONTH by default
break ;
}
// Concatenate the year and the chosen timeframe (Month, Week or Day)
$timeframe = 'strftime("%Y", ' . $entityType . '_date) || strftime("' . $dateFormat . '", ' . $entityType . '_date)' ;
}
else
{
// Supported by Laravel's other DBMS drivers (MySQL, MSSQL and PostgreSQL)
$timeframe = 'concat(YEAR(' . $entityType . '_date), ' . $groupBy . '(' . $entityType . '_date))' ;
}
2015-04-30 19:54:19 +02:00
$records = DB :: table ( $entityType . 's' )
2015-06-19 01:28:43 +02:00
-> select ( DB :: raw ( 'sum(amount) as total, ' . $timeframe . ' as ' . $groupBy ))
-> where ( 'account_id' , '=' , Auth :: user () -> account_id )
-> 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_INVOICE )
{
2015-04-30 19:54:19 +02:00
$records -> where ( 'is_quote' , '=' , false )
-> where ( 'is_recurring' , '=' , false );
2015-03-16 22:45:25 +01:00
}
2015-04-30 19:54:19 +02:00
$totals = $records -> lists ( 'total' );
2015-06-19 01:28:43 +02:00
$dates = $records -> lists ( $groupBy );
$data = array_combine ( $dates , $totals );
2015-04-30 19:54:19 +02:00
2015-05-05 11:48:23 +02:00
$padding = $groupBy == 'DAYOFYEAR' ? 'day' : ( $groupBy == 'WEEK' ? 'week' : 'month' );
$endDate -> modify ( '+1 ' . $padding );
2015-04-30 19:54:19 +02:00
$interval = new DateInterval ( 'P1' . substr ( $groupBy , 0 , 1 ));
2015-06-19 01:28:43 +02:00
$period = new DatePeriod ( $startDate , $interval , $endDate );
2015-05-05 11:48:23 +02:00
$endDate -> modify ( '-1 ' . $padding );
2015-04-30 19:54:19 +02:00
$totals = [];
2015-06-19 01:28:43 +02:00
foreach ( $period as $d )
{
2015-04-30 19:54:19 +02:00
$dateFormat = $groupBy == 'DAYOFYEAR' ? 'z' : ( $groupBy == 'WEEK' ? 'W' : 'n' );
2015-08-30 14:08:15 +02:00
// 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 );
2015-04-30 19:54:19 +02:00
$totals [] = isset ( $data [ $date ]) ? $data [ $date ] : 0 ;
2015-06-19 01:28:43 +02:00
if ( $entityType == ENTITY_INVOICE )
{
2015-04-30 19:54:19 +02:00
$labelFormat = $groupBy == 'DAYOFYEAR' ? 'j' : ( $groupBy == 'WEEK' ? 'W' : 'F' );
2015-06-19 01:28:43 +02:00
$label = $d -> format ( $labelFormat );
2015-04-30 19:54:19 +02:00
$labels [] = $label ;
}
}
2015-06-19 01:28:43 +02:00
2015-04-30 19:54:19 +02:00
$max = max ( $totals );
2015-06-19 01:28:43 +02:00
if ( $max > 0 )
{
2015-04-30 19:54:19 +02:00
$datasets [] = [
'totals' => $totals ,
'colors' => $entityType == ENTITY_INVOICE ? '78,205,196' : ( $entityType == ENTITY_CREDIT ? '199,244,100' : '255,107,107' ),
];
$maxTotals = max ( $max , $maxTotals );
}
2015-03-16 22:45:25 +01:00
}
2015-04-30 19:54:19 +02:00
$width = ( ceil ( $maxTotals / 100 ) * 100 ) / 10 ;
$width = max ( $width , 10 );
}
2015-03-16 22:45:25 +01:00
}
$dateTypes = [
'DAYOFYEAR' => 'Daily' ,
'WEEK' => 'Weekly' ,
'MONTH' => 'Monthly' ,
];
$chartTypes = [
'Bar' => 'Bar' ,
'Line' => 'Line' ,
];
2015-04-30 19:54:19 +02:00
$reportTypes = [
'' => '' ,
'Client' => trans ( 'texts.client' )
];
2015-03-16 22:45:25 +01:00
$params = [
'labels' => $labels ,
'datasets' => $datasets ,
'scaleStepWidth' => $width ,
'dateTypes' => $dateTypes ,
'chartTypes' => $chartTypes ,
'chartType' => $chartType ,
'startDate' => $startDate -> format ( Session :: get ( SESSION_DATE_FORMAT )),
2015-05-05 11:48:23 +02:00
'endDate' => $endDate -> format ( Session :: get ( SESSION_DATE_FORMAT )),
2015-03-16 22:45:25 +01:00
'groupBy' => $groupBy ,
'feature' => ACCOUNT_CHART_BUILDER ,
2015-04-30 19:54:19 +02:00
'displayData' => $displayData ,
'columns' => $columns ,
'reportTotals' => $reportTotals ,
'reportTypes' => $reportTypes ,
'reportType' => $reportType ,
'enableChart' => $enableChart ,
'enableReport' => $enableReport ,
2015-07-07 22:08:16 +02:00
'title' => trans ( 'texts.charts_and_reports' ),
2015-03-16 22:45:25 +01:00
];
2015-04-30 19:54:19 +02:00
return View :: make ( 'reports.chart_builder' , $params );
}
private function export ( $data , $totals )
{
$output = fopen ( 'php://output' , 'w' ) or Utils :: fatalError ();
header ( 'Content-Type:application/csv' );
header ( 'Content-Disposition:attachment;filename=ninja-report.csv' );
Utils :: exportData ( $output , $data );
foreach ([ 'amount' , 'paid' , 'balance' ] as $type ) {
$csv = trans ( " texts. { $type } " ) . ',' ;
foreach ( $totals [ $type ] as $currencyId => $amount ) {
$csv .= Utils :: formatMoney ( $amount , $currencyId ) . ',' ;
}
fwrite ( $output , $csv . " \n " );
}
fclose ( $output );
exit ;
2015-03-16 22:45:25 +01:00
}
}