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',
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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 {
|
||||
|
|
|
|||
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') !!};
|
||||
|
|
@ -28,7 +29,7 @@
|
|||
NINJA.parseFloat = function(str) {
|
||||
if (!str) return '';
|
||||
str = (str+'').replace(/[^0-9\.\-]/g, '');
|
||||
|
||||
|
||||
return window.parseFloat(str);
|
||||
}
|
||||
|
||||
|
|
@ -97,4 +98,4 @@
|
|||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -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