1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-19 16:01:34 +02:00

Working on the calendar

This commit is contained in:
Hillel Coren 2017-09-12 13:43:59 +03:00
parent 52940b5242
commit c472bf92c8
20 changed files with 302 additions and 9 deletions

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers;
use App\Jobs\GenerateCalendarEvents;
/**
* Class ReportController.
*/
class CalendarController extends BaseController
{
/**
* @return \Illuminate\Contracts\View\View
*/
public function showCalendar()
{
if (! auth()->user()->hasPermission('view_all')) {
return redirect('/');
}
$data = [
//'showBreadcrumbs' => false,
];
return view('calendar', $data);
}
public function loadEvents()
{
$events = dispatch(new GenerateCalendarEvents());
//dd($events);
\Log::info(print_r(request()->input(), true));
\Log::info(print_r($events, true));
//echo '[{"title": "Test Event", "start": "2017-09-14T16:00:00", "color": "green"}]';
//exit;
return response()->json($events);
}
}

View File

@ -248,6 +248,8 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
Route::get('reports', 'ReportController@showReports'); Route::get('reports', 'ReportController@showReports');
Route::post('reports', 'ReportController@showReports'); Route::post('reports', 'ReportController@showReports');
Route::get('calendar', 'CalendarController@showCalendar');
Route::get('calendar_events', 'CalendarController@loadEvents');
}); });
Route::group([ Route::group([

View File

@ -0,0 +1,49 @@
<?php
namespace App\Jobs;
use App\Jobs\Job;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Expense;
use App\Models\Task;
class GenerateCalendarEvents extends Job
{
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$data = [];
$invoices = Invoice::scope()
->where('is_recurring', '=', false)
->get();
foreach ($invoices as $invoice) {
$data[] = $invoice->present()->calendarEvent;
}
$tasks = Task::scope()
->get();
foreach ($tasks as $task) {
$data[] = $task->present()->calendarEvent;
}
$payments = Payment::scope()
->get();
foreach ($payments as $payment) {
$data[] = $payment->present()->calendarEvent;
}
$expenses = Expense::scope()
->get();
foreach ($expenses as $expense) {
$data[] = $expense->present()->calendarEvent;
}
return $data;
}
}

View File

@ -5,6 +5,7 @@ namespace App\Ninja\Presenters;
use Laracasts\Presenter\Presenter; use Laracasts\Presenter\Presenter;
use URL; use URL;
use Utils; use Utils;
use stdClass;
class EntityPresenter extends Presenter class EntityPresenter extends Presenter
{ {
@ -67,4 +68,17 @@ class EntityPresenter extends Presenter
return sprintf('%s: %s', trans('texts.' . $entityType), $entity->getDisplayName()); return sprintf('%s: %s', trans('texts.' . $entityType), $entity->getDisplayName());
} }
public function calendarEvent()
{
$entity = $this->entity;
$data = new stdClass();
$data->id = $entity->getEntityType() . ':' . $entity->public_id;
$data->allDay = true;
$data->url = $this->url();
return $data;
}
} }

View File

@ -48,4 +48,26 @@ class ExpensePresenter extends EntityPresenter
{ {
return $this->entity->expense_category ? $this->entity->expense_category->name : ''; return $this->entity->expense_category ? $this->entity->expense_category->name : '';
} }
public function calendarEvent()
{
$data = parent::calendarEvent();
$expense = $this->entity;
$data->title = trans('texts.expense') . ' ' . $this->amount() . ' | ' . $this->category();
$data->title = trans('texts.expense') . ' ' . $this->amount();
if ($category = $this->category()) {
$data->title .= ' | ' . $category;
}
if ($this->public_notes) {
$data->title .= ' | ' . $this->public_notes;
}
$data->start = $expense->expense_date;
$data->borderColor = $data->backgroundColor = 'gray';
return $data;
}
} }

View File

@ -323,4 +323,17 @@ class InvoicePresenter extends EntityPresenter
return $link; return $link;
} }
public function calendarEvent()
{
$data = parent::calendarEvent();
$invoice = $this->entity;
$entityType = $invoice->getEntityType();
$data->title = trans("texts.{$entityType}") . ' ' . $invoice->invoice_number . ' | ' . $this->amount() . ' | ' . $this->client();
$data->start = $invoice->invoice_date;
$data->borderColor = $data->backgroundColor = $invoice->isQuote() ? 'orange' : 'blue';
return $data;
}
} }

View File

@ -45,4 +45,17 @@ class PaymentPresenter extends EntityPresenter
return trans('texts.payment_type_' . $this->entity->payment_type->name); return trans('texts.payment_type_' . $this->entity->payment_type->name);
} }
} }
public function calendarEvent()
{
$data = parent::calendarEvent();
$payment = $this->entity;
$invoice = $payment->invoice;
$data->title = trans('texts.payment') . ' ' . $invoice->invoice_number . ' | ' . $this->amount() . ' | ' . $this->client();
$data->start = $payment->payment_date;
$data->borderColor = $data->backgroundColor = 'green';
return $data;
}
} }

View File

@ -70,4 +70,32 @@ class TaskPresenter extends EntityPresenter
return $str . implode("\n", $times); return $str . implode("\n", $times);
} }
public function calendarEvent()
{
$data = parent::calendarEvent();
$task = $this->entity;
$data->title = trans('texts.task');
if ($project = $this->project()) {
$data->title .= ' | ' . $project;
}
$data->title .= ' | ' . $this->description();
$data->allDay = false;
$data->borderColor = $data->backgroundColor = 'purple';
$parts = json_decode($task->time_log) ?: [];
if (count($parts)) {
$first = $parts[0];
$start = $first[0];
$data->start = date('Y-m-d H:i:m', $start);
$last = $parts[count($parts) - 1];
$end = count($last) == 2 ? $last[1] : $last[0];
$data->end = date('Y-m-d H:i:m', $end);
}
return $data;
}
} }

