Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -1,34 +1,17 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
|
||||
{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %}
|
||||
{% block report_form_layout %}
|
||||
{{ form_widget(form.date) }}
|
||||
{{ form_widget(form.customer, {'label': false, 'placeholder': 'please_choose'}) }}
|
||||
{{ form_widget(form.sumType) }}
|
||||
{% from '@theme/components/buttons.html.twig' import submit_button %}
|
||||
{{ submit_button('download', {'attr': {'formaction': path(export_route)}, 'icon': 'download', 'combined': false}, 'primary') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% embed '@theme/embeds/card.html.twig' %}
|
||||
{% from "macros/widgets.html.twig" import nothing_found %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline form-reporting', 'id': 'report-toolbar-form'}}) }}
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{{ form_widget(form.date) }}
|
||||
{% if form.sumType.vars.choices|length > 1 %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'display'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
<li>
|
||||
{{ form_widget(form.sumType) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-primary" formaction="{{ path(export_route) }}" type="submit"><i class="{{ 'download'|icon }}"></i></button>
|
||||
{% endblock %}
|
||||
{% block box_body_class %} table-responsive {% if stats is not empty %}no-padding{% endif %}{% endblock %}
|
||||
{% block box_body_class %} table-responsive {% if stats is not empty %}p-0{% endif %}{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if stats is empty %}
|
||||
{{ nothing_found() }}
|
||||
@@ -39,14 +22,4 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
jQuery('#report-toolbar-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
{% if dataType == 'rate' %}
|
||||
{% set dataTypeTitle = 'stats.amountTotal' %}
|
||||
{% elseif dataType == 'internalRate' %}
|
||||
{% set dataTypeTitle = 'label.rate_internal' %}
|
||||
{% set dataTypeTitle = 'internalRate' %}
|
||||
{% else %}
|
||||
{% set dataTypeTitle = 'stats.durationTotal' %}
|
||||
{% endif %}
|
||||
<table class="table table-bordered dataTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2" style="{{ rowspanStyle }}">{{ 'label.project'|trans }}</th>
|
||||
<th rowspan="2" style="{{ rowspanStyle }}">{{ 'project'|trans }}</th>
|
||||
{% for activity in stats.activities %}
|
||||
<th colspan="3" class="text-center">{{ activity.name }}</th>
|
||||
{% endfor %}
|
||||
@@ -22,7 +22,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
{% for activity in stats.activities %}
|
||||
<th class="text-center">{{ 'label.user'|trans }}</th>
|
||||
<th class="text-center">{{ 'user'|trans }}</th>
|
||||
<th class="text-center">{{ dataTypeTitle|trans }}</th>
|
||||
<th class="text-center">{{ 'sum.total'|trans }}</th>
|
||||
{% endfor %}
|
||||
|
||||
26
templates/reporting/index.html.twig
Normal file
26
templates/reporting/index.html.twig
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block page_actions %}
|
||||
{% set event = actions(app.user, 'reporting', 'report') %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row row-cards">
|
||||
|
||||
{% for report in reports %}
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body p-4 text-center">
|
||||
<span class="avatar avatar-xl mb-3 avatar-rounded"><i class="{{ report.reportIcon|icon(false, report.reportIcon) }}" style="color: {{ null|colorize(report.reportIcon) }}"></i></span>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<a href="{{ path(report.route) }}" class="card-btn">{{ report.label|trans({}, 'reporting') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -2,16 +2,45 @@
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block page_title %}{{ 'menu.reporting'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{% block report_title %}{% endblock %}{% endblock %}
|
||||
{% block page_actions %}
|
||||
{% set event = actions(app.user, 'reporting', 'index') %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{% from "macros/breadcrumb.html.twig" import breadcrumb %}
|
||||
{{ breadcrumb({
|
||||
('menu.reporting'|trans): path('reporting'),
|
||||
(report_title|trans({}, 'reporting')): '#'
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if form is defined %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
{% block report_form %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-reporting', 'id': 'report-form'}}) }}
|
||||
<div class="{% block report_form_container_class %}btn-list w-100{% endblock %}">
|
||||
{% block report_form_layout %}{{ form_rest(form) }}{% endblock %}
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="reporting-content">
|
||||
{% block report %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
const reportForm = document.getElementById('report-form');
|
||||
if (reportForm !== null) {
|
||||
reportForm.addEventListener('change', (event) => {
|
||||
reportForm.submit();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% block report_title %}{{ 'report_project_daterange'|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% set showMoneyBudget = is_granted('budget_money', 'project') %}
|
||||
{% set showTimeBudget = is_granted('budget_time', 'project') %}
|
||||
{% set viewRevenue = is_granted('view_rate_other_timesheet') %}
|
||||
@@ -12,12 +10,20 @@
|
||||
} %}
|
||||
{% if showTimeBudget %}
|
||||
{% set columns = columns|merge({
|
||||
'timeBudget': {'class': 'hidden-xs', 'title': 'label.timeBudget'|trans},
|
||||
'timeBudget': {'class': 'd-none d-md-table-cell', 'title': 'timeBudget'|trans},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% if showMoneyBudget %}
|
||||
{% set columns = columns|merge({
|
||||
'budget': {'class': 'hidden-xs', 'title': 'label.budget'|trans},
|
||||
'budget': {'class': 'd-none d-md-table-cell', 'title': 'budget'|trans},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% set columns = columns|merge({
|
||||
'duration': {'class': 'text-end hw-min w-min', 'title': 'stats.durationMonth'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% if showMoneyBudget %}
|
||||
{% set columns = columns|merge({
|
||||
'rate': {'class': 'text-end hw-min w-min', 'title': 'stats.amountMonth'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% set columns = columns|merge({
|
||||
@@ -29,7 +35,7 @@
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% set columns = columns|merge({
|
||||
'billable': {'class': 'text-center hw-min', 'columnClass': 'w-min'},
|
||||
'billable': {'class': 'd-none d-sm-table-cell text-center hw-min w-min', 'columnClass': 'w-min'},
|
||||
'actions': {'class': 'actions alwaysVisible'},
|
||||
}) %}
|
||||
{% set tableName = 'project_daterange_reporting' %}
|
||||
@@ -38,52 +44,46 @@
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report_form_layout %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-reporting', 'id': 'report-form'}}) }}
|
||||
<div class="btn-list w-100">
|
||||
{{ form_widget(form.month, {'label': false}) }}
|
||||
{{ form_widget(form.customer, {'label': false, 'placeholder': 'please_choose'}) }}
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ icon('filter', true) }}
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
{% for option in form.budgetType.children %}
|
||||
<li class="dropdown-item">
|
||||
{{ form_widget(option) }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="dropdown-divider"></li>
|
||||
<li class="dropdown-item">
|
||||
{{ form_widget(form.includeNoWork) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% set hasData = entries|length > 0 %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% embed '@theme/embeds/card.html.twig' %}
|
||||
{% import "macros/progressbar.html.twig" as progress %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
{% import "project/actions.html.twig" as projectActions %}
|
||||
{% block box_body_class %}{{ tableName }}-box {% if hasData %}no-padding{% endif %}{% endblock %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline form-reporting', 'id': 'project-daterange-form'}}) }}
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
{% block box_tools %}
|
||||
{{ widgets.action_button('visibility', {'modal': ('#modal_' ~ tableName), 'class': 'btn-sm'}) }}
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{% if form.customer is defined %}
|
||||
{{ form_widget(form.customer) }}
|
||||
{% endif %}
|
||||
{% if form.month is defined %}
|
||||
{{ form_widget(form.month) }}
|
||||
{% endif %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'filter'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
<li>
|
||||
{{ form_widget(form.includeNoWork) }}
|
||||
</li>
|
||||
<li>
|
||||
{{ form_widget(form.budgetType) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{{ form_rest(form) }}
|
||||
{% endblock %}
|
||||
{% block box_body_class %}{{ tableName }}-box {% if hasData %}p-0{% endif %}{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if not hasData %}
|
||||
{{ widgets.nothing_found() }}
|
||||
{% else %}
|
||||
{{ tables.datatable_header(tableName, columns, null, {'bordered': true, 'boxClass': ''}) }}
|
||||
{{ tables.datatable_header(tableName, columns, null, {'boxClass': ''}) }}
|
||||
|
||||
{% for id, mapping in entries|sort((a, b) => a.customer.name <=> b.customer.name) %}
|
||||
<tr class="summary">
|
||||
@@ -125,14 +125,3 @@
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#project-daterange-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
{% import "macros/charts.html.twig" as charts %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block report_title %}{{ 'report_project_details'|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% set tableName = tableName|default('project_details_reporting') %}
|
||||
{% set tableId = 'project-details-form' %}
|
||||
{% set showMoneyBudget = project_details is not null and is_granted('budget', project_details.project) %}
|
||||
@@ -23,35 +21,31 @@
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
{% set options = {'label': 'duration', 'title': 'name', 'legend': 'false'} %}
|
||||
{% set options = {'label': 'duration', 'title': 'name', 'legend': {'display': false}} %}
|
||||
{% if view_revenue %}
|
||||
{% set options = options|merge({'footer': 'rate'}) %}
|
||||
{% endif %}
|
||||
{{ charts.doughnut_javascript(options) }}
|
||||
{{ charts.bar_javascript({'legend': 'false'}) }}
|
||||
{{ charts.bar_javascript({'legend': {'display': false}}) }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#{{ tableId }}').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
[].slice.call(document.querySelectorAll('a[data-bs-toggle="tab"]')).forEach(function (triggerEl) {
|
||||
triggerEl.addEventListener('shown.bs.tab', function (event) {
|
||||
renderChart(event.target.dataset.chart);
|
||||
});
|
||||
{% if project is not null %}
|
||||
renderChart('project{{ project.id }}Budget');
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
jQuery('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
renderChart(e.target.dataset.chart);
|
||||
});
|
||||
|
||||
function renderChart(chartName)
|
||||
{
|
||||
var found = false;
|
||||
let found = false;
|
||||
|
||||
Chart.helpers.each(Chart.instances, function(instance){
|
||||
if (instance.chart.canvas.id === chartName) {
|
||||
for (const instance of Object.values(Chart.instances)) {
|
||||
if (instance.canvas.id === chartName) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
document.dispatchEvent(new Event('render.' + chartName))
|
||||
@@ -60,29 +54,16 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block report_form_layout %}
|
||||
{{ form_widget(form.project, {'label': false, 'placeholder': 'please_choose'}) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
{% set hasData = project is not null and project_view is not null %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline form-reporting', 'id': tableId}}) }}
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{{ form_widget(form) }}
|
||||
{% endblock %}
|
||||
{% block box_body %}{% endblock %}
|
||||
{% block box_body_class %}hidden{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% if not hasData %}
|
||||
{{ widgets.nothing_found() }}
|
||||
{% endif %}
|
||||
|
||||
{% if hasData %}
|
||||
{% else %}
|
||||
{{ _self.project_details(project, project_view, project_details, showMoneyBudget, showTimeBudget, view_revenue, see_users) }}
|
||||
{% set currency = project.customer.currency %}
|
||||
|
||||
@@ -109,19 +90,29 @@
|
||||
{% set rates = rates|merge([{'label': rate|money(currency), 'value': rate}]) %}
|
||||
{% endfor -%}
|
||||
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs pull-right">
|
||||
{% if see_users %}
|
||||
<li><a data-chart="{{ chartPrefix }}User" href="#user-chart-{{ id}}" data-toggle="tab">{{ 'label.user'|trans }}</a></li>
|
||||
{% endif %}
|
||||
<li><a data-chart="{{ chartPrefix }}Activity" href="#activity-chart-{{ id}}" data-toggle="tab">{{ 'label.activity'|trans }}</a></li>
|
||||
{% if view_revenue %}
|
||||
<li><a data-chart="{{ chartPrefix }}Rate" href="#revenue-chart-{{ id }}" data-toggle="tab">{{ 'stats.revenue'|trans }}</a></li>
|
||||
{% endif %}
|
||||
<li class="active"><a data-chart="{{ chartPrefix }}Duration" href="#time-chart-{{ id }}" data-toggle="tab">{{ 'stats.workingTime'|trans }}</a></li>
|
||||
<li class="pull-left header">{{ title }} {{ widgets.label(yearStat.duration|duration, 'gray') }}{% if view_revenue %} {{ widgets.label(yearStat.rate|money(currency), 'gray') }}{% endif %}</small></li>
|
||||
</ul>
|
||||
<div class="tab-content no-padding">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title me-3">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<ul class="nav nav-pills" data-bs-toggle="tabs">
|
||||
<li class="nav-item"><a class="nav-link active" data-chart="{{ chartPrefix }}Duration" href="#time-chart-{{ id }}" data-bs-toggle="tab">{{ 'stats.workingTime'|trans }}</a></li>
|
||||
{% if view_revenue %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}Rate" href="#revenue-chart-{{ id }}" data-bs-toggle="tab">{{ 'stats.revenue'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if see_users %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}User" href="#user-chart-{{ id}}" data-bs-toggle="tab">{{ 'user'|trans }}</a></li>
|
||||
{% endif %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}Activity" href="#activity-chart-{{ id}}" data-bs-toggle="tab">{{ 'activity'|trans }}</a></li>
|
||||
</ul>
|
||||
<div class="card-actions">
|
||||
{{ widgets.badge_counter(yearStat.duration|duration) }}
|
||||
{% if view_revenue %}
|
||||
{{ widgets.badge_counter(yearStat.rate|money(currency)) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content mt-2 p-2">
|
||||
<div class="chart tab-pane active" id="time-chart-{{ id }}">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
@@ -155,7 +146,7 @@
|
||||
<div class="col-xs-12 table-responsive">
|
||||
<table class="table table-hover dataTable">
|
||||
<tr>
|
||||
<th>{{ 'label.username'|trans }}</th>
|
||||
<th>{{ 'username'|trans }}</th>
|
||||
<th></th>
|
||||
{% for monthName in month_names() %}
|
||||
<th class="text-center">{{ monthName }}</th>
|
||||
@@ -228,17 +219,17 @@
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{{ widgets.label_activity(stat.activity, {'inherit': false, 'random': true}) }}</td>
|
||||
<td class="text-nowrap text-right">{{ stat.duration|duration }}</td>
|
||||
<td class="text-nowrap text-end">{{ stat.duration|duration }}</td>
|
||||
{% if view_revenue %}
|
||||
<td class="text-nowrap text-right">{{ stat.rate|money(currency) }}</td>
|
||||
<td class="text-nowrap text-end">{{ stat.rate|money(currency) }}</td>
|
||||
{% endif %}
|
||||
<td class="text-nowrap text-right">{{ percentage|number_format(1) }} %</td>
|
||||
<td class="text-nowrap text-end">{{ percentage|number_format(1) }} %</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-6">
|
||||
{% set chartOptions = {'height': (dataset|length > 12 ? '600px' : '300px'), 'legend': {'position': (dataset|length > 12 ? 'top' : 'right')}, 'renderEvent': 'render.' ~ chartPrefix ~ 'Activity'} %}
|
||||
{% set chartOptions = {'height': (dataset|length > 12 ? '600px' : '300px'), 'renderEvent': 'render.' ~ chartPrefix ~ 'Activity'} %}
|
||||
{{ charts.doughnut_chart(chartPrefix ~ 'Activity', labels, dataset, chartOptions) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -266,106 +257,116 @@
|
||||
{% set rates = rates|merge([{'label': year.rate|money(currency), 'value': year.rate}]) %}
|
||||
{% set durations = durations|merge([{'label': year.duration|duration, 'value': year.duration|chart_duration}]) %}
|
||||
{% endfor -%}
|
||||
{% set showTotalDurationChart = project_view.durationTotal > 0 and years|length > 1 %}
|
||||
{% set showTotalRevenueChart = view_revenue and project_view.rateTotal > 0 and years|length > 1 %}
|
||||
|
||||
<div class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs pull-right">
|
||||
{% if see_users and project_details.userStats|length > 0 %}
|
||||
<li><a data-chart="{{ chartPrefix }}User" href="#user-chart" data-toggle="tab">{{ 'label.user'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if activities is not empty %}
|
||||
<li><a data-chart="{{ chartPrefix }}Activity" href="#activity-chart" data-toggle="tab">{{ 'label.activity'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if view_revenue and project_view.rateTotal > 0 %}
|
||||
<li><a data-chart="{{ chartPrefix }}Rate" href="#revenue-chart" data-toggle="tab">{{ 'stats.revenue'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if project_view.durationTotal > 0 %}
|
||||
<li><a data-chart="{{ chartPrefix }}Duration" href="#time-chart" data-toggle="tab">{{ 'stats.workingTime'|trans }}</a></li>
|
||||
{% endif %}
|
||||
<li class="active"><a href="#details-chart" data-toggle="tab">{{ 'report_project_details'|trans({}, 'reporting') }}</a></li>
|
||||
<li class="pull-left header hidden-xs">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title me-3">
|
||||
{{ widgets.label_project(project, {'inherit': false}) }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content no-padding">
|
||||
</h3>
|
||||
<ul class="nav nav-pills" data-bs-toggle="tabs">
|
||||
<li class="nav-item"><a class="nav-link active" href="#details-chart" data-bs-toggle="tab">{{ 'report_project_details'|trans({}, 'reporting') }}</a></li>
|
||||
{% if showTotalDurationChart %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}Duration" href="#time-chart" data-bs-toggle="tab">{{ 'stats.workingTime'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if showTotalRevenueChart %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}Rate" href="#revenue-chart" data-bs-toggle="tab">{{ 'stats.revenue'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if see_users and project_details.userStats|length > 0 %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}User" href="#user-chart" data-bs-toggle="tab">{{ 'user'|trans }}</a></li>
|
||||
{% endif %}
|
||||
{% if activities is not empty %}
|
||||
<li class="nav-item"><a class="nav-link" data-chart="{{ chartPrefix }}Activity" href="#activity-chart" data-bs-toggle="tab">{{ 'activity'|trans }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="card-actions">
|
||||
{{ widgets.badge_counter(project_view.durationTotal|duration) }}
|
||||
{% if view_revenue %}
|
||||
{{ widgets.badge_counter(project_view.rateTotal|money(currency)) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content mt-2 p-2 ps-3">
|
||||
<div class="chart tab-pane active" id="details-chart">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<table class="table table-hover dataTable">
|
||||
<tr {{ widgets.customer_row_attr(project.customer) }}>
|
||||
<th class="w-min">
|
||||
{{ 'label.customer'|trans }}
|
||||
</th>
|
||||
<td>
|
||||
<div class="col-xs-12 col-sm-6 col-md-7 col-lg-7 col-xl-6 col-xxl-5">
|
||||
<div class="datagrid" style="--tblr-datagrid-item-width: 12rem">
|
||||
<div class="datagrid-item" {{ widgets.customer_row_attr(project.customer) }}>
|
||||
<div class="datagrid-title">
|
||||
{{ 'customer'|trans }}
|
||||
</div>
|
||||
<div class="datagrid-content">
|
||||
{{ widgets.label_customer(project.customer) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr {{ widgets.project_row_attr(project) }}>
|
||||
<th class="w-min">
|
||||
{{ 'label.project'|trans }}
|
||||
</th>
|
||||
<td>
|
||||
</div>
|
||||
</div>
|
||||
<div class="datagrid-item" {{ widgets.project_row_attr(project) }}>
|
||||
<div class="datagrid-title">
|
||||
{{ 'project'|trans }}
|
||||
</div>
|
||||
<div class="datagrid-content">
|
||||
{{ widgets.label_project(project) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">
|
||||
{{ 'stats.durationTotal'|trans }}
|
||||
</th>
|
||||
<td>{{ project_view.durationTotal|duration }}</td>
|
||||
</tr>
|
||||
</div>
|
||||
<div class="datagrid-content">{{ project_view.durationTotal|duration }}</div>
|
||||
</div>
|
||||
{% if view_revenue %}
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">
|
||||
{{ 'stats.amountTotal'|trans }}
|
||||
</th>
|
||||
<td>{{ project_view.rateTotal|money(currency) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
{{ 'label.billable'|trans }}
|
||||
</th>
|
||||
<td>{{ project_view.billableRate|money(currency) }}</td>
|
||||
</tr>
|
||||
</div>
|
||||
<div class="datagrid-content">{{ project_view.rateTotal|money(currency) }}</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">
|
||||
{{ 'billable'|trans }}
|
||||
</div>
|
||||
<div class="datagrid-content">{{ project_view.billableRate|money(currency) }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
{{ 'label.last_record'|trans }}
|
||||
</th>
|
||||
<td>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">
|
||||
{{ 'last_record'|trans }}
|
||||
</div>
|
||||
<div class="datagrid-content">
|
||||
{% if project_view.lastRecord is not null %}
|
||||
{{ project_view.lastRecord|date_short }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</div>
|
||||
{% if is_granted('create_export') %}
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
{{ 'label.not_exported'|trans }}
|
||||
</th>
|
||||
<td>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">
|
||||
{{ 'not_exported'|trans }}
|
||||
</div>
|
||||
<div class="datagrid-content">
|
||||
<a href="{{ path('export', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': '', 'preview': true}) }}">
|
||||
{{ project_view.notExportedDuration|duration }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if is_granted('view_invoice') %}
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
{{ 'label.not_invoiced'|trans }}
|
||||
</th>
|
||||
<td>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">
|
||||
{{ 'not_invoiced'|trans }}
|
||||
</div>
|
||||
<div class="datagrid-content">
|
||||
<a href="{{ path('invoice', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': ''}) }}">
|
||||
{{ project_view.notBilledRate|money(currency) }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="col-xs-12 col-sm-6 col-md-5 col-lg-5 col-xl-6 col-xxl-7">
|
||||
{% if (showMoneyBudget and project.hasBudget()) or (showTimeBudget and project.hasTimeBudget()) %}
|
||||
{% from "project/charts.html.twig" import project_budget %}
|
||||
{{ project_budget(project, project_details, chartPrefix) }}
|
||||
@@ -376,13 +377,13 @@
|
||||
{% set budgetStats = project_details.budgetStatisticModel %}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<table class="table table-hover dataTable">
|
||||
<table class="table table-borderless">
|
||||
{% if showTimeBudget and project.hasTimeBudget() %}
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
{{ 'label.timeBudget'|trans }}
|
||||
{{ 'timeBudget'|trans }}
|
||||
{% if budgetStats.isMonthlyBudget() %}
|
||||
({{ 'label.budgetType_month'|trans }})
|
||||
({{ 'budgetType_month'|trans }})
|
||||
{% endif %}
|
||||
</th>
|
||||
<td>
|
||||
@@ -393,9 +394,9 @@
|
||||
{% if showMoneyBudget and project.hasBudget() %}
|
||||
<tr>
|
||||
<th class="w-min">
|
||||
{{ 'label.budget'|trans }}
|
||||
{{ 'budget'|trans }}
|
||||
{% if budgetStats.isMonthlyBudget() %}
|
||||
({{ 'label.budgetType_month'|trans }})
|
||||
({{ 'budgetType_month'|trans }})
|
||||
{% endif %}
|
||||
</th>
|
||||
<td>
|
||||
@@ -409,15 +410,15 @@
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% if view_revenue and project_view.rateTotal > 0 %}
|
||||
{% if showTotalDurationChart %}
|
||||
<div class="chart tab-pane" id="time-chart">
|
||||
{{ charts.bar_chart(chartPrefix ~ 'Duration', labels, [durations], {'height': '300px', 'renderEvent': 'render.' ~ chartPrefix ~ 'Duration'}) }}
|
||||
</div>
|
||||
{% if showTotalRevenueChart %}
|
||||
<div class="chart tab-pane" id="revenue-chart">
|
||||
{{ charts.bar_chart(chartPrefix ~ 'Rate', labels, [rates], {'height': '300px', 'renderEvent': 'render.' ~ chartPrefix ~ 'Rate'}) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if project_view.durationTotal > 0 %}
|
||||
<div class="chart tab-pane" id="time-chart">
|
||||
{{ charts.bar_chart(chartPrefix ~ 'Duration', labels, [durations], {'height': '300px', 'renderEvent': 'render.' ~ chartPrefix ~ 'Duration'}) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if activities is not empty %}
|
||||
<div class="chart tab-pane" id="activity-chart">
|
||||
@@ -447,31 +448,31 @@
|
||||
<td>
|
||||
{{ widgets.label_dot(user.displayName, user.color) }}
|
||||
</th>
|
||||
<td class="text-nowrap text-right">
|
||||
<td class="text-nowrap text-end">
|
||||
{{ userStat.duration|duration }}
|
||||
</td>
|
||||
{% if view_revenue %}
|
||||
<td class="text-nowrap text-right">
|
||||
<td class="text-nowrap text-end">
|
||||
{{ userStat.rate|money(currency) }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-nowrap text-right">
|
||||
<td class="text-nowrap text-end">
|
||||
{{ percentage|number_format(1) }} %
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<th class="text-nowrap text-right total">{{ totalDuration|duration }}</th>
|
||||
<th class="text-nowrap text-end total">{{ totalDuration|duration }}</th>
|
||||
{% if view_revenue %}
|
||||
<th class="text-nowrap text-right total">{{ rateTotal|money(currency) }}</th>
|
||||
<th class="text-nowrap text-end total">{{ rateTotal|money(currency) }}</th>
|
||||
{% endif %}
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-6">
|
||||
{% set chartOptions = {'height': (datasets|length > 12 ? '600px' : '300px'), 'legend': {'position': (datasets|length > 12 ? 'top' : 'right')}, 'renderEvent': 'render.' ~ chartPrefix ~ 'User'} %}
|
||||
{% set chartOptions = {'height': (datasets|length > 12 ? '600px' : '300px'), 'renderEvent': 'render.' ~ chartPrefix ~ 'User'} %}
|
||||
{{ charts.doughnut_chart(chartPrefix ~ 'User', labels, datasets, chartOptions) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
8
templates/reporting/project_inactive.html.twig
Normal file
8
templates/reporting/project_inactive.html.twig
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'reporting/project_list_data.html.twig' %}
|
||||
|
||||
{% block report_form_layout %}
|
||||
{% form_theme form 'form/horizontal.html.twig' %}
|
||||
{{ form_row(form.lastChange, {'row_attr': {'class': 'row'}}) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report_form_container_class %}w-100{% endblock %}
|
||||
134
templates/reporting/project_list_data.html.twig
Normal file
134
templates/reporting/project_list_data.html.twig
Normal file
@@ -0,0 +1,134 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
|
||||
{% set showMoneyBudget = is_granted('budget_money', 'project') %}
|
||||
{% set showTimeBudget = is_granted('budget_time', 'project') %}
|
||||
|
||||
{% set availableColumns = {
|
||||
'name': {'class': 'alwaysVisible'},
|
||||
} %}
|
||||
{% if showTimeBudget %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'timeBudget': {'class': 'd-none d-md-table-cell', 'title': 'timeBudget'|trans},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% if showMoneyBudget %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'budget': {'class': 'd-none d-md-table-cell', 'title': 'budget'|trans},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'lastRecord': {'class': 'd-none d-sm-table-cell text-start hw-min w-min', 'title': 'last_record'|trans, 'columnClass': 'w-min'},
|
||||
'today': {'class': 'd-none text-end hw-min w-min', 'title': 'stats.durationToday'|trans},
|
||||
'week': {'class': 'd-none text-end hw-min w-min', 'title': 'stats.durationWeek'|trans},
|
||||
'month': {'class': 'd-none d-lg-table-cell text-end hw-min w-min', 'title': 'stats.durationMonth'|trans},
|
||||
'durationTotal': {'class': 'text-end hw-min w-min', 'title': 'stats.durationTotal'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% if showTimeBudget and is_granted('create_export') %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'exported': {'class': 'd-none d-xl-table-cell text-end hw-min w-min', 'title': 'not_exported'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% if showMoneyBudget and is_granted('view_invoice') %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'invoiced': {'class': 'd-none d-xl-table-cell text-end hw-min w-min', 'title': 'not_invoiced'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'projectStart': {'class': 'd-none text-start hw-min w-min', 'title': 'project_start'|trans},
|
||||
'projectEnd': {'class': 'd-none text-start hw-min w-min', 'title': 'project_end'|trans},
|
||||
'comment': {'class': 'd-none', 'title': 'comment'|trans},
|
||||
'actions': {'class': 'actions alwaysVisible'},
|
||||
}) %}
|
||||
{% set tableName = tableName|default('project_view_reporting') %}
|
||||
{% set skipColumns = skipColumns is defined ? skipColumns : {} %}
|
||||
{% set columns = {} %}
|
||||
{% for name, config in availableColumns %}
|
||||
{% if name not in skipColumns %}
|
||||
{% set columns = columns|merge({(name): config}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% set hasData = entries|length > 0 %}
|
||||
|
||||
{% embed '@theme/embeds/card.html.twig' %}
|
||||
{% import "macros/progressbar.html.twig" as progress %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
{% import "project/actions.html.twig" as projectActions %}
|
||||
{% block box_body_class %}{{ tableName }}-box {% if hasData %}p-0{% endif %}{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if not hasData %}
|
||||
{{ widgets.nothing_found() }}
|
||||
{% else %}
|
||||
{{ tables.datatable_header(tableName, columns, null, {boxClass: ''}) }}
|
||||
|
||||
{% for id, mapping in entries|sort((a, b) => a.customer.name <=> b.customer.name) %}
|
||||
<tr class="summary">
|
||||
<td colspan="{{ columns|length }}">{{ widgets.label_customer(mapping.customer) }}</td>
|
||||
</tr>
|
||||
{% for entry in mapping.projects|sort((a, b) => a.project.name <=> b.project.name) %}
|
||||
{% set project = entry.project %}
|
||||
{% set budgetStats = entry.getBudgetStatisticModel() %}
|
||||
{% set currency = project.customer.currency %}
|
||||
<tr {{ widgets.project_row_attr(project, now) }}>
|
||||
{% for name, column_config in columns %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, name) }}">
|
||||
{% if name == 'name' %}
|
||||
{{ widgets.label_project(project) }}
|
||||
{% elseif name == 'lastRecord' %}
|
||||
{% if entry.lastRecord is not null %}
|
||||
{{ entry.lastRecord|date_short }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
{% elseif name == 'today' %}
|
||||
{{ entry.durationDay|duration }}
|
||||
{% elseif name == 'week' %}
|
||||
{{ entry.durationWeek|duration }}
|
||||
{% elseif name == 'month' %}
|
||||
{{ entry.durationMonth|duration }}
|
||||
{% elseif name == 'durationTotal' %}
|
||||
{{ entry.durationTotal|duration }}
|
||||
{% elseif name == 'timeBudget' %}
|
||||
{% if budgetStats.hasTimeBudget() and is_granted('time', project) %}
|
||||
{{ progress.progressbar_timebudget(budgetStats) }}
|
||||
{% endif %}
|
||||
{% elseif name == 'budget' %}
|
||||
{% if project.hasBudget() and is_granted('budget', project) %}
|
||||
{{ progress.progressbar_budget(budgetStats, project.customer.currency) }}
|
||||
{% endif %}
|
||||
{% elseif name == 'exported' %}
|
||||
<a href="{{ path('export', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': '', 'preview': true}) }}">
|
||||
{{ entry.notExportedDuration|duration }}
|
||||
</a>
|
||||
{% elseif name == 'invoiced' %}
|
||||
<a href="{{ path('invoice', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': ''}) }}">
|
||||
{{ entry.notBilledRate|money(currency) }}
|
||||
</a>
|
||||
{% elseif name == 'projectStart' %}
|
||||
{% if project.start is not null %}{{ project.start|date_short }}{% endif %}
|
||||
{% elseif name == 'projectEnd' %}
|
||||
{% if project.end is not null %}{{ project.end|date_short }}{% endif %}
|
||||
{% elseif name == 'comment' %}
|
||||
{{ project.comment }}
|
||||
{% elseif name == 'actions' %}
|
||||
{{ projectActions.project(project, 'custom') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{{ tables.data_table_footer(entries) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,193 +1,21 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
{% extends 'reporting/project_list_data.html.twig' %}
|
||||
|
||||
{% block report_title %}{{ (title|default('report_project_view'))|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% set showMoneyBudget = is_granted('budget_money', 'project') %}
|
||||
{% set showTimeBudget = is_granted('budget_time', 'project') %}
|
||||
{% set showBudgets = showMoneyBudget or showTimeBudget %}
|
||||
|
||||
{% set availableColumns = {
|
||||
'name': {'class': 'alwaysVisible'},
|
||||
} %}
|
||||
{% if showMoneyBudget %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'budget': {'class': 'hidden-xs', 'title': 'label.budget'|trans},
|
||||
'invoiced': {'class': 'hidden-sm hidden-xs hidden text-center hw-min', 'title': 'label.not_invoiced'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% if showTimeBudget %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'timeBudget': {'class': 'hidden-xs', 'title': 'label.timeBudget'|trans},
|
||||
'exported': {'class': 'hidden-sm hidden-xs hidden text-center hw-min', 'title': 'label.not_exported'|trans, 'columnClass': 'w-min'},
|
||||
}) %}
|
||||
{% endif %}
|
||||
{% set availableColumns = availableColumns|merge({
|
||||
'lastRecord': {'class': 'text-center hw-min', 'title': 'label.last_record'|trans, 'columnClass': 'w-min'},
|
||||
'today': {'class': 'hidden hidden-md hidden-sm hidden-xs text-center hw-min', 'title': 'stats.durationToday'|trans},
|
||||
'week': {'class': 'hidden hidden-md hidden-sm hidden-xs text-center hw-min', 'title': 'stats.durationWeek'|trans},
|
||||
'month': {'class': 'hidden hidden-md hidden-sm hidden-xs text-center hw-min', 'title': 'stats.durationMonth'|trans},
|
||||
'durationTotal': {'class': 'text-center hw-min', 'title': 'stats.durationTotal'|trans, 'columnClass': 'w-min'},
|
||||
'projectStart': {'class': 'hidden-md hidden-sm hidden-xs hidden text-center w-min', 'title': 'label.project_start'|trans},
|
||||
'projectEnd': {'class': 'hidden-md hidden-sm hidden-xs hidden text-center w-min', 'title': 'label.project_end'|trans},
|
||||
'comment': {'class': 'hidden-md hidden-sm hidden-xs hidden', 'title': 'label.comment'|trans},
|
||||
'actions': {'class': 'actions alwaysVisible'},
|
||||
}) %}
|
||||
{% set tableName = tableName|default('project_view_reporting') %}
|
||||
{% set skipColumns = skipColumns is defined ? skipColumns : {} %}
|
||||
{% set columns = {} %}
|
||||
{% for name, config in availableColumns %}
|
||||
{% if name not in skipColumns %}
|
||||
{% set columns = columns|merge({(name): config}) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% set hasData = entries|length > 0 %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% import "macros/progressbar.html.twig" as progress %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
{% import "project/actions.html.twig" as projectActions %}
|
||||
{% block box_body_class %}{{ tableName }}-box {% if hasData %}no-padding{% endif %}{% endblock %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline form-reporting', 'id': 'project-view-form'}}) }}
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
{% block box_tools %}
|
||||
{{ widgets.action_button('visibility', {'modal': ('#modal_' ~ tableName), 'class': 'btn-sm'}) }}
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{% if form.customer is defined %}
|
||||
{{ form_widget(form.customer) }}
|
||||
{% endif %}
|
||||
{% if form.month is defined %}
|
||||
{{ form_widget(form.month) }}
|
||||
{% endif %}
|
||||
{% if form.includeNoBudget is defined or form.includeNoWork is defined %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'filter'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
{% if form.includeNoBudget is defined %}
|
||||
<li>
|
||||
{{ form_widget(form.includeNoBudget) }}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if form.includeNoWork is defined %}
|
||||
<li>
|
||||
{{ form_widget(form.includeNoWork) }}
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ form_rest(form) }}
|
||||
{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if not hasData %}
|
||||
{{ widgets.nothing_found() }}
|
||||
{% else %}
|
||||
{{ tables.datatable_header(tableName, columns, null, {'bordered': true, 'boxClass': ''}) }}
|
||||
|
||||
{% for id, mapping in entries|sort((a, b) => a.customer.name <=> b.customer.name) %}
|
||||
<tr class="summary">
|
||||
<td colspan="{{ columns|length }}">{{ widgets.label_customer(mapping.customer) }}</td>
|
||||
</tr>
|
||||
{% for entry in mapping.projects|sort((a, b) => a.project.name <=> b.project.name) %}
|
||||
{% set project = entry.project %}
|
||||
{% set budgetStats = entry.getBudgetStatisticModel() %}
|
||||
{% set currency = project.customer.currency %}
|
||||
<tr {{ widgets.project_row_attr(project, now) }}>
|
||||
{% for name, column_config in columns %}
|
||||
{% if name == 'name' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'name') }}">{{ widgets.label_project(project) }}</td>
|
||||
{% elseif name == 'lastRecord' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'lastRecord') }}">
|
||||
{% if entry.lastRecord is not null %}
|
||||
{{ entry.lastRecord|date_short }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
{% elseif name == 'today' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'today') }}">{{ entry.durationDay|duration }}</td>
|
||||
{% elseif name == 'week' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'week') }}">{{ entry.durationWeek|duration }}</td>
|
||||
{% elseif name == 'month' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'month') }}">{{ entry.durationMonth|duration }}</td>
|
||||
{% elseif name == 'durationTotal' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'durationTotal') }} w-min">{{ entry.durationTotal|duration }}</td>
|
||||
{% elseif name == 'timeBudget' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
|
||||
{% if budgetStats.hasTimeBudget() and is_granted('time', project) %}
|
||||
{{ progress.progressbar_timebudget(budgetStats) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% elseif name == 'budget' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
|
||||
{% if project.hasBudget() and is_granted('budget', project) %}
|
||||
{{ progress.progressbar_budget(budgetStats, project.customer.currency) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% elseif name == 'exported' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'exported') }}">
|
||||
{% if is_granted('create_export') %}
|
||||
<a href="{{ path('export', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': '', 'preview': true}) }}">
|
||||
{{ entry.notExportedDuration|duration }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ entry.notExportedDuration|duration }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% elseif name == 'invoiced' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'invoiced') }}">
|
||||
{% if is_granted('view_invoice') %}
|
||||
<a href="{{ path('invoice', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': ''}) }}">
|
||||
{{ entry.notBilledRate|money(currency) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ entry.notBilledRate|money(currency) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% elseif name == 'projectStart' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'projectStart') }}">{% if project.start is not null %}{{ project.start|date_short }}{% endif %}</td>
|
||||
{% elseif name == 'projectEnd' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'projectEnd') }}">{% if project.end is not null %}{{ project.end|date_short }}{% endif %}</td>
|
||||
{% elseif name == 'comment' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'comment') }}">{{ project.comment }}</td>
|
||||
{% elseif name == 'actions' %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'actions') }}">
|
||||
{{ projectActions.project(project, 'custom') }}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{{ tables.data_table_footer(entries) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#project-view-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% block report_form_layout %}
|
||||
{{ form_widget(form.customer, {'label': false, 'placeholder': 'please_choose'}) }}
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ icon('filter', true) }}
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
{% for option in form.budgetType.children %}
|
||||
<li class="dropdown-item">
|
||||
{{ form_widget(option) }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="dropdown-divider"></li>
|
||||
<li class="dropdown-item">
|
||||
{{ form_widget(form.includeNoWork) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,65 +1,20 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% extends 'reporting/report_by_user_layout.html.twig' %}
|
||||
|
||||
{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline', 'id': 'user-filter-form'}}) }}
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{% if form.user is defined %}
|
||||
{{ form_row(form.user, {'label': false}) }}
|
||||
{% else %}
|
||||
{{ widgets.username(user) }}
|
||||
{% endif %}
|
||||
{{ form_widget(form.date) }}
|
||||
<div class="btn-group"{% if form.sumType.vars.choices|length <= 1 %} style="display: none"{% endif %}>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'display'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
<li>
|
||||
{{ form_widget(form.sumType) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block box_body_class %}{{ box_id }} table-responsive no-padding{% endblock %}
|
||||
{% block box_body %}
|
||||
{% embed 'reporting/report_by_user_data.html.twig' %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap{% if column is weekend %} weekend{% endif %}{% if column is today %} today{% endif %}">
|
||||
{{ column|date_weekday }}
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block column_classes_project -%}
|
||||
{% if column.date is weekend %} weekend{% endif %}{% if column.date is today %} today{% endif %}
|
||||
{%- endblock %}
|
||||
{% block column_classes_activity -%}
|
||||
{% if column.date is weekend %} weekend{% endif %}{% if column.date is today %} today{% endif %}
|
||||
{%- endblock %}
|
||||
{% block column_classes_total -%}
|
||||
{% if column is weekend %} weekend{% endif %}
|
||||
{%- endblock %}
|
||||
{% endembed %}
|
||||
{% block report_content %}
|
||||
{% embed 'reporting/report_by_user_data.html.twig' %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap{% if column is weekend %} weekend{% endif %}{% if column is today %} today{% endif %}">
|
||||
{{ column|date_weekday }}
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block column_classes_project -%}
|
||||
{% if column.date is weekend %} weekend{% endif %}{% if column.date is today %} today{% endif %}
|
||||
{%- endblock %}
|
||||
{% block column_classes_activity -%}
|
||||
{% if column.date is weekend %} weekend{% endif %}{% if column.date is today %} today{% endif %}
|
||||
{%- endblock %}
|
||||
{% block column_classes_total -%}
|
||||
{% if column is weekend %} weekend{% endif %}
|
||||
{%- endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#user-filter-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
{% if dataType == 'rate' %}
|
||||
{% set dataTypeTitle = 'stats.amountTotal' %}
|
||||
{% elseif dataType == 'internalRate' %}
|
||||
{% set dataTypeTitle = 'label.rate_internal' %}
|
||||
{% set dataTypeTitle = 'internalRate' %}
|
||||
{% else %}
|
||||
{% set dataTypeTitle = 'stats.durationTotal' %}
|
||||
{% endif %}
|
||||
{% set columns = 2 %}
|
||||
{% set totalCurrency = false %}
|
||||
<table class="table table-bordered table-hover dataTable">
|
||||
<table class="table table-hover dataTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
|
||||
22
templates/reporting/report_by_user_layout.html.twig
Normal file
22
templates/reporting/report_by_user_layout.html.twig
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% block report_form_layout %}
|
||||
{{ form_widget(form.date) }}
|
||||
{% if form.user is defined %}
|
||||
{{ form_widget(form.user, {'label': false}) }}
|
||||
{% elseif app.user != user %}
|
||||
{{ widgets.username(user) }}
|
||||
{% endif %}
|
||||
{{ form_widget(form.sumType) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
{% embed '@theme/embeds/card.html.twig' with {report_content: block('report_content'), box_id: box_id, fullsize: true} only %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% block box_body_class %}{{ box_id }} table-responsive m-0{% endblock %}
|
||||
{% block box_body %}
|
||||
{{ report_content|raw }}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
@@ -1,62 +1,17 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% extends 'reporting/report_by_user_layout.html.twig' %}
|
||||
|
||||
{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline', 'id': 'user-filter-form'}}) }}
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{% if form.user is defined %}
|
||||
{{ form_row(form.user, {'label': false}) }}
|
||||
{% else %}
|
||||
{{ widgets.username(user) }}
|
||||
{% endif %}
|
||||
{{ form_widget(form.date) }}
|
||||
<div class="btn-group"{% if form.sumType.vars.choices|length <= 1 %} style="display: none"{% endif %}>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'display'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
<li>
|
||||
{{ form_widget(form.sumType) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block box_body_class %}{{ box_id }} table-responsive no-padding{% endblock %}
|
||||
{% block box_body %}
|
||||
{% embed 'reporting/report_by_user_data.html.twig' %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap">
|
||||
<a href="{{ path('report_user_month', {'date': column|report_date, 'sumType': dataType, 'user': user.id}) }}">
|
||||
{{ column|month_name }}<br>
|
||||
{{ column|date_format('Y') }}
|
||||
</a>
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block column_classes_project %}{% endblock %}
|
||||
{% block column_classes_activity %}{% endblock %}
|
||||
{% block column_classes_total %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% block report_content %}
|
||||
{% embed 'reporting/report_by_user_data.html.twig' %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap">
|
||||
<a href="{{ path('report_user_month', {'date': column|report_date, 'sumType': dataType, 'user': user.id}) }}">
|
||||
{{ column|month_name }}<br>
|
||||
{{ column|date_format('Y') }}
|
||||
</a>
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block column_classes_project %}{% endblock %}
|
||||
{% block column_classes_activity %}{% endblock %}
|
||||
{% block column_classes_total %}{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#user-filter-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,80 +1,30 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% extends 'reporting/report_user_list_layout.html.twig' %}
|
||||
|
||||
{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% from "macros/widgets.html.twig" import nothing_found %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline', 'id': 'user-list-filter-form'}}) }}
|
||||
{% block report_content %}
|
||||
{% embed 'reporting/user_list_period_data.html.twig' with {stats: stats, dataType: dataType, period_attribute: period_attribute, subReportRoute: subReportRoute, subReportDate: subReportDate, decimal: decimal} only %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap{% if column is weekend %} weekend{% endif %}{% if column is today %} today{% endif %}">
|
||||
{{ column|date_weekday }}
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% block total_rate_user %}
|
||||
<a href="{{ path(subReportRoute, {'sumType': dataType, 'date': subReportDate|report_date, 'user': userPeriod.user.id}) }}">{{ usersTotalRate|money }}</a>
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{{ form_widget(form.date) }}
|
||||
{% if form.team is defined %}
|
||||
{{ form_widget(form.team) }}
|
||||
{% endif %}
|
||||
{% if form.sumType.vars.choices|length > 1 %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'display'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
<li>
|
||||
{{ form_widget(form.sumType) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-primary" formaction="{{ path(export_route) }}" type="submit"><i class="{{ 'download'|icon }}"></i></button>
|
||||
{% block total_internal_rate_user %}
|
||||
<a href="{{ path(subReportRoute, {'sumType': dataType, 'date': subReportDate|report_date, 'user': userPeriod.user.id}) }}">{{ usersTotalInternalRate|money }}</a>
|
||||
{% endblock %}
|
||||
{% block box_body_class %}{{ box_id }} table-responsive {% if hasData %}no-padding{% endif %}{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if not hasData %}
|
||||
{{ nothing_found() }}
|
||||
{% else %}
|
||||
{% embed 'reporting/user_list_period_data.html.twig' %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap{% if column is weekend %} weekend{% endif %}{% if column is today %} today{% endif %}">
|
||||
{{ column|date_weekday }}
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block total_rate_user %}
|
||||
<a href="{{ path(subReportRoute, {'sumType': dataType, 'date': subReportDate|report_date, 'user': userPeriod.user.id}) }}">{{ usersTotalRate|money }}</a>
|
||||
{% endblock %}
|
||||
{% block total_internal_rate_user %}
|
||||
<a href="{{ path(subReportRoute, {'sumType': dataType, 'date': subReportDate|report_date, 'user': userPeriod.user.id}) }}">{{ usersTotalInternalRate|money }}</a>
|
||||
{% endblock %}
|
||||
{% block total_duration_user %}
|
||||
<a href="{{ path(subReportRoute, {'sumType': dataType, 'date': subReportDate|report_date, 'user': userPeriod.user.id}) }}">{{ usersTotalDuration|duration(decimal) }}</a>
|
||||
{% endblock %}
|
||||
{% block rate %}
|
||||
{{ period.totalRate|money }}
|
||||
{% endblock %}
|
||||
{% block internal_rate %}
|
||||
{{ period.totalInternalRate|money }}
|
||||
{% endblock %}
|
||||
{% block duration %}
|
||||
{{ period.totalDuration|duration(decimal) }}
|
||||
{% endblock %}
|
||||
{% block period_cell_class %}{% if period.date is weekend %} weekend{% endif %}{% if period.date is today %} today{% endif %}{% endblock %}
|
||||
{% endembed %}
|
||||
{% endif %}
|
||||
{% block total_duration_user %}
|
||||
<a href="{{ path(subReportRoute, {'sumType': dataType, 'date': subReportDate|report_date, 'user': userPeriod.user.id}) }}">{{ usersTotalDuration|duration(decimal) }}</a>
|
||||
{% endblock %}
|
||||
{% block rate %}
|
||||
{{ period.totalRate|money }}
|
||||
{% endblock %}
|
||||
{% block internal_rate %}
|
||||
{{ period.totalInternalRate|money }}
|
||||
{% endblock %}
|
||||
{% block duration %}
|
||||
{{ period.totalDuration|duration(decimal) }}
|
||||
{% endblock %}
|
||||
{% block period_cell_class %}{% if period.date is weekend %} weekend{% endif %}{% if period.date is today %} today{% endif %}{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#user-list-filter-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% embed 'reporting/user_list_period_data.html.twig' with {'showAccountNumber': true, 'stats': stats, 'dataType': dataType, 'period_attribute': period_attribute} only %}
|
||||
{% embed 'reporting/user_list_period_data.html.twig' with {'avatar': false, 'showAccountNumber': true, 'stats': stats, 'dataType': dataType, 'period_attribute': period_attribute} only %}
|
||||
{% set dataTypeFormat = '' %}
|
||||
{% if dataType == 'rate' or dataType == 'internalRate' %}
|
||||
{% set dataTypeFormat = ' data-format="' ~ constant('App\\Export\\Base\\AbstractSpreadsheetRenderer::RATE_FORMAT_NO_CURRENCY') ~ '"' %}
|
||||
|
||||
25
templates/reporting/report_user_list_layout.html.twig
Normal file
25
templates/reporting/report_user_list_layout.html.twig
Normal file
@@ -0,0 +1,25 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
|
||||
{% block report_form_layout %}
|
||||
{{ form_widget(form.date) }}
|
||||
{% if form.team is defined %}
|
||||
{{ form_widget(form.team, {'label': false, 'placeholder': 'please_choose'}) }}
|
||||
{% endif %}
|
||||
{{ form_widget(form.sumType) }}
|
||||
{% from '@theme/components/buttons.html.twig' import submit_button %}
|
||||
{{ submit_button('download', {'attr': {'formaction': path(export_route)}, 'icon': 'download', 'combined': false}, 'primary') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
{% embed '@theme/embeds/card.html.twig' with {report_content: block('report_content'), box_id: box_id, hasData: hasData, fullsize: hasData} only %}
|
||||
{% block box_body_class %}{{ box_id }} table-responsive m-0{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if not hasData %}
|
||||
{% from "macros/widgets.html.twig" import nothing_found %}
|
||||
{{ nothing_found() }}
|
||||
{% else %}
|
||||
{{ report_content|raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endblock %}
|
||||
@@ -1,94 +1,44 @@
|
||||
{% extends 'reporting/layout.html.twig' %}
|
||||
{% extends 'reporting/report_user_list_layout.html.twig' %}
|
||||
|
||||
{% block report_title %}{{ report_title|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% block report %}
|
||||
|
||||
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
|
||||
{% from "macros/widgets.html.twig" import nothing_found %}
|
||||
{% block box_before %}
|
||||
{{ form_start(form, {'attr': {'class': 'form-inline form-reporting', 'id': 'user-list-filter-form'}}) }}
|
||||
{% block report_content %}
|
||||
{% embed 'reporting/user_list_period_data.html.twig' with {stats: stats, dataType: dataType, period_attribute: period_attribute, subReportDate: subReportDate, decimal: decimal} only %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap">
|
||||
<a href="{{ path('report_monthly_users', {'date': column|report_date, 'sumType': dataType}) }}">
|
||||
{{ column|month_name }}<br>
|
||||
{{ column|date_format('Y') }}
|
||||
</a>
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block box_after %}
|
||||
{{ form_end(form) }}
|
||||
{% block total_rate_user %}
|
||||
<a href="{{ path('report_user_year', {'sumType': dataType, 'date': create_date(subReportDate.format('Y') ~'-01-01')|report_date, 'user': userPeriod.user.id}) }}">
|
||||
{{ usersTotalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block box_title %}
|
||||
{{ form_widget(form.date) }}
|
||||
{% if form.team is defined %}
|
||||
{{ form_widget(form.team) }}
|
||||
{% endif %}
|
||||
{% if form.sumType.vars.choices|length > 1 %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="{{ 'display'|icon }}"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu checkbox-menu">
|
||||
<li>
|
||||
{{ form_widget(form.sumType) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-primary" formaction="{{ path(export_route) }}" type="submit"><i class="{{ 'download'|icon }}"></i></button>
|
||||
{% block total_internal_rate_user %}
|
||||
<a href="{{ path('report_user_year', {'sumType': dataType, 'date': create_date(subReportDate.format('Y') ~'-01-01')|report_date, 'user': userPeriod.user.id}) }}">
|
||||
{{ usersTotalInternalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block box_body_class %}{{ box_id }} table-responsive {% if hasData %}no-padding{% endif %}{% endblock %}
|
||||
{% block box_body %}
|
||||
{% if not hasData %}
|
||||
{{ nothing_found() }}
|
||||
{% else %}
|
||||
{% embed 'reporting/user_list_period_data.html.twig' %}
|
||||
{% block period_name %}
|
||||
<th class="text-center text-nowrap">
|
||||
<a href="{{ path('report_monthly_users', {'date': column|report_date, 'sumType': dataType}) }}">
|
||||
{{ column|month_name }}<br>
|
||||
{{ column|date_format('Y') }}
|
||||
</a>
|
||||
</th>
|
||||
{% endblock %}
|
||||
{% block total_rate_user %}
|
||||
<a href="{{ path('report_user_year', {'sumType': dataType, 'date': create_date(query.date.format('Y') ~'-01-01')|report_date, 'user': userPeriod.user.id}) }}">
|
||||
{{ usersTotalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block total_internal_rate_user %}
|
||||
<a href="{{ path('report_user_year', {'sumType': dataType, 'date': create_date(query.date.format('Y') ~'-01-01')|report_date, 'user': userPeriod.user.id}) }}">
|
||||
{{ usersTotalInternalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block total_duration_user %}
|
||||
<a href="{{ path('report_user_year', {'sumType': dataType, 'date': create_date(query.date.format('Y') ~'-01-01')|report_date, 'user': userPeriod.user.id}) }}">
|
||||
{{ usersTotalDuration|duration(decimal) }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block rate %}
|
||||
<a href="{{ path('report_user_month', {'sumType': dataType, 'date': create_date(period.date.format('Y') ~'-'~period.date.format('m')~'-01')|report_date, 'user': userPeriod.user.id}) }}" data-toggle="tooltip" title="{{ 'label.billable'|trans }}: {{ period.billableDuration|duration(decimal) }}">
|
||||
{{ period.totalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block internal_rate %}
|
||||
<a href="{{ path('report_user_month', {'sumType': dataType, 'date': create_date(period.date.format('Y') ~'-'~period.date.format('m')~'-01')|report_date, 'user': userPeriod.user.id}) }}" data-toggle="tooltip" title="{{ 'label.billable'|trans }}: {{ period.billableDuration|duration(decimal) }}">
|
||||
{{ period.totalInternalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block duration %}
|
||||
<a href="{{ path('report_user_month', {'sumType': dataType, 'date': create_date(period.date.format('Y') ~'-'~period.date.format('m')~'-01')|report_date, 'user': userPeriod.user.id}) }}" data-toggle="tooltip" title="{{ 'label.billable'|trans }}: {{ period.billableDuration|duration(decimal) }}">
|
||||
{{ period.totalDuration|duration(decimal) }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endif %}
|
||||
{% block total_duration_user %}
|
||||
<a href="{{ path('report_user_year', {'sumType': dataType, 'date': create_date(subReportDate.format('Y') ~'-01-01')|report_date, 'user': userPeriod.user.id}) }}">
|
||||
{{ usersTotalDuration|duration(decimal) }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block rate %}
|
||||
<a href="{{ path('report_user_month', {'sumType': dataType, 'date': create_date(period.date.format('Y') ~'-'~period.date.format('m')~'-01')|report_date, 'user': userPeriod.user.id}) }}" data-toggle="tooltip" title="{{ 'billable'|trans }}: {{ period.billableDuration|duration(decimal) }}">
|
||||
{{ period.totalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block internal_rate %}
|
||||
<a href="{{ path('report_user_month', {'sumType': dataType, 'date': create_date(period.date.format('Y') ~'-'~period.date.format('m')~'-01')|report_date, 'user': userPeriod.user.id}) }}" data-toggle="tooltip" title="{{ 'billable'|trans }}: {{ period.billableDuration|duration(decimal) }}">
|
||||
{{ period.totalInternalRate|money }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block duration %}
|
||||
<a href="{{ path('report_user_month', {'sumType': dataType, 'date': create_date(period.date.format('Y') ~'-'~period.date.format('m')~'-01')|report_date, 'user': userPeriod.user.id}) }}" data-toggle="tooltip" title="{{ 'billable'|trans }}: {{ period.billableDuration|duration(decimal) }}">
|
||||
{{ period.totalDuration|duration(decimal) }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('kimai.initialized', function() {
|
||||
jQuery('#user-list-filter-form').on('change', function(ev) {
|
||||
jQuery(this).submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% embed 'reporting/user_list_period_data.html.twig' with {'showAccountNumber': true, 'stats': stats, 'dataType': dataType, 'period_attribute': period_attribute} only %}
|
||||
{% embed 'reporting/user_list_period_data.html.twig' with {'avatar': false, 'showAccountNumber': true, 'stats': stats, 'dataType': dataType, 'period_attribute': period_attribute} only %}
|
||||
{% set dataTypeFormat = '' %}
|
||||
{% if dataType == 'rate' or dataType == 'internalRate' %}
|
||||
{% set dataTypeFormat = ' data-format="' ~ constant('App\\Export\\Base\\AbstractSpreadsheetRenderer::RATE_FORMAT_NO_CURRENCY') ~ '"' %}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{%- set with_avatar = avatar is defined ? avatar : true -%}
|
||||
{%- set absoluteDuration = 0 -%}
|
||||
{%- set absoluteInternalRate = 0 -%}
|
||||
{%- set absoluteRate = 0 -%}
|
||||
@@ -8,16 +9,19 @@
|
||||
{% if dataType == 'rate' %}
|
||||
{% set dataTypeTitle = 'stats.amountTotal' %}
|
||||
{% elseif dataType == 'internalRate' %}
|
||||
{% set dataTypeTitle = 'label.rate_internal' %}
|
||||
{% set dataTypeTitle = 'internalRate' %}
|
||||
{% else %}
|
||||
{% set dataTypeTitle = 'stats.durationTotal' %}
|
||||
{% endif %}
|
||||
<table class="table table-bordered table-hover dataTable">
|
||||
<table class="table table-hover dataTable">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if with_avatar %}
|
||||
<th class="w-avatar"></th>
|
||||
{% endif %}
|
||||
<th> </th>
|
||||
{% if showAccountNumber %}
|
||||
<th>{{ 'label.account_number'|trans }}</th>
|
||||
<th>{{ 'account_number'|trans }}</th>
|
||||
{% endif %}
|
||||
<th class="text-center reportDataTypeTitle">{{ dataTypeTitle|trans }}</th>
|
||||
{% for column in stats.0.getDateTimes() %}
|
||||
@@ -35,11 +39,14 @@
|
||||
{% set usersTotalInternalRate = 0 %}
|
||||
{% set usersTotalRate = 0 %}
|
||||
<tr class="user">
|
||||
{% if with_avatar %}
|
||||
<td class="w-avatar">
|
||||
{% from "macros/widgets.html.twig" import user_avatar %}
|
||||
{{ user_avatar(userPeriod.user) }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-nowrap"{% block user_column_cell_attribute %}{% endblock %}>
|
||||
{% block user_column %}
|
||||
{% from "macros/widgets.html.twig" import label_dot %}
|
||||
{{ label_dot(userPeriod.user.displayName, userPeriod.user.color) }}
|
||||
{% endblock %}
|
||||
{{ userPeriod.user.displayName }}
|
||||
</td>
|
||||
{% if showAccountNumber %}
|
||||
<td>{{ userPeriod.user.accountNumber }}</td>
|
||||
@@ -89,7 +96,7 @@
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="summary">
|
||||
<td>{{ dataTypeTitle|trans }}</td>
|
||||
<td{% if with_avatar %} colspan="2"{% endif %}>{{ dataTypeTitle|trans }}</td>
|
||||
{% if showAccountNumber %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user