Add report command, rename to invoice.
parent
1e2e6bade4
commit
c8e38922c0
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
** Usage
|
** Usage
|
||||||
|
|
||||||
~./rprt.php rprt -y -p -s~
|
~./rprt.php invoice -y -p -s~
|
||||||
|
|
||||||
This command would send an invoice for last month created from the template
|
This command would send an invoice for last month created from the template
|
||||||
file and last month report from youtrack as an email attachment.
|
file and last month report from youtrack as an email attachment.
|
||||||
|
@ -69,9 +69,9 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n
|
||||||
|
|
||||||
*** most current
|
*** most current
|
||||||
1. For version 0.6.7
|
1. For version 0.6.7
|
||||||
- data value objects
|
- [X] data value objects
|
||||||
- phar file
|
- phar file
|
||||||
- additional expenses
|
- [X] additional expenses
|
||||||
2. For version 1.0
|
2. For version 1.0
|
||||||
- plugin system
|
- plugin system
|
||||||
- for time tracking services
|
- for time tracking services
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
use function DI\create;
|
use function DI\create;
|
||||||
use function DI\get;
|
use function DI\get;
|
||||||
|
|
||||||
use RprtCli\Commands\RprtCommand;
|
use RprtCli\Commands\InvoiceCommand;
|
||||||
|
use RprtCli\Commands\ReportCommand;
|
||||||
|
use RprtCli\Commands\TrackCommand;
|
||||||
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||||
use RprtCli\Utils\Configuration\ConfigurationService;
|
use RprtCli\Utils\Configuration\ConfigurationService;
|
||||||
use RprtCli\Utils\CsvReport\CsvReport;
|
|
||||||
use RprtCli\Utils\CsvReport\CsvReportInterface;
|
|
||||||
use RprtCli\Utils\CsvReport\ReportCsv;
|
use RprtCli\Utils\CsvReport\ReportCsv;
|
||||||
use RprtCli\Utils\CsvReport\ReportCsvInterface;
|
use RprtCli\Utils\CsvReport\ReportCsvInterface;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
@ -60,7 +60,7 @@ return [
|
||||||
get('pdf_export.service')
|
get('pdf_export.service')
|
||||||
),
|
),
|
||||||
'mailer' => get(MailerInterface::class),
|
'mailer' => get(MailerInterface::class),
|
||||||
RprtCommand::class => create()->constructor(
|
InvoiceCommand::class => create()->constructor(
|
||||||
get('csv.report'),
|
get('csv.report'),
|
||||||
get('config.service'),
|
get('config.service'),
|
||||||
get('youtrack.service'),
|
get('youtrack.service'),
|
||||||
|
@ -70,5 +70,10 @@ return [
|
||||||
TrackCommand::class => create()->constructor(
|
TrackCommand::class => create()->constructor(
|
||||||
get('config.service'),
|
get('config.service'),
|
||||||
get('youtrack.service')
|
get('youtrack.service')
|
||||||
|
),
|
||||||
|
ReportCommand::class => create()->constructor(
|
||||||
|
get('config.service'),
|
||||||
|
get('youtrack.service'),
|
||||||
|
get('csv.report')
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
use RprtCli\Commands\RprtCommand;
|
use RprtCli\Commands\InvoiceCommand;
|
||||||
|
use RprtCli\Commands\ReportCommand;
|
||||||
use DI\ContainerBuilder;
|
use DI\ContainerBuilder;
|
||||||
|
|
||||||
require __DIR__ . '/vendor/autoload.php';
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
@ -13,7 +14,9 @@ $container = $builder->build();
|
||||||
|
|
||||||
$application = new Application();
|
$application = new Application();
|
||||||
|
|
||||||
$rprtCommand = $container->get(RprtCommand::class);
|
$invoiceCommand = $container->get(InvoiceCommand::class);
|
||||||
$application->add($rprtCommand);
|
$application->add($invoiceCommand);
|
||||||
|
$reportCommand = $container->get(ReportCommand::class);
|
||||||
|
$application->add($reportCommand);
|
||||||
|
|
||||||
$application->run();
|
$application->run();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
// src/Commands/RprtCommand.php;
|
// src/Commands/InvoiceCommand.php;
|
||||||
|
|
||||||
namespace RprtCli\Commands;
|
namespace RprtCli\Commands;
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use function var_dump;
|
use function var_dump;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main file - rprt command.
|
* Main file - invoice command.
|
||||||
*/
|
*/
|
||||||
class RprtCommand extends Command
|
class InvoiceCommand extends Command
|
||||||
{
|
{
|
||||||
protected $csv;
|
protected $csv;
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ class RprtCommand extends Command
|
||||||
*/
|
*/
|
||||||
protected function configure() : void
|
protected function configure() : void
|
||||||
{
|
{
|
||||||
$this->setName('rprt');
|
$this->setName('invoice');
|
||||||
$this->setDescription('Generate monthly report');
|
$this->setDescription('Generate an invoice from (monthly) report');
|
||||||
// @TODO $this->addUsage('');
|
// @TODO $this->addUsage('');
|
||||||
// @TODO add sub options (config overrides)
|
// @TODO add sub options (config overrides)
|
||||||
$this->addOption(
|
$this->addOption(
|
||||||
|
@ -85,7 +85,7 @@ class RprtCommand extends Command
|
||||||
);
|
);
|
||||||
$this->addOption(
|
$this->addOption(
|
||||||
'test',
|
'test',
|
||||||
't',
|
'',
|
||||||
InputOption::VALUE_NONE,
|
InputOption::VALUE_NONE,
|
||||||
'Test login into youtrack service. Prints out your name.'
|
'Test login into youtrack service. Prints out your name.'
|
||||||
);
|
);
|
||||||
|
@ -129,7 +129,7 @@ class RprtCommand extends Command
|
||||||
);
|
);
|
||||||
$this->addOption(
|
$this->addOption(
|
||||||
'report',
|
'report',
|
||||||
'g',
|
't',
|
||||||
InputOption::VALUE_OPTIONAL,
|
InputOption::VALUE_OPTIONAL,
|
||||||
'Show time tracked for report.',
|
'Show time tracked for report.',
|
||||||
FALSE
|
FALSE
|
||||||
|
@ -146,7 +146,7 @@ class RprtCommand extends Command
|
||||||
$list = $this->youtrack->listReports();
|
$list = $this->youtrack->listReports();
|
||||||
$output->writeln(var_export($list, TRUE));
|
$output->writeln(var_export($list, TRUE));
|
||||||
}
|
}
|
||||||
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-g')) {
|
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-t')) {
|
||||||
if ($report = $input->getOption('report')) {
|
if ($report = $input->getOption('report')) {
|
||||||
$this->youtrack->setReportId($report);
|
$this->youtrack->setReportId($report);
|
||||||
}
|
}
|
||||||
|
@ -174,8 +174,8 @@ class RprtCommand extends Command
|
||||||
}
|
}
|
||||||
if ($youtrack || $file = $input->getOption('file')) {
|
if ($youtrack || $file = $input->getOption('file')) {
|
||||||
// Youtrack can also provide a file name.
|
// Youtrack can also provide a file name.
|
||||||
var_dump($file);
|
// var_dump($file);
|
||||||
$data = $this->csv->getReportData($file);
|
$data = $this->csv->getInvoiceData($file);
|
||||||
if (!empty($expenses)) {
|
if (!empty($expenses)) {
|
||||||
$data = array_merge($data, $expenses);
|
$data = array_merge($data, $expenses);
|
||||||
}
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace RprtCli\Commands;
|
||||||
|
|
||||||
|
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||||
|
use RprtCli\Utils\CsvReport\ReportCsvInterface;
|
||||||
|
use RprtCli\Utils\TimeTrackingServices\YoutrackInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
|
use Symfony\Component\Console\Helper\TableSeparator;
|
||||||
|
use Symfony\Component\Console\Helper\TableCell;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class ReportCommand extends Command {
|
||||||
|
|
||||||
|
protected $trackingService;
|
||||||
|
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
protected $csv;
|
||||||
|
|
||||||
|
public function __construct(ConfigurationInterface $configuration, YoutrackInterface $tracking_service, ReportCsvInterface $csv, ?string $name = null) {
|
||||||
|
$this->config = $configuration;
|
||||||
|
// @TODO generalize tracking service.
|
||||||
|
$this->trackingService = $tracking_service;
|
||||||
|
$this->csv = $csv;
|
||||||
|
parent::__construct($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure() :void {
|
||||||
|
$this->setName('report');
|
||||||
|
$this->setDescription('Get a time-tracking report into command line.');
|
||||||
|
$this->addOption(
|
||||||
|
'report',
|
||||||
|
'r',
|
||||||
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
'Select a report from list of your reports'
|
||||||
|
);
|
||||||
|
$this->addOption(
|
||||||
|
'time-range',
|
||||||
|
't',
|
||||||
|
InputOption::VALUE_REQUIRED,
|
||||||
|
'Calculates report from tracking service work items directly for time range'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output) :int {
|
||||||
|
// Could just parse a csv file or actually get workItems from youtrack ...
|
||||||
|
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) {
|
||||||
|
if ($report = $input->getOption('report')) {
|
||||||
|
$this->trackingService->setReportId($report);
|
||||||
|
} else {
|
||||||
|
$reports = $this->trackingService->listReports();
|
||||||
|
$count = 1;
|
||||||
|
foreach ($reports as $id => $name) {
|
||||||
|
$output->writeln("[{$count}] {$name} ({$id})");
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
$output->writeln("[{$count}] None (null)");
|
||||||
|
$report = readline('Select id of the report: ');
|
||||||
|
// Asume people are literate.
|
||||||
|
if (!in_array($report, array_keys($reports) )) {
|
||||||
|
$output->writeln('Non-existing report. Exiting.');
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
$this->trackingService->setReportId($report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently we only support csv download.
|
||||||
|
$report_id = $this->trackingService->getReportId();
|
||||||
|
$file = $this->trackingService->downloadReport($report_id);
|
||||||
|
// var_dump($file);
|
||||||
|
$data = $this->csv->generateReportTable($file);
|
||||||
|
$table = $this->buildTable($output, $data);
|
||||||
|
$table->render();
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds table from the report csv data.
|
||||||
|
*
|
||||||
|
* @TODO: Code duplication with InvoiceCommand::getTable.
|
||||||
|
*/
|
||||||
|
protected function buildTable(OutputInterface $output, array $rows): Table {
|
||||||
|
$table = new Table($output);
|
||||||
|
$table->setHeaders([
|
||||||
|
'Ticket Id', 'Name', 'Time', 'Estimation',
|
||||||
|
]);
|
||||||
|
foreach ($rows as $key => $row) {
|
||||||
|
if (!$row) {
|
||||||
|
$rows[$key] = new TableSeparator();
|
||||||
|
} elseif (is_array($row) && is_null($row[0]) && is_null($row[2])) {
|
||||||
|
// Check which elements in array are null.
|
||||||
|
$rows[$key] = [new TableCell($row[1], ['colspan' => 2]), new TableCell((string) $row[3], ['colspan' => 2])];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$table->setRows($rows);
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,7 +36,7 @@ class CsvReport implements CsvReportInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getReportData(string $filePath) : array
|
public function getInvoiceData(string $filePath) : array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
// @TODO replace with config service.
|
// @TODO replace with config service.
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface CsvReportInterface
|
||||||
*
|
*
|
||||||
* Project key as key and number of hours as value.
|
* Project key as key and number of hours as value.
|
||||||
*/
|
*/
|
||||||
public function getReportData(string $filePath) : array;
|
public function getInvoiceData(string $filePath) : array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data for default drunomics pdf export.
|
* Data for default drunomics pdf export.
|
||||||
|
|
|
@ -38,7 +38,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getReportData(string $filePath) : array
|
public function getInvoiceData(string $filePath) : array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
// @TODO replace with config service.
|
// @TODO replace with config service.
|
||||||
|
@ -117,6 +117,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($add_separator) {
|
if ($add_separator) {
|
||||||
|
// @TODO replace separators with constants for normal separating.
|
||||||
$rows[] = null;
|
$rows[] = null;
|
||||||
$rows[] = ['Gesamt netto', number_format($totalHours, 2, ',', '.'), ' ', number_format($totalPrice, 2, ',', '.')];
|
$rows[] = ['Gesamt netto', number_format($totalHours, 2, ',', '.'), ' ', number_format($totalPrice, 2, ',', '.')];
|
||||||
$add_separator = FALSE;
|
$add_separator = FALSE;
|
||||||
|
@ -124,7 +125,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
foreach ($data as $invoice_element) {
|
foreach ($data as $invoice_element) {
|
||||||
if ($invoice_element instanceof ExpensesInterface) {
|
if ($invoice_element instanceof ExpensesInterface) {
|
||||||
if (!isset($added_expenses)) {
|
if (!isset($added_expenses)) {
|
||||||
// Make next line bold and centered.
|
// @TODO - separator 0: Make next line bold and centered.
|
||||||
$rows[] = 0;
|
$rows[] = 0;
|
||||||
$rows[] = [
|
$rows[] = [
|
||||||
null,
|
null,
|
||||||
|
@ -168,6 +169,64 @@ class ReportCsv implements ReportCsvInterface
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generateReportTable(string $filePath) {
|
||||||
|
// ticket-id, ticket-name, time-spent
|
||||||
|
$data = $this->parseReportData($filePath);
|
||||||
|
[$previous, $time_sum, $project_time, $table, $all_projects] = [$data[0]['id'], 0, 0, [], []];
|
||||||
|
foreach ($data as $line) {
|
||||||
|
$project = explode('-', $line['id'])[0];
|
||||||
|
$previous_project = explode('-', $previous)[0];
|
||||||
|
$project_time += (float) $line['time'];
|
||||||
|
$table[] = array_values($line);
|
||||||
|
if ($project !== $previous_project) {
|
||||||
|
// When project changes, add a sum of time for that project.
|
||||||
|
$table[] = null;
|
||||||
|
$table[] = [null, $previous_project, null, $project_time/60];
|
||||||
|
$table[] = null;
|
||||||
|
$time_sum += (float) $project_time;
|
||||||
|
$project_time = 0;
|
||||||
|
$all_projects[] = $project;
|
||||||
|
}
|
||||||
|
$previous = $line['id'];
|
||||||
|
}
|
||||||
|
// Add sum for the last project.
|
||||||
|
$table[] = null;
|
||||||
|
$table[] = [null, $project, null, $project_time / 60];
|
||||||
|
$time_sum += (float) $project_time;
|
||||||
|
$all_projects[] = $project;
|
||||||
|
// Add a sum of time for whole day.
|
||||||
|
$table[] = null;
|
||||||
|
$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, 80),
|
||||||
|
'time' => $line[4],
|
||||||
|
'estimation' => $line[3],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be moved into test class.
|
* Should be moved into test class.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface ReportCsvInterface
|
||||||
*
|
*
|
||||||
* Project key as key and number of hours as value.
|
* Project key as key and number of hours as value.
|
||||||
*/
|
*/
|
||||||
public function getReportData(string $filePath): array;
|
public function getInvoiceData(string $filePath): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of rows created from array of InvoiceElements.
|
* Returns array of rows created from array of InvoiceElements.
|
||||||
|
|
Loading…
Reference in New Issue