View File

@ -35,7 +35,8 @@
"select2": "select2-dist#^4.0.3", "select2": "select2-dist#^4.0.3",
"mousetrap": "^1.6.0", "mousetrap": "^1.6.0",
"tablesorter": "jquery.tablesorter#^2.28.4", "tablesorter": "jquery.tablesorter#^2.28.4",
"card": "^2.1.1" "card": "^2.1.1",
"fullcalendar": "^3.5.1"
}, },
"resolutions": { "resolutions": {
"jquery": "~1.11" "jquery": "~1.11"

View File

@ -76,6 +76,10 @@ elixir(function(mix) {
bowerDir + '/tablesorter/dist/css/widget.grouping.min.css' bowerDir + '/tablesorter/dist/css/widget.grouping.min.css'
], 'public/css/tablesorter.css'); ], 'public/css/tablesorter.css');
mix.styles([
bowerDir + '/fullcalendar/dist/fullcalendar.css'
], 'public/css/fullcalendar.css');
/** /**
* JS configuration * JS configuration
@ -94,6 +98,10 @@ elixir(function(mix) {
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js' bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
], 'public/js/daterangepicker.min.js'); ], 'public/js/daterangepicker.min.js');
mix.scripts([
bowerDir + '/fullcalendar/dist/fullcalendar.js'
], 'public/js/fullcalendar.min.js');
mix.scripts([ mix.scripts([
bowerDir + '/card/dist/card.js', bowerDir + '/card/dist/card.js',
], 'public/js/card.min.js'); ], 'public/js/card.min.js');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
public/css/fullcalendar.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
public/js/fullcalendar.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -726,7 +726,7 @@ function calculateAmounts(invoice) {
} }
// calculate line item tax // calculate line item tax
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty)); var lineTotal = NINJA.parseFloat(item.cost) * NINJA.parseFloat(item.qty);
if (invoice.discount != 0) { if (invoice.discount != 0) {
if (parseInt(invoice.is_amount_discount)) { if (parseInt(invoice.is_amount_discount)) {
lineTotal -= roundToTwo((lineTotal/total) * invoice.discount); lineTotal -= roundToTwo((lineTotal/total) * invoice.discount);

View File

@ -2445,6 +2445,7 @@ $LANG = array(
'enable_sofort' => 'Accept EU bank transfers', 'enable_sofort' => 'Accept EU bank transfers',
'stripe_alipay_help' => 'These gateways also need to be activated in :link.', 'stripe_alipay_help' => 'These gateways also need to be activated in :link.',
'gocardless_webhook_help_link_text' => 'add this URL as an endpoint in GoCardless', 'gocardless_webhook_help_link_text' => 'add this URL as an endpoint in GoCardless',
'calendar' => 'Calendar',
); );

View File

@ -0,0 +1,83 @@
@extends('header')
@section('head')
@parent
<script src="{{ asset('js/select2.min.js') }}" type="text/javascript"></script>
<link href="{{ asset('css/select2.css') }}" rel="stylesheet" type="text/css"/>
<script src="{{ asset('js/fullcalendar.min.js') }}" type="text/javascript"></script>
<link href="{{ asset('css/fullcalendar.css') }}" rel="stylesheet" type="text/css"/>
@stop
@section('top-right')
<select class="form-control" style="width: 220px" id="entityTypeFilter" multiple="true">
@foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_PAYMENT, ENTITY_TASK, ENTITY_EXPENSE] as $value)
<option value="{{ $value }}">{{ trans("texts.{$value}") }}</option>
@endforeach
</select>
@stop
@section('content')
<div id='calendar'></div>
<script type="text/javascript">
$(function() {
// Setup state/status filter
$('#entityTypeFilter').select2({
placeholder: "{{ trans('texts.filter') }}",
/*
templateSelection: function(data, container) {
if (data.id == 'archived') {
$(container).css('color', '#fff');
$(container).css('background-color', '#f0ad4e');
$(container).css('border-color', '#eea236');
}
return data.text;
}
*/
}).on('change', function() {
/*
var filter = $('#statuses').val();
if (filter) {
filter = filter.join(',');
} else {
filter = '';
}
*/
}).maximizeSelect2Height();
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay,listWeek'
},
defaultDate: '{{ date('Y-m-d') }}',
//navLinks: true,
//editable: true,
eventLimit: true,
events: {
url: '{{ url('/calendar_events') }}',
type: 'GET',
data: {
custom_param1: 'something',
custom_param2: 'somethingelse'
},
error: function() {
alert('there was an error while fetching events!');
},
color: 'red', // a non-ajax option
textColor: 'white' // a non-ajax option
}
});
});
</script>
@stop

View File

@ -448,6 +448,10 @@
<div class="alert alert-danger">{!! Session::get('error') !!}</div> <div class="alert alert-danger">{!! Session::get('error') !!}</div>
@endif @endif
<div class="pull-right">
@yield('top-right')
</div>
@if (!isset($showBreadcrumbs) || $showBreadcrumbs) @if (!isset($showBreadcrumbs) || $showBreadcrumbs)
{!! Form::breadcrumbs((isset($entity) && $entity->exists) ? $entity->present()->statusLabel : false) !!} {!! Form::breadcrumbs((isset($entity) && $entity->exists) ? $entity->present()->statusLabel : false) !!}
@endif @endif