Fix coding standard.
parent
ff2e35bb93
commit
624b895fd6
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
// src/Commands/InvoiceCommand.php;
|
||||||
|
|
||||||
|
namespace RprtCli\Commands;
|
||||||
|
|
||||||
|
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
|
||||||
|
// use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class that adds some common services and input options.
|
||||||
|
*/
|
||||||
|
class AbstractCliCommand extends Command
|
||||||
|
{
|
||||||
|
protected $configuration;
|
||||||
|
|
||||||
|
// @TODO Add service factory service.
|
||||||
|
// protected $trackingServiceFactory;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ConfigurationInterface $configuration,
|
||||||
|
?string $name = null
|
||||||
|
) {
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
parent::__construct($name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,8 +38,8 @@ use function var_export;
|
||||||
/**
|
/**
|
||||||
* Main file - invoice command.
|
* Main file - invoice command.
|
||||||
*/
|
*/
|
||||||
class InvoiceCommand extends Command {
|
class InvoiceCommand extends Command
|
||||||
|
{
|
||||||
use SelectReportTrait;
|
use SelectReportTrait;
|
||||||
|
|
||||||
protected $csv;
|
protected $csv;
|
||||||
|
@ -50,8 +50,8 @@ class InvoiceCommand extends Command {
|
||||||
|
|
||||||
protected $pdfExport;
|
protected $pdfExport;
|
||||||
|
|
||||||
const TYPE_WORK = 1;
|
protected const TYPE_WORK = 1;
|
||||||
const TYPE_EXPENSE = 2;
|
protected const TYPE_EXPENSE = 2;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ReportCsvInterface $csv,
|
ReportCsvInterface $csv,
|
||||||
|
@ -61,18 +61,18 @@ class InvoiceCommand extends Command {
|
||||||
MailerInterface $mailer,
|
MailerInterface $mailer,
|
||||||
?string $name = null
|
?string $name = null
|
||||||
) {
|
) {
|
||||||
$this->csv = $csv;
|
$this->csv = $csv;
|
||||||
$this->config = $configuration;
|
$this->config = $configuration;
|
||||||
$this->trackingService = $trackingService;
|
$this->trackingService = $trackingService;
|
||||||
$this->pdfExport = $pdf_export;
|
$this->pdfExport = $pdf_export;
|
||||||
$this->mailer = $mailer;
|
$this->mailer = $mailer;
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get configuration.
|
* Get configuration.
|
||||||
*/
|
*/
|
||||||
protected function configure() : void
|
protected function configure(): void
|
||||||
{
|
{
|
||||||
$this->setName('invoice');
|
$this->setName('invoice');
|
||||||
$this->setDescription('Generate an invoice from (monthly) report');
|
$this->setDescription('Generate an invoice from (monthly) report');
|
||||||
|
@ -124,6 +124,7 @@ class InvoiceCommand extends Command {
|
||||||
'expenses',
|
'expenses',
|
||||||
'e',
|
'e',
|
||||||
InputOption::VALUE_OPTIONAL,
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
// phpcs:ignore
|
||||||
'List of additional expenses in format expense1=value1;expenses2=value2... or empty for interactive output.',
|
'List of additional expenses in format expense1=value1;expenses2=value2... or empty for interactive output.',
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -131,6 +132,7 @@ class InvoiceCommand extends Command {
|
||||||
'custom',
|
'custom',
|
||||||
'c',
|
'c',
|
||||||
InputOption::VALUE_OPTIONAL,
|
InputOption::VALUE_OPTIONAL,
|
||||||
|
// phpcs:ignore
|
||||||
'Additional custom work untracked in format: name1=time1;name2=time2... Project to assign work items to has to be configured in app config. Leave empty for interactive output.',
|
'Additional custom work untracked in format: name1=time1;name2=time2... Project to assign work items to has to be configured in app config. Leave empty for interactive output.',
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -149,7 +151,7 @@ class InvoiceCommand extends Command {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
if ($input->getOption('test')) {
|
if ($input->getOption('test')) {
|
||||||
$test = $this->trackingService->testYoutrackapi();
|
$test = $this->trackingService->testYoutrackapi();
|
||||||
|
@ -161,9 +163,7 @@ class InvoiceCommand extends Command {
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
// Gets report parameter.
|
// Gets report parameter.
|
||||||
$this->getReportParameter($input, $output, 'tracking_service.youtrack.invoice.report');
|
$file = $this->getReportCsvFilePath($input, $output, 'tracking_service.youtrack.invoice.report');
|
||||||
$report_id = $this->trackingService->getReportId();
|
|
||||||
$file = $this->getReportCsvFilePath($input, $output, $report_id);
|
|
||||||
$report_name = $this->trackingService->getReportName();
|
$report_name = $this->trackingService->getReportName();
|
||||||
if ($input->hasParameterOption('--expenses') || $input->hasParameterOption('-e')) {
|
if ($input->hasParameterOption('--expenses') || $input->hasParameterOption('-e')) {
|
||||||
$expenses = $this->getCustomWorkOrExpenses($input->getOption('expenses'), self::TYPE_EXPENSE);
|
$expenses = $this->getCustomWorkOrExpenses($input->getOption('expenses'), self::TYPE_EXPENSE);
|
||||||
|
@ -202,7 +202,7 @@ class InvoiceCommand extends Command {
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getTable(OutputInterface $output, array $data) : Table
|
protected function getTable(OutputInterface $output, array $data): Table
|
||||||
{
|
{
|
||||||
$rows = $this->csv->generateTable($data);
|
$rows = $this->csv->generateTable($data);
|
||||||
$table = new Table($output);
|
$table = new Table($output);
|
||||||
|
@ -230,7 +230,7 @@ class InvoiceCommand extends Command {
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* This method was almost exact copy of CsvReport::arangeDataForDefaultPdfExport
|
* This method was almost exact copy of CsvReport::arangeDataForDefaultPdfExport
|
||||||
*/
|
*/
|
||||||
protected function generateTable(OutputInterface $output, array $data) : Table
|
protected function generateTable(OutputInterface $output, array $data): Table
|
||||||
{
|
{
|
||||||
$table = new Table($output);
|
$table = new Table($output);
|
||||||
$table->setHeaders([
|
$table->setHeaders([
|
||||||
|
@ -282,7 +282,7 @@ class InvoiceCommand extends Command {
|
||||||
/**
|
/**
|
||||||
* Dummy output for testing.
|
* Dummy output for testing.
|
||||||
*/
|
*/
|
||||||
protected function dummyOutput(InputInterface $input, OutputInterface $output) : void
|
protected function dummyOutput(InputInterface $input, OutputInterface $output): void
|
||||||
{
|
{
|
||||||
// $txt = $this->translator->trans('From [start-date] to [end-date].', [], 'rprt', 'sl_SI');
|
// $txt = $this->translator->trans('From [start-date] to [end-date].', [], 'rprt', 'sl_SI');
|
||||||
// $output->writeln($txt);
|
// $output->writeln($txt);
|
||||||
|
@ -326,7 +326,8 @@ class InvoiceCommand extends Command {
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCustomWorkOrExpenses($custom, $type) {
|
protected function getCustomWorkOrExpenses($custom, $type)
|
||||||
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
if (is_string($custom)) {
|
if (is_string($custom)) {
|
||||||
foreach (explode(';', $custom) as $item) {
|
foreach (explode(';', $custom) as $item) {
|
||||||
|
|
|
@ -18,8 +18,8 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function is_null;
|
use function is_null;
|
||||||
|
|
||||||
class ReportCommand extends Command {
|
class ReportCommand extends Command
|
||||||
|
{
|
||||||
use SelectReportTrait;
|
use SelectReportTrait;
|
||||||
|
|
||||||
protected $trackingService;
|
protected $trackingService;
|
||||||
|
@ -28,6 +28,7 @@ class ReportCommand extends Command {
|
||||||
|
|
||||||
protected $csv;
|
protected $csv;
|
||||||
|
|
||||||
|
// phpcs:ignore
|
||||||
public function __construct(ConfigurationInterface $configuration, YoutrackInterface $tracking_service, ReportCsvInterface $csv, ?string $name = null)
|
public function __construct(ConfigurationInterface $configuration, YoutrackInterface $tracking_service, ReportCsvInterface $csv, ?string $name = null)
|
||||||
{
|
{
|
||||||
$this->config = $configuration;
|
$this->config = $configuration;
|
||||||
|
@ -37,7 +38,8 @@ class ReportCommand extends Command {
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function configure() : void {
|
protected function configure(): void
|
||||||
|
{
|
||||||
$this->setName('report');
|
$this->setName('report');
|
||||||
$this->setDescription('Get a time-tracking report into command line.');
|
$this->setDescription('Get a time-tracking report into command line.');
|
||||||
$this->addOption(
|
$this->addOption(
|
||||||
|
@ -63,7 +65,7 @@ class ReportCommand extends Command {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
if ($timeRange = $input->getOption('time-range')) {
|
if ($timeRange = $input->getOption('time-range')) {
|
||||||
// @TODO: Implement time range option:
|
// @TODO: Implement time range option:
|
||||||
|
@ -77,10 +79,8 @@ class ReportCommand extends Command {
|
||||||
$output->writeln('<error>This option is not supported yet.</error>');
|
$output->writeln('<error>This option is not supported yet.</error>');
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
$this->getReportParameter($input, $output);
|
|
||||||
// Currently we only support csv download.
|
// Currently we only support csv download.
|
||||||
$report_id = $this->trackingService->getReportId();
|
$file = $this->getReportCsvFilePath($input, $output);
|
||||||
$file = $this->getReportCsvFilePath($input, $output, $report_id);
|
|
||||||
$report_name = $this->trackingService->getReportName();
|
$report_name = $this->trackingService->getReportName();
|
||||||
$output->writeln("report: <info>{$report_name}</info>");
|
$output->writeln("report: <info>{$report_name}</info>");
|
||||||
$data = $this->csv->generateReportTable($file);
|
$data = $this->csv->generateReportTable($file);
|
||||||
|
@ -95,7 +95,7 @@ class ReportCommand extends Command {
|
||||||
*
|
*
|
||||||
* @TODO: Code duplication with InvoiceCommand::getTable.
|
* @TODO: Code duplication with InvoiceCommand::getTable.
|
||||||
*/
|
*/
|
||||||
protected function buildTable(OutputInterface $output, array $rows) : Table
|
protected function buildTable(OutputInterface $output, array $rows): Table
|
||||||
{
|
{
|
||||||
$table = new Table($output);
|
$table = new Table($output);
|
||||||
$table->setHeaders([
|
$table->setHeaders([
|
||||||
|
@ -109,7 +109,10 @@ class ReportCommand extends Command {
|
||||||
$rows[$key] = new TableSeparator();
|
$rows[$key] = new TableSeparator();
|
||||||
} elseif (is_array($row) && is_null($row[0]) && is_null($row[2])) {
|
} elseif (is_array($row) && is_null($row[0]) && is_null($row[2])) {
|
||||||
// Check which elements in array are null.
|
// Check which elements in array are null.
|
||||||
$rows[$key] = [new TableCell($row[1], ['colspan' => 2]), new TableCell((string) $row[3], ['colspan' => 2])];
|
$rows[$key] = [
|
||||||
|
new TableCell($row[1], ['colspan' => 2]),
|
||||||
|
new TableCell((string) $row[3], ['colspan' => 2]),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$table->setRows($rows);
|
$table->setRows($rows);
|
||||||
|
|
|
@ -9,6 +9,8 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
|
|
||||||
|
use function array_flip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait to select report.
|
* Trait to select report.
|
||||||
*
|
*
|
||||||
|
@ -16,17 +18,22 @@ use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
* Command must have report input option. It is not the most elegant solution
|
* Command must have report input option. It is not the most elegant solution
|
||||||
* but it helps avoiding code duplication.
|
* but it helps avoiding code duplication.
|
||||||
*/
|
*/
|
||||||
trait SelectReportTrait {
|
trait SelectReportTrait
|
||||||
|
{
|
||||||
protected function checkContext() {
|
protected function checkContext()
|
||||||
if (!isset($this->trackingService)) {
|
{
|
||||||
return FALSE;
|
if (! isset($this->trackingService)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return $this instanceof Command;
|
return $this instanceof Command;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getReportParameter(InputInterface $input, OutputInterface $output, $default = 'tracking_service.youtrack.report.report') {
|
protected function getReportParameter(
|
||||||
if (!$this->checkContext()) {
|
InputInterface $input,
|
||||||
|
OutputInterface $output,
|
||||||
|
$default = 'tracking_service.youtrack.report.report'
|
||||||
|
) {
|
||||||
|
if (! $this->checkContext()) {
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
$reports = $this->trackingService->listReports();
|
$reports = $this->trackingService->listReports();
|
||||||
|
@ -38,8 +45,8 @@ trait SelectReportTrait {
|
||||||
$helper = $this->getHelper('question');
|
$helper = $this->getHelper('question');
|
||||||
$question = new ChoiceQuestion('Select report:', $reports);
|
$question = new ChoiceQuestion('Select report:', $reports);
|
||||||
$question
|
$question
|
||||||
->setErrorMessage('Report %s does not exist!')
|
->setErrorMessage('Report %s does not exist!')
|
||||||
->setAutocompleterValues($reports);
|
->setAutocompleterValues($reports);
|
||||||
$report_id = $helper->ask($input, $output, $question);
|
$report_id = $helper->ask($input, $output, $question);
|
||||||
$output->writeln('Report ' . $report_id . ' selected.');
|
$output->writeln('Report ' . $report_id . ' selected.');
|
||||||
}
|
}
|
||||||
|
@ -60,8 +67,14 @@ trait SelectReportTrait {
|
||||||
$this->trackingService->setReportName();
|
$this->trackingService->setReportName();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getReportCsvFilePath(InputInterface $input, OutputInterface $output, ?string $report_id) : ?string {
|
protected function getReportCsvFilePath(
|
||||||
if (!$file = $input->getOption('file')) {
|
InputInterface $input,
|
||||||
|
OutputInterface $output,
|
||||||
|
?string $default = 'tracking_service.youtrack.report.report'
|
||||||
|
): ?string {
|
||||||
|
if (! $file = $input->getOption('file')) {
|
||||||
|
$this->getReportParameter($input, $output, $default);
|
||||||
|
$report_id = $this->trackingService->getReportId();
|
||||||
$cache_clear_status = $this->trackingService->clearReportCache($report_id);
|
$cache_clear_status = $this->trackingService->clearReportCache($report_id);
|
||||||
if ($output->isVerbose()) {
|
if ($output->isVerbose()) {
|
||||||
$output->writeln("Report <info>{$report_id}</info> cache cleared, status: {$cache_clear_status}");
|
$output->writeln("Report <info>{$report_id}</info> cache cleared, status: {$cache_clear_status}");
|
||||||
|
@ -75,6 +88,4 @@ trait SelectReportTrait {
|
||||||
}
|
}
|
||||||
return $file;
|
return $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,11 @@ class TrackCommand extends Command
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function configure() : void
|
protected function configure(): void
|
||||||
{
|
{
|
||||||
$this->setName('youtrack');
|
$this->setName('youtrack');
|
||||||
$this->setDescription('Track time into your youtrack service');
|
$this->setDescription('Track time into your youtrack service');
|
||||||
|
// phpcs:ignore
|
||||||
$this->addUsage('rprt-cli youtrack --issue=[issue-name] --minutes=[minutes] --date=[days-ago] --description=[text] --work-type=[work-type]');
|
$this->addUsage('rprt-cli youtrack --issue=[issue-name] --minutes=[minutes] --date=[days-ago] --description=[text] --work-type=[work-type]');
|
||||||
// Options or arguments? Technically they are arguments but default value could be provided by config.
|
// Options or arguments? Technically they are arguments but default value could be provided by config.
|
||||||
// Options are more suitable.
|
// Options are more suitable.
|
||||||
|
|
|
@ -11,7 +11,7 @@ interface ConfigurationInterface
|
||||||
/**
|
/**
|
||||||
* Get and read the configuration from file.
|
* Get and read the configuration from file.
|
||||||
*/
|
*/
|
||||||
public function getConfig() : bool;
|
public function getConfig(): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific configuration for key.
|
* Get a specific configuration for key.
|
||||||
|
@ -31,5 +31,5 @@ interface ConfigurationInterface
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* Key to check for.
|
* Key to check for.
|
||||||
*/
|
*/
|
||||||
public function exists($key) : bool;
|
public function exists($key): bool;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ class ConfigurationService implements ConfigurationInterface
|
||||||
return $fullPath;
|
return $fullPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @TODO This should be some kind of error!
|
// @TODO This should be some kind of error!
|
||||||
var_dump('Config File Not Found!');
|
var_dump('Config File Not Found!');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ class ConfigurationService implements ConfigurationInterface
|
||||||
/**
|
/**
|
||||||
* Get and read the configuration from file.
|
* Get and read the configuration from file.
|
||||||
*/
|
*/
|
||||||
public function getConfig() : bool
|
public function getConfig(): bool
|
||||||
{
|
{
|
||||||
if ($this->configFilePath) {
|
if ($this->configFilePath) {
|
||||||
$config = Yaml::parseFile($this->configFilePath);
|
$config = Yaml::parseFile($this->configFilePath);
|
||||||
|
@ -125,7 +125,7 @@ class ConfigurationService implements ConfigurationInterface
|
||||||
*
|
*
|
||||||
* Value of configuration key is not equal to default.
|
* Value of configuration key is not equal to default.
|
||||||
*/
|
*/
|
||||||
public function exists($key) : bool
|
public function exists($key): bool
|
||||||
{
|
{
|
||||||
return $this->get($key) !== $this->default;
|
return $this->get($key) !== $this->default;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
// src/Utils/Configuration/TranslationService.php
|
// src/Utils/Configuration/TranslationService.php
|
||||||
|
|
||||||
|
namespace RprtCli\Utils\Translation;
|
||||||
|
|
||||||
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||||
|
|
||||||
class TranslationService
|
class TranslationService
|
||||||
|
|
|
@ -39,7 +39,7 @@ class CsvReport implements CsvReportInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getInvoiceData(string $filePath) : array
|
public function getInvoiceData(string $filePath): array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
// @TODO replace with config service.
|
// @TODO replace with config service.
|
||||||
|
@ -69,7 +69,7 @@ class CsvReport implements CsvReportInterface
|
||||||
*
|
*
|
||||||
* Project key and unit of time spent.
|
* Project key and unit of time spent.
|
||||||
*/
|
*/
|
||||||
protected function parseCsvFile(array $rawData) : array
|
protected function parseCsvFile(array $rawData): array
|
||||||
{
|
{
|
||||||
$config = $this->configurationService->get('projects');
|
$config = $this->configurationService->get('projects');
|
||||||
foreach ($config as $key => $project) {
|
foreach ($config as $key => $project) {
|
||||||
|
@ -80,7 +80,7 @@ class CsvReport implements CsvReportInterface
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function arangeDataForDefaultPdfExport(array $data) : array
|
public function arangeDataForDefaultPdfExport(array $data): array
|
||||||
{
|
{
|
||||||
[$rows, $totalHours, $totalPrice] = [[], 0, 0];
|
[$rows, $totalHours, $totalPrice] = [[], 0, 0];
|
||||||
$projectsConfig = $this->configurationService->get('projects');
|
$projectsConfig = $this->configurationService->get('projects');
|
||||||
|
@ -111,13 +111,18 @@ class CsvReport implements CsvReportInterface
|
||||||
}
|
}
|
||||||
// @TODO Check rate in final result.
|
// @TODO Check rate in final result.
|
||||||
// $rows[] = [$this->translator->trans('Sum'), $totalHours, $config['price'], $totalPrice];
|
// $rows[] = [$this->translator->trans('Sum'), $totalHours, $config['price'], $totalPrice];
|
||||||
$rows[] = ['Gesamt netto', number_format($totalHours, 2, ',', '.'), number_format($config['price'], 2, ',', '.'), number_format($totalPrice, 2, ',', '.')];
|
$rows[] = [
|
||||||
|
'Gesamt netto',
|
||||||
|
number_format($totalHours, 2, ',', '.'),
|
||||||
|
number_format($config['price'], 2, ',', '.'),
|
||||||
|
number_format($totalPrice, 2, ',', '.')
|
||||||
|
];
|
||||||
$rows[] = [null, null, 'Gessamt brutto', number_format($totalPrice, 2, ',', '.')];
|
$rows[] = [null, null, 'Gessamt brutto', number_format($totalPrice, 2, ',', '.')];
|
||||||
|
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function dummyConfig() : array
|
protected function dummyConfig(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'projects' => [
|
'projects' => [
|
||||||
|
@ -127,15 +132,15 @@ class CsvReport implements CsvReportInterface
|
||||||
'price' => 26,
|
'price' => 26,
|
||||||
// optional specify columns
|
// optional specify columns
|
||||||
],
|
],
|
||||||
'WV' => [
|
'TNP' => [
|
||||||
'name' => 'Wirtschaftsverlag',
|
'name' => 'Triglav National Park',
|
||||||
'pattern' => 'WV-[0-9]+',
|
'pattern' => 'TNP-[0-9]+',
|
||||||
'price' => 26,
|
'price' => 26,
|
||||||
// optional specify columns
|
// optional specify columns
|
||||||
],
|
],
|
||||||
'Other' => [
|
'Other' => [
|
||||||
'name' => 'Other projects',
|
'name' => 'Other projects',
|
||||||
'pattern' => '(?!.\bLDP\b)(?!.\bWV\b)',
|
'pattern' => '(?!.\bLDP\b)(?!.\bTNP\b)',
|
||||||
'price' => 26,
|
'price' => 26,
|
||||||
// optional specify columns
|
// optional specify columns
|
||||||
],
|
],
|
||||||
|
|
|
@ -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 getInvoiceData(string $filePath) : array;
|
public function getInvoiceData(string $filePath): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data for default drunomics pdf export.
|
* Data for default drunomics pdf export.
|
||||||
|
@ -25,5 +25,5 @@ interface CsvReportInterface
|
||||||
*
|
*
|
||||||
* Parsed data from csv report.
|
* Parsed data from csv report.
|
||||||
*/
|
*/
|
||||||
public function arangeDataForDefaultPdfExport(array $data) : array;
|
public function arangeDataForDefaultPdfExport(array $data): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getInvoiceData(string $filePath) : array
|
public function getInvoiceData(string $filePath): array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
// @TODO replace with config service.
|
// @TODO replace with config service.
|
||||||
|
@ -80,7 +80,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
*
|
*
|
||||||
* Project key and unit of time spent.
|
* Project key and unit of time spent.
|
||||||
*/
|
*/
|
||||||
protected function parseCsvFile(array $rawData) : array
|
protected function parseCsvFile(array $rawData): array
|
||||||
{
|
{
|
||||||
$config = $this->configurationService->get('projects');
|
$config = $this->configurationService->get('projects');
|
||||||
foreach ($config as $key => $project) {
|
foreach ($config as $key => $project) {
|
||||||
|
@ -94,7 +94,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
/**
|
/**
|
||||||
* Input is array of Work elements and expenses.
|
* Input is array of Work elements and expenses.
|
||||||
*/
|
*/
|
||||||
public function generateTable(array $data) : array
|
public function generateTable(array $data): array
|
||||||
{
|
{
|
||||||
[$rows, $totalHours, $totalPrice, $add_separator] = [[], 0, 0, false];
|
[$rows, $totalHours, $totalPrice, $add_separator] = [[], 0, 0, false];
|
||||||
$projectsConfig = $this->configurationService->get('projects');
|
$projectsConfig = $this->configurationService->get('projects');
|
||||||
|
@ -126,8 +126,13 @@ class ReportCsv implements ReportCsvInterface
|
||||||
}
|
}
|
||||||
if ($add_separator) {
|
if ($add_separator) {
|
||||||
// @TODO replace separators with constants for normal separating.
|
// @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;
|
||||||
}
|
}
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
|
@ -168,7 +173,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function arangeDataForDefaultPdfExport(array $data) : array
|
public function arangeDataForDefaultPdfExport(array $data): array
|
||||||
{
|
{
|
||||||
$rows = $this->generateTable($data);
|
$rows = $this->generateTable($data);
|
||||||
$header = $this->configurationService->get('export.labels', null);
|
$header = $this->configurationService->get('export.labels', null);
|
||||||
|
@ -189,9 +194,15 @@ class ReportCsv implements ReportCsvInterface
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
$explodeMinus = fn($ticket) => explode('-', $ticket)[0] ?? 'UNKNOWN';
|
$explodeMinus = fn($ticket) => explode('-', $ticket)[0] ?? 'UNKNOWN';
|
||||||
[$previous, $time_sum, $project_time, $table, $all_projects] = [$data[0]['id'], 0, 0, [], [$explodeMinus($data[0]['id'])]];
|
[$previous, $time_sum, $project_time, $table, $all_projects] = [
|
||||||
|
$data[0]['id'],
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
[],
|
||||||
|
[$explodeMinus($data[0]['id'])]
|
||||||
|
];
|
||||||
foreach ($data as $line) {
|
foreach ($data as $line) {
|
||||||
$project = $explodeMinus($line['id']);
|
$project = $explodeMinus($line['id']);
|
||||||
$previous_project = $explodeMinus($previous);
|
$previous_project = $explodeMinus($previous);
|
||||||
if ($project !== $previous_project) {
|
if ($project !== $previous_project) {
|
||||||
// When project changes, add a sum of time for that project.
|
// When project changes, add a sum of time for that project.
|
||||||
|
@ -220,7 +231,7 @@ class ReportCsv implements ReportCsvInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
protected function parseReportData(string $filePath) : array
|
protected function parseReportData(string $filePath): array
|
||||||
{
|
{
|
||||||
$output = [];
|
$output = [];
|
||||||
// @TODO replace with config service.
|
// @TODO replace with config service.
|
||||||
|
@ -242,5 +253,4 @@ class ReportCsv implements ReportCsvInterface
|
||||||
}
|
}
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,17 +12,17 @@ interface ReportCsvInterface
|
||||||
/**
|
/**
|
||||||
* Normal separator.
|
* Normal separator.
|
||||||
*/
|
*/
|
||||||
const SEPARATOR_SOFT = false;
|
public const SEPARATOR_SOFT = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Medium separator. Next line bold.
|
* Medium separator. Next line bold.
|
||||||
*/
|
*/
|
||||||
const SEPARATOR_MEDIUM = null;
|
public const SEPARATOR_MEDIUM = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next line should be bold and centered.
|
* Next line should be bold and centered.
|
||||||
*/
|
*/
|
||||||
const SEPARATOR_HARD = 0;
|
public const SEPARATOR_HARD = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of hours per configured projects.
|
* Returns array of hours per configured projects.
|
||||||
|
@ -32,14 +32,14 @@ interface ReportCsvInterface
|
||||||
*
|
*
|
||||||
* Project key as key and number of hours as value.
|
* Project key as key and number of hours as value.
|
||||||
*/
|
*/
|
||||||
public function getInvoiceData(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.
|
||||||
*
|
*
|
||||||
* If row is null, it is meant to be a table separator.
|
* If row is null, it is meant to be a table separator.
|
||||||
*/
|
*/
|
||||||
public function generateTable(array $data) : array;
|
public function generateTable(array $data): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data for default drunomics pdf export.
|
* Data for default drunomics pdf export.
|
||||||
|
@ -47,5 +47,5 @@ interface ReportCsvInterface
|
||||||
*
|
*
|
||||||
* Parsed data from csv report.
|
* Parsed data from csv report.
|
||||||
*/
|
*/
|
||||||
public function arangeDataForDefaultPdfExport(array $data) : array;
|
public function arangeDataForDefaultPdfExport(array $data): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,17 +52,17 @@ class MailerService implements MailerInterface
|
||||||
$this->pdf = $pdf;
|
$this->pdf = $pdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRecipients(array $to) : void
|
public function setRecipients(array $to): void
|
||||||
{
|
{
|
||||||
$this->to = $to;
|
$this->to = $to;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setSubject(string $subject) : void
|
public function setSubject(string $subject): void
|
||||||
{
|
{
|
||||||
$this->subject = $subject;
|
$this->subject = $subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setAttachment(string $path) : void
|
public function setAttachment(string $path): void
|
||||||
{
|
{
|
||||||
// @TODO - add some error handling.
|
// @TODO - add some error handling.
|
||||||
$this->attachment = $path;
|
$this->attachment = $path;
|
||||||
|
@ -115,7 +115,7 @@ class MailerService implements MailerInterface
|
||||||
return $this->password;
|
return $this->password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMail(string $from, array $to, string $subject, string $text, array $attachment = []) : void
|
public function sendMail(string $from, array $to, string $subject, string $text, array $attachment = []): void
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
$email->from($from);
|
$email->from($from);
|
||||||
|
@ -153,9 +153,14 @@ class MailerService implements MailerInterface
|
||||||
return new Mailer($transport);
|
return new Mailer($transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendDefaultMail(string $output) : void
|
public function sendDefaultMail(string $output): void
|
||||||
{
|
{
|
||||||
$tokens = $this->pdf->gatherTokensForTemplate($this->getEmailTemplatePath(), false, $this->getDefaultTokens(), 'email.tokens');
|
$tokens = $this->pdf->gatherTokensForTemplate(
|
||||||
|
$this->getEmailTemplatePath(),
|
||||||
|
false,
|
||||||
|
$this->getDefaultTokens(),
|
||||||
|
'email.tokens'
|
||||||
|
);
|
||||||
$text = $this->pdf->replaceTokensInTemplate($this->getEmailTemplatePath(), $tokens);
|
$text = $this->pdf->replaceTokensInTemplate($this->getEmailTemplatePath(), $tokens);
|
||||||
$this->sendMail(
|
$this->sendMail(
|
||||||
$this->getProperty('from'),
|
$this->getProperty('from'),
|
||||||
|
@ -166,7 +171,7 @@ class MailerService implements MailerInterface
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDefaultTokens() : array
|
public function getDefaultTokens(): array
|
||||||
{
|
{
|
||||||
$tokens = [];
|
$tokens = [];
|
||||||
$date = strtotime('-1 month');
|
$date = strtotime('-1 month');
|
||||||
|
@ -175,7 +180,7 @@ class MailerService implements MailerInterface
|
||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getEmailTemplatePath() : ?string
|
protected function getEmailTemplatePath(): ?string
|
||||||
{
|
{
|
||||||
if (! isset($this->templatePath)) {
|
if (! isset($this->templatePath)) {
|
||||||
$template_path = $this->config->get('email.template_path', false);
|
$template_path = $this->config->get('email.template_path', false);
|
||||||
|
@ -190,7 +195,7 @@ class MailerService implements MailerInterface
|
||||||
return $this->templatePath;
|
return $this->templatePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setEmailTemplatePath(string $path) : void
|
public function setEmailTemplatePath(string $path): void
|
||||||
{
|
{
|
||||||
if (file_exists($path)) {
|
if (file_exists($path)) {
|
||||||
$this->templatePath = $path;
|
$this->templatePath = $path;
|
||||||
|
|
|
@ -13,9 +13,9 @@ interface PdfExportInterface
|
||||||
* Retrieves path to template file either from command option, configuration
|
* Retrieves path to template file either from command option, configuration
|
||||||
* or from uer input.
|
* or from uer input.
|
||||||
*/
|
*/
|
||||||
public function getTemplatePath() : ?string;
|
public function getTemplatePath(): ?string;
|
||||||
|
|
||||||
public function setTemplatePath(string $path) : void;
|
public function setTemplatePath(string $path): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates html table from parsed csv data.
|
* Creates html table from parsed csv data.
|
||||||
|
@ -23,12 +23,12 @@ interface PdfExportInterface
|
||||||
*
|
*
|
||||||
* First line is header. The rest of them is table body.
|
* First line is header. The rest of them is table body.
|
||||||
*/
|
*/
|
||||||
public function parsedDataToHtmlTable(array $data) : ?string;
|
public function parsedDataToHtmlTable(array $data): ?string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the template file and replaces token values.
|
* Reads the template file and replaces token values.
|
||||||
*/
|
*/
|
||||||
public function replaceTokensInTemplate(string $template_path, array $tokens) : ?string;
|
public function replaceTokensInTemplate(string $template_path, array $tokens): ?string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and export file.
|
* Creates and export file.
|
||||||
|
@ -38,7 +38,7 @@ interface PdfExportInterface
|
||||||
*
|
*
|
||||||
* True if export was successfull.
|
* True if export was successfull.
|
||||||
*/
|
*/
|
||||||
public function pdfExport(string $html) : bool;
|
public function pdfExport(string $html): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Goes through the whole process of creating a pdf for the invoice.
|
* Goes through the whole process of creating a pdf for the invoice.
|
||||||
|
@ -48,7 +48,7 @@ interface PdfExportInterface
|
||||||
*
|
*
|
||||||
* Path of the pdf file.
|
* Path of the pdf file.
|
||||||
*/
|
*/
|
||||||
public function fromDefaultDataToPdf(array $nice_data, array $tokens = []) : string;
|
public function fromDefaultDataToPdf(array $nice_data, array $tokens = []): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets output file override via command paramater.
|
* Sets output file override via command paramater.
|
||||||
|
@ -56,7 +56,7 @@ interface PdfExportInterface
|
||||||
*
|
*
|
||||||
* Path of the output pdf file ('/tmp/test.pdf').
|
* Path of the output pdf file ('/tmp/test.pdf').
|
||||||
*/
|
*/
|
||||||
public function setOutput(string $output) : void;
|
public function setOutput(string $output): void;
|
||||||
|
|
||||||
// @TODO support multiple templates by adding template id in config.
|
// @TODO support multiple templates by adding template id in config.
|
||||||
// @TODO implement twig.
|
// @TODO implement twig.
|
||||||
|
@ -75,5 +75,10 @@ interface PdfExportInterface
|
||||||
*
|
*
|
||||||
* Token keys and values array.
|
* Token keys and values array.
|
||||||
*/
|
*/
|
||||||
public function gatherTokensForTemplate(string $template_path, bool $skip_missing, array $runtime_tokens = [], string $config = 'export.token') : array;
|
public function gatherTokensForTemplate(
|
||||||
|
string $template_path,
|
||||||
|
bool $skip_missing,
|
||||||
|
array $runtime_tokens = [],
|
||||||
|
string $config = 'export.token'
|
||||||
|
): array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
$this->mpdf = $mpdf;
|
$this->mpdf = $mpdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTemplatePath() : ?string
|
public function getTemplatePath(): ?string
|
||||||
{
|
{
|
||||||
if (! isset($this->templatePath)) {
|
if (! isset($this->templatePath)) {
|
||||||
$template_path = $this->config->get('export.template_path', false);
|
$template_path = $this->config->get('export.template_path', false);
|
||||||
|
@ -50,7 +50,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
return $this->templatePath;
|
return $this->templatePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTemplatePath(string $path) : void
|
public function setTemplatePath(string $path): void
|
||||||
{
|
{
|
||||||
if (file_exists($path)) {
|
if (file_exists($path)) {
|
||||||
$this->templatePath = $path;
|
$this->templatePath = $path;
|
||||||
|
@ -64,7 +64,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
// @TODO would it make sense to allow more per user extending?
|
// @TODO would it make sense to allow more per user extending?
|
||||||
// @TODO - too much assumptions on data structure. Create a class with
|
// @TODO - too much assumptions on data structure. Create a class with
|
||||||
// precise data structure and use that to pass the data around.
|
// precise data structure and use that to pass the data around.
|
||||||
public function parsedDataToHtmlTable(array $data) : ?string
|
public function parsedDataToHtmlTable(array $data): ?string
|
||||||
{
|
{
|
||||||
$table = '<table><thead><tr class="tr-header">';
|
$table = '<table><thead><tr class="tr-header">';
|
||||||
$header = array_shift($data);
|
$header = array_shift($data);
|
||||||
|
@ -105,7 +105,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
return $table;
|
return $table;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function replaceTokensInTemplate(string $template_path, array $tokens) : ?string
|
public function replaceTokensInTemplate(string $template_path, array $tokens): ?string
|
||||||
{
|
{
|
||||||
$template = file_get_contents($template_path);
|
$template = file_get_contents($template_path);
|
||||||
foreach ($tokens as $key => $value) {
|
foreach ($tokens as $key => $value) {
|
||||||
|
@ -115,7 +115,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO write a method to gather tokens.
|
// @TODO write a method to gather tokens.
|
||||||
public function getTokensInTemplate(string $template) : array
|
public function getTokensInTemplate(string $template): array
|
||||||
{
|
{
|
||||||
// @TODO find substrings of type [[key]]
|
// @TODO find substrings of type [[key]]
|
||||||
preg_match_all('/\[\[([a-z0-9-_]+)\]\]/', $template, $match);
|
preg_match_all('/\[\[([a-z0-9-_]+)\]\]/', $template, $match);
|
||||||
|
@ -138,8 +138,12 @@ class PdfExportService implements PdfExportInterface
|
||||||
*
|
*
|
||||||
* Token keys and values array.
|
* Token keys and values array.
|
||||||
*/
|
*/
|
||||||
public function gatherTokensForTemplate(string $template_path, bool $skip_missing = false, array $runtime_tokens = [], string $config = 'export.tokens') : array
|
public function gatherTokensForTemplate(
|
||||||
{
|
string $template_path,
|
||||||
|
bool $skip_missing = false,
|
||||||
|
array $runtime_tokens = [],
|
||||||
|
string $config = 'export.tokens'
|
||||||
|
): array {
|
||||||
[$tokens, $missing] = [[], []];
|
[$tokens, $missing] = [[], []];
|
||||||
$token_keys = $this->getTokensInTemplate(file_get_contents($template_path));
|
$token_keys = $this->getTokensInTemplate(file_get_contents($template_path));
|
||||||
$config_tokens = $this->config->get($config);
|
$config_tokens = $this->config->get($config);
|
||||||
|
@ -157,7 +161,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pdfExport(string $html, $output = null) : bool
|
public function pdfExport(string $html, $output = null): bool
|
||||||
{
|
{
|
||||||
$this->mpdf->SetProtection(['print']);
|
$this->mpdf->SetProtection(['print']);
|
||||||
// @TODO make configurable.
|
// @TODO make configurable.
|
||||||
|
@ -182,12 +186,12 @@ class PdfExportService implements PdfExportInterface
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setOutput(string $path) : void
|
public function setOutput(string $path): void
|
||||||
{
|
{
|
||||||
$this->output = $path;
|
$this->output = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fromDefaultDataToPdf(array $data, array $tokens = []) : string
|
public function fromDefaultDataToPdf(array $data, array $tokens = []): string
|
||||||
{
|
{
|
||||||
$template_path = $this->getTemplatePath();
|
$template_path = $this->getTemplatePath();
|
||||||
$tokens = $this->defaultTokens();
|
$tokens = $this->defaultTokens();
|
||||||
|
@ -204,7 +208,7 @@ class PdfExportService implements PdfExportInterface
|
||||||
/**
|
/**
|
||||||
* Get default tokens.
|
* Get default tokens.
|
||||||
*/
|
*/
|
||||||
protected function defaultTokens() : array
|
protected function defaultTokens(): array
|
||||||
{
|
{
|
||||||
$tokens = [];
|
$tokens = [];
|
||||||
$tokens['today'] = date('j. m. y');
|
$tokens['today'] = date('j. m. y');
|
||||||
|
|
|
@ -9,12 +9,12 @@ interface YoutrackInterface
|
||||||
/**
|
/**
|
||||||
* Check if client can sign into youtrack with provided token.
|
* Check if client can sign into youtrack with provided token.
|
||||||
*/
|
*/
|
||||||
public function testYoutrackapi() : ?string;
|
public function testYoutrackapi(): ?string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of the report from configuration.
|
* Get the id of the report from configuration.
|
||||||
*/
|
*/
|
||||||
public function getReportId() : ?string;
|
public function getReportId(): ?string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads report and returns file path.
|
* Downloads report and returns file path.
|
||||||
|
@ -25,7 +25,7 @@ interface YoutrackInterface
|
||||||
*
|
*
|
||||||
* If fetch was unsuccssefull return false, otherwise the file path.
|
* If fetch was unsuccssefull return false, otherwise the file path.
|
||||||
*/
|
*/
|
||||||
public function downloadReport(string $report_id) : ?string;
|
public function downloadReport(string $report_id): ?string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of reports.
|
* Get a list of reports.
|
||||||
|
@ -33,9 +33,11 @@ interface YoutrackInterface
|
||||||
*
|
*
|
||||||
* Array of reports with ids as keys and names as values.
|
* Array of reports with ids as keys and names as values.
|
||||||
*/
|
*/
|
||||||
public function listReports() : array;
|
public function listReports(): array;
|
||||||
|
|
||||||
public function setReportId(string $report_id) :void;
|
public function setReportId(string $report_id): void;
|
||||||
public function setReportName(?string $report_name = NULL) :void;
|
|
||||||
public function getReportName() : ?string;
|
public function setReportName(?string $report_name = null): void;
|
||||||
|
|
||||||
|
public function getReportName(): ?string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class YoutrackService implements YoutrackInterface
|
||||||
protected ClientInterface $httpClient;
|
protected ClientInterface $httpClient;
|
||||||
|
|
||||||
protected string $report_id;
|
protected string $report_id;
|
||||||
protected ?string $reportName = NULL;
|
protected ?string $reportName = null;
|
||||||
|
|
||||||
public function __construct(ConfigurationInterface $config, ClientInterface $http_client)
|
public function __construct(ConfigurationInterface $config, ClientInterface $http_client)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,7 @@ class YoutrackService implements YoutrackInterface
|
||||||
$this->httpClient = $http_client;
|
$this->httpClient = $http_client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testYoutrackapi() : ?string
|
public function testYoutrackapi(): ?string
|
||||||
{
|
{
|
||||||
// Get base url from config or add input.
|
// Get base url from config or add input.
|
||||||
// Get token or add input.
|
// Get token or add input.
|
||||||
|
@ -88,7 +88,7 @@ class YoutrackService implements YoutrackInterface
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReportId() : ?string
|
public function getReportId(): ?string
|
||||||
{
|
{
|
||||||
// --report option value should take precedence.
|
// --report option value should take precedence.
|
||||||
// @TODO error handling.
|
// @TODO error handling.
|
||||||
|
@ -102,29 +102,30 @@ class YoutrackService implements YoutrackInterface
|
||||||
return $yt_report_id;
|
return $yt_report_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setReportId(string $report_id) : void
|
public function setReportId(string $report_id): void
|
||||||
{
|
{
|
||||||
$this->report_id = $report_id;
|
$this->report_id = $report_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setReportName(?string $report_name = NULL) : void {
|
public function setReportName(?string $report_name = null): void
|
||||||
if (!$report_name) {
|
{
|
||||||
|
if (! $report_name) {
|
||||||
$reports = $this->listReports();
|
$reports = $this->listReports();
|
||||||
if ($report_id = $this->getReportId()) {
|
if ($report_id = $this->getReportId()) {
|
||||||
$report_name = $reports[$report_id] ?? NULL;
|
$report_name = $reports[$report_id] ?? null;
|
||||||
}
|
} else {
|
||||||
else {
|
$report_name = null;
|
||||||
$report_name = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->reportName = $report_name;
|
$this->reportName = $report_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReportName() : ?string {
|
public function getReportName(): ?string
|
||||||
|
{
|
||||||
return $this->reportName;
|
return $this->reportName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function downloadReport(string $report_id) : ?string
|
public function downloadReport(string $report_id): ?string
|
||||||
{
|
{
|
||||||
$path = "youtrack/api/reports/$report_id/export/csv";
|
$path = "youtrack/api/reports/$report_id/export/csv";
|
||||||
$query = ['$top' => -1];
|
$query = ['$top' => -1];
|
||||||
|
@ -160,7 +161,7 @@ class YoutrackService implements YoutrackInterface
|
||||||
throw new Exception("Unable to download report {$report_id}!");
|
throw new Exception("Unable to download report {$report_id}!");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getYtToken() : string
|
protected function getYtToken(): string
|
||||||
{
|
{
|
||||||
if (isset($this->ytToken)) {
|
if (isset($this->ytToken)) {
|
||||||
return $this->ytToken;
|
return $this->ytToken;
|
||||||
|
@ -172,12 +173,12 @@ class YoutrackService implements YoutrackInterface
|
||||||
return $yt_token;
|
return $yt_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setYtToken(string $token) : void
|
public function setYtToken(string $token): void
|
||||||
{
|
{
|
||||||
$this->ytToken = $token;
|
$this->ytToken = $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getYtUrl(string $path = '') : ?string
|
protected function getYtUrl(string $path = ''): ?string
|
||||||
{
|
{
|
||||||
if (isset($this->ytBaseUrl)) {
|
if (isset($this->ytBaseUrl)) {
|
||||||
$yt_base_url = $this->ytBaseUrl;
|
$yt_base_url = $this->ytBaseUrl;
|
||||||
|
@ -198,7 +199,7 @@ class YoutrackService implements YoutrackInterface
|
||||||
$this->ytBaseUrl = $base_url;
|
$this->ytBaseUrl = $base_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listReports() : array
|
public function listReports(): array
|
||||||
{
|
{
|
||||||
// Now filter results by own = true;
|
// Now filter results by own = true;
|
||||||
$url = '/youtrack/api/reports';
|
$url = '/youtrack/api/reports';
|
||||||
|
@ -217,11 +218,12 @@ class YoutrackService implements YoutrackInterface
|
||||||
return $reports;
|
return $reports;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function clearReportCache(string $report_id) : int
|
public function clearReportCache(string $report_id): int
|
||||||
{
|
{
|
||||||
$path = "/youtrack/api/reports/${report_id}/status";
|
$path = "/youtrack/api/reports/${report_id}/status";
|
||||||
$query = [
|
$query = [
|
||||||
'$top' => -1,
|
'$top' => -1,
|
||||||
|
// phpcs:ignore
|
||||||
'fields' => 'calculationInProgress,error(id),errorMessage,isOutdated,lastCalculated,progress,wikifiedErrorMessage',
|
'fields' => 'calculationInProgress,error(id),errorMessage,isOutdated,lastCalculated,progress,wikifiedErrorMessage',
|
||||||
];
|
];
|
||||||
$post = [
|
$post = [
|
||||||
|
|
|
@ -11,8 +11,22 @@ use DI\ContainerBuilder;
|
||||||
use RprtCli\Commands\InvoiceCommand;
|
use RprtCli\Commands\InvoiceCommand;
|
||||||
use RprtCli\Commands\ReportCommand;
|
use RprtCli\Commands\ReportCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report and invoice command test.
|
||||||
|
*
|
||||||
|
* Does not cover the part of talking to the api, nor does it yet cover email
|
||||||
|
* and pdf generation. @TODO
|
||||||
|
*/
|
||||||
class ReportCommandTest extends TestCase {
|
class ReportCommandTest extends TestCase {
|
||||||
|
|
||||||
|
protected const INPUT_CSV_FILE = __DIR__ . '/../data/21-03.csv';
|
||||||
|
|
||||||
|
protected const REPORT_OUTPUT_FILE = __DIR__ . '/../data/report-21-03.txt';
|
||||||
|
|
||||||
|
protected const INVOICE_OUTPUT_FILE = __DIR__ . '/../data/invoice-21-03.txt';
|
||||||
|
|
||||||
|
protected const INVOICE_OUTPUT_PDF = __DIR__ . '/../data/output/Invoice-test.pdf';
|
||||||
|
|
||||||
public function testExecute() {
|
public function testExecute() {
|
||||||
$builder = new ContainerBuilder();
|
$builder = new ContainerBuilder();
|
||||||
$builder->addDefinitions(__DIR__ . '/../test-dependencies.php');
|
$builder->addDefinitions(__DIR__ . '/../test-dependencies.php');
|
||||||
|
@ -26,9 +40,9 @@ class ReportCommandTest extends TestCase {
|
||||||
$application->add($reportCommand);
|
$application->add($reportCommand);
|
||||||
|
|
||||||
|
|
||||||
$command = $application->find('report');
|
$reportCommand = $application->find('report');
|
||||||
$commandTester = new CommandTester($command);
|
$reportCommandTester = new CommandTester($reportCommand);
|
||||||
$commandTester->execute([
|
$reportCommandTester->execute([
|
||||||
// pass arguments to the helper
|
// pass arguments to the helper
|
||||||
'--file' => __DIR__ . '/../data/21-03.csv',
|
'--file' => __DIR__ . '/../data/21-03.csv',
|
||||||
|
|
||||||
|
@ -38,13 +52,37 @@ class ReportCommandTest extends TestCase {
|
||||||
// e.g: '--some-option' => ['option_value'],
|
// e.g: '--some-option' => ['option_value'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$commandTester->assertCommandIsSuccessful();
|
$reportCommandTester->assertCommandIsSuccessful();
|
||||||
|
|
||||||
// the output of the command in the console
|
// the output of the command in the console
|
||||||
$output = $commandTester->getDisplay();
|
$output = $reportCommandTester->getDisplay();
|
||||||
var_dump($output);
|
// var_dump($output);
|
||||||
$this->assertStringContainsString('21-03.csv', $output);
|
$this->assertStringContainsString('21-03.csv', $output);
|
||||||
|
$this->assertStringEqualsFile(self::REPORT_OUTPUT_FILE, $output);
|
||||||
|
|
||||||
|
$invoiceCommand = $application->find('invoice');
|
||||||
|
$invoiceCommandTester = new CommandTester($invoiceCommand);
|
||||||
|
$invoiceCommandTester->execute([
|
||||||
|
'--file' => self::INPUT_CSV_FILE,
|
||||||
|
'--pdf' => TRUE,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$invoiceCommandTester->assertCommandIsSuccessful();
|
||||||
|
|
||||||
|
// the output of the command in the console
|
||||||
|
$invoice_output = $invoiceCommandTester->getDisplay();
|
||||||
|
var_dump($invoice_output);
|
||||||
|
$this->assertStringContainsString('21-03.csv', $invoice_output);
|
||||||
|
$this->assertStringEqualsFile(self::INVOICE_OUTPUT_FILE, $invoice_output);
|
||||||
|
|
||||||
|
// $invoiceCommandTester->execute([
|
||||||
|
// '--file' => self::INPUT_CSV_FILE,
|
||||||
|
// '--pdf'
|
||||||
|
// ]);
|
||||||
|
// $invoice_output = $invoiceCommandTester->getDisplay();
|
||||||
|
// var_dump($invoice_output);
|
||||||
|
// $this->assertFileExists();
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ Group name,Item,Item Summary,Estimation time,Spent time
|
||||||
-,DEV-56,Developer training,0,60
|
-,DEV-56,Developer training,0,60
|
||||||
-,DEV-122,Daily standup,0,255
|
-,DEV-122,Daily standup,0,255
|
||||||
-,INF-6,"Infrastructure misc, diverse",0,75
|
-,INF-6,"Infrastructure misc, diverse",0,75
|
||||||
-,KUR-181,Sprint meetings (PM),240,390
|
-,SND-181,Sprint meetings (PM),240,390
|
||||||
-,KUR-507,(TKT-307) Change webhook integration for Slack,0,330
|
-,SND-507,(TKT-307) Change webhook integration for Slack,0,330
|
||||||
-,KUR-552,(TKT-740) Allow kicking users out of collections,780,45
|
-,SND-552,(TKT-740) Allow kicking users out of collections,780,45
|
||||||
-,KUR-554,(TKT-1448) Adaptions in Backend,0,30
|
-,SND-554,(TKT-1448) Adaptions in Backend,0,30
|
||||||
-,KUR-556,[story] (TKT-1393) Make Paragraph embedding more user friendly,0,75
|
-,SND-556,[story] (TKT-1393) Make Paragraph embedding more user friendly,0,75
|
||||||
-,KUR-558,(TKT-1467) Full copy of article doesn't update the timestamp,0,15
|
-,SND-558,(TKT-1467) Full copy of article doesn't update the timestamp,0,15
|
||||||
-,KUR-562,(TKT-1441) Implement SEO title in BE,0,45
|
-,SND-562,(TKT-1441) Implement SEO title in BE,0,45
|
||||||
-,LDP-668,Add json-ld schema.org metadata to articles,0,300
|
-,LDP-668,Add json-ld schema.org metadata to articles,0,300
|
||||||
-,LDP-685,"LDP-Contentpool sitemaps. Extend ""simple_sitemap_extended"" setup to support per front-end site sitemaps.",0,15
|
-,LDP-685,"LDP-Contentpool sitemaps. Extend ""simple_sitemap_extended"" setup to support per front-end site sitemaps.",0,15
|
||||||
-,LDP-692,Output json-ld script,0,30
|
-,LDP-692,Output json-ld script,0,30
|
||||||
|
@ -28,26 +28,26 @@ Group name,Item,Item Summary,Estimation time,Spent time
|
||||||
-,LDP-740,CP: Add portal path prefixes,480,30
|
-,LDP-740,CP: Add portal path prefixes,480,30
|
||||||
-,LDP-741,Defects marketing Blocks,0,450
|
-,LDP-741,Defects marketing Blocks,0,450
|
||||||
-,LDP-742,[Red Alert] Develop branch fails to build,0,45
|
-,LDP-742,[Red Alert] Develop branch fails to build,0,45
|
||||||
-,VAL-8,Plan hosting setup on custom hosting provider,0,45
|
-,TES-8,Plan hosting setup on custom hosting provider,0,45
|
||||||
-,WV-31,"Internal sprint meetings (PM, fixed-price).",0,150
|
-,TNP-31,"Internal sprint meetings (PM, fixed-price).",0,150
|
||||||
-,WV-4304,"(ODT-1240) Structured data on article (satellite) pages and overview pages such as the startpage, branch, topic & channel pages.",600,750
|
-,TNP-4304,"(ODT-1240) Structured data on article (satellite) pages and overview pages such as the startpage, branch, topic & channel pages.",600,750
|
||||||
-,WV-4305,(ODT-1202) Add mobile-banner-3 & 4 (advertisement in articles),0,60
|
-,TNP-4305,(ODT-1202) Add mobile-banner-3 & 4 (advertisement in articles),0,60
|
||||||
-,WV-4326,(ODT-1174) Make image optional in the Call to Action block,135,60
|
-,TNP-4326,(ODT-1174) Make image optional in the Call to Action block,135,60
|
||||||
-,WV-4355,(ODT-1270) Add article title to breadcrumbs,0,270
|
-,TNP-4355,(ODT-1270) Add article title to breadcrumbs,0,270
|
||||||
-,WV-4359,"(ODT-1254) Display the text paragraph ""zwischentitel"" in H2",0,90
|
-,TNP-4359,"(ODT-1254) Display the text paragraph ""zwischentitel"" in H2",0,90
|
||||||
-,WV-4372,(ODT-1297) Branch analytics for articles associated with specific branch (GA),135,15
|
-,TNP-4372,(ODT-1297) Branch analytics for articles associated with specific branch (GA),135,15
|
||||||
-,WV-4384,(ODT-1306) Layout-builder rendering in Chrome browser,120,120
|
-,TNP-4384,(ODT-1306) Layout-builder rendering in Chrome browser,120,120
|
||||||
-,WV-4393,WVsat docker build error,0,90
|
-,TNP-4393,TNPsat docker build error,0,90
|
||||||
-,WV-4410,(ODT-1317) SOLR Search optimization >> suggested articles does NOT affect the relevance for search results.,375,30
|
-,TNP-4410,(ODT-1317) SOLR Search optimization >> suggested articles does NOT affect the relevance for search results.,375,30
|
||||||
-,WV-4411,Extend simple_sitemap_extensions for dynamic variants & Install and configure simple_sitemap_extensions,600,2070
|
-,TNP-4411,Extend simple_sitemap_extensions for dynamic variants & Install and configure simple_sitemap_extensions,600,2070
|
||||||
-,WV-4412,Implement custom dynamic variants for sitemaps to group by month,0,105
|
-,TNP-4412,Implement custom dynamic variants for sitemaps to group by month,0,105
|
||||||
-,WV-4414,Invalid JSON in the cache_tools composer.json file,0,60
|
-,TNP-4414,Invalid JSON in the cache_tools composer.json file,0,60
|
||||||
-,WV-4421,WV Satellites Hand bau image file entities are not replicated,0,15
|
-,TNP-4421,TNP Satellites Hand bau image file entities are not replicated,0,15
|
||||||
-,WV-4422,"[Red Alert] Behat test fail. ""Demo content loaded..Error pages work.""",0,75
|
-,TNP-4422,"[Red Alert] Behat test fail. ""Demo content loaded..Error pages work.""",0,75
|
||||||
-,WV-4423,(ODT-1325) Site managers must be able to change article status from Unpublished to Unpublished,0,15
|
-,TNP-4423,(ODT-1325) Site managers must be able to change article status from Unpublished to Unpublished,0,15
|
||||||
-,WV-4425,Unpublished Advertorials are returned,0,30
|
-,TNP-4425,Unpublished Advertorials are returned,0,30
|
||||||
-,WV-4427,"(ODT-1332) ""Og: image"" is not specified explicitly",120,15
|
-,TNP-4427,"(ODT-1332) ""Og: image"" is not specified explicitly",120,15
|
||||||
-,WV-4437,"(ODT-1339) User (id 511) on wv-contentpool is locked and cannot access the system.",0,30
|
-,TNP-4437,"(ODT-1339) User (id 511) on wv-contentpool is locked and cannot access the system.",0,30
|
||||||
-,WV-4442,(ODT-1341) Custom HTML block is not rendered,0,120
|
-,TNP-4442,(ODT-1341) Custom HTML block is not rendered,0,120
|
||||||
-,WV-4478,(ODT-1368) Hosting provider transition of all projects hosted on their OpenShift infrastructure to the new Kuberneets (k8) infrastructure.,0,90
|
-,TNP-4478,(ODT-1368) Hosting provider transition of all projects hosted on their OpenShift infrastructure to the new Kuberneets (k8) infrastructure.,0,90
|
||||||
-,WV-4480,(ODT-1370) Error: Call to a member function getUrl() on null / cannot save an article on stage systems,0,90
|
-,TNP-4480,(ODT-1370) Error: Call to a member function getUrl() on null / cannot save an article on stage systems,0,90
|
||||||
|
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
report: /home/lio/Projects/drunomix/rprt-cli/app/tests/Kernel/../data/21-03.csv
|
||||||
|
+-----------------------------+--------+-------+----------+
|
||||||
|
| Project | Hours | Rate | Price |
|
||||||
|
+-----------------------------+--------+-------+----------+
|
||||||
|
| lupus.digital publishing | 61,75 | 25,60 | 1.580,80 |
|
||||||
|
| Projektbezogene Entwicklung | 100,50 | 29,10 | 2.924,55 |
|
||||||
|
+-----------------------------+--------+-------+----------+
|
||||||
|
| Gesamt netto | 162,25 | | 4.505,35 |
|
||||||
|
+-----------------------------+--------+-------+----------+
|
||||||
|
| Gessamt brutto | 4.505,35 |
|
||||||
|
+-----------------------------+--------+-------+----------+
|
|
@ -0,0 +1,76 @@
|
||||||
|
report: /home/lio/Projects/drunomix/rprt-cli/app/tests/Kernel/../data/21-03.csv
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| Ticket Id | Name | Time | Estimation |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| DEV-46 | Wochenplanung, Weekly | 315 | 0 |
|
||||||
|
| DEV-56 | Developer training | 60 | 0 |
|
||||||
|
| DEV-122 | Daily standup | 255 | 0 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| DEV | 10.5 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| INF-6 | Infrastructure misc, diverse | 75 | 0 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| INF | 1.25 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| SND-181 | Sprint meetings (PM) | 390 | 240 |
|
||||||
|
| SND-507 | (TKT-307) Change webhook integration for Slack | 330 | 0 |
|
||||||
|
| SND-552 | (TKT-740) Allow kicking users out of collections | 45 | 780 |
|
||||||
|
| SND-554 | (TKT-1448) Adaptions in Backend | 30 | 0 |
|
||||||
|
| SND-556 | [story] (TKT-1393) Make Paragraph embedding more user friend | 75 | 0 |
|
||||||
|
| SND-558 | (TKT-1467) Full copy of article doesn't update the timestamp | 15 | 0 |
|
||||||
|
| SND-562 | (TKT-1441) Implement SEO title in BE | 45 | 0 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| SND | 15.5 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| LDP-668 | Add json-ld schema.org metadata to articles | 300 | 0 |
|
||||||
|
| LDP-685 | LDP-Contentpool sitemaps. Extend "simple_sitemap_extended" s | 15 | 0 |
|
||||||
|
| LDP-692 | Output json-ld script | 30 | 0 |
|
||||||
|
| LDP-693 | Add WebPage schema.org support | 180 | 120 |
|
||||||
|
| LDP-700 | LDP onboarding Somebody | 15 | 0 |
|
||||||
|
| LDP-706 | Backend FAQ Marketing Block | 165 | 0 |
|
||||||
|
| LDP-707 | Backend "Hero" Marketing Block | 1035 | 0 |
|
||||||
|
| LDP-709 | Backend "Newsletter Signup" Marketing Block | 195 | 0 |
|
||||||
|
| LDP-711 | Backend "Call to action" Marketing Block | 165 | 0 |
|
||||||
|
| LDP-712 | Backend "Feature section" Marketing Block | 300 | 0 |
|
||||||
|
| LDP-713 | Provide a field type and pre-configuration for choosing icon | 45 | 300 |
|
||||||
|
| LDP-716 | Entity browser UX is not as good as expected | 15 | 0 |
|
||||||
|
| LDP-720 | Improve display of nodes and add support for article heros | 45 | 0 |
|
||||||
|
| LDP-728 | ldp-marketing blocks break the page when they are added to t | 195 | 0 |
|
||||||
|
| LDP-729 | ldp-cp portal entity | 480 | 0 |
|
||||||
|
| LDP-740 | CP: Add portal path prefixes | 30 | 480 |
|
||||||
|
| LDP-741 | Defects marketing Blocks | 450 | 0 |
|
||||||
|
| LDP-742 | [Red Alert] Develop branch fails to build | 45 | 0 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| LDP | 61.75 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| TES-8 | Plan hosting setup on custom hosting provider | 45 | 0 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| TES | 0.75 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| TNP-31 | Internal sprint meetings (PM, fixed-price). | 150 | 0 |
|
||||||
|
| TNP-4304 | (ODT-1240) Structured data on article (satellite) pages and | 750 | 600 |
|
||||||
|
| TNP-4305 | (ODT-1202) Add mobile-banner-3 & 4 (advertisement in article | 60 | 0 |
|
||||||
|
| TNP-4326 | (ODT-1174) Make image optional in the Call to Action block | 60 | 135 |
|
||||||
|
| TNP-4355 | (ODT-1270) Add article title to breadcrumbs | 270 | 0 |
|
||||||
|
| TNP-4359 | (ODT-1254) Display the text paragraph "zwischentitel" in H2 | 90 | 0 |
|
||||||
|
| TNP-4372 | (ODT-1297) Branch analytics for articles associated with spe | 15 | 135 |
|
||||||
|
| TNP-4384 | (ODT-1306) Layout-builder rendering in Chrome browser | 120 | 120 |
|
||||||
|
| TNP-4393 | TNPsat docker build error | 90 | 0 |
|
||||||
|
| TNP-4410 | (ODT-1317) SOLR Search optimization >> suggested articles do | 30 | 375 |
|
||||||
|
| TNP-4411 | Extend simple_sitemap_extensions for dynamic variants & Inst | 2070 | 600 |
|
||||||
|
| TNP-4412 | Implement custom dynamic variants for sitemaps to group by m | 105 | 0 |
|
||||||
|
| TNP-4414 | Invalid JSON in the cache_tools composer.json file | 60 | 0 |
|
||||||
|
| TNP-4421 | TNP Satellites Hand bau image file entities are not replicat | 15 | 0 |
|
||||||
|
| TNP-4422 | [Red Alert] Behat test fail. "Demo content loaded..Error pag | 75 | 0 |
|
||||||
|
| TNP-4423 | (ODT-1325) Site managers must be able to change article stat | 15 | 0 |
|
||||||
|
| TNP-4425 | Unpublished Advertorials are returned | 30 | 0 |
|
||||||
|
| TNP-4427 | (ODT-1332) "Og: image" is not specified explicitly | 15 | 120 |
|
||||||
|
| TNP-4437 | (ODT-1339) User (id 511) on wv-contentpool is locked and can | 30 | 0 |
|
||||||
|
| TNP-4442 | (ODT-1341) Custom HTML block is not rendered | 120 | 0 |
|
||||||
|
| TNP-4478 | (ODT-1368) Hosting provider transition of all projects hoste | 90 | 0 |
|
||||||
|
| TNP-4480 | (ODT-1370) Error: Call to a member function getUrl() on null | 90 | 0 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| TNP | 72.5 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
||||||
|
| DEV, INF, SND, LDP, TES, TNP | 162.25 |
|
||||||
|
+----------------+--------------------------------------------------------------+------+------------+
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
$dependencies = require_once(__DIR__ . '/../dependencies.php');
|
$dependencies = require_once(__DIR__ . '/../dependencies.php');
|
||||||
|
|
||||||
$dependencies['config.path'] = __DIR__ . '/data';
|
$dependencies['config.path'] = __DIR__ . '/data/';
|
||||||
|
|
||||||
return $dependencies;
|
return $dependencies;
|
||||||
|
|
4
todo.txt
4
todo.txt
|
@ -21,8 +21,8 @@ TODO:
|
||||||
TODO (end of 2022):
|
TODO (end of 2022):
|
||||||
- DONE fix project list output (first is missing)
|
- DONE fix project list output (first is missing)
|
||||||
- DONE report selection trait
|
- DONE report selection trait
|
||||||
- automated tests
|
- DONE automated tests
|
||||||
- phpcs
|
- DONE phpcs
|
||||||
- phpstan
|
- phpstan
|
||||||
OPTIONAL:
|
OPTIONAL:
|
||||||
- phing build system to run quality assurance
|
- phing build system to run quality assurance
|
||||||
|
|
Loading…
Reference in New Issue