Add tests, unify input.
parent
4dcbcb228a
commit
ff2e35bb93
|
@ -3,3 +3,4 @@
|
||||||
/scratch
|
/scratch
|
||||||
.phpcs-cache
|
.phpcs-cache
|
||||||
*~undo-tree~
|
*~undo-tree~
|
||||||
|
/app/tests/data/output
|
||||||
|
|
22
README.org
22
README.org
|
@ -17,8 +17,7 @@
|
||||||
|
|
||||||
** Install/Getting started
|
** Install/Getting started
|
||||||
|
|
||||||
Get ~.phar~ file from r, run configuration wizzard (planned for version 1.0).
|
Get ~.phar~ file from r, Run configuration wizzard (planned for version 1.0).
|
||||||
|
|
||||||
|
|
||||||
Before version 1.0 is released you have to navigate into ~/app~ directory
|
Before version 1.0 is released you have to navigate into ~/app~ directory
|
||||||
and call command through ~.rprt.php~ file.
|
and call command through ~.rprt.php~ file.
|
||||||
|
@ -46,7 +45,6 @@
|
||||||
- hourly wage
|
- hourly wage
|
||||||
- folder for reports
|
- folder for reports
|
||||||
|
|
||||||
|
|
||||||
** Components
|
** Components
|
||||||
- report service - provides a csv of a report
|
- report service - provides a csv of a report
|
||||||
- local csv file read (provided via argument)
|
- local csv file read (provided via argument)
|
||||||
|
@ -77,18 +75,31 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n
|
||||||
5 days of development.
|
5 days of development.
|
||||||
- remove errors from reports
|
- remove errors from reports
|
||||||
|
|
||||||
|
* Development
|
||||||
|
|
||||||
|
** Testing
|
||||||
|
|
||||||
|
Main source at symfony [[https://symfony.com/doc/current/console.html#testing-commands][console command page]]. If you read thouroughly:
|
||||||
|
|
||||||
|
When using the Console component in a standalone project, use Application and
|
||||||
|
extend the normal \PHPUnit\Framework\TestCase.
|
||||||
|
|
||||||
** Plan
|
** Plan
|
||||||
|
|
||||||
*** most current
|
*** most current
|
||||||
1. For version 0.6.7
|
1. For version 0.6.7
|
||||||
- [X] data value objects
|
- [X] data value objects
|
||||||
- phar file
|
- [X] phar file
|
||||||
- [X] 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
|
||||||
- for reports
|
- for reports
|
||||||
- symfony/config & symfony/di components
|
- symfony/config & symfony/di components
|
||||||
|
3. Time tracking service
|
||||||
|
- youtrack-api
|
||||||
|
- jira-api
|
||||||
|
- kimai (https://www.kimai.org/documentation/timesheet.html)
|
||||||
|
|
||||||
*** current
|
*** current
|
||||||
|
|
||||||
|
@ -126,7 +137,6 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n
|
||||||
2. configuration wizard
|
2. configuration wizard
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
** Learning
|
** Learning
|
||||||
- https://www.youtube.com/watch?v=aCqM9YnjTe0
|
- https://www.youtube.com/watch?v=aCqM9YnjTe0
|
||||||
- Choices (~new ChoiceQuestion~)
|
- Choices (~new ChoiceQuestion~)
|
||||||
|
@ -134,8 +144,8 @@ curl 'https://drunomics.myjetbrains.com/youtrack/api/reports?$top=-1&fields=id,n
|
||||||
|
|
||||||
** API calls
|
** API calls
|
||||||
|
|
||||||
|
|
||||||
*** Get csv file
|
*** Get csv file
|
||||||
|
|
||||||
~curl 'https://drunomics.myjetbrains.com/youtrack/api/reports/83-554/export/csv?&$top=-1' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H "Authorization: Bearer $TKN" > ~/Documents/Drunomics/workhours/2021/21-09.csv~
|
~curl 'https://drunomics.myjetbrains.com/youtrack/api/reports/83-554/export/csv?&$top=-1' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H "Authorization: Bearer $TKN" > ~/Documents/Drunomics/workhours/2021/21-09.csv~
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,15 @@
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"RprtCli\\": "src"
|
"RprtCli\\": ["src", "tests"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"squizlabs/php_codesniffer": "^3.7",
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"opsway/psr12-strict-coding-standard": "^0.5.0",
|
"opsway/psr12-strict-coding-standard": "^0.5.0",
|
||||||
"phpcompatibility/php-compatibility": "^9.3"
|
"phpcompatibility/php-compatibility": "^9.3",
|
||||||
|
"phpstan/phpstan": "^1.9"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"cs": "phpcs --colors --standard=PSR12",
|
"cs": "phpcs --colors --standard=PSR12",
|
||||||
|
@ -41,5 +42,6 @@
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"minimum-stability": "alpha"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "7b0be09c8f282dfcceb075d1455caa28",
|
"content-hash": "eb0c874610e45fa74d8ccc691f5bd9f8",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "doctrine/lexer",
|
"name": "doctrine/lexer",
|
||||||
|
@ -3287,6 +3287,65 @@
|
||||||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||||
"time": "2020-08-03T20:32:43+00:00"
|
"time": "2020-08-03T20:32:43+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan",
|
||||||
|
"version": "1.9.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
|
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
||||||
|
"reference": "d03bccee595e2146b7c9d174486b84f4dc61b0f2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2|^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan-shim": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"phpstan",
|
||||||
|
"phpstan.phar"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
|
"keywords": [
|
||||||
|
"dev",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
|
"source": "https://github.com/phpstan/phpstan/tree/1.9.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ondrejmirtes",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/phpstan",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2022-12-17T13:33:52+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.15",
|
"version": "9.2.15",
|
||||||
|
@ -4930,7 +4989,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "alpha",
|
||||||
"stability-flags": [],
|
"stability-flags": [],
|
||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
|
|
|
@ -38,11 +38,13 @@ use function var_export;
|
||||||
/**
|
/**
|
||||||
* Main file - invoice command.
|
* Main file - invoice command.
|
||||||
*/
|
*/
|
||||||
class InvoiceCommand extends Command
|
class InvoiceCommand extends Command {
|
||||||
{
|
|
||||||
|
use SelectReportTrait;
|
||||||
|
|
||||||
protected $csv;
|
protected $csv;
|
||||||
|
|
||||||
protected $configuration;
|
protected $config;
|
||||||
|
|
||||||
protected $youtrack;
|
protected $youtrack;
|
||||||
|
|
||||||
|
@ -54,14 +56,14 @@ class InvoiceCommand extends Command
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ReportCsvInterface $csv,
|
ReportCsvInterface $csv,
|
||||||
ConfigurationInterface $configuration,
|
ConfigurationInterface $configuration,
|
||||||
YoutrackInterface $youtrack,
|
YoutrackInterface $trackingService,
|
||||||
PdfExportInterface $pdf_export,
|
PdfExportInterface $pdf_export,
|
||||||
MailerInterface $mailer,
|
MailerInterface $mailer,
|
||||||
?string $name = null
|
?string $name = null
|
||||||
) {
|
) {
|
||||||
$this->csv = $csv;
|
$this->csv = $csv;
|
||||||
$this->configuration = $configuration;
|
$this->config = $configuration;
|
||||||
$this->youtrack = $youtrack;
|
$this->trackingService = $trackingService;
|
||||||
$this->pdfExport = $pdf_export;
|
$this->pdfExport = $pdf_export;
|
||||||
$this->mailer = $mailer;
|
$this->mailer = $mailer;
|
||||||
parent::__construct($name);
|
parent::__construct($name);
|
||||||
|
@ -82,12 +84,12 @@ class InvoiceCommand extends Command
|
||||||
InputOption::VALUE_REQUIRED,
|
InputOption::VALUE_REQUIRED,
|
||||||
'Specify the input csv file to generate report from.'
|
'Specify the input csv file to generate report from.'
|
||||||
);
|
);
|
||||||
$this->addOption(
|
// $this->addOption(
|
||||||
'youtrack',
|
// 'youtrack',
|
||||||
'y',
|
// 'y',
|
||||||
InputOption::VALUE_NONE,
|
// InputOption::VALUE_NONE,
|
||||||
'Use youtrack api to get a report. If this option is used --file does not have any effect..'
|
// 'Use youtrack api to get a report. If this option is used --file does not have any effect..'
|
||||||
);
|
// );
|
||||||
$this->addOption(
|
$this->addOption(
|
||||||
'pdf',
|
'pdf',
|
||||||
'p',
|
'p',
|
||||||
|
@ -150,59 +152,33 @@ 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->youtrack->testYoutrackapi();
|
$test = $this->trackingService->testYoutrackapi();
|
||||||
$output->writeln($test);
|
$output->writeln($test);
|
||||||
}
|
}
|
||||||
if ($input->getOption('list-reports')) {
|
if ($input->getOption('list-reports')) {
|
||||||
$list = $this->youtrack->listReports();
|
$list = $this->trackingService->listReports();
|
||||||
$output->writeln(var_export($list, true));
|
$output->writeln(var_export($list, true));
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) {
|
// Gets report parameter.
|
||||||
if ($report = $input->getOption('report')) {
|
$this->getReportParameter($input, $output, 'tracking_service.youtrack.invoice.report');
|
||||||
$this->youtrack->setReportId($report);
|
$report_id = $this->trackingService->getReportId();
|
||||||
} else {
|
$file = $this->getReportCsvFilePath($input, $output, $report_id);
|
||||||
$reports = $this->youtrack->listReports();
|
$report_name = $this->trackingService->getReportName();
|
||||||
$count = 1;
|
|
||||||
foreach ($reports as $id => $name) {
|
|
||||||
$output->writeln("[{$count}] {$name} ({$id})");
|
|
||||||
$count++;
|
|
||||||
}
|
|
||||||
$report = readline('Select id of the report: ');
|
|
||||||
// Asume people are literate.
|
|
||||||
$this->youtrack->setReportId($report);
|
|
||||||
}
|
|
||||||
if ($output->isVerbose()) {
|
|
||||||
$output->writeln("Setting report: <info>{$report}</info>.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($youtrack = $input->getOption('youtrack')) {
|
|
||||||
$report_id = $this->youtrack->getReportId();
|
|
||||||
$cache_clear_status = $this->youtrack->clearReportCache($report_id);
|
|
||||||
if ($output->isVerbose()) {
|
|
||||||
$output->writeln("Report <info>{$report_id}</info> cache cleared, status: {$cache_clear_status}");
|
|
||||||
}
|
|
||||||
$file = $this->youtrack->downloadReport($report_id);
|
|
||||||
}
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
if ($input->hasParameterOption('--custom') || $input->hasParameterOption('-c')) {
|
if ($input->hasParameterOption('--custom') || $input->hasParameterOption('-c')) {
|
||||||
|
// @TODO Add option for custom time tracking data.
|
||||||
$custom = $this->getCustomWorkOrExpenses($input->getOption('custom'), self::TYPE_WORK);
|
$custom = $this->getCustomWorkOrExpenses($input->getOption('custom'), self::TYPE_WORK);
|
||||||
}
|
}
|
||||||
if ($youtrack || $file = $input->getOption('file')) {
|
$output->writeln("report: <info>{$report_name}</info>");
|
||||||
// Youtrack can also provide a file name.
|
|
||||||
if ($output->isVerbose()) {
|
|
||||||
$output->writeln("Csv file downloaded to: <info>{$file}</info>");
|
|
||||||
}
|
|
||||||
$data = $this->csv->getInvoiceData($file);
|
$data = $this->csv->getInvoiceData($file);
|
||||||
if (! empty($expenses)) {
|
if (! empty($expenses)) {
|
||||||
$data = array_merge($data, $expenses);
|
$data = array_merge($data, $expenses);
|
||||||
}
|
}
|
||||||
// $table = $this->generateTable($output, $data);
|
|
||||||
$table = $this->getTable($output, $data);
|
$table = $this->getTable($output, $data);
|
||||||
$table->render();
|
$table->render();
|
||||||
|
|
||||||
if ($input->getOption('pdf')) {
|
if ($input->getOption('pdf')) {
|
||||||
$nice_data = $this->csv->arangeDataForDefaultPdfExport($data);
|
$nice_data = $this->csv->arangeDataForDefaultPdfExport($data);
|
||||||
// @TODO method gatherTokens();
|
// @TODO method gatherTokens();
|
||||||
|
@ -213,9 +189,6 @@ class InvoiceCommand extends Command
|
||||||
// Notify the user where the file was generated to.
|
// Notify the user where the file was generated to.
|
||||||
$output->writeln("The file was generated at <info>${output_path}</info>.");
|
$output->writeln("The file was generated at <info>${output_path}</info>.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
if ($input->getOption('send') && $output_path) {
|
if ($input->getOption('send') && $output_path) {
|
||||||
// @TODO If no output path print an error.
|
// @TODO If no output path print an error.
|
||||||
// Send email to configured address.
|
// Send email to configured address.
|
||||||
|
@ -267,7 +240,7 @@ class InvoiceCommand extends Command
|
||||||
'Price',
|
'Price',
|
||||||
]);
|
]);
|
||||||
[$rows, $totalHours, $totalPrice] = [[], 0, 0];
|
[$rows, $totalHours, $totalPrice] = [[], 0, 0];
|
||||||
$projectsConfig = $this->configuration->get('projects');
|
$projectsConfig = $this->config->get('projects');
|
||||||
foreach ($projectsConfig as $name => $config) {
|
foreach ($projectsConfig as $name => $config) {
|
||||||
if (! isset($data[$name])) {
|
if (! isset($data[$name])) {
|
||||||
// @TODO Proper error handling.
|
// @TODO Proper error handling.
|
||||||
|
@ -353,8 +326,7 @@ 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) {
|
||||||
|
|
|
@ -14,14 +14,14 @@ use Symfony\Component\Console\Helper\TableSeparator;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
|
||||||
|
|
||||||
use function array_flip;
|
|
||||||
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;
|
||||||
|
|
||||||
protected $trackingService;
|
protected $trackingService;
|
||||||
|
|
||||||
protected $config;
|
protected $config;
|
||||||
|
@ -37,22 +37,30 @@ 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(
|
||||||
'report',
|
'report',
|
||||||
'r',
|
'r',
|
||||||
InputOption::VALUE_OPTIONAL,
|
InputOption::VALUE_OPTIONAL,
|
||||||
'Select a report from list of your reports'
|
'Select a report from list ofo your reports'
|
||||||
);
|
);
|
||||||
|
// Not supported by by ReportCsv service!
|
||||||
|
// @TODO Build factory for time tracking service (youtrack-csv-export,
|
||||||
|
// youtrack-api, jira-api, kimai-api (biro.radiostudent.si)).
|
||||||
$this->addOption(
|
$this->addOption(
|
||||||
'time-range',
|
'time-range',
|
||||||
't',
|
't',
|
||||||
InputOption::VALUE_REQUIRED,
|
InputOption::VALUE_REQUIRED,
|
||||||
'Calculates report from tracking service work items directly for time range'
|
'Calculates report from tracking service work items directly for time range'
|
||||||
);
|
);
|
||||||
|
$this->addOption(
|
||||||
|
'file',
|
||||||
|
'f',
|
||||||
|
InputOption::VALUE_REQUIRED,
|
||||||
|
'Specify the input csv file to generate report from.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
protected function execute(InputInterface $input, OutputInterface $output) : int
|
||||||
|
@ -61,50 +69,19 @@ class ReportCommand extends Command
|
||||||
// @TODO: Implement time range option:
|
// @TODO: Implement time range option:
|
||||||
// - Request workTime items from tracking service
|
// - Request workTime items from tracking service
|
||||||
// - Filter them, join by issue, project ...
|
// - Filter them, join by issue, project ...
|
||||||
|
// This will came in other report service that will gather data
|
||||||
|
// directly from cache.
|
||||||
if ($output->isVerbose()) {
|
if ($output->isVerbose()) {
|
||||||
$output->writeln("Time range: {$timeRange}");
|
$output->writeln("Time range: {$timeRange}");
|
||||||
}
|
}
|
||||||
$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;
|
||||||
}
|
}
|
||||||
$reports = $this->trackingService->listReports();
|
$this->getReportParameter($input, $output);
|
||||||
// Could just parse a csv file or actually get workItems from youtrack ...
|
|
||||||
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) {
|
|
||||||
if (! $report = $input->getOption('report')) {
|
|
||||||
// @TODO Make selection nicer.
|
|
||||||
// QuestionHelper: https://symfony.com/doc/current/components/console/helpers/questionhelper.html
|
|
||||||
$helper = $this->getHelper('question');
|
|
||||||
$question = new ChoiceQuestion('Select report:', $reports);
|
|
||||||
$question
|
|
||||||
->setErrorMessage('Report %s does not exist!')
|
|
||||||
->setAutocompleterValues($reports);
|
|
||||||
$report = $helper->ask($input, $output, $question);
|
|
||||||
$output->writeln('Report ' . $report . ' selected.');
|
|
||||||
}
|
|
||||||
// If parameter option is not recognised check if report name was given.
|
|
||||||
if (! isset($reports[$report])) {
|
|
||||||
if (! isset(array_flip($reports)[$report])) {
|
|
||||||
$output->writeln('Non-existing report ' . $report . '. Exiting.');
|
|
||||||
return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
$report = array_flip($reports)[$report];
|
|
||||||
}
|
|
||||||
$this->trackingService->setReportId($report);
|
|
||||||
} elseif ($report = $this->config->get('tracking_service.youtrack.report.default')) {
|
|
||||||
$this->trackingService->setReportId($report);
|
|
||||||
}
|
|
||||||
// Currently we only support csv download.
|
// Currently we only support csv download.
|
||||||
$report_id = $this->trackingService->getReportId();
|
$report_id = $this->trackingService->getReportId();
|
||||||
$report_name = $reports[$report_id];
|
$file = $this->getReportCsvFilePath($input, $output, $report_id);
|
||||||
// Code duplication.
|
$report_name = $this->trackingService->getReportName();
|
||||||
$cache_clear_status = $this->trackingService->clearReportCache($report_id);
|
|
||||||
if ($output->isVerbose()) {
|
|
||||||
$output->writeln("Report cache cleared, status: {$cache_clear_status}");
|
|
||||||
}
|
|
||||||
$file = $this->trackingService->downloadReport($report_id);
|
|
||||||
if ($output->isVerbose()) {
|
|
||||||
$output->writeln("Csv file downloaded to: <info>{$file}</info>");
|
|
||||||
}
|
|
||||||
$output->writeln("report: <info>{$report_name}</info>");
|
$output->writeln("report: <info>{$report_name}</info>");
|
||||||
$data = $this->csv->generateReportTable($file);
|
$data = $this->csv->generateReportTable($file);
|
||||||
$table = $this->buildTable($output, $data);
|
$table = $this->buildTable($output, $data);
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace RprtCli\Commands;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait to select report.
|
||||||
|
*
|
||||||
|
* To be used on commands with trackingService property and config property.
|
||||||
|
* Command must have report input option. It is not the most elegant solution
|
||||||
|
* but it helps avoiding code duplication.
|
||||||
|
*/
|
||||||
|
trait SelectReportTrait {
|
||||||
|
|
||||||
|
protected function checkContext() {
|
||||||
|
if (!isset($this->trackingService)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return $this instanceof Command;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getReportParameter(InputInterface $input, OutputInterface $output, $default = 'tracking_service.youtrack.report.report') {
|
||||||
|
if (!$this->checkContext()) {
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
$reports = $this->trackingService->listReports();
|
||||||
|
// Could just parse a csv file or actually get workItems from youtrack ...
|
||||||
|
if ($input->hasParameterOption('--report') || $input->hasParameterOption('-r')) {
|
||||||
|
if (! $report_id = $input->getOption('report')) {
|
||||||
|
// @TODO Make selection nicer.
|
||||||
|
// QuestionHelper: https://symfony.com/doc/current/components/console/helpers/questionhelper.html
|
||||||
|
$helper = $this->getHelper('question');
|
||||||
|
$question = new ChoiceQuestion('Select report:', $reports);
|
||||||
|
$question
|
||||||
|
->setErrorMessage('Report %s does not exist!')
|
||||||
|
->setAutocompleterValues($reports);
|
||||||
|
$report_id = $helper->ask($input, $output, $question);
|
||||||
|
$output->writeln('Report ' . $report_id . ' selected.');
|
||||||
|
}
|
||||||
|
// If parameter option is not recognised check if report name was given.
|
||||||
|
if (! isset($reports[$report_id])) {
|
||||||
|
if (! isset(array_flip($reports)[$report_id])) {
|
||||||
|
$output->writeln('Non-existing report ' . $report_id . '. Exiting.');
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
$report_id = array_flip($reports)[$report_id];
|
||||||
|
}
|
||||||
|
$this->trackingService->setReportId($report_id);
|
||||||
|
} elseif ($report_id = $this->config->get($default)) {
|
||||||
|
// If report is not set, try getting default report from configuration.
|
||||||
|
// This is dependant on the config.
|
||||||
|
$this->trackingService->setReportId($report_id);
|
||||||
|
}
|
||||||
|
$this->trackingService->setReportName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getReportCsvFilePath(InputInterface $input, OutputInterface $output, ?string $report_id) : ?string {
|
||||||
|
if (!$file = $input->getOption('file')) {
|
||||||
|
$cache_clear_status = $this->trackingService->clearReportCache($report_id);
|
||||||
|
if ($output->isVerbose()) {
|
||||||
|
$output->writeln("Report <info>{$report_id}</info> cache cleared, status: {$cache_clear_status}");
|
||||||
|
}
|
||||||
|
$file = $this->trackingService->downloadReport($report_id);
|
||||||
|
if ($output->isVerbose()) {
|
||||||
|
$output->writeln("Csv file downloaded to: <info>{$file}</info>");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->trackingService->setReportName($file);
|
||||||
|
}
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -188,10 +188,11 @@ class ReportCsv implements ReportCsvInterface
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
[$previous, $time_sum, $project_time, $table, $all_projects] = [$data[0]['id'], 0, 0, [], []];
|
$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) {
|
foreach ($data as $line) {
|
||||||
$project = explode('-', $line['id'])[0];
|
$project = $explodeMinus($line['id']);
|
||||||
$previous_project = explode('-', $previous)[0];
|
$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.
|
||||||
$table[] = ReportCsvInterface::SEPARATOR_MEDIUM;
|
$table[] = ReportCsvInterface::SEPARATOR_MEDIUM;
|
||||||
|
@ -242,32 +243,4 @@ class ReportCsv implements ReportCsvInterface
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Should be moved into test class.
|
|
||||||
*/
|
|
||||||
protected function dummyConfig() : array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'projects' => [
|
|
||||||
'LDP' => [
|
|
||||||
'name' => 'lupus.digital',
|
|
||||||
'pattern' => 'LDP-[0-9]+',
|
|
||||||
'price' => 25,
|
|
||||||
// optional specify columns
|
|
||||||
],
|
|
||||||
'WV' => [
|
|
||||||
'name' => 'Wirtschaftsverlag',
|
|
||||||
'pattern' => 'WV-[0-9]+',
|
|
||||||
'price' => 25,
|
|
||||||
// optional specify columns
|
|
||||||
],
|
|
||||||
'Other' => [
|
|
||||||
'name' => 'Other projects',
|
|
||||||
'pattern' => '(?!.\bLDP\b)(?!.\bWV\b)',
|
|
||||||
'price' => 25,
|
|
||||||
// optional specify columns
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,4 +34,8 @@ 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 setReportName(?string $report_name = NULL) :void;
|
||||||
|
public function getReportName() : ?string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,15 @@ use function var_dump;
|
||||||
|
|
||||||
class YoutrackService implements YoutrackInterface
|
class YoutrackService implements YoutrackInterface
|
||||||
{
|
{
|
||||||
protected $ytToken;
|
protected string $ytToken;
|
||||||
protected $ytBaseUrl;
|
protected string $ytBaseUrl;
|
||||||
|
|
||||||
protected $config;
|
protected ConfigurationInterface $config;
|
||||||
|
|
||||||
protected $httpClient;
|
protected ClientInterface $httpClient;
|
||||||
|
|
||||||
protected $report_id;
|
protected string $report_id;
|
||||||
|
protected ?string $reportName = NULL;
|
||||||
|
|
||||||
public function __construct(ConfigurationInterface $config, ClientInterface $http_client)
|
public function __construct(ConfigurationInterface $config, ClientInterface $http_client)
|
||||||
{
|
{
|
||||||
|
@ -106,6 +107,23 @@ class YoutrackService implements YoutrackInterface
|
||||||
$this->report_id = $report_id;
|
$this->report_id = $report_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setReportName(?string $report_name = NULL) : void {
|
||||||
|
if (!$report_name) {
|
||||||
|
$reports = $this->listReports();
|
||||||
|
if ($report_id = $this->getReportId()) {
|
||||||
|
$report_name = $reports[$report_id] ?? NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$report_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->reportName = $report_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReportName() : ?string {
|
||||||
|
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";
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace RprtCli\Tests\Kernel;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
use Symfony\Component\Console\Tester\CommandTester;
|
||||||
|
use DI\ContainerBuilder;
|
||||||
|
use RprtCli\Commands\InvoiceCommand;
|
||||||
|
use RprtCli\Commands\ReportCommand;
|
||||||
|
|
||||||
|
class ReportCommandTest extends TestCase {
|
||||||
|
|
||||||
|
public function testExecute() {
|
||||||
|
$builder = new ContainerBuilder();
|
||||||
|
$builder->addDefinitions(__DIR__ . '/../test-dependencies.php');
|
||||||
|
$container = $builder->build();
|
||||||
|
|
||||||
|
$application = new Application('Command Line Tool to process Youtrack Reports', '0.1.0');
|
||||||
|
|
||||||
|
$invoiceCommand = $container->get(InvoiceCommand::class);
|
||||||
|
$application->add($invoiceCommand);
|
||||||
|
$reportCommand = $container->get(ReportCommand::class);
|
||||||
|
$application->add($reportCommand);
|
||||||
|
|
||||||
|
|
||||||
|
$command = $application->find('report');
|
||||||
|
$commandTester = new CommandTester($command);
|
||||||
|
$commandTester->execute([
|
||||||
|
// pass arguments to the helper
|
||||||
|
'--file' => __DIR__ . '/../data/21-03.csv',
|
||||||
|
|
||||||
|
// prefix the key with two dashes when passing options,
|
||||||
|
// e.g: '--some-option' => 'option_value',
|
||||||
|
// use brackets for testing array value,
|
||||||
|
// e.g: '--some-option' => ['option_value'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$commandTester->assertCommandIsSuccessful();
|
||||||
|
|
||||||
|
// the output of the command in the console
|
||||||
|
$output = $commandTester->getDisplay();
|
||||||
|
var_dump($output);
|
||||||
|
$this->assertStringContainsString('21-03.csv', $output);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
Group name,Item,Item Summary,Estimation time,Spent time
|
||||||
|
-,DEV-46,"Wochenplanung, Weekly",0,315
|
||||||
|
-,DEV-56,Developer training,0,60
|
||||||
|
-,DEV-122,Daily standup,0,255
|
||||||
|
-,INF-6,"Infrastructure misc, diverse",0,75
|
||||||
|
-,KUR-181,Sprint meetings (PM),240,390
|
||||||
|
-,KUR-507,(TKT-307) Change webhook integration for Slack,0,330
|
||||||
|
-,KUR-552,(TKT-740) Allow kicking users out of collections,780,45
|
||||||
|
-,KUR-554,(TKT-1448) Adaptions in Backend,0,30
|
||||||
|
-,KUR-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
|
||||||
|
-,KUR-562,(TKT-1441) Implement SEO title in BE,0,45
|
||||||
|
-,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-692,Output json-ld script,0,30
|
||||||
|
-,LDP-693,Add WebPage schema.org support,120,180
|
||||||
|
-,LDP-700,LDP onboarding Somebody,0,15
|
||||||
|
-,LDP-706,Backend FAQ Marketing Block,0,165
|
||||||
|
-,LDP-707,"Backend ""Hero"" Marketing Block",0,1035
|
||||||
|
-,LDP-709,"Backend ""Newsletter Signup"" Marketing Block",0,195
|
||||||
|
-,LDP-711,"Backend ""Call to action"" Marketing Block",0,165
|
||||||
|
-,LDP-712,"Backend ""Feature section"" Marketing Block",0,300
|
||||||
|
-,LDP-713,Provide a field type and pre-configuration for choosing icons,300,45
|
||||||
|
-,LDP-716,Entity browser UX is not as good as expected,0,15
|
||||||
|
-,LDP-720,Improve display of nodes and add support for article heros,0,45
|
||||||
|
-,LDP-728,ldp-marketing blocks break the page when they are added to the layout builder,0,195
|
||||||
|
-,LDP-729,ldp-cp portal entity,0,480
|
||||||
|
-,LDP-740,CP: Add portal path prefixes,480,30
|
||||||
|
-,LDP-741,Defects marketing Blocks,0,450
|
||||||
|
-,LDP-742,[Red Alert] Develop branch fails to build,0,45
|
||||||
|
-,VAL-8,Plan hosting setup on custom hosting provider,0,45
|
||||||
|
-,WV-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
|
||||||
|
-,WV-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
|
||||||
|
-,WV-4355,(ODT-1270) Add article title to breadcrumbs,0,270
|
||||||
|
-,WV-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
|
||||||
|
-,WV-4384,(ODT-1306) Layout-builder rendering in Chrome browser,120,120
|
||||||
|
-,WV-4393,WVsat docker build error,0,90
|
||||||
|
-,WV-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
|
||||||
|
-,WV-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
|
||||||
|
-,WV-4421,WV 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
|
||||||
|
-,WV-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
|
||||||
|
-,WV-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
|
||||||
|
-,WV-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
|
||||||
|
-,WV-4480,(ODT-1370) Error: Call to a member function getUrl() on null / cannot save an article on stage systems,0,90
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
Hello,
|
||||||
|
|
||||||
|
I'm sending you the invoice for [[month]] [[year]].
|
||||||
|
|
||||||
|
This email and invoice were generated and sent by (rprt-cli)[https://git.kompot.si/lio/RprtCli] (WIP).
|
||||||
|
|
||||||
|
|
||||||
|
Kind regards
|
||||||
|
|
||||||
|
Liopold Novelli
|
|
@ -0,0 +1,91 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.table table {
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0px auto;
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
p { margin: 0pt; }
|
||||||
|
td { vertical-align: top; }
|
||||||
|
table td {
|
||||||
|
border-left: 0.1mm solid #000000;
|
||||||
|
border-right: 0.1mm solid #000000;
|
||||||
|
}
|
||||||
|
table tbody td {
|
||||||
|
background-color: #FFF;
|
||||||
|
border: 0.1mm solid #000000;
|
||||||
|
padding-right: 2mm;
|
||||||
|
padding-left: 2mm;
|
||||||
|
}
|
||||||
|
tbody td.td-3,
|
||||||
|
tbody td.td-1,
|
||||||
|
.right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
tbody td.td-2,
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
table td.colspan.td-2 {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
table thead th,
|
||||||
|
table tr.bold td,
|
||||||
|
table tr.bold td.td-3 {
|
||||||
|
background-color: #EEEEEE;
|
||||||
|
border: 0.1mm solid #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
table tr.center td,
|
||||||
|
table tr.center td.td-3,
|
||||||
|
table tbody tr.center td.td-2.colspan {
|
||||||
|
text-align: center!important;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
tbody tr.tr-2 td,
|
||||||
|
tbody tr.tr-3 td {
|
||||||
|
background-color: #EEE;
|
||||||
|
border-top: 0.4mm solid #000;
|
||||||
|
}
|
||||||
|
tbody tr.tr-3 td {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
.left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p class="center">Spletni razvoj, Liopold Doron Novelli s.p.,<br>ADDRESS<br>NUMBER, liopold@drunomics.com<br>NUMBER</p>
|
||||||
|
<p><br><br></p>
|
||||||
|
<p>Company name<br>Company Name<br>Wien<br>Austria</p>
|
||||||
|
<p><br></p>
|
||||||
|
<p><br></p>
|
||||||
|
<p><br></p>
|
||||||
|
<p class="right">Laibach, am [[today]]</p>
|
||||||
|
<p><strong>Honorarnote<br>Nummer [[number]]</strong></p>
|
||||||
|
<p><br></p>
|
||||||
|
<p>Für meine Tätigkeit Programmieren von [[date_start]] bis [[date_end]] erlaube ich mir, folgenden Betrag in Rechnung zu stellen:<br></p>
|
||||||
|
<p><br></p>
|
||||||
|
<div class="table">[[table]]</div>
|
||||||
|
<p><br>Ich ersuche Sie höflich, den oben angeführt auf meine Kontonummer ACCOUNT NUMBER mit der Bankleitzahl BANK zu überweisen.<br></p>
|
||||||
|
<p><br>Vielen Dank für den Auftrag,<br>
|
||||||
|
mit besten Grüßen,</p>
|
||||||
|
<p><br><br></p>
|
||||||
|
<p><br><br></p>
|
||||||
|
<p>Liopold Doron Novelli</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,47 @@
|
||||||
|
tracking_service:
|
||||||
|
youtrack:
|
||||||
|
auth_token: 'perm:<secret>'
|
||||||
|
base_url: 'https://<organization>.myjetbrains.com'
|
||||||
|
report_id: '83-554'
|
||||||
|
# report command
|
||||||
|
report:
|
||||||
|
report: '83-541'
|
||||||
|
# invoice command
|
||||||
|
invoice:
|
||||||
|
# default value for report parameter
|
||||||
|
report: '83-554'
|
||||||
|
export:
|
||||||
|
template_path: 'tests/data/invoice-template.html'
|
||||||
|
output: 'tests/data/output/Invoice-Novelli-[[month]]-[[year]].pdf'
|
||||||
|
labels:
|
||||||
|
- 'Tätigkeit'
|
||||||
|
- 'Stunden'
|
||||||
|
- 'Stundensatz'
|
||||||
|
- 'EUR'
|
||||||
|
email:
|
||||||
|
template_path: 'tests/data/rprt-cli/email-template.txt'
|
||||||
|
username: '<username>'
|
||||||
|
tokens:
|
||||||
|
me: 'Liopold Novelli'
|
||||||
|
to:
|
||||||
|
- 'wolfgang.ziegler@drunomics.com'
|
||||||
|
- 'accounting@drunomics.com'
|
||||||
|
from: 'liopolddoron@gmail.com'
|
||||||
|
projects:
|
||||||
|
LDP:
|
||||||
|
name: 'lupus.digital publishing'
|
||||||
|
pattern: 'LDP'
|
||||||
|
price: 25.6
|
||||||
|
currency: 'EUR'
|
||||||
|
project_column: 1
|
||||||
|
time_column: 4
|
||||||
|
# time format m - minutes, h - hours
|
||||||
|
time_format: 'm'
|
||||||
|
Other:
|
||||||
|
name: 'Projektbezogene Entwicklung'
|
||||||
|
pattern: '(?!.\bLDP\b)'
|
||||||
|
price: 29.1
|
||||||
|
currency: 'EUR'
|
||||||
|
project_column: 1
|
||||||
|
time_column: 4
|
||||||
|
time_format: 'm'
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$dependencies = require_once(__DIR__ . '/../dependencies.php');
|
||||||
|
|
||||||
|
$dependencies['config.path'] = __DIR__ . '/data';
|
||||||
|
|
||||||
|
return $dependencies;
|
14
todo.txt
14
todo.txt
|
@ -18,6 +18,20 @@ TODO:
|
||||||
- phing for build automation
|
- phing for build automation
|
||||||
- add ddev for dockerization
|
- add ddev for dockerization
|
||||||
|
|
||||||
|
TODO (end of 2022):
|
||||||
|
- DONE fix project list output (first is missing)
|
||||||
|
- DONE report selection trait
|
||||||
|
- automated tests
|
||||||
|
- phpcs
|
||||||
|
- phpstan
|
||||||
|
OPTIONAL:
|
||||||
|
- phing build system to run quality assurance
|
||||||
|
- DONE fix -y parameter in invoice
|
||||||
|
- time track command
|
||||||
|
- report from api service and factory:
|
||||||
|
- youtrack
|
||||||
|
- jira
|
||||||
|
- kimai
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue