RprtCli/app/src/Utils/CsvReport/ReportCsv.php

257 lines
8.6 KiB
PHP

<?php
declare(strict_types=1);
namespace RprtCli\Utils\CsvReport;
use RprtCli\Utils\Configuration\ConfigurationInterface;
use RprtCli\ValueObjects\ExpensesInterface;
use RprtCli\ValueObjects\WorkInvoiceElement;
use RprtCli\ValueObjects\WorkInvoiceElementInterface;
use function array_key_first;
use function array_keys;
use function array_unshift;
use function array_values;
use function explode;
use function fgetcsv;
use function fopen;
use function implode;
use function is_array;
use function is_numeric;
use function number_format;
use function preg_match;
use function reset;
use function substr;
/**
* Creates a report of projects and hours.
*
* Uses value objects instead of arrays.
*/
class ReportCsv implements ReportCsvInterface
{
/**
* A configuration service.
*
* @var ConfigurationInterface
*/
protected $configurationService;
public function __construct(ConfigurationInterface $config)
{
$this->configurationService = $config;
}
/**
* {@inheritdoc}
*/
public function getInvoiceData(string $filePath): array
{
$output = [];
// @TODO replace with config service.
// $config = $this->dummyConfig()['projects'];
$config = $this->configurationService->get('projects');
foreach (array_keys($config) as $key) {
$output[$key] = 0;
}
if ($file = fopen($filePath, 'r')) {
while (($line = fgetcsv($file)) !== false) {
$parsed = $this->parseCsvFile($line);
// $key = reset(array_keys($parsed));
$key = array_key_first($parsed);
if (isset($output[$key])) {
$output[$key] += (float) reset($parsed);
}
}
}
$report_data = [];
foreach ($output as $project => $hours) {
$report_data[] = new WorkInvoiceElement($project, $hours);
}
return $report_data;
}
/**
* Get correct values from the raw data lines of csv.
*
*
* Columns with data are specified in config.
*
* Project key and unit of time spent.
*/
protected function parseCsvFile(array $rawData): array
{
$config = $this->configurationService->get('projects');
foreach ($config as $key => $project) {
if (preg_match('/' . $project['pattern'] . '/', $rawData[1])) {
return [$key => $rawData[4]];
}
}
return [];
}
/**
* Input is array of Work elements and expenses.
*/
public function generateTable(array $data): array
{
[$rows, $totalHours, $totalPrice, $add_separator] = [[], 0, 0, false];
$projectsConfig = $this->configurationService->get('projects');
// $header = $this->configurationService->get('export.labels', null);
$header = null;
if (is_array($header)) {
$rows[] = $header;
}
// First only list work invoice elements.
foreach ($data as $key => $invoice_element) {
if ($invoice_element instanceof WorkInvoiceElementInterface) {
$add_separator = true;
$project = $invoice_element->getName();
$time = $invoice_element->getTime();
$config = $projectsConfig[$project];
$hours = $config['time_format'] === 'm' ? $time / 60 : $time;
$price = $hours * (float) $config['price'];
$row = [
$config['name'] ?? $project,
number_format($hours, 2, ',', '.'),
number_format($config['price'], 2, ',', '.'),
number_format($price, 2, ',', '.'),
];
$totalHours += $hours;
$totalPrice += $price;
$rows[] = $row;
unset($data[$key]);
}
}
if ($add_separator) {
// @TODO replace separators with constants for normal separating.
$rows[] = null;
$rows[] = [
'Gesamt netto',
number_format($totalHours, 2, ',', '.'),
' ',
number_format($totalPrice, 2, ',', '.')
];
$add_separator = false;
}
if (empty($data)) {
$add_separator = true;
}
foreach ($data as $invoice_element) {
if ($invoice_element instanceof ExpensesInterface) {
if (! isset($added_expenses)) {
// Separator 0: Make next line bold and centered.
$rows[] = ReportCsvInterface::SEPARATOR_HARD;
$rows[] = [
null,
null,
'Kosten',
'EUR',
];
// Don't make next line bold. See RprtCli\PdfExport\PdfExportService::parsedDataToHtml.
$rows[] = ReportCsvInterface::SEPARATOR_SOFT;
$added_expenses = true;
}
$add_separator = true;
$rows[] = [
null,
null,
$invoice_element->getName(),
number_format($invoice_element->getValue(), 2, ',', '.'),
];
$totalPrice += $invoice_element->getValue();
}
}
if ($add_separator) {
$rows[] = ReportCsvInterface::SEPARATOR_MEDIUM;
}
$rows[] = [null, null, 'Gessamt brutto', number_format($totalPrice, 2, ',', '.')];
return $rows;
}
/**
* {@inheritdoc}
*/
public function arangeDataForDefaultPdfExport(array $data): array
{
$rows = $this->generateTable($data);
$header = $this->configurationService->get('export.labels', null);
array_unshift($rows, $header);
foreach ($rows as $key => $row) {
if (! $row) {
unset($data[$key]);
}
}
return $rows;
}
public function generateReportTable(string $filePath)
{
// ticket-id, ticket-name, time-spent
$data = $this->parseReportData($filePath);
if (empty($data)) {
return [];
}
$explodeMinus = fn($ticket) => explode('-', $ticket)[0] ?? 'UNKNOWN';
[$previous, $time_sum, $project_time, $table, $all_projects] = [
$data[0]['id'],
0,
0,
[],
[$explodeMinus($data[0]['id'])]
];
foreach ($data as $line) {
$project = $explodeMinus($line['id']);
$previous_project = $explodeMinus($previous);
if ($project !== $previous_project) {
// When project changes, add a sum of time for that project.
$table[] = ReportCsvInterface::SEPARATOR_MEDIUM;
$table[] = [null, $previous_project, null, $project_time / 60];
$table[] = ReportCsvInterface::SEPARATOR_MEDIUM;
$time_sum += (float) $project_time;
$project_time = 0;
$all_projects[] = $project;
}
$project_time += (float) $line['time'];
$previous = $line['id'];
$table[] = array_values($line);
}
// Add sum for the last project.
$table[] = ReportCsvInterface::SEPARATOR_MEDIUM;
$table[] = [null, $project, null, $project_time / 60];
$time_sum += (float) $project_time;
// $all_projects[] = $project;
// Add a sum of time for whole day.
$table[] = ReportCsvInterface::SEPARATOR_MEDIUM;
$table[] = [null, implode(', ', $all_projects), null, $time_sum / 60];
return $table;
}
/**
* {@inheritdoc}
*/
protected function parseReportData(string $filePath): array
{
$output = [];
// @TODO replace with config service.
// $config = $this->dummyConfig()['projects'];
if ($file = fopen($filePath, 'r')) {
while (($line = fgetcsv($file)) !== false) {
if (! is_numeric($line[4])) {
// Skip header at least.
continue;
}
// @TODO validate line
$output[] = [
'id' => $line[1],
'name' => substr($line[2], 0, 60),
'time' => $line[4],
'estimation' => $line[3],
];
}
}
return $output;
}
}