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:
@@ -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 }}
|
||||
|
||||
@@ -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)) %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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) %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user