Add report exports

This commit is contained in:
Constantin Graf
2024-10-25 16:37:40 +02:00
committed by Constantin Graf
parent 7c1fe35754
commit 8712cfb9dc
16 changed files with 924 additions and 82 deletions

View File

@@ -0,0 +1,130 @@
@use('App\Enums\ExportFormat')
@use('Brick\Math\BigDecimal')
@use('PhpOffice\PhpSpreadsheet\Cell\DataType')
@use('PhpOffice\PhpSpreadsheet\Style\NumberFormat')
@use('Carbon\CarbonInterval')
@use('App\Enums\TimeEntryAggregationType')
@inject('interval', 'App\Service\IntervalService')
<table>
<thead>
<tr>
<th style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
{{ $group->description() }}
</th>
<th style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
{{ $subGroup->description() }}
</th>
<th style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
Duration
</th>
<th style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
Duration (decimal)
</th>
<th style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
Amount ({{ Str::upper($currency) }})
</th>
</tr>
</thead>
<tbody>
@php
$counter = 1;
$totalDuration = 0;
$totalCost = 0;
@endphp
@foreach($data['grouped_data'] as $group1Entry)
@foreach($group1Entry['grouped_data'] as $group2Entry)
@php
$duration = CarbonInterval::seconds($group2Entry['seconds']);
@endphp
<tr>
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
@if ($group === TimeEntryAggregationType::Billable)
{{ $group1Entry['key'] ? 'Yes' : 'No' }}
@else
{{ $group1Entry['description'] ?? $group1Entry['key'] ?? '-' }}
@endif
</td>
@if($exportFormat === ExportFormat::ODS || $exportFormat === ExportFormat::CSV)
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
@if ($subGroup === TimeEntryAggregationType::Billable)
{{ $group2Entry['key'] ? 'Yes' : 'No' }}
@else
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
@endif
</td>
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
{{ $interval->format($duration) }}
</td>
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
{{ round($duration->totalHours, 2) }}
</td>
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
{{ round(BigDecimal::ofUnscaledValue($group2Entry['cost'], 2)->toFloat(), 2) }}
</td>
@else
@if ($subGroup === TimeEntryAggregationType::Billable)
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
{{ $group1Entry['key'] ? 'Yes' : 'No' }}
</td>
@else
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_STRING }}">
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
</td>
@endif
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_NUMERIC }}"
data-format="[hh]:mm:ss">
{{ $duration->totalDays }}
</td>
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_NUMERIC }}"
data-format="{{ NumberFormat::FORMAT_NUMBER_00 }}">
{{ $duration->totalHours }}
</td>
<td style="border: 1px solid black;" data-type="{{ DataType::TYPE_NUMERIC }}"
data-format="{{ NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1 }}">
{{ BigDecimal::ofUnscaledValue($group2Entry['cost'], 2)->__toString() }}
</td>
@endif
</tr>
@php
++$counter;
$totalDuration += $group2Entry['seconds'];
$totalCost += $group2Entry['cost'];
@endphp
@endforeach
@endforeach
@php
$totalDurationInterval = CarbonInterval::seconds($totalDuration);
@endphp
<tr style="border: 1px solid black;">
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}"></td>
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
Total
</td>
@if($exportFormat === ExportFormat::ODS || $exportFormat === ExportFormat::CSV)
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
{{ $interval->format($totalDurationInterval) }}
</td>
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
{{ round($totalDurationInterval->totalHours, 2) }}
</td>
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_STRING }}">
{{ round(BigDecimal::ofUnscaledValue($totalCost, 2)->toFloat(), 2) }}
</td>
@else
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_FORMULA }}"
data-format="[hh]:mm:ss">
=SUM(C2:C{{ $counter }})
</td>
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_FORMULA }}"
data-format="{{ NumberFormat::FORMAT_NUMBER_00 }}">
=SUM(D2:D{{ $counter }})
</td>
<td style="border: 1px solid black; font-weight: bold;" data-type="{{ DataType::TYPE_FORMULA }}"
data-format="{{ NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1 }}">
=SUM(E2:E{{ $counter }})
</td>
@endif
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,127 @@
@use('Brick\Math\BigDecimal')
@use('PhpOffice\PhpSpreadsheet\Cell\DataType')
@use('Carbon\CarbonInterval')
@inject('interval', 'App\Service\IntervalService')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Report</title>
<style>
body {
font-family: "Open Sans", sans-serif;
}
table {
font-size: 10px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js"></script>
</head>
<body>
<h1>Report</h1>
<div>
<span>{{ $start->format('Y-m-d') }} - {{ $end->format('Y-m-d') }}</span><br><br>
</div>
<div>
<span>Duration: {{ $interval->format(CarbonInterval::seconds($aggregatedData['seconds'])) }}</span><br>
<span>Total cost: {{ round(BigDecimal::ofUnscaledValue($aggregatedData['cost'], 2)->toFloat(), 2) }}</span><br>
</div>
<div id="main-chart" style="width: 800px; height:400px;"></div>
@foreach($aggregatedData['grouped_data'] as $group1Entry)
<h2>{{ $group->description() }}: {{ $group1Entry['description'] ?? $group1Entry['key'] ?? '-' }}</h2>
<table>
<thead>
<tr>
<th>
{{ $subGroup->description() }}
</th>
<th>
Duration
</th>
<th>
Duration (decimal)
</th>
<th>
Amount ({{ Str::upper($currency) }})
</th>
</tr>
</thead>
<tbody>
@php
$counter = 1;
$totalDuration = 0;
$totalCost = 0;
@endphp
@foreach($group1Entry['grouped_data'] as $group2Entry)
@php
$duration = CarbonInterval::seconds($group2Entry['seconds']);
@endphp
<tr>
<td>
{{ $group2Entry['description'] ?? $group2Entry['key'] ?? '-' }}
</td>
<td>
{{ $interval->format($duration) }}
</td>
<td>
{{ round($duration->totalHours, 2) }}
</td>
<td>
{{ round(BigDecimal::ofUnscaledValue($group2Entry['cost'], 2)->toFloat(), 2) }}
</td>
</tr>
@php
$totalDuration += $group2Entry['seconds'];
$totalCost += $group2Entry['cost'];
@endphp
@endforeach
</tbody>
</table>
@endforeach
<script>
// Initialize the echarts instance based on the prepared dom
let element = document.getElementById('main-chart');
let myChart = echarts.init(element, null, {
renderer: 'svg'
});
// Specify the configuration items and data for the chart
let option = {
tooltip: {},
xAxis: {
data: ['{!! collect($dataHistoryChart['grouped_data'])->pluck('key')->implode("', '") !!}'],
rotate: 0
},
yAxis: {
minInterval: 1,
axisLabel: {
formatter: function (value, index) {
let totalSeconds = value;
let hours = Math.floor(totalSeconds / 3600);
totalSeconds %= 3600;
let minutes = Math.floor(totalSeconds / 60);
let seconds = totalSeconds % 60;
return hours + ':' + minutes + ':' + seconds;
}
}
},
series: [
{
name: 'time',
type: 'bar',
data: [{!! collect($dataHistoryChart['grouped_data'])->pluck('seconds')->implode(', ') !!}],
}
]
};
// Display the chart using the configuration items and data just specified.
myChart.setOption(option);
</script>
</body>
</html>

View File

@@ -13,7 +13,7 @@
</style>
</head>
<body>
<h1>Report</h1>
<h1>Detailed Report</h1>
<div>
<span>01.01.2020 - 01.01.2024</span>