Release 2.6.0 (#4472)

- Added: calendar entry title combination for customer, project and activity
- Added: show not_invoiced and not_exported data in detail screens
- Added: force logout if user is disabled
- Added: reduced amount of database queries on several screens
- Fixed: open-close status on work-contract screen for users without configuration
- Fixed: failsafe order/orderBy in query if manually manipulated to be null
- Fixed: unify statistic calculation (not_invoiced and not_exported) across screens
- Tech: bump packages
- Tech: Symfony 6.4
This commit is contained in:
Kevin Papst
2023-12-17 16:19:50 +01:00
committed by GitHub
parent af82fd040d
commit 5f4b6d3cfa
77 changed files with 1362 additions and 1242 deletions

View File

@@ -88,7 +88,7 @@
{% endif %}
{% if year is defined %}
{% embed '@theme/embeds/collapsible.html.twig' with {open: not box_configuration.collapsed, id: 'working_time_detail_box', border: false, item: {options: {bodyExtraClass: 'border-top'}, bodyClasses: 'p-0 table-responsive'}} %}
{% embed '@theme/embeds/collapsible.html.twig' with {id: 'working_time_detail_box', border: false, item: {options: {bodyExtraClass: 'border-top', open: not box_configuration.collapsed}, bodyClasses: 'p-0 table-responsive'}} %}
{% set yearShould = 0 %}
{% set yearIs = 0 %}
{% set approved = 0 %}
@@ -228,7 +228,7 @@
{% if withWorkHourConfiguration %}
{% set expectedWeekTimes = user.workHoursMonday + user.workHoursTuesday + user.workHoursWednesday + user.workHoursThursday + user.workHoursFriday + user.workHoursSaturday + user.workHoursSunday %}
{% embed '@theme/embeds/collapsible.html.twig' with {open: not box_configuration.collapsed, id: 'work_contract_should_preview', border: false, item: {options: {bodyExtraClass: 'border-top'}}} %}
{% embed '@theme/embeds/collapsible.html.twig' with {id: 'work_contract_should_preview', border: false, item: {options: {bodyExtraClass: 'border-top'}}} %}
{% from "macros/status.html.twig" import status_duration %}
{% block title %}
{{ 'work_times_should'|trans }}

View File

@@ -15,14 +15,14 @@
<table class="table table-hover dataTable">
<tr>
<td>{{ 'timeBudget'|trans }}</td>
<td class="text-nowrap text-end">
<td class="text-nowrap text-end w-min">
{% if entity.timeBudget > 0 %}
{{ entity.timeBudget|duration }}
{% else %}
-
{% endif %}
</td>
<td class="text-nowrap text-end">
<td class="text-nowrap text-end w-min">
{% if entity.timeBudget > 0 %}
100%
{% else %}
@@ -48,6 +48,18 @@
<td class="text-nowrap text-end">{{ stats.durationBillable|duration }}</td>
<td class="text-nowrap text-end">{{ percentReached|number_format(2) }}%</td>
</tr>
<tr>
<td>{{ 'not_exported'|trans }}</td>
<td class="text-nowrap text-end">
{% set not_exported = (stats.statisticTotal.durationBillable - stats.statisticTotal.durationBillableExported) %}
{% if export_url is defined and export_url is not null %}
<a href="{{ export_url }}">{{ not_exported|duration }}</a>
{% else %}
{{ not_exported|duration }}
{% endif %}
</td>
<td class="text-nowrap text-end">-</td>
</tr>
{% if entity.timeBudget > 0 %}
<tr>
<th colspan="3" class="text-nowrap text-end">
@@ -87,14 +99,14 @@
<table class="table table-hover dataTable">
<tr>
<td>{{ 'budget'|trans }}</td>
<td class="text-nowrap text-end">
<td class="text-nowrap text-end w-min">
{% if entity.budget > 0 %}
{{ entity.budget|money(currency) }}
{% else %}
-
{% endif %}
</td>
<td class="text-nowrap text-end">
<td class="text-nowrap text-end w-min">
{% if entity.budget > 0 %}
100%
{% else %}
@@ -120,6 +132,18 @@
<td class="text-nowrap text-end">{{ stats.rateBillable|money(currency) }}</td>
<td class="text-nowrap text-end">{{ percentReached|number_format(2) }}%</td>
</tr>
<tr>
<td>{{ 'not_invoiced'|trans }}</td>
<td class="text-nowrap text-end">
{% set not_exported = (stats.statisticTotal.rateBillable - stats.statisticTotal.rateBillableExported) %}
{% if invoice_url is defined and invoice_url is not null %}
<a href="{{ invoice_url }}">{{ not_exported|money(currency) }}</a>
{% else %}
{{ not_exported|money(currency) }}
{% endif %}
</td>
<td class="text-nowrap text-end">-</td>
</tr>
{% set percentReached = 0 %}
{% if stats.rateBillable > 0 %}
{% set percentReached = (stats.internalRate / (stats.rateBillable / 100)) %}

View File

@@ -147,7 +147,7 @@
<a href="#" data-form-widget="date-now" data-format="{{ jsFormat }}" data-target="{{ id }}">{{ icon('calendar') }}</a>
</span>
{{ block('form_widget_simple') }}
{% set time_presets = form_time_presets(app.user.timezone, format) %}
{% set time_presets = form_time_presets(app.user.timezone) %}
{% if time_presets|length > 0 and form.vars.disabled is same as (false) %}
<button type="button" class="input-group-text dropdown-toggle btn-duration-preset" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
<div class="dropdown-menu dropdown-menu-end pre-scrollable">
@@ -155,6 +155,7 @@
{% for index in [0, 1, 2, 3] %}
<div class="dropdown-menu-column" style="min-width: 4rem">
{% for value in time_presets %}
{% set value = value|time %}
{% if loop.index0 % 4 == index %}
<a class="dropdown-item justify-content-center" href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ value }}" data-event="change">{{ value }}</a>
{% endif %}

