Blame | Last modification | View Log | Download
{% extends 'base.html.twig' %}{% import "macros/widgets.html.twig" as widgets %}{% import "macros/datatables.html.twig" as tables %}{% set totalDuration = 0 %}{% set totalRates = {} %}{% if too_many is not same as (true) and preview_show and entries is not empty %}{% for row in by_customer %}{% set totalDuration = totalDuration + row.duration %}{% set customerCurrency = row.customer.currency %}{% if totalRates[customerCurrency] is not defined %}{% set totalRates = totalRates|merge({(customerCurrency): 0.00}) %}{% endif %}{% set totalRates = totalRates|merge({(customerCurrency): totalRates[customerCurrency] + row.rate}) %}{% endfor %}{% endif %}{% set columns = {'avatar': {'class': 'text-nowrap w-avatar d-none d-md-table-cell', 'title': false, 'orderBy': false},'date': {'class': 'alwaysVisible text-nowrap', 'orderBy': false},'user': {'class': 'd-none', 'orderBy': false},'project': {'class': 'd-none d-sm-table-cell', 'orderBy': false},'activity': {'class': 'd-none', 'orderBy': false},'description': {'class': 'd-none d-xl-table-cell timesheet-description', 'orderBy': false},'unit_price': {'class': 'd-none text-nowrap text-end', 'orderBy': false},'duration': {'class': 'text-end text-nowrap', 'orderBy': false},'internalRate': {'class': 'text-end d-none text-nowrap', 'orderBy': false},'total_rate': {'class': 'text-end text-nowrap', 'orderBy': false},'actions': {'class': 'actions alwaysVisible', 'orderBy': false},} %}{% set tableName = 'export' %}{% set editExported = is_granted('edit_exported_timesheet') %}{% set showMarkAsExportedButton = false %}{% set showToggleButton = false %}{% if entries is not empty and form.markAsExported is defined %}{% set showMarkAsExportedButton = form.exported.vars.value != constant('App\\Repository\\Query\\TimesheetQuery::STATE_EXPORTED') %}{% set showToggleButton = preview_show and form.exported.vars.value != constant('App\\Repository\\Query\\TimesheetQuery::STATE_ALL') %}{% endif %}{% block status %}{% from "macros/status.html.twig" import status_duration, status_money %}{% if totalDuration > 0 %}{{ status_duration(totalDuration|duration) }}{% endif %}{% for totalCurrency, totalRate in totalRates %}{{ status_money(totalRate|money(totalCurrency)) }}{% endfor %}{% endblock %}{% block main_before %}{{ tables.data_table_column_modal(tableName, columns) }}{% endblock %}{% block main %}{% embed '@theme/embeds/card.html.twig' %}{% import "macros/search.html.twig" as search %}{% form_theme form 'form/horizontal.html.twig' %}{% block box_title %}{{ 'export.filter'|trans }}{% endblock %}{% block box_before %}{{ form_start(form) }}{% endblock %}{% block box_body %}{{ form_errors(form) }}{{ form_row(form.searchTerm) }}{{ form_row(form.daterange) }}{{ form_row(form.customers) }}{{ form_row(form.projects) }}{{ form_row(form.activities) }}{{ form_row(form.tags) }}{% if form.users is defined %}{{ form_row(form.users) }}{% endif %}{% if form.teams is defined %}{{ form_row(form.teams) }}{% endif %}{{ form_row(form.billable) }}{{ form_row(form.exported) }}{{ form_row(form.state) }}{% if form.markAsExported is defined %}{{ form_row(form.markAsExported) }}{% endif %}{% endblock %}{% block box_footer%}{{ search.searchButton(form) }}{% endblock %}{% block box_after %}{{ form_end(form) }}{% endblock %}{% endembed %}{% if too_many is same as (true) %}{{ widgets.callout('danger', 'error.too_many_entries') }}{% elseif preview_show %}{% if entries is empty %}{{ widgets.nothing_found() }}{% else %}{% embed '@theme/embeds/card.html.twig' %}{% import "macros/widgets.html.twig" as widgets %}{% block box_title %}{{ 'preview'|trans }}{% endblock %}{% block box_attributes %}id="preview_export"{% endblock %}{% block box_body_class %}p-0{% endblock %}{% block box_footer %}{% set buttons = {} %}{% for button in renderer %}{% set title = button.title %}{% set group = [] %}{% if buttons[(title)] is defined %}{% set group = buttons[(title)] %}{% endif %}{% set group = group|merge([button]) %}{% set buttons = buttons|merge({(title): group}) %}{% endfor %}{% if showMarkAsExportedButton %}<div class="d-flex"><div class="form-check form-switch"><input type="checkbox" id="markAsExportedCheck" name="markAsExportedCheck" class="form-check-input" value="1" {% if form.markAsExported.vars.value == '1' %} checked="checked"{% endif %}><label class="form-check-label" for="markAsExportedCheck">{{ 'mark_as_exported'|trans }}</label></div></div>{% endif %}<div class="d-flex"><div class="btn-group me-auto" id="export-buttons" role="group">{% for group in buttons %}{% set button = group.0 %}{% set btnTitle = ('button.' ~ button.title)|trans %}{% if btnTitle == ('button.' ~ button.title) %}{% set btnTitle = button.title %}{% endif %}{% if group|length == 1 %}<button type="button" id="export-{{ button.id }}-button" class="btn btn-success startExportBtn" data-type="{{ button.id }}">{{ btnTitle }}</button>{% elseif group|length > 1 %}<div class="btn-group"><button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ btnTitle }}</button><div class="dropdown-menu">{% for button in group %}{% set btnTitle = (button.id)|trans({}, 'export') %}{% if btnTitle == button.id %}{% set btnTitle = button.id|split('.')|first|replace({'-': ' ', '_': ' '})|split(' ')|map(t => t|capitalize)|join(' ') %}{% endif %}<a href="#" class="dropdown-item startExportBtn" data-type="{{ button.id }}">{{ btnTitle }}</a>{% endfor %}</div></div>{% endif %}{% endfor %}</div>{% if showToggleButton %}<button id="toggle-button" class="btn ms-auto d-none d-sm-inline-flex">{% if form.exported.vars.value == '5' %}{{ icon('off', true) }}{{ 'mark_as_exported'|trans }}{% elseif form.exported.vars.value == '4' %}{{ icon('on', true) }}{{ 'mark_as_open'|trans }}{% endif %}</button>{% endif %}</div>{% endblock %}{% block box_body %}<table class="table table-hover dataTable"><thead><tr><th>{{ 'customer'|trans }}</th><th class="w-min text-end d-none d-sm-table-cell">{{ 'duration'|trans }}</th><th class="w-min text-end">{{ 'total_rate'|trans }}</th></tr></thead><tbody>{% for row in by_customer %}{% set currency = row.customer.currency %}<tr><td>{{ widgets.label_customer(row.customer) }}</td><td class="w-min text-end d-none d-sm-table-cell">{{ row.duration|duration(decimal) }}</td><td class="w-min text-end">{{ row.rate|money(currency) }}</td></tr>{% endfor %}</tbody></table>{% endblock %}{% endembed %}{% set itemsAmount = entries|length %}{% if preview_limit %}{% set entries = entries|slice(0, preview_limit) %}{% endif %}{{ tables.datatable_header(tableName, columns, query) }}{% set edit_route = is_granted('view_other_timesheet') ? 'admin_timesheet_edit' : 'timesheet_edit' %}{% for entry in entries %}{% set currency = entry.project.customer.currency %}{% if entry.fixedRate is not null %}{% set rate = entry.fixedRate %}{% else %}{% set rate = entry.hourlyRate %}{% endif %}<tr{%- if entry.type == 'timesheet' and is_granted('edit', entry) %}class="modal-ajax-form open-edit" data-href="{{ path(edit_route, {'id': entry.id}) }}"{% endif -%}><td class="{{ tables.data_table_column_class(tableName, columns, 'avatar') }}">{{ widgets.user_avatar(entry.user) }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'date') }}">{{ entry.begin|date_short }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'user') }}">{{ widgets.label_user(entry.user) }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'project') }}">{{ widgets.label_project(entry.project) }}<br><small>{{ widgets.label_customer(entry.project.customer) }}</small></td><td class="{{ tables.data_table_column_class(tableName, columns, 'activity') }}">{% if entry.activity is not null %}{{ widgets.label_activity(entry.activity) }}{% endif %}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'description') }}">{{ entry.description|desc2html }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'unit_price') }}">{{ rate|money(currency) }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'duration') }}" data-duration="{{ entry.duration }}">{{ entry.duration|duration(decimal) }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'internalRate') }}">{{ entry.internalRate|money(currency) }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'total_rate') }}">{{ entry.rate|money(currency) }}</td><td class="{{ tables.data_table_column_class(tableName, columns, 'actions') }}">{% if is_granted('edit_export', entry) %}{% if entry.exported %}{% if editExported %}<button type="button" class="btn btn-outline-secondary exportBtn active" data-toggle="button" aria-pressed="true"data-exported-text="{{ 'entryState.exported'|trans }}" data-clean-text="{{ 'entryState.not_exported'|trans }}" data-timesheet="{{ entry.id }}">{{ 'entryState.exported'|trans }}</button>{% else %}{{ 'entryState.exported'|trans }}{% endif %}{% else %}<button type="button" class="btn btn-light exportBtn" data-toggle="button" aria-pressed="false"data-exported-text="{{ 'entryState.exported'|trans }}" data-clean-text="{{ 'entryState.not_exported'|trans }}" data-timesheet="{{ entry.id }}">{{ 'entryState.not_exported'|trans }}</button>{% endif %}{% else %}{% if entry.exported %}{{ 'entryState.exported'|trans }}{% else %}{{ 'entryState.not_exported'|trans }}{% endif %}{% endif %}</td></tr>{% endfor %}{% if preview_limit and itemsAmount > preview_limit %}<tr class="warning"><td colspan="10">» {{ 'preview.skipped_rows'|trans({'%rows%': (itemsAmount - preview_limit)}) }}</td></tr>{% endif %}{{ tables.data_table_footer(entries) }}{% endif %}{% endif %}{% endblock %}{% block javascripts %}{{ parent() }}<script type="text/javascript">function updateTimesheetExportState(node, id){/** @type {KimaiAlert} */const ALERT = kimai.getPlugin('alert');/** @type {KimaiAPI} */const API = kimai.getPlugin('api');API.patch('{{ path('export_timesheet', {id: 1}) }}'.replace('1', id),{},function(data) {const table = node.closest('table')const row = node.closest('tr');row.remove();const list = table.querySelectorAll('tbody tr');if (list.length === 0) {document.querySelector('form#export-form button.performSearch').click();}});}document.addEventListener('kimai.initialized', function() {document.addEventListener('click', function (event) {const node = event.target;if (node.matches('.exportBtn')) {if (node.dataset['timesheet'] !== undefined) {const id = node.dataset['timesheet'];updateTimesheetExportState(node, id);}}{#THE GROUP OF EXPORT BUTTONS FOR ALL FORMATSToggle the form, because by default it triggers the search#}if (node.matches('#export-buttons .startExportBtn')) {document.getElementById('renderer').value = node.dataset['type'];const form = document.getElementById("export-form");const previousAction = form.action;const previousMethod = form.method;const previousTarget = form.target;form.target = '_blank';form.method = 'POST';form.action = '{{ path('export_data') }}';form.submit();document.getElementById('renderer').value = '';form.target = previousTarget;form.method = previousMethod;form.action = previousAction;}}, false);{% if showMarkAsExportedButton %}document.getElementById('markAsExportedCheck').addEventListener('change', function(event) {document.getElementById('markAsExported').value = event.target.checked ? 1 : 0;});{% endif %}});{% if showToggleButton %}function confirmToggleState(doExport){let ALERT = kimai.getPlugin('alert');let message = '{{ 'export.clear_all'|trans }}';if (doExport) {message = '{{ 'export.mark_all'|trans }}';}ALERT.question(message, function(value) {if (!value) {return;}[].slice.call(document.querySelectorAll('.exportBtn')).map(function (element) {element.click();});document.getElementById('toggle-button').classList.add('d-none');});}document.getElementById('toggle-button').addEventListener('click', function (event) {confirmToggleState({% if form.exported.vars.value == '5' %}true{% else %}false{% endif %});}, false);{% endif %}</script>{% endblock %}