diff --git a/src/Export/Renderer/RendererTrait.php b/src/Export/Renderer/RendererTrait.php index 04f7e9e8..47d753ca 100644 --- a/src/Export/Renderer/RendererTrait.php +++ b/src/Export/Renderer/RendererTrait.php @@ -23,17 +23,32 @@ trait RendererTrait foreach ($timesheets as $timesheet) { $id = $timesheet->getProject()->getCustomer()->getId() . '_' . $timesheet->getProject()->getId(); + $activityId = $timesheet->getActivity()->getId(); + if (!isset($summary[$id])) { $summary[$id] = [ 'customer' => $timesheet->getProject()->getCustomer()->getName(), 'project' => $timesheet->getProject()->getName(), + 'activities' => [], 'currency' => $timesheet->getProject()->getCustomer()->getCurrency(), 'rate' => 0, 'duration' => 0, ]; } + + if (!isset($summary[$id]['activities'][$activityId])) { + $summary[$id]['activities'][$activityId] = [ + 'activity' => $timesheet->getActivity()->getName(), + 'currency' => $timesheet->getProject()->getCustomer()->getCurrency(), + 'rate' => 0, + 'duration' => 0, + ]; + } + $summary[$id]['rate'] += $timesheet->getRate(); $summary[$id]['duration'] += $timesheet->getDuration(); + $summary[$id]['activities'][$activityId]['rate'] += $timesheet->getRate(); + $summary[$id]['activities'][$activityId]['duration'] += $timesheet->getDuration(); } asort($summary); diff --git a/templates/export/layout.html.twig b/templates/export/layout.html.twig index 56e83593..2f2a246c 100644 --- a/templates/export/layout.html.twig +++ b/templates/export/layout.html.twig @@ -6,6 +6,7 @@ + {% block styles %}{% endblock %}
@@ -13,5 +14,6 @@ {% block export %}{% endblock %}
+{% block javascripts %}{% endblock %} diff --git a/templates/export/renderer/default.html.twig b/templates/export/renderer/default.html.twig index 5eb39707..65bf94c2 100644 --- a/templates/export/renderer/default.html.twig +++ b/templates/export/renderer/default.html.twig @@ -1,7 +1,45 @@ {% import "macros/widgets.html.twig" as widgets %} {% extends 'export/layout.html.twig' %} -{% block export %} +{% set columns = { + 'date': true, + 'username': false, + 'customer': true, + 'project': true, + 'activity': true, + 'description': false, + 'exported': false, + 'hourlyRate': false, + 'fixedRate': false, + 'duration': 'label.duration', + 'rate': 'label.rate', +} %} + +{% block javascripts %} + +{% endblock %} + +{% block styles %} +{% endblock %} + +{% block export %} +
+
+ + + +
+
@@ -27,10 +114,10 @@
-
+

{{ 'export.summary'|trans }}

- +
@@ -78,33 +165,70 @@ {% endif %}
{{ 'label.customer'|trans }}
+ + + + + + + + + + + + + {% set customer = null %} + {% set customerDuration = 0 %} + {% set customerRate = 0 %} + {% set customerCurrency = null %} + {% for summary in summaries %} + {% if customer is same as(null) %} + {% set customer = summary.customer %} + {% set customerCurrency = summary.currency %} + {% endif %} + {% if customer is not same as(summary.customer) %} + + + + + + {% set customerCurrency = summary.currency %} + {% set customer = summary.customer %} + {% set customerDuration = 0 %} + {% set customerRate = 0 %} + {% endif %} + {% for activitySummary in summary.activities %} + + + + + + + + {% endfor %} + {% set customerDuration = customerDuration + summary.duration %} + {% set customerRate = customerRate + summary.rate %} + {% endfor %} + {% if customer is not same as(null) %} + + + + + + {% endif %} + +
- {% set columns = [ - 'label.date', - 'label.begin', - 'label.end', - 'label.username', - 'label.customer', - 'label.project', - 'label.activity', - 'label.description', - 'label.exported', - 'label.hourlyRate', - 'label.fixedRate', - 'label.duration', - 'label.rate', - ] %} -

