added new permission to separate time and money budget (#3352)

This commit is contained in:
Kevin Papst
2022-06-11 12:23:02 +02:00
committed by GitHub
parent 3f827b3104
commit b46fdcefad
47 changed files with 853 additions and 346 deletions

View File

@@ -28,18 +28,22 @@
{% if form.project is defined %}
{{ form_row(form.project) }}
{% endif %}
{% if form.budget is defined %}
<div class="row">
<div class="col-md-4">
{{ form_row(form.budget) }}
{% if form.budgetType is defined %}
<div class="row">
{% if form.budget is defined %}
<div class="col-md-4">
{{ form_row(form.budget) }}
</div>
{% endif %}
{% if form.timeBudget is defined %}
<div class="col-md-4">
{{ form_row(form.timeBudget) }}
</div>
{% endif %}
<div class="col-md-4">
{{ form_row(form.budgetType) }}
</div>
</div>
<div class="col-md-4">
{{ form_row(form.timeBudget) }}
</div>
<div class="col-md-4">
{{ form_row(form.budgetType) }}
</div>
</div>
{% endif %}
{% if form.invoiceText is defined %}
{{ form_row(form.invoiceText) }}

View File

@@ -14,9 +14,17 @@
('mf_' ~ field.name): {'title': field.label|trans, 'class': 'hidden-xs hidden-sm mf_' ~ field.name, 'orderBy': false}
}) %}
{% endfor %}
{% if is_granted('budget_money', 'activity') %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.budget'|trans},
}) %}
{% endif %}
{% if is_granted('budget_time', 'activity') %}
{% set columns = columns|merge({
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.timeBudget'|trans},
}) %}
{% endif %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.budget'|trans},
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.timeBudget'|trans},
'team': {'class': 'text-center w-min', 'orderBy': false},
'visible': {'class': 'text-center hidden w-min'},
'actions': {'class': 'actions alwaysVisible'},
@@ -63,14 +71,14 @@
</td>
{% endfor %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
{% if entry.hasBudget() %}
{% if entry.hasBudget() and is_granted('budget', entry) %}
{{ entry.budget|money((entry.project is null ? defaultCurrency : entry.project.customer.currency)) }}
{% else %}
&ndash;
{% endif %}
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
{% if entry.hasTimeBudget() %}
{% if entry.hasTimeBudget() and is_granted('time', entry) %}
{{ entry.timeBudget|duration }}
{% else %}
&ndash;

View File

@@ -67,14 +67,18 @@
{{ form_row(form.fax) }}
</div>
</div>
{% if form.budget is defined %}
{% if form.budgetType is defined %}
<div class="row">
<div class="col-md-4">
{{ form_row(form.budget) }}
</div>
<div class="col-md-4">
{{ form_row(form.timeBudget) }}
</div>
{% if form.budget is defined %}
<div class="col-md-4">
{{ form_row(form.budget) }}
</div>
{% endif %}
{% if form.timeBudget is defined %}
<div class="col-md-4">
{{ form_row(form.timeBudget) }}
</div>
{% endif %}
<div class="col-md-4">
{{ form_row(form.budgetType) }}
</div>

View File

@@ -6,7 +6,7 @@
id="project_list_box" data-href="{{ path('customer_projects', {'id': customer.id}) }}" data-reload="kimai.projectUpdate kimai.projectDelete"
{% endblock %}
{% block box_tools %}
{% if is_granted('view_reporting') and is_granted('budget_project') %}
{% if is_granted('view_reporting') and is_granted('budget_any', 'project') %}
<a class="btn btn-default btn-sm btn-pager" href="{{ path('report_project_view', {'customer': customer.id}) }}" data-toggle="tooltip" data-placement="top" title="{{ 'report_project_view'|trans({}, 'reporting') }}"><i class="{{ 'reporting'|icon }}"></i></a>
{% endif %}
{% if customer.visible and is_granted('create_project') %}

View File

@@ -25,9 +25,17 @@
('mf_' ~ field.name): {'title': field.label|trans, 'class': 'hidden-xs hidden-sm mf_' ~ field.name, 'orderBy': false}
}) %}
{% endfor %}
{% if is_granted('budget_money', 'customer') %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.budget'|trans},
}) %}
{% endif %}
{% if is_granted('budget_time', 'customer') %}
{% set columns = columns|merge({
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.timeBudget'|trans},
}) %}
{% endif %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs hidden-sm hidden text-right w-min'},
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-right w-min'},
'team': {'class': 'text-center w-min', 'orderBy': false},
'visible': {'class': 'text-center hidden w-min'},
'actions': {'class': 'actions alwaysVisible'},
@@ -74,14 +82,14 @@
</td>
{% endfor %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
{% if entry.hasBudget() %}
{% if entry.hasBudget() and is_granted('budget', entry) %}
{{ entry.budget|money(entry.currency) }}
{% else %}
&ndash;
{% endif %}
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
{% if entry.hasTimeBudget() %}
{% if entry.hasTimeBudget() and is_granted('time', entry) %}
{{ entry.timeBudget|duration }}
{% else %}
&ndash;

View File

@@ -1,154 +1,158 @@
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
{% import "macros/progressbar.html.twig" as progress %}
{% import "macros/widgets.html.twig" as widgets %}
{% block box_title %}
{{ 'label.timeBudget'|trans }} {% if entity.isMonthlyBudget() %}({{ 'label.budgetType_month'|trans }}){% endif %}
{% endblock %}
{% block box_body_class %}no-padding{% endblock %}
{% block box_attributes %}id="time_budget_box"{% endblock %}
{% block box_body %}
{% set durationTrans = entity.isMonthlyBudget() ? 'stats.durationMonth' : 'stats.durationTotal' %}
<div class="row">
<div class="col-xs-12 col-sm-5">
<table class="table table-hover dataTable">
<tr>
<td>{{ 'label.timeBudget'|trans }}</td>
<td class="text-nowrap text-right">
{% if entity.timeBudget > 0 %}
{{ entity.timeBudget|duration }}
{% else %}
-
{% endif %}
</td>
<td class="text-nowrap text-right">
{% if entity.timeBudget > 0 %}
100%
{% else %}
-
{% endif %}
</td>
</tr>
{% set totalPercentReached = 100 %}
{% if entity.timeBudget > 0 %}
{% set totalPercentReached = (stats.durationBillable / (entity.timeBudget / 100)) %}
{% endif %}
<tr>
<td>{{ durationTrans|trans }}</td>
<td class="text-nowrap text-right">{{ stats.duration|duration }}</td>
<td class="text-nowrap text-right">{{ totalPercentReached|number_format(2) }}%</td>
</tr>
{% set percentReached = 0 %}
{% if stats.duration > 0 %}
{% set percentReached = (stats.durationBillable / (stats.duration / 100)) %}
{% endif %}
<tr>
<td>{{ 'label.billable'|trans }}</td>
<td class="text-nowrap text-right">{{ stats.durationBillable|duration }}</td>
<td class="text-nowrap text-right">{{ percentReached|number_format(2) }}%</td>
</tr>
{% if entity.timeBudget > 0 %}
<tr>
<th colspan="3" class="text-nowrap text-right">
{% if totalPercentReached < 100 %}
{{ 'stats.percentUsedLeft'|trans({'%percent%': totalPercentReached|number_format(2), '%left%': (entity.timeBudget - stats.durationBillable)|duration}) }}
{% else %}
{{ 'stats.percentUsed'|trans({'%percent%': totalPercentReached|number_format(2)}) }}
{% endif %}
</th>
</tr>
{% endif %}
</table>
</div>
<div class="col-xs-12 col-sm-7">
<div class="box-padding">
{% if entity.timeBudget > 0 %}
{{ progress.progressbar(entity.timeBudget, stats.durationBillable, durationTrans|trans, stats.durationBillable|duration ~ ' / ' ~ entity.timeBudget|duration) }}
{% endif %}
{{ progress.progressbar(stats.duration, stats.durationBillable, 'label.billable'|trans, stats.durationBillable|duration ~ ' / ' ~ stats.duration|duration, true) }}
</div>
</div>
</div>
{% endblock %}
{% endembed %}
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
{% import "macros/progressbar.html.twig" as progress %}
{% import "macros/widgets.html.twig" as widgets %}
{% block box_title %}
{{ 'label.budget'|trans }} {% if entity.isMonthlyBudget() %}({{ 'label.budgetType_month'|trans }}){% endif %}
{% endblock %}
{% block box_body_class %}no-padding{% endblock %}
{% block box_attributes %}id="budget_box"{% endblock %}
{% block box_body %}
{% set rateTrans = entity.isMonthlyBudget() ? 'stats.amountMonth' : 'stats.amountTotal' %}
<div class="row">
<div class="col-xs-12 col-sm-5">
<table class="table table-hover dataTable">
<tr>
<td>{{ 'label.budget'|trans }}</td>
<td class="text-nowrap text-right">
{% if entity.budget > 0 %}
{{ entity.budget|money(currency) }}
{% else %}
-
{% endif %}
</td>
<td class="text-nowrap text-right">
{% if entity.budget > 0 %}
{% if is_granted('time', entity) %}
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
{% import "macros/progressbar.html.twig" as progress %}
{% import "macros/widgets.html.twig" as widgets %}
{% block box_title %}
{{ 'label.timeBudget'|trans }} {% if entity.isMonthlyBudget() %}({{ 'label.budgetType_month'|trans }}){% endif %}
{% endblock %}
{% block box_body_class %}no-padding{% endblock %}
{% block box_attributes %}id="time_budget_box"{% endblock %}
{% block box_body %}
{% set durationTrans = entity.isMonthlyBudget() ? 'stats.durationMonth' : 'stats.durationTotal' %}
<div class="row">
<div class="col-xs-12 col-sm-5">
<table class="table table-hover dataTable">
<tr>
<td>{{ 'label.timeBudget'|trans }}</td>
<td class="text-nowrap text-right">
{% if entity.timeBudget > 0 %}
{{ entity.timeBudget|duration }}
{% else %}
-
{% endif %}
</td>
<td class="text-nowrap text-right">
{% if entity.timeBudget > 0 %}
100%
{% else %}
-
{% endif %}
</td>
</tr>
{% set totalPercentReached = 100 %}
{% if entity.budget > 0 %}
{% set totalPercentReached = (stats.rateBillable / (entity.budget / 100)) %}
{% endif %}
<tr>
<td>{{ rateTrans|trans }}</td>
<td class="text-nowrap text-right">{{ stats.rate|money(currency) }}</td>
<td class="text-nowrap text-right">{{ totalPercentReached|number_format(2) }}%</td>
</tr>
{% set percentReached = 0 %}
{% if stats.rate > 0 %}
{% set percentReached = (stats.rateBillable / (stats.rate / 100)) %}
{% endif %}
<tr>
<td>{{ 'label.billable'|trans }}</td>
<td class="text-nowrap text-right">{{ stats.rateBillable|money(currency) }}</td>
<td class="text-nowrap text-right">{{ percentReached|number_format(2) }}%</td>
</tr>
{% set percentReached = 0 %}
{% if stats.rateBillable > 0 %}
{% set percentReached = (stats.internalRate / (stats.rateBillable / 100)) %}
{% endif %}
<tr>
<td>{{ 'label.rate_internal'|trans }}</td>
<td class="text-nowrap text-right">{{ stats.internalRate|money(currency) }}</td>
<td class="text-nowrap text-right">{{ percentReached|number_format(2) }}%</td>
</tr>
{% if entity.budget > 0 %}
{% else %}
-
{% endif %}
</td>
</tr>
{% set totalPercentReached = 100 %}
{% if entity.timeBudget > 0 %}
{% set totalPercentReached = (stats.durationBillable / (entity.timeBudget / 100)) %}
{% endif %}
<tr>
<td>{{ durationTrans|trans }}</td>
<td class="text-nowrap text-right">{{ stats.duration|duration }}</td>
<td class="text-nowrap text-right">{{ totalPercentReached|number_format(2) }}%</td>
</tr>
{% set percentReached = 0 %}
{% if stats.duration > 0 %}
{% set percentReached = (stats.durationBillable / (stats.duration / 100)) %}
{% endif %}
<tr>
<td>{{ 'label.billable'|trans }}</td>
<td class="text-nowrap text-right">{{ stats.durationBillable|duration }}</td>
<td class="text-nowrap text-right">{{ percentReached|number_format(2) }}%</td>
</tr>
{% if entity.timeBudget > 0 %}
<tr>
<th colspan="3" class="text-nowrap text-right">
{% if totalPercentReached < 100 %}
{{ 'stats.percentUsedLeft'|trans({'%percent%': totalPercentReached|number_format(2), '%left%': (entity.budget - stats.rateBillable)|money(currency)}) }}
{{ 'stats.percentUsedLeft'|trans({'%percent%': totalPercentReached|number_format(2), '%left%': (entity.timeBudget - stats.durationBillable)|duration}) }}
{% else %}
{{ 'stats.percentUsed'|trans({'%percent%': totalPercentReached|number_format(2)}) }}
{% endif %}
</th>
</tr>
{% endif %}
</table>
</div>
<div class="col-xs-12 col-sm-7">
<div class="box-padding">
{% if entity.budget > 0 %}
{{ progress.progressbar(entity.budget, stats.rateBillable, rateTrans|trans, stats.rateBillable|money(currency) ~ ' / ' ~ entity.budget|money(currency)) }}
{% endif %}
{{ progress.progressbar(stats.rate, stats.rateBillable, 'label.billable'|trans, stats.rateBillable|money(currency) ~ ' / ' ~ stats.rate|money(currency), true) }}
{{ progress.progressbar(stats.rateBillable, stats.internalRate, 'label.rate_internal'|trans, stats.internalRate|money(currency) ~ ' / ' ~ stats.rateBillable|money(currency)) }}
{% endif %}
</table>
</div>
<div class="col-xs-12 col-sm-7">
<div class="box-padding">
{% if entity.timeBudget > 0 %}
{{ progress.progressbar(entity.timeBudget, stats.durationBillable, durationTrans|trans, stats.durationBillable|duration ~ ' / ' ~ entity.timeBudget|duration) }}
{% endif %}
{{ progress.progressbar(stats.duration, stats.durationBillable, 'label.billable'|trans, stats.durationBillable|duration ~ ' / ' ~ stats.duration|duration, true) }}
</div>
</div>
</div>
</div>
{% endblock %}
{% endembed %}
{% endblock %}
{% endembed %}
{% endif %}
{% if is_granted('budget', entity) %}
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
{% import "macros/progressbar.html.twig" as progress %}
{% import "macros/widgets.html.twig" as widgets %}
{% block box_title %}
{{ 'label.budget'|trans }} {% if entity.isMonthlyBudget() %}({{ 'label.budgetType_month'|trans }}){% endif %}
{% endblock %}
{% block box_body_class %}no-padding{% endblock %}
{% block box_attributes %}id="budget_box"{% endblock %}
{% block box_body %}
{% set rateTrans = entity.isMonthlyBudget() ? 'stats.amountMonth' : 'stats.amountTotal' %}
<div class="row">
<div class="col-xs-12 col-sm-5">
<table class="table table-hover dataTable">
<tr>
<td>{{ 'label.budget'|trans }}</td>
<td class="text-nowrap text-right">
{% if entity.budget > 0 %}
{{ entity.budget|money(currency) }}
{% else %}
-
{% endif %}
</td>
<td class="text-nowrap text-right">
{% if entity.budget > 0 %}
100%
{% else %}
-
{% endif %}
</td>
</tr>
{% set totalPercentReached = 100 %}
{% if entity.budget > 0 %}
{% set totalPercentReached = (stats.rateBillable / (entity.budget / 100)) %}
{% endif %}
<tr>
<td>{{ rateTrans|trans }}</td>
<td class="text-nowrap text-right">{{ stats.rate|money(currency) }}</td>
<td class="text-nowrap text-right">{{ totalPercentReached|number_format(2) }}%</td>
</tr>
{% set percentReached = 0 %}
{% if stats.rate > 0 %}
{% set percentReached = (stats.rateBillable / (stats.rate / 100)) %}
{% endif %}
<tr>
<td>{{ 'label.billable'|trans }}</td>
<td class="text-nowrap text-right">{{ stats.rateBillable|money(currency) }}</td>
<td class="text-nowrap text-right">{{ percentReached|number_format(2) }}%</td>
</tr>
{% set percentReached = 0 %}
{% if stats.rateBillable > 0 %}
{% set percentReached = (stats.internalRate / (stats.rateBillable / 100)) %}
{% endif %}
<tr>
<td>{{ 'label.rate_internal'|trans }}</td>
<td class="text-nowrap text-right">{{ stats.internalRate|money(currency) }}</td>
<td class="text-nowrap text-right">{{ percentReached|number_format(2) }}%</td>
</tr>
{% if entity.budget > 0 %}
<tr>
<th colspan="3" class="text-nowrap text-right">
{% if totalPercentReached < 100 %}
{{ 'stats.percentUsedLeft'|trans({'%percent%': totalPercentReached|number_format(2), '%left%': (entity.budget - stats.rateBillable)|money(currency)}) }}
{% else %}
{{ 'stats.percentUsed'|trans({'%percent%': totalPercentReached|number_format(2)}) }}
{% endif %}
</th>
</tr>
{% endif %}
</table>
</div>
<div class="col-xs-12 col-sm-7">
<div class="box-padding">
{% if entity.budget > 0 %}
{{ progress.progressbar(entity.budget, stats.rateBillable, rateTrans|trans, stats.rateBillable|money(currency) ~ ' / ' ~ entity.budget|money(currency)) }}
{% endif %}
{{ progress.progressbar(stats.rate, stats.rateBillable, 'label.billable'|trans, stats.rateBillable|money(currency) ~ ' / ' ~ stats.rate|money(currency), true) }}
{{ progress.progressbar(stats.rateBillable, stats.internalRate, 'label.rate_internal'|trans, stats.internalRate|money(currency) ~ ' / ' ~ stats.rateBillable|money(currency)) }}
</div>
</div>
</div>
{% endblock %}
{% endembed %}
{% endif %}

View File

@@ -90,8 +90,7 @@
</tr>
{% endif %}
{% endif %}
{% if is_granted('budget', project) %}
{% if project.hasBudget() %}
{% if project.hasBudget() and is_granted('budget', project) %}
<tr>
<th>
{{ 'label.budget'|trans }}
@@ -103,8 +102,8 @@
{{ project.getBudget()|money(project.customer.currency) }}
</td>
</tr>
{% endif %}
{% if project.hasTimeBudget() %}
{% endif %}
{% if project.hasTimeBudget() and is_granted('time', project) %}
<tr>
<th>
{{ 'label.timeBudget'|trans }}
@@ -116,7 +115,6 @@
{{ project.getTimeBudget()|duration }}
</td>
</tr>
{% endif %}
{% endif %}
{% for metaField in project.visibleMetaFields|sort((a, b) => a.order <=> b.order) %}
<tr>

View File

@@ -39,14 +39,18 @@
{{ form_row(form.end) }}
</div>
</div>
{% if form.budget is defined %}
{% if form.budgetType is defined %}
<div class="row">
{% if form.budget is defined %}
<div class="col-md-4">
{{ form_row(form.budget) }}
</div>
{% endif %}
{% if form.timeBudget is defined %}
<div class="col-md-4">
{{ form_row(form.timeBudget) }}
</div>
{% endif %}
<div class="col-md-4">
{{ form_row(form.budgetType) }}
</div>

View File

@@ -18,9 +18,17 @@
('mf_' ~ field.name): {'title': field.label|trans, 'class': 'hidden-xs hidden-sm mf_' ~ field.name, 'orderBy': false}
}) %}
{% endfor %}
{% if is_granted('budget_money', 'project') %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.budget'|trans},
}) %}
{% endif %}
{% if is_granted('budget_time', 'project') %}
{% set columns = columns|merge({
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.timeBudget'|trans},
}) %}
{% endif %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.budget'|trans},
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-right w-min', 'title': 'label.timeBudget'|trans},
'team': {'class': 'text-center w-min', 'orderBy': false},
'visible': {'class': 'text-center hidden w-min'},
'actions': {'class': 'actions alwaysVisible'},
@@ -60,14 +68,14 @@
</td>
{% endfor %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
{% if entry.hasBudget() %}
{% if entry.hasBudget() and is_granted('budget', entry) %}
{{ entry.budget|money(entry.customer.currency) }}
{% else %}
&ndash;
{% endif %}
</td>
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
{% if entry.hasTimeBudget() %}
{% if entry.hasTimeBudget() and is_granted('time', entry) %}
{{ entry.timeBudget|duration }}
{% else %}
&ndash;

View File

@@ -3,15 +3,28 @@
{% block report_title %}{{ 'report_project_daterange'|trans({}, 'reporting') }}{% endblock %}
{% set showMoneyBudget = is_granted('budget_money', 'project') %}
{% set showTimeBudget = is_granted('budget_time', 'project') %}
{% set columns = {
'name': {'class': 'alwaysVisible'},
'timeBudget': {'class': 'hidden-xs', 'title': 'label.timeBudget'|trans},
'budget': {'class': 'hidden-xs', 'title': 'label.budget'|trans},
} %}
{% if showTimeBudget %}
{% set columns = columns|merge({
'timeBudget': {'class': 'hidden-xs', 'title': 'label.timeBudget'|trans},
}) %}
{% endif %}
{% if showMoneyBudget %}
{% set columns = columns|merge({
'budget': {'class': 'hidden-xs', 'title': 'label.budget'|trans},
}) %}
{% endif %}
{% set columns = columns|merge({
'duration': {'class': 'text-center hw-min', 'title': 'stats.durationMonth'|trans, 'columnClass': 'w-min'},
'rate': {'class': 'text-center hw-min', 'title': 'stats.amountMonth'|trans, 'columnClass': 'w-min'},
'billable': {'class': 'text-center hw-min', 'columnClass': 'w-min'},
'actions': {'class': 'actions alwaysVisible'},
} %}
}) %}
{% set tableName = 'project_daterange_reporting' %}
{% block main_before %}
@@ -72,29 +85,31 @@
{% for entry in mapping.projects|sort((a, b) => a.entity.name <=> b.entity.name) %}
{% set project = entry.entity %}
{% set currency = project.customer.currency %}
{% if is_granted('budget', project) %}
<tr {{ widgets.project_row_attr(project, queryEnd) }}>
{% for name, column_config in columns %}
<td class="{{ tables.data_table_column_class(tableName, columns, name) }}">
{% if name == 'name' %}
<tr {{ widgets.project_row_attr(project, queryEnd) }}>
{% 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 == 'duration' %}
{{ entry.statistic.duration|duration }}
{% elseif name == 'rate' %}
{{ entry.statistic.rate|money(currency) }}
{% elseif name == 'billable' %}
{{ widgets.percent(entry.statistic.rate, entry.statistic.rateBillable) }}
{% elseif name == 'timeBudget' %}
{% elseif name == 'duration' %}
{{ entry.statistic.duration|duration }}
{% elseif name == 'rate' %}
{{ entry.statistic.rate|money(currency) }}
{% elseif name == 'billable' %}
{{ widgets.percent(entry.statistic.rate, entry.statistic.rateBillable) }}
{% elseif name == 'timeBudget' %}
{% if is_granted('time', project) %}
{{ progress.progressbar_timebudget(entry) }}
{% elseif name == 'budget' %}
{{ progress.progressbar_budget(entry, currency) }}
{% elseif name == 'actions' %}
{{ projectActions.project(project, 'custom') }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endif %}
{% elseif name == 'budget' %}
{% if is_granted('budget', project) %}
{{ progress.progressbar_budget(entry, currency) }}
{% endif %}
{% elseif name == 'actions' %}
{{ projectActions.project(project, 'custom') }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
{% endfor %}
{{ tables.data_table_footer(entries) }}

View File

@@ -6,7 +6,8 @@
{% set tableName = tableName|default('project_details_reporting') %}
{% set tableId = 'project-details-form' %}
{% set view_budget = project_details is not null and is_granted('budget', project_details.project) %}
{% set showMoneyBudget = project_details is not null and is_granted('budget', project_details.project) %}
{% set showTimeBudget = project_details is not null and is_granted('time', project_details.project) %}
{% set view_revenue = project_details is not null and is_granted('view_rate_other_timesheet') %}
{% set see_users = is_granted('view_other_timesheet') or is_granted('view_other_reporting') %}
@@ -79,7 +80,7 @@
{% endif %}
{% if hasData %}
{{ _self.project_details(project, project_view, project_details, view_budget, view_revenue, see_users) }}
{{ _self.project_details(project, project_view, project_details, showMoneyBudget, showTimeBudget, view_revenue, see_users) }}
{% set currency = project.customer.currency %}
{%- for yearStat in project_details.years|reverse %}
@@ -245,7 +246,7 @@
project_view = ProjectViewModel
project_details = ProjectDetailsModel
#}
{% macro project_details(project, project_view, project_details, view_budget, view_revenue, see_users) %}
{% macro project_details(project, project_view, project_details, showMoneyBudget, showTimeBudget, view_revenue, see_users) %}
{% set activities = project_details.activities %}
{% set years = project_details.years %}
{% import "macros/progressbar.html.twig" as progress %}
@@ -352,12 +353,12 @@
</table>
</div>
</div>
{% if view_budget and (project.timeBudget > 0 or project.budget > 0) %}
{% if (showMoneyBudget and project.budget > 0) or (showTimeBudget and project.timeBudget > 0) %}
{% set budgetStats = project_details.budgetStatisticModel %}
<div class="row">
<div class="col-xs-12">
<table class="table table-hover dataTable">
{% if project.timeBudget > 0 %}
{% if showTimeBudget and project.timeBudget > 0 %}
<tr>
<th class="w-min">
{{ 'label.timeBudget'|trans }}
@@ -370,7 +371,7 @@
</td>
</tr>
{% endif %}
{% if project.budget > 0 %}
{% if showMoneyBudget and project.budget > 0 %}
<tr>
<th class="w-min">
{{ 'label.budget'|trans }}

View File

@@ -3,22 +3,36 @@
{% 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'},
'timeBudget': {'class': 'hidden-xs', 'title': 'label.timeBudget'|trans},
'budget': {'class': 'hidden-xs', 'title': 'label.budget'|trans},
} %}
{% 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'},
'exported': {'class': 'hidden-sm hidden-xs hidden text-center hw-min', 'title': 'label.not_exported'|trans, 'columnClass': 'w-min'},
'invoiced': {'class': 'hidden-sm hidden-xs hidden text-center hw-min', 'title': 'label.not_invoiced'|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 = {} %}
@@ -93,73 +107,71 @@
{% set project = entry.project %}
{% set budgetStats = entry.getBudgetStatisticModel() %}
{% set currency = project.customer.currency %}
{% if is_granted('budget', project) %}
<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 %}
&ndash;
{% 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() %}
{{ progress.progressbar_timebudget(budgetStats) }}
{% endif %}
</td>
{% elseif name == 'budget' %}
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
{% if project.hasBudget() %}
{{ 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>
<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 %}
&ndash;
{% endif %}
{% endfor %}
</tr>
{% 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) }}

View File

@@ -25,10 +25,10 @@
<small>{{ widgets.label_customer(project.customer) }}</small>
</td>
<td style="width:50%">
{% if row.hasTimeBudget() %}
{{ progress.progressbar_timebudget(row) }}
{% elseif row.hasBudget() %}
{% if row.hasBudget() and is_granted('budget', row.entity) %}
{{ progress.progressbar_budget(row, row.entity.customer.currency) }}
{% elseif row.hasTimeBudget() and is_granted('time', row.entity) %}
{{ progress.progressbar_timebudget(row) }}
{% endif %}
</td>
</tr>