View File

@@ -11,32 +11,30 @@
{% set help = null %}
<div class="page-actions">
<div class="pa-desktop d-none d-sm-inline-flex btn-list">
{%- apply spaceless -%}
{%- for icon, values in actions %}
{% if 'help' in icon %}
{% set help = values %}
{% elseif 'divider' in icon and values is null %}
{# what to do here ? #}
{% else %}
{% if values.children is defined and values.children|length > 0 %}
<div class="dropdown">
<button type="button" class="btn {{ btnClasses}} dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ icon(icon, true) }}{% if large %} {{ values.title is defined ? values.title|trans : icon|trans({}, 'actions') }}{% endif %}
</button>
<div class="dropdown-menu">
{%- for icon, values in values.children %}
{% set values = values|merge({class: 'dropdown-item action-' ~ icon ~ ' ' ~ (values.class|default(''))}) %}
{{ _self.action_button(false, values, false) }}
{% endfor %}
</div>
{%- for icon, values in actions %}
{% if 'help' in icon %}
{% set help = values %}
{% elseif 'divider' in icon and values is null %}
{# what to do here ? #}
{% else %}
{% if values.children is defined and values.children|length > 0 %}
<div class="dropdown">
<button type="button" class="btn {{ btnClasses}} dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ icon(icon, true) }}{% if large %} {{ values.title is defined ? values.title|trans : icon|trans({}, 'actions') }}{% endif %}
</button>
<div class="dropdown-menu">
{%- for icon, values in values.children %}
{% set values = values|merge({class: 'dropdown-item action-' ~ icon ~ ' ' ~ (values.class|default(''))}) %}
{{ _self.action_button(false, values, false) }}
{% endfor %}
</div>
{% else %}
{% set values = values|merge({combined: large, class: btnClasses ~ ' action-' ~ icon ~ ' ' ~ (values.class|default(''))}) %}
{{ _self.action_button(icon, values) }}
{% endif %}
</div>
{% else %}
{% set values = values|merge({combined: large, class: btnClasses ~ ' action-' ~ icon ~ ' ' ~ (values.class|default(''))}) %}
{{ _self.action_button(icon, values) }}
{% endif %}
{% endfor -%}
{% endapply %}
{% endif %}
{% endfor -%}
{% if help is not null %}
{% set help = help|merge({class: helpClasses ~ ' action-help ' ~ (help.class|default(''))}) %}
{{ _self.action_button('help', help) }}
@@ -191,14 +189,12 @@
{# for @internal use only #}
{% macro label_color_dot(type, isVisible, name, url, color) %}
{% apply spaceless %}
{%- if url is not empty -%}<a href="{{ url }}">{% endif %}
<span class="pe-1 label-{{ type }}{{ isVisible is same as (false) ? ' label-invisible' : '' }}">
<span class="badge me-1" {% if color is not empty %} style="background-color:{{ color }}"{% endif %}></span>
{{- name -}}
</span>
{%- if url is not empty -%}</a>{% endif %}
{% endapply %}
{%- if url is not empty -%}</a>{% endif %}
{% endmacro %}
{% macro badge_team_access(teams) %}

View File

@@ -395,7 +395,7 @@
</div>
<div class="datagrid-content">
<a href="{{ path('invoice', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': ''}) }}">
{{ project_view.notBilledRate|money(currency) }}
{{ project_view.notExportedRate|money(currency) }}
</a>
</div>
</div>

View File

@@ -110,7 +110,7 @@
</a>
{% elseif name == 'invoiced' %}
<a href="{{ path('invoice', {'customers[]': project.customer.id, 'projects[]': project.id, 'daterange': ''}) }}">
{{ entry.notBilledRate|money(currency) }}
{{ entry.notExportedRate|money(currency) }}
</a>
{% elseif name == 'projectStart' %}
{% if project.start is not null %}{{ project.start|date_short }}{% endif %}