{{ 'export.full_list'|trans }}

- {% for columnTitle in columns %} - + {% for columnId, visibility in columns %} + {% endfor %} @@ -122,41 +246,59 @@ {% set currency = null %} {% endif %} - - - - - - - - + + + + + - - - - - + + + {% endfor %} - - + {# leave in tbody instead of adding it to tfoot, as tfoot will be repeated on each page when printing #} - - + {% endif %} + {% endfor %} + - - +
{{ columnTitle|trans }}{{ ('label.'~columnId)|trans }}
{{ entry.begin|date_short }}{{ entry.begin|time }}{{ entry.end|time }}{{ widgets.username(entry.user) }}{{ entry.project.customer.name }}{{ entry.project.name }}{{ entry.activity.name }} + + {{ entry.begin|date_time }} +
+ {{ entry.end|date_time }} +
+ {{ widgets.username(entry.user) }} + + {{ entry.project.customer.name }} + {{ entry.project.name }} + + {{ entry.activity.name }} + {% if entry.description is not empty %} {{ entry.description|desc2html }} {% endif %} + {% if entry.exported %} {{ 'entryState.exported'|trans }} {% else %} {{ 'entryState.not_exported'|trans }} {% endif %} {{ entry.hourlyRate|money(entry.project.customer.currency) }}{{ entry.fixedRate|money(entry.project.customer.currency) }}{{ entry.duration|duration }} + + {{ entry.hourlyRate|money(entry.project.customer.currency) }} + + {{ entry.fixedRate|money(entry.project.customer.currency) }} + + {{ entry.duration|duration }} + {{ entry.rate|money(entry.project.customer.currency) }}
+ {% for id, visibility in columns %} + {% if id != 'duration' and id != 'rate' %} + {{ timeWorked|duration }} + {% if currency is not null and currency is not same as(false) %} {{ rateTotal|money(currency) }} {% else %} @@ -164,7 +306,7 @@ {% endif %}
diff --git a/tests/Controller/ExportControllerTest.php b/tests/Controller/ExportControllerTest.php index 6a297414..e59abb81 100644 --- a/tests/Controller/ExportControllerTest.php +++ b/tests/Controller/ExportControllerTest.php @@ -129,6 +129,7 @@ class ExportControllerTest extends ControllerBaseTest $this->assertContains('

Summary

', $response->getContent()); $node = $client->getCrawler()->filter('section.export div#export-records table.dataTable tbody tr'); - $this->assertEquals(20, $node->count()); + // 20 rows + the summary footer + $this->assertEquals(21, $node->count()); } } diff --git a/tests/Export/Renderer/HtmlRendererTest.php b/tests/Export/Renderer/HtmlRendererTest.php index 6cc25448..dce941ce 100644 --- a/tests/Export/Renderer/HtmlRendererTest.php +++ b/tests/Export/Renderer/HtmlRendererTest.php @@ -48,12 +48,18 @@ class HtmlRendererTest extends AbstractRendererTest $this->assertContains('

List of expenses

', $content); $this->assertContains('

Summary

', $content); + $this->assertEquals(1, substr_count($content, 'id="export-summary"')); + $this->assertEquals(1, substr_count($content, 'id="export-records"')); + $this->assertEquals(1, substr_count($content, 'id="summary-project"')); + $this->assertEquals(1, substr_count($content, 'id="summary-activity"')); $this->assertContains('Customer Name', $content); $this->assertContains('project name', $content); $this->assertContains('01:50 h', $content); $this->assertContains('€2,437.12', $content); - $this->assertEquals(5, substr_count($content, 'activity description')); + // 5 times in the "full list" and once in the "summary with activities" + $this->assertEquals(6, substr_count($content, 'activity description')); + $this->assertEquals(1, substr_count($content, 'activity description')); } }