mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 12:12:48 +01:00
Working on the dashboard
This commit is contained in:
parent
c895cb2bbd
commit
ce3a510037
@ -135,7 +135,8 @@ class CreateTestData extends Command
|
||||
$data = [
|
||||
'invoice_id' => $invoice->id,
|
||||
'client_id' => $client->id,
|
||||
'amount' => $this->faker->randomFloat(2, 0, $invoice->amount)
|
||||
'amount' => $this->faker->randomFloat(2, 0, $invoice->amount),
|
||||
'payment_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$payment = $this->paymentRepo->save($data);
|
||||
|
@ -4,6 +4,8 @@ use stdClass;
|
||||
use Auth;
|
||||
use DB;
|
||||
use View;
|
||||
use Utils;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Ninja\Repositories\DashboardRepository;
|
||||
@ -26,7 +28,8 @@ class DashboardController extends BaseController
|
||||
$user = Auth::user();
|
||||
$viewAll = $user->hasPermission('view_all');
|
||||
$userId = $user->id;
|
||||
$accountId = $user->account->id;
|
||||
$account = $user->account;
|
||||
$accountId = $account->id;
|
||||
|
||||
$dashboardRepo = $this->dashboardRepo;
|
||||
$metrics = $dashboardRepo->totals($accountId, $userId, $viewAll);
|
||||
@ -37,7 +40,10 @@ class DashboardController extends BaseController
|
||||
$pastDue = $dashboardRepo->pastDue($accountId, $userId, $viewAll);
|
||||
$upcoming = $dashboardRepo->upcoming($accountId, $userId, $viewAll);
|
||||
$payments = $dashboardRepo->payments($accountId, $userId, $viewAll);
|
||||
$expenses = $dashboardRepo->expenses($accountId, $userId, $viewAll);
|
||||
$tasks = $dashboardRepo->tasks($accountId, $userId, $viewAll);
|
||||
|
||||
// check if the account has quotes
|
||||
$hasQuotes = false;
|
||||
foreach ([$upcoming, $pastDue] as $data) {
|
||||
foreach ($data as $invoice) {
|
||||
@ -47,6 +53,26 @@ class DashboardController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
// check if the account has multiple curencies
|
||||
$currencyIds = $account->currency_id ? [$account->currency_id] : [DEFAULT_CURRENCY];
|
||||
$data = Client::scope()
|
||||
->withArchived()
|
||||
->distinct()
|
||||
->get(['currency_id'])
|
||||
->toArray();
|
||||
|
||||
array_map(function ($item) use (&$currencyIds) {
|
||||
$currencyId = intval($item['currency_id']);
|
||||
if ($currencyId && ! in_array($currencyId, $currencyIds)) {
|
||||
$currencyIds[] = $currencyId;
|
||||
}
|
||||
}, $data);
|
||||
|
||||
$currencies = [];
|
||||
foreach ($currencyIds as $currencyId) {
|
||||
$currencies[$currencyId] = Utils::getFromCache($currencyId, 'currencies')->code;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'account' => $user->account,
|
||||
'paidToDate' => $paidToDate,
|
||||
@ -60,8 +86,20 @@ class DashboardController extends BaseController
|
||||
'payments' => $payments,
|
||||
'title' => trans('texts.dashboard'),
|
||||
'hasQuotes' => $hasQuotes,
|
||||
'showBreadcrumbs' => false,
|
||||
'currencies' => $currencies,
|
||||
'expenses' => $expenses,
|
||||
'tasks' => $tasks,
|
||||
];
|
||||
|
||||
return View::make('dashboard', $data);
|
||||
}
|
||||
|
||||
public function chartData($groupBy, $startDate, $endDate, $currencyCode, $includeExpenses)
|
||||
{
|
||||
$includeExpenses = filter_var($includeExpenses, FILTER_VALIDATE_BOOLEAN);
|
||||
$data = $this->dashboardRepo->chartData(Auth::user()->account, $groupBy, $startDate, $endDate, $currencyCode, $includeExpenses);
|
||||
|
||||
return json_encode($data);
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ use Config;
|
||||
use Input;
|
||||
use Utils;
|
||||
use DB;
|
||||
use DateInterval;
|
||||
use DatePeriod;
|
||||
use Session;
|
||||
use View;
|
||||
use App\Models\Account;
|
||||
@ -56,36 +54,17 @@ class ReportController extends BaseController
|
||||
$action = Input::get('action');
|
||||
|
||||
if (Input::all()) {
|
||||
$groupBy = Input::get('group_by');
|
||||
$chartType = Input::get('chart_type');
|
||||
$reportType = Input::get('report_type');
|
||||
$dateField = Input::get('date_field');
|
||||
$startDate = Utils::toSqlDate(Input::get('start_date'), false);
|
||||
$endDate = Utils::toSqlDate(Input::get('end_date'), false);
|
||||
$enableReport = boolval(Input::get('enable_report'));
|
||||
$enableChart = boolval(Input::get('enable_chart'));
|
||||
} else {
|
||||
$groupBy = 'MONTH';
|
||||
$chartType = 'Bar';
|
||||
$reportType = ENTITY_INVOICE;
|
||||
$dateField = FILTER_INVOICE_DATE;
|
||||
$startDate = Utils::today(false)->modify('-3 month');
|
||||
$endDate = Utils::today(false);
|
||||
$enableReport = true;
|
||||
$enableChart = true;
|
||||
}
|
||||
|
||||
$dateTypes = [
|
||||
'DAYOFYEAR' => 'Daily',
|
||||
'WEEK' => 'Weekly',
|
||||
'MONTH' => 'Monthly',
|
||||
];
|
||||
|
||||
$chartTypes = [
|
||||
'Bar' => 'Bar',
|
||||
'Line' => 'Line',
|
||||
];
|
||||
|
||||
$reportTypes = [
|
||||
ENTITY_CLIENT => trans('texts.client'),
|
||||
ENTITY_INVOICE => trans('texts.invoice'),
|
||||
@ -96,148 +75,29 @@ class ReportController extends BaseController
|
||||
];
|
||||
|
||||
$params = [
|
||||
'dateTypes' => $dateTypes,
|
||||
'chartTypes' => $chartTypes,
|
||||
'chartType' => $chartType,
|
||||
'startDate' => $startDate->format(Session::get(SESSION_DATE_FORMAT)),
|
||||
'endDate' => $endDate->format(Session::get(SESSION_DATE_FORMAT)),
|
||||
'groupBy' => $groupBy,
|
||||
'reportTypes' => $reportTypes,
|
||||
'reportType' => $reportType,
|
||||
'enableChart' => $enableChart,
|
||||
'enableReport' => $enableReport,
|
||||
'title' => trans('texts.charts_and_reports'),
|
||||
];
|
||||
|
||||
if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
|
||||
if ($enableReport) {
|
||||
$isExport = $action == 'export';
|
||||
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));
|
||||
$isExport = $action == 'export';
|
||||
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));
|
||||
|
||||
if ($isExport) {
|
||||
self::export($reportType, $params['displayData'], $params['columns'], $params['reportTotals']);
|
||||
}
|
||||
}
|
||||
if ($enableChart) {
|
||||
$params = array_merge($params, self::generateChart($groupBy, $startDate, $endDate));
|
||||
if ($isExport) {
|
||||
self::export($reportType, $params['displayData'], $params['columns'], $params['reportTotals']);
|
||||
}
|
||||
} else {
|
||||
$params['columns'] = [];
|
||||
$params['displayData'] = [];
|
||||
$params['reportTotals'] = [];
|
||||
$params['labels'] = [];
|
||||
$params['datasets'] = [];
|
||||
$params['scaleStepWidth'] = 100;
|
||||
}
|
||||
|
||||
return View::make('reports.chart_builder', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $groupBy
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @return array
|
||||
*/
|
||||
private function generateChart($groupBy, $startDate, $endDate)
|
||||
{
|
||||
$width = 10;
|
||||
$datasets = [];
|
||||
$labels = [];
|
||||
$maxTotals = 0;
|
||||
|
||||
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))';
|
||||
}
|
||||
|
||||
$records = DB::table($entityType.'s')
|
||||
->select(DB::raw('sum('.$entityType.'s.amount) as total, '.$timeframe.' as '.$groupBy))
|
||||
->join('clients', 'clients.id', '=', $entityType.'s.client_id')
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->where($entityType.'s.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) {
|
||||
$records->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||
->where('is_recurring', '=', false);
|
||||
} elseif ($entityType == ENTITY_PAYMENT) {
|
||||
$records->join('invoices', 'invoices.id', '=', 'payments.invoice_id')
|
||||
->where('invoices.is_deleted', '=', false)
|
||||
->whereNotIn('payment_status_id', [PAYMENT_STATUS_VOIDED, PAYMENT_STATUS_FAILED]);
|
||||
}
|
||||
|
||||
$totals = $records->lists('total');
|
||||
$dates = $records->lists($groupBy);
|
||||
$data = array_combine($dates, $totals);
|
||||
|
||||
$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);
|
||||
|
||||
$totals = [];
|
||||
|
||||
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);
|
||||
$totals[] = isset($data[$date]) ? $data[$date] : 0;
|
||||
|
||||
if ($entityType == ENTITY_INVOICE) {
|
||||
$labelFormat = $groupBy == 'DAYOFYEAR' ? 'j' : ($groupBy == 'WEEK' ? 'W' : 'F');
|
||||
$label = $d->format($labelFormat);
|
||||
$labels[] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
$max = max($totals);
|
||||
|
||||
if ($max > 0) {
|
||||
$datasets[] = [
|
||||
'totals' => $totals,
|
||||
'colors' => $entityType == ENTITY_INVOICE ? '78,205,196' : ($entityType == ENTITY_CREDIT ? '199,244,100' : '255,107,107'),
|
||||
];
|
||||
$maxTotals = max($max, $maxTotals);
|
||||
}
|
||||
}
|
||||
|
||||
$width = (ceil($maxTotals / 100) * 100) / 10;
|
||||
$width = max($width, 10);
|
||||
|
||||
return [
|
||||
'datasets' => $datasets,
|
||||
'scaleStepWidth' => $width,
|
||||
'labels' => $labels,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param $startDate
|
||||
|
@ -18,8 +18,8 @@ class Kernel extends HttpKernel {
|
||||
'App\Http\Middleware\VerifyCsrfToken',
|
||||
'App\Http\Middleware\DuplicateSubmissionCheck',
|
||||
'App\Http\Middleware\QueryLogging',
|
||||
'App\Http\Middleware\StartupCheck',
|
||||
'App\Http\Middleware\SessionDataCheckMiddleware',
|
||||
'App\Http\Middleware\StartupCheck',
|
||||
];
|
||||
|
||||
|
||||
|
@ -14,18 +14,16 @@ class SessionDataCheckMiddleware {
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next) {
|
||||
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$bag = Session::getMetadataBag();
|
||||
$max = env('IDLE_TIMEOUT_MINUTES', 6 * 60) * 60; // minute to second conversion
|
||||
$elapsed = time() - $bag->getLastUsed();
|
||||
|
||||
$max = config('session.lifetime') * 60; // minute to second conversion
|
||||
|
||||
if (($bag && $max < (time() - $bag->getLastUsed()))) {
|
||||
|
||||
$request->session()->flush(); // remove all the session data
|
||||
|
||||
Auth::logout(); // logout user
|
||||
|
||||
if ( ! $bag || $elapsed > $max) {
|
||||
$request->session()->flush();
|
||||
Auth::logout();
|
||||
$request->session()->flash('warning', trans('texts.inactive_logout'));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
@ -123,6 +123,7 @@ if (Utils::isReseller()) {
|
||||
|
||||
Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::get('dashboard', 'DashboardController@index');
|
||||
Route::get('dashboard_chart_data/{group_by}/{start_date}/{end_date}/{currency_id}/{include_expenses}', 'DashboardController@chartData');
|
||||
Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
|
||||
Route::get('hide_message', 'HomeController@hideMessage');
|
||||
Route::get('force_inline_pdf', 'UserController@forcePDFJS');
|
||||
@ -238,8 +239,8 @@ Route::group([
|
||||
Route::get('settings/email_preview', 'AccountController@previewEmail');
|
||||
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
|
||||
Route::get('settings/data_visualizations', 'ReportController@d3');
|
||||
Route::get('settings/charts_and_reports', 'ReportController@showReports');
|
||||
Route::post('settings/charts_and_reports', 'ReportController@showReports');
|
||||
Route::get('settings/reports', 'ReportController@showReports');
|
||||
Route::post('settings/reports', 'ReportController@showReports');
|
||||
|
||||
Route::post('settings/change_plan', 'AccountController@changePlan');
|
||||
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
|
||||
@ -411,7 +412,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
|
||||
define('ACCOUNT_CLIENT_PORTAL', 'client_portal');
|
||||
define('ACCOUNT_EMAIL_SETTINGS', 'email_settings');
|
||||
define('ACCOUNT_CHARTS_AND_REPORTS', 'charts_and_reports');
|
||||
define('ACCOUNT_REPORTS', 'reports');
|
||||
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
|
||||
define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations');
|
||||
define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders');
|
||||
|
@ -96,7 +96,7 @@ class Account extends Eloquent
|
||||
ACCOUNT_TEMPLATES_AND_REMINDERS,
|
||||
ACCOUNT_BANKS,
|
||||
ACCOUNT_CLIENT_PORTAL,
|
||||
ACCOUNT_CHARTS_AND_REPORTS,
|
||||
ACCOUNT_REPORTS,
|
||||
ACCOUNT_DATA_VISUALIZATIONS,
|
||||
ACCOUNT_API_TOKENS,
|
||||
ACCOUNT_USER_MANAGEMENT,
|
||||
@ -401,11 +401,7 @@ class Account extends Eloquent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $date
|
||||
* @return DateTime|null|string
|
||||
*/
|
||||
public function getDateTime($date = 'now')
|
||||
public function getDate($date = 'now')
|
||||
{
|
||||
if ( ! $date) {
|
||||
return null;
|
||||
@ -413,6 +409,16 @@ class Account extends Eloquent
|
||||
$date = new \DateTime($date);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $date
|
||||
* @return DateTime|null|string
|
||||
*/
|
||||
public function getDateTime($date = 'now')
|
||||
{
|
||||
$date = $this->getDate($date);
|
||||
$date->setTimeZone(new \DateTimeZone($this->getTimezone()));
|
||||
|
||||
return $date;
|
||||
@ -469,7 +475,7 @@ class Account extends Eloquent
|
||||
*/
|
||||
public function formatDate($date)
|
||||
{
|
||||
$date = $this->getDateTime($date);
|
||||
$date = $this->getDate($date);
|
||||
|
||||
if ( ! $date) {
|
||||
return null;
|
||||
|
@ -156,6 +156,15 @@ class Task extends EntityModel
|
||||
{
|
||||
return '#' . $this->public_id;
|
||||
}
|
||||
|
||||
public function getDisplayName()
|
||||
{
|
||||
if ($this->description) {
|
||||
return mb_strimwidth($this->description, 0, 16, "...");
|
||||
}
|
||||
|
||||
return '#' . $this->public_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -597,8 +597,11 @@ class BasePaymentDriver
|
||||
$term = strtolower($matches[2]);
|
||||
$price = $invoice_item->cost;
|
||||
if ($plan == PLAN_ENTERPRISE) {
|
||||
preg_match('/###[\d] [\w]* (\d*)/', $invoice_item->notes, $matches);
|
||||
$numUsers = $matches[1];
|
||||
if (count($matches)) {
|
||||
$numUsers = $matches[1];
|
||||
} else {
|
||||
$numUsers = 5;
|
||||
}
|
||||
} else {
|
||||
$numUsers = 1;
|
||||
}
|
||||
|
@ -1,10 +1,148 @@
|
||||
<?php namespace App\Ninja\Repositories;
|
||||
|
||||
use stdClass;
|
||||
use DB;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Task;
|
||||
use DateInterval;
|
||||
use DatePeriod;
|
||||
|
||||
class DashboardRepository
|
||||
{
|
||||
/**
|
||||
* @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;
|
||||
$records = $this->rawChartData($entityType, $account, $groupBy, $startDate, $endDate, $currencyId);
|
||||
|
||||
array_map(function ($item) use (&$data, &$count, $groupBy) {
|
||||
$data[$item->$groupBy] = $item->total;
|
||||
$count += $item->count;
|
||||
}, $records->get());
|
||||
|
||||
$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;
|
||||
} elseif ($entityType == ENTITY_PAYMENT) {
|
||||
$totals->revenue = array_sum($data);
|
||||
$totals->balance = $totals->invoices - $totals->revenue;
|
||||
} 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)
|
||||
{
|
||||
$accountId = $account->id;
|
||||
$currencyId = intval($currencyId);
|
||||
$timeframe = 'concat(YEAR('.$entityType.'_date), '.$groupBy.'('.$entityType.'_date))';
|
||||
|
||||
$records = DB::table($entityType.'s')
|
||||
->join('clients', 'clients.id', '=', $entityType.'s.client_id')
|
||||
->where('clients.is_deleted', '=', false)
|
||||
->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) {
|
||||
$records->select(DB::raw('sum(invoices.amount) as total, count(invoices.id) as count, '.$timeframe.' as '.$groupBy))
|
||||
->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) {
|
||||
$records->select(DB::raw('sum(expenses.amount) as total, count(expenses.id) as count, '.$timeframe.' as '.$groupBy));
|
||||
}
|
||||
|
||||
return $records;
|
||||
}
|
||||
|
||||
public function totals($accountId, $userId, $viewAll)
|
||||
{
|
||||
// total_income, billed_clients, invoice_sent and active_clients
|
||||
@ -193,4 +331,33 @@ class DashboardRepository
|
||||
->take(50)
|
||||
->get();
|
||||
}
|
||||
|
||||
public function expenses($accountId, $userId, $viewAll)
|
||||
{
|
||||
$select = DB::raw(
|
||||
'SUM('.DB::getQueryGrammar()->wrap('expenses.amount', true).') as value,'
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,8 @@
|
||||
"stacktrace-js": "~1.0.1",
|
||||
"fuse.js": "~2.0.2",
|
||||
"dropzone": "~4.3.0",
|
||||
"sweetalert": "~1.1.3"
|
||||
"sweetalert": "~1.1.3",
|
||||
"bootstrap-daterangepicker": "~2.1.24"
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "~1.11"
|
||||
|
@ -29,7 +29,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => env('SESSION_LIFETIME', 120),
|
||||
'lifetime' => env('SESSION_LIFETIME', (60 * 8)),
|
||||
|
||||
'expire_on_close' => false,
|
||||
|
||||
|
@ -58,6 +58,11 @@ elixir(function(mix) {
|
||||
'fonts.css'
|
||||
], 'public/css/built.css');
|
||||
|
||||
mix.styles([
|
||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.css'
|
||||
], 'public/css/daterangepicker.css');
|
||||
|
||||
|
||||
/**
|
||||
* JS configuration
|
||||
*/
|
||||
@ -71,6 +76,10 @@ elixir(function(mix) {
|
||||
'vfs.js'
|
||||
], 'public/pdf.built.js');
|
||||
|
||||
mix.scripts([
|
||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
||||
], 'public/js/daterangepicker.min.js');
|
||||
|
||||
mix.scripts([
|
||||
bowerDir + '/jquery/dist/jquery.js',
|
||||
bowerDir + '/jquery-ui/jquery-ui.js',
|
||||
|
2
public/css/built.css
vendored
2
public/css/built.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/css/daterangepicker.css
vendored
Normal file
2
public/css/daterangepicker.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/css/daterangepicker.css.map
Normal file
1
public/css/daterangepicker.css.map
Normal file
File diff suppressed because one or more lines are too long
6
public/js/Chart.min.js
vendored
6
public/js/Chart.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
public/js/daterangepicker.min.js
vendored
Normal file
3
public/js/daterangepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/daterangepicker.min.js.map
Normal file
1
public/js/daterangepicker.min.js.map
Normal file
File diff suppressed because one or more lines are too long
4
resources/assets/css/sidebar.css
vendored
4
resources/assets/css/sidebar.css
vendored
@ -100,8 +100,8 @@
|
||||
|
||||
#right-sidebar-wrapper .sidebar-nav li {
|
||||
text-indent: 8px;
|
||||
font-size: 16px;
|
||||
line-height: 44px;
|
||||
font-size: 15px;
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
#right-sidebar-wrapper .sidebar-nav li a.btn {
|
||||
|
11805
resources/assets/js/Chart.js
vendored
11805
resources/assets/js/Chart.js
vendored
File diff suppressed because it is too large
Load Diff
@ -2099,6 +2099,15 @@ $LANG = array(
|
||||
'facebook_and_twitter_help' => 'Follow our feeds to help support our project',
|
||||
'reseller_text' => 'Note: the white-label license is intended for personal use, please email us at :email if you\'d like to resell our app.',
|
||||
'unnamed_client' => 'Unnamed Client',
|
||||
|
||||
'day' => 'Day',
|
||||
'week' => 'Week',
|
||||
'month' => 'Month',
|
||||
'inactive_logout' => 'You have been logged out due to inactivity',
|
||||
'reports' => 'Reports',
|
||||
'total_profit' => 'Total Profit',
|
||||
'total_expenses' => 'Total Expenses',
|
||||
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -1,22 +1,214 @@
|
||||
@extends('header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
@include('money_script')
|
||||
|
||||
<script src="{!! asset('js/Chart.min.js') !!}" type="text/javascript"></script>
|
||||
<script src="{{ asset('js/daterangepicker.min.js') }}" type="text/javascript"></script>
|
||||
<link href="{{ asset('css/daterangepicker.css') }}" rel="stylesheet" type="text/css"/>
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
@if (Auth::user()->hasPermission('view_all'))
|
||||
function loadChart(data) {
|
||||
var ctx = document.getElementById('chart-canvas').getContext('2d');
|
||||
|
||||
if (window.myChart) {
|
||||
window.myChart.config.data = data;
|
||||
window.myChart.config.options.scales.xAxes[0].time.unit = chartGropuBy.toLowerCase();
|
||||
window.myChart.update();
|
||||
} else {
|
||||
$('#progress-div').hide();
|
||||
$('#chart-canvas').fadeIn();
|
||||
window.myChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: data,
|
||||
options: {
|
||||
tooltips: {
|
||||
mode: 'x-axis',
|
||||
titleFontSize: 15,
|
||||
titleMarginBottom: 12,
|
||||
bodyFontSize: 15,
|
||||
bodySpacing: 10,
|
||||
callbacks: {
|
||||
title: function(item) {
|
||||
return moment(item[0].xLabel).format("{{ $account->getMomentDateFormat() }}");
|
||||
},
|
||||
label: function(item, data) {
|
||||
if (item.datasetIndex == 0) {
|
||||
var label = " {{ trans('texts.invoices') }}: ";
|
||||
} else if (item.datasetIndex == 1) {
|
||||
var label = " {{ trans('texts.payments') }}: ";
|
||||
} else if (item.datasetIndex == 2) {
|
||||
var label = " {{ trans('texts.expenses') }}: ";
|
||||
}
|
||||
|
||||
return label + formatMoney(item.yLabel, chartCurrencyId, account.country_id);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
fontSize: 18,
|
||||
text: '{{ trans('texts.total_revenue') }}'
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'day',
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
callback: function(label, index, labels) {
|
||||
return formatMoney(label, chartCurrencyId, account.country_id);
|
||||
}
|
||||
},
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var account = {!! $account !!};
|
||||
var chartStartDate = moment().subtract(29, 'days');
|
||||
var chartEndDate = moment();
|
||||
var chartGropuBy = 'day';
|
||||
var chartCurrencyId = {{ $account->getCurrencyId() }};
|
||||
|
||||
$(function() {
|
||||
|
||||
// Initialize date range selector
|
||||
|
||||
function cb(start, end) {
|
||||
$('#reportrange span').html(start.format('{{ $account->getMomentDateFormat() }}') + ' - ' + end.format('{{ $account->getMomentDateFormat() }}'));
|
||||
chartStartDate = start;
|
||||
chartEndDate = end;
|
||||
loadData();
|
||||
}
|
||||
|
||||
$('#reportrange').daterangepicker({
|
||||
locale: {
|
||||
"format": "{{ $account->getMomentDateFormat() }}",
|
||||
},
|
||||
startDate: chartStartDate,
|
||||
endDate: chartEndDate,
|
||||
linkedCalendars: false,
|
||||
ranges: {
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
}
|
||||
}, cb);
|
||||
|
||||
cb(chartStartDate, chartEndDate);
|
||||
|
||||
$("#currency-btn-group > .btn").click(function(){
|
||||
$(this).addClass("active").siblings().removeClass("active");
|
||||
chartCurrencyId = currencyMap[$(this).text()].id;
|
||||
loadData();
|
||||
});
|
||||
|
||||
$("#group-btn-group > .btn").click(function(){
|
||||
$(this).addClass("active").siblings().removeClass("active");
|
||||
chartGropuBy = $(this).text();
|
||||
loadData();
|
||||
});
|
||||
|
||||
function loadData() {
|
||||
var includeExpenses = "{{ count($expenses) ? 'true' : 'false' }}";
|
||||
var url = "{!! url('/dashboard_chart_data') !!}/" + chartGropuBy + '/' + chartStartDate.format('YYYY-MM-DD') + '/' + chartEndDate.format('YYYY-MM-DD') + '/' + chartCurrencyId + '/' + includeExpenses;
|
||||
$.get(url, function(response) {
|
||||
response = JSON.parse(response);
|
||||
loadChart(response.data);
|
||||
|
||||
var totals = response.totals;
|
||||
$('.revenue-div').text(formatMoney(totals.revenue, chartCurrencyId, account.country_id));
|
||||
$('.outstanding-div').text(formatMoney(totals.balance, chartCurrencyId, account.country_id));
|
||||
$('.expenses-div').text(formatMoney(totals.expenses, chartCurrencyId, account.country_id));
|
||||
$('.average-div').text(formatMoney(totals.average, chartCurrencyId, account.country_id));
|
||||
|
||||
$('.currency').hide();
|
||||
$('.currency_' + chartCurrencyId).show();
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
@else
|
||||
$(function() {
|
||||
$('.currency').show();
|
||||
})
|
||||
@endif
|
||||
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<ol class="breadcrumb"><li class='active'>{{ trans('texts.dashboard') }}</li></ol>
|
||||
</div>
|
||||
@if (count($tasks))
|
||||
<div class="col-md-2" style="padding-top:6px">
|
||||
@foreach ($tasks as $task)
|
||||
{!! Button::primary($task->present()->titledName)->small()->asLinkTo($task->present()->url) !!}
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
@else
|
||||
<div class="col-md-10">
|
||||
@endif
|
||||
@if (Auth::user()->hasPermission('view_all'))
|
||||
<div class="pull-right">
|
||||
@if (count($currencies) > 1)
|
||||
<div id="currency-btn-group" class="btn-group" role="group" style="border: 1px solid #ccc;">
|
||||
@foreach ($currencies as $key => $val)
|
||||
<button type="button" class="btn btn-normal {{ array_values($currencies)[0] == $val ? 'active' : '' }}"
|
||||
style="font-weight:normal !important;background-color:white">{{ $val }}</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
<div id="group-btn-group" class="btn-group" role="group" style="border: 1px solid #ccc; margin-left:18px">
|
||||
<button type="button" class="btn btn-normal active" style="font-weight:normal !important;background-color:white">{{ trans('texts.day') }}</button>
|
||||
<button type="button" class="btn btn-normal" style="font-weight:normal !important;background-color:white">{{ trans('texts.week') }}</button>
|
||||
<button type="button" class="btn btn-normal" style="font-weight:normal !important;background-color:white">{{ trans('texts.month') }}</button>
|
||||
</div>
|
||||
<div id="reportrange" class="pull-right" style="background: #fff; cursor: pointer; padding: 9px 14px; border: 1px solid #ccc; margin-top: 0px; margin-left:18px">
|
||||
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>
|
||||
<span></span> <b class="caret"></b>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<img src="{{ asset('images/totalinvoices.png') }}"
|
||||
class="in-image" style="float:left" width="80" height="80"/>
|
||||
<div style="overflow:hidden">
|
||||
<div class="in-thin">
|
||||
{{ trans('texts.total_revenue') }}
|
||||
</div>
|
||||
<div class="revenue-div in-bold pull-right" style="color:#337ab7">
|
||||
</div>
|
||||
<div class="in-bold">
|
||||
@if (count($paidToDate))
|
||||
@foreach ($paidToDate as $item)
|
||||
{{ Utils::formatMoney($item->value, $item->currency_id) }}<br/>
|
||||
<div class="currency currency_{{ $item->currency_id ?: $account->getCurrencyId() }}" style="display:none">
|
||||
{{ Utils::formatMoney($item->value, $item->currency_id) }}
|
||||
</div>
|
||||
@endforeach
|
||||
@else
|
||||
{{ Utils::formatMoney(0) }}
|
||||
@ -29,21 +221,38 @@
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<img src="{{ asset('images/clients.png') }}"
|
||||
class="in-image" style="float:left" width="80" height="80"/>
|
||||
<div style="overflow:hidden">
|
||||
<div class="in-thin">
|
||||
{{ trans('texts.average_invoice') }}
|
||||
</div>
|
||||
<div class="in-bold">
|
||||
@if (count($averageInvoice))
|
||||
@foreach ($averageInvoice as $item)
|
||||
{{ Utils::formatMoney($item->invoice_avg, $item->currency_id) }}<br/>
|
||||
@if (count($expenses))
|
||||
<div class="in-thin">
|
||||
{{ trans('texts.total_expenses') }}
|
||||
</div>
|
||||
<div class="expenses-div in-bold pull-right" style="color:#337ab7">
|
||||
</div>
|
||||
<div class="in-bold">
|
||||
@foreach ($expenses as $item)
|
||||
<div class="currency currency_{{ $item->currency_id ?: $account->getCurrencyId() }}" style="display:none">
|
||||
{{ Utils::formatMoney($item->value, $item->currency_id) }}<br/>
|
||||
</div>
|
||||
@endforeach
|
||||
@else
|
||||
{{ Utils::formatMoney(0) }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="in-thin">
|
||||
{{ trans('texts.average_invoice') }}
|
||||
</div>
|
||||
<div class="average-div in-bold pull-right" style="color:#337ab7">
|
||||
</div>
|
||||
<div class="in-bold">
|
||||
@if (count($averageInvoice))
|
||||
@foreach ($averageInvoice as $item)
|
||||
<div class="currency currency_{{ $item->currency_id ?: $account->getCurrencyId() }}" style="display:none">
|
||||
{{ Utils::formatMoney($item->invoice_avg, $item->currency_id) }}<br/>
|
||||
</div>
|
||||
@endforeach
|
||||
@else
|
||||
{{ Utils::formatMoney(0) }}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -51,16 +260,18 @@
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<img src="{{ asset('images/totalincome.png') }}"
|
||||
class="in-image" style="float:left" width="80" height="80"/>
|
||||
<div style="overflow:hidden">
|
||||
<div class="in-thin">
|
||||
{{ trans('texts.outstanding') }}
|
||||
</div>
|
||||
<div class="outstanding-div in-bold pull-right" style="color:#337ab7">
|
||||
</div>
|
||||
<div class="in-bold">
|
||||
@if (count($balances))
|
||||
@foreach ($balances as $item)
|
||||
{{ Utils::formatMoney($item->value, $item->currency_id) }}<br/>
|
||||
<div class="currency currency_{{ $item->currency_id ?: $account->getCurrencyId() }}" style="display:none">
|
||||
{{ Utils::formatMoney($item->value, $item->currency_id) }}<br/>
|
||||
</div>
|
||||
@endforeach
|
||||
@else
|
||||
{{ Utils::formatMoney(0) }}
|
||||
@ -72,13 +283,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@if (Auth::user()->hasPermission('view_all'))
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="progress-div" class="progress">
|
||||
<div class="progress-bar progress-bar-striped active" role="progressbar"
|
||||
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
|
||||
</div>
|
||||
<canvas id="chart-canvas" height="70px" style="background-color:white;padding:20px;display:none"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<p> </p>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default dashboard" style="height:320px">
|
||||
<div class="panel-heading">
|
||||
<div class="panel-heading" style="background-color:#286090 !important">
|
||||
<h3 class="panel-title in-bold-white">
|
||||
<i class="glyphicon glyphicon-exclamation-sign"></i> {{ trans('texts.activity') }}
|
||||
@if ($invoicesSent)
|
||||
@ -103,6 +324,17 @@
|
||||
<div class="panel panel-default dashboard" style="height:320px;">
|
||||
<div class="panel-heading" style="margin:0; background-color: #f5f5f5 !important;">
|
||||
<h3 class="panel-title" style="color: black !important">
|
||||
@if (count($expenses) && count($averageInvoice))
|
||||
<div class="pull-right" style="font-size:14px;padding-top:4px;font-weight:bold">
|
||||
@foreach ($averageInvoice as $item)
|
||||
<span class="currency currency_{{ $item->currency_id ?: $account->getCurrencyId() }}" style="display:none">
|
||||
{{ trans('texts.average_invoice') }}
|
||||
{{ Utils::formatMoney($item->invoice_avg, $item->currency_id) }} |
|
||||
</span>
|
||||
@endforeach
|
||||
<span class="average-div" style="color:#337ab7"/>
|
||||
</div>
|
||||
@endif
|
||||
<i class="glyphicon glyphicon-ok-sign"></i> {{ trans('texts.recent_payments') }}
|
||||
</h3>
|
||||
</div>
|
||||
@ -172,7 +404,7 @@
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default dashboard" style="height:320px">
|
||||
<div class="panel-heading" style="background-color:#e37329 !important">
|
||||
<div class="panel-heading" style="background-color:#777 !important">
|
||||
<h3 class="panel-title in-bold-white">
|
||||
<i class="glyphicon glyphicon-time"></i> {{ trans('texts.invoices_past_due') }}
|
||||
</h3>
|
||||
@ -242,7 +474,7 @@
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default dashboard" style="height:320px">
|
||||
<div class="panel-heading" style="background-color:#e37329 !important">
|
||||
<div class="panel-heading" style="background-color:#777 !important">
|
||||
<h3 class="panel-title in-bold-white">
|
||||
<i class="glyphicon glyphicon-time"></i> {{ trans('texts.expired_quotes') }}
|
||||
</h3>
|
||||
|
@ -6,7 +6,6 @@
|
||||
<link href="{{ asset('css/built.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
@if (Auth::check() && Auth::user()->dark_mode)
|
||||
body {
|
||||
background: #000 !important;
|
||||
|
@ -5,6 +5,7 @@
|
||||
for (var i=0; i<currencies.length; i++) {
|
||||
var currency = currencies[i];
|
||||
currencyMap[currency.id] = currency;
|
||||
currencyMap[currency.code] = currency;
|
||||
}
|
||||
|
||||
var countries = {!! \Cache::get('countries') !!};
|
||||
|
@ -69,10 +69,14 @@
|
||||
} else {
|
||||
// response contains id and card, which contains additional card details
|
||||
var token = response.id;
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
if (token) {
|
||||
// Insert the token into the form so it gets submitted to the server
|
||||
$form.append($('<input type="hidden" name="sourceToken"/>').val(token));
|
||||
// and submit
|
||||
$form.get(0).submit();
|
||||
} else {
|
||||
logError(JSON.stringify(response));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,14 +1,8 @@
|
||||
@extends('header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<script src="{!! asset('js/Chart.min.js') !!}" type="text/javascript"></script>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_CHARTS_AND_REPORTS, 'advanced' => true])
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_REPORTS, 'advanced' => true])
|
||||
|
||||
|
||||
{!! Former::open()->rules(['start_date' => 'required', 'end_date' => 'required'])->addClass('warn-on-exit') !!}
|
||||
@ -54,18 +48,12 @@
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{!! Former::checkbox('enable_report')->text(trans('texts.enable'))->check($enableReport)->forceValue(1) !!}
|
||||
{!! Former::select('report_type')->options($reportTypes, $reportType)->label(trans('texts.type')) !!}
|
||||
<div id="dateField" style="display:{{ $reportType == ENTITY_TAX_RATE ? 'block' : 'none' }}">
|
||||
{!! Former::select('date_field')->label(trans('texts.filter'))
|
||||
->addOption(trans('texts.invoice_date'), FILTER_INVOICE_DATE)
|
||||
->addOption(trans('texts.payment_date'), FILTER_PAYMENT_DATE) !!}
|
||||
</div>
|
||||
<p> </p>
|
||||
{!! Former::checkbox('enable_chart')->text(trans('texts.enable'))->check($enableChart)->forceValue(1) !!}
|
||||
{!! Former::select('group_by')->options($dateTypes, $groupBy) !!}
|
||||
{!! Former::select('chart_type')->options($chartTypes, $chartType) !!}
|
||||
|
||||
|
||||
{!! Former::close() !!}
|
||||
</div>
|
||||
@ -73,7 +61,6 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@if ($enableReport)
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped invoice-table">
|
||||
@ -128,29 +115,6 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($enableChart)
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<canvas id="monthly-reports" width="700" height="400"></canvas>
|
||||
<p> </p>
|
||||
<div style="padding-bottom:8px">
|
||||
<div style="float:left; height:22px; width:60px; background-color:rgba(78,205,196,.5); border: 1px solid rgba(78,205,196,1)"></div>
|
||||
<div style="vertical-align: middle"> Invoices</div>
|
||||
</div>
|
||||
<div style="padding-bottom:8px; clear:both">
|
||||
<div style="float:left; height:22px; width:60px; background-color:rgba(255,107,107,.5); border: 1px solid rgba(255,107,107,1)"></div>
|
||||
<div style="vertical-align: middle"> Payments</div>
|
||||
</div>
|
||||
<div style="clear:both">
|
||||
<div style="float:left; height:22px; width:60px; background-color:rgba(199,244,100,.5); border: 1px solid rgba(199,244,100,1)"></div>
|
||||
<div style="vertical-align: middle"> Credits</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
@ -162,32 +126,6 @@
|
||||
$('#action').val('');
|
||||
}
|
||||
|
||||
@if ($enableChart)
|
||||
var ctx = document.getElementById('monthly-reports').getContext('2d');
|
||||
var chart = {
|
||||
labels: {!! json_encode($labels) !!},
|
||||
datasets: [
|
||||
@foreach ($datasets as $dataset)
|
||||
{
|
||||
data: {!! json_encode($dataset['totals']) !!},
|
||||
fillColor : "rgba({!! $dataset['colors'] !!},0.5)",
|
||||
strokeColor : "rgba({!! $dataset['colors'] !!},1)",
|
||||
},
|
||||
@endforeach
|
||||
]
|
||||
}
|
||||
|
||||
var options = {
|
||||
scaleOverride: true,
|
||||
scaleSteps: 10,
|
||||
scaleStepWidth: {!! $scaleStepWidth !!},
|
||||
scaleStartValue: 0,
|
||||
scaleLabel : "<%=value%>",
|
||||
};
|
||||
|
||||
new Chart(ctx).{!! $chartType !!}(chart, options);
|
||||
@endif
|
||||
|
||||
$(function() {
|
||||
$('.start_date .input-group-addon').click(function() {
|
||||
toggleDatePicker('start_date');
|
||||
|
Loading…
Reference in New Issue
Block a user