mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Working on email reports
This commit is contained in:
parent
82b298c3d4
commit
3e81fc3925
@ -12,6 +12,10 @@ use Utils;
|
|||||||
use View;
|
use View;
|
||||||
use Carbon;
|
use Carbon;
|
||||||
use Validator;
|
use Validator;
|
||||||
|
use stdClass;
|
||||||
|
use DateInterval;
|
||||||
|
use DatePeriod;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ReportController.
|
* Class ReportController.
|
||||||
@ -173,4 +177,79 @@ class ReportController extends BaseController
|
|||||||
|
|
||||||
session()->flash('message', trans('texts.deleted_scheduled_report'));
|
session()->flash('message', trans('texts.deleted_scheduled_report'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function showEmailReport()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('reports.emails', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadEmailReport($startDate, $endDate)
|
||||||
|
{
|
||||||
|
$account = auth()->user()->account;
|
||||||
|
$startDate = date_create($startDate);
|
||||||
|
$endDate = date_create($endDate);
|
||||||
|
$postmark = new \Postmark\PostmarkClient(config('services.postmark'));
|
||||||
|
$obj = new stdClass;
|
||||||
|
|
||||||
|
$eventTypes = ['sent', 'opened'];
|
||||||
|
|
||||||
|
foreach ($eventTypes as $eventType) {
|
||||||
|
$data = [];
|
||||||
|
$endDate->modify('+1 day');
|
||||||
|
$interval = new DateInterval('P1D');
|
||||||
|
$period = new DatePeriod($startDate, $interval, $endDate);
|
||||||
|
$endDate->modify('-1 day');
|
||||||
|
$records = [];
|
||||||
|
|
||||||
|
if ($eventType == 'sent') {
|
||||||
|
$response = $postmark->getOutboundSendStatistics(null, request()->start_date, request()->end_date);
|
||||||
|
} else {
|
||||||
|
$response = $postmark->getOutboundOpenStatistics(null, request()->start_date, request()->end_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($response->days as $key => $val) {
|
||||||
|
$field = $eventType == 'opened' ? 'unique' : $eventType;
|
||||||
|
$data[$val['date']] = $val[$field];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($period as $day) {
|
||||||
|
$date = $day->format('Y-m-d');
|
||||||
|
$records[] = isset($data[$date]) ? $data[$date] : 0;
|
||||||
|
|
||||||
|
if ($eventType == 'sent') {
|
||||||
|
$labels[] = $day->format('m/d/Y');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($eventType == 'sent') {
|
||||||
|
$color = '51,122,183';
|
||||||
|
} elseif ($eventType == 'opened') {
|
||||||
|
$color = '54,193,87';
|
||||||
|
} elseif ($eventType == 'bounced') {
|
||||||
|
$color = '128,128,128';
|
||||||
|
}
|
||||||
|
|
||||||
|
$group = new stdClass();
|
||||||
|
$group->data = $records;
|
||||||
|
$group->label = trans("texts.{$eventType}");
|
||||||
|
$group->lineTension = 0;
|
||||||
|
$group->borderWidth = 4;
|
||||||
|
$group->borderColor = "rgba({$color}, 1)";
|
||||||
|
$group->backgroundColor = "rgba({$color}, 0.1)";
|
||||||
|
$datasets[] = $group;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = new stdClass();
|
||||||
|
$data->labels = $labels;
|
||||||
|
$data->datasets = $datasets;
|
||||||
|
|
||||||
|
$response = new stdClass();
|
||||||
|
$response->data = $data;
|
||||||
|
|
||||||
|
return response()->json($response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2752,6 +2752,9 @@ $LANG = array(
|
|||||||
'processing' => 'Processing',
|
'processing' => 'Processing',
|
||||||
'reactivate' => 'Reactivate',
|
'reactivate' => 'Reactivate',
|
||||||
'reactivated_email' => 'The email address has been reactivated',
|
'reactivated_email' => 'The email address has been reactivated',
|
||||||
|
'emails' => 'Emails',
|
||||||
|
'opened' => 'Opened',
|
||||||
|
'bounced' => 'Bounced',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
@if (Auth::user()->hasPermission('view_all'))
|
@if (Auth::user()->hasPermission('view_all'))
|
||||||
function loadChart(data) {
|
function loadChart(data) {
|
||||||
|
console.log(data);
|
||||||
var ctx = document.getElementById('chart-canvas').getContext('2d');
|
var ctx = document.getElementById('chart-canvas').getContext('2d');
|
||||||
if (window.myChart) {
|
if (window.myChart) {
|
||||||
window.myChart.config.data = data;
|
window.myChart.config.data = data;
|
||||||
|
187
resources/views/reports/emails.blade.php
Normal file
187
resources/views/reports/emails.blade.php
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
@extends('header')
|
||||||
|
|
||||||
|
@section('head')
|
||||||
|
@parent
|
||||||
|
|
||||||
|
<script src="{!! asset('js/Chart.min.js') !!}" type="text/javascript"></script>
|
||||||
|
<script src="{{ asset('js/daterangepicker.min.js') }}?no_cache={{ NINJA_VERSION }}" type="text/javascript"></script>
|
||||||
|
<link href="{{ asset('css/daterangepicker.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('top-right')
|
||||||
|
<div class="pull-right">
|
||||||
|
<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>
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
@if (!Utils::isPro())
|
||||||
|
<div class="alert alert-warning" style="font-size:larger;">
|
||||||
|
<center>
|
||||||
|
{!! trans('texts.pro_plan_reports', ['link'=>'<a href="javascript:showUpgradeModal()">' . trans('texts.pro_plan_remove_logo_link') . '</a>']) !!}
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
|
||||||
|
<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 = 'day';
|
||||||
|
window.myChart.config.options.scales.xAxes[0].time.round = 'day';
|
||||||
|
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.sent') !!}: ";
|
||||||
|
} else if (item.datasetIndex == 1) {
|
||||||
|
var label = " {!! trans('texts.opened') !!}: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return label + ' ' + item.yLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
fontSize: 18,
|
||||||
|
text: '{{ trans('texts.total_revenue') }}'
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'day',
|
||||||
|
round: 'day',
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
callback: function(label, index, labels) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var account = {!! $account !!};
|
||||||
|
var dateRanges = {!! $account->present()->dateRangeOptions !!};
|
||||||
|
var chartStartDate;
|
||||||
|
var chartEndDate;
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
// Initialize date range selector
|
||||||
|
chartStartDate = moment().subtract(29, 'days');
|
||||||
|
chartEndDate = moment();
|
||||||
|
lastRange = false;
|
||||||
|
|
||||||
|
if (isStorageSupported()) {
|
||||||
|
lastRange = localStorage.getItem('last:dashboard_range');
|
||||||
|
dateRange = dateRanges[lastRange];
|
||||||
|
|
||||||
|
if (dateRange) {
|
||||||
|
chartStartDate = dateRange[0];
|
||||||
|
chartEndDate = dateRange[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cb(start, end, label) {
|
||||||
|
$('#reportrange span').html(start.format('{{ $account->getMomentDateFormat() }}') + ' - ' + end.format('{{ $account->getMomentDateFormat() }}'));
|
||||||
|
chartStartDate = start;
|
||||||
|
chartEndDate = end;
|
||||||
|
$('.range-label-div').show();
|
||||||
|
if (label) {
|
||||||
|
$('.range-label-div').text(label);
|
||||||
|
}
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
if (isStorageSupported() && label && label != "{{ trans('texts.custom_range') }}") {
|
||||||
|
localStorage.setItem('last:dashboard_range', label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#reportrange').daterangepicker({
|
||||||
|
locale: {
|
||||||
|
format: "{{ $account->getMomentDateFormat() }}",
|
||||||
|
customRangeLabel: "{{ trans('texts.custom_range') }}",
|
||||||
|
applyLabel: "{{ trans('texts.apply') }}",
|
||||||
|
cancelLabel: "{{ trans('texts.cancel') }}",
|
||||||
|
},
|
||||||
|
startDate: chartStartDate,
|
||||||
|
endDate: chartEndDate,
|
||||||
|
linkedCalendars: false,
|
||||||
|
ranges: dateRanges,
|
||||||
|
}, cb);
|
||||||
|
|
||||||
|
cb(chartStartDate, chartEndDate, lastRange);
|
||||||
|
|
||||||
|
$("#currency-btn-group > .btn").click(function(){
|
||||||
|
$(this).addClass("active").siblings().removeClass("active");
|
||||||
|
loadData();
|
||||||
|
if (isStorageSupported()) {
|
||||||
|
localStorage.setItem('last:dashboard_currency_id', $(this).attr('data-button'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#group-btn-group > .btn").click(function(){
|
||||||
|
$(this).addClass("active").siblings().removeClass("active");
|
||||||
|
loadData();
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadData() {
|
||||||
|
var url = "{!! url('/reports/emails_report') !!}/" + chartStartDate.format('YYYY-MM-DD') + '/' + chartEndDate.format('YYYY-MM-DD');
|
||||||
|
$.get(url, function(response) {
|
||||||
|
//response = JSON.parse(response);
|
||||||
|
loadChart(response.data);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
@stop
|
@ -37,6 +37,11 @@
|
|||||||
@stop
|
@stop
|
||||||
|
|
||||||
@section('top-right')
|
@section('top-right')
|
||||||
|
@if (config('services.postmark'))
|
||||||
|
{!! Button::normal(trans('texts.emails'))
|
||||||
|
->asLinkTo(url('/reports/emails'))
|
||||||
|
->appendIcon(Icon::create('envelope')) !!}
|
||||||
|
@endif
|
||||||
{!! Button::normal(trans('texts.calendar'))
|
{!! Button::normal(trans('texts.calendar'))
|
||||||
->asLinkTo(url('/calendar'))
|
->asLinkTo(url('/calendar'))
|
||||||
->appendIcon(Icon::create('calendar')) !!}
|
->appendIcon(Icon::create('calendar')) !!}
|
||||||
|
@ -285,6 +285,8 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
|
|||||||
Route::post('reports', 'ReportController@showReports');
|
Route::post('reports', 'ReportController@showReports');
|
||||||
Route::get('calendar', 'CalendarController@showCalendar');
|
Route::get('calendar', 'CalendarController@showCalendar');
|
||||||
Route::get('calendar_events', 'CalendarController@loadEvents');
|
Route::get('calendar_events', 'CalendarController@loadEvents');
|
||||||
|
Route::get('reports/emails', 'ReportController@showEmailReport');
|
||||||
|
Route::get('reports/emails_report/{start_date}/{end_date}', 'ReportController@loadEmailReport');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group([
|
Route::group([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user