Add option for custom work and expenses.
parent
df662c3eb9
commit
9f8c1616f5
16
README.org
16
README.org
|
@ -47,6 +47,10 @@
|
|||
- Create an invoice
|
||||
- send the invoice (to Wolfgang)
|
||||
|
||||
*** ValueObjects
|
||||
|
||||
|
||||
|
||||
** Motivation
|
||||
- practice php
|
||||
- automatization of repetitive task
|
||||
|
@ -58,6 +62,17 @@
|
|||
|
||||
** Plan
|
||||
|
||||
*** most current
|
||||
1. For version 0.6.7
|
||||
- data value objects
|
||||
- phar file
|
||||
- additional expenses
|
||||
2. For version 1.0
|
||||
- plugin system
|
||||
- for time tracking services
|
||||
- for reports
|
||||
- symfony/config & symfony/di components
|
||||
|
||||
*** current
|
||||
|
||||
1. For Version 1.0
|
||||
|
@ -94,6 +109,7 @@
|
|||
2. configuration wizard
|
||||
|
||||
|
||||
|
||||
** Learning
|
||||
- https://www.youtube.com/watch?v=aCqM9YnjTe0
|
||||
- Choices (~new ChoiceQuestion~)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "lio/rprt-cli",
|
||||
"description": "Automate invoicing from youtrack service.",
|
||||
"type": "project",
|
||||
"keywords": ["cli", "report"],
|
||||
"keywords": ["cli", "report", "youtrack"],
|
||||
"readme": "README.org",
|
||||
"time": "2021-04-04",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
|
|
@ -722,20 +722,20 @@
|
|||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
|
||||
"reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
|
||||
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
|
||||
"reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.0"
|
||||
"php": ">=7.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -762,7 +762,11 @@
|
|||
"container-interop",
|
||||
"psr"
|
||||
],
|
||||
"time": "2021-03-05T17:36:06+00:00"
|
||||
"support": {
|
||||
"issues": "https://github.com/php-fig/container/issues",
|
||||
"source": "https://github.com/php-fig/container/tree/1.1.2"
|
||||
},
|
||||
"time": "2021-11-05T16:50:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/event-dispatcher",
|
||||
|
@ -1064,27 +1068,29 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.2.6",
|
||||
"version": "v5.4.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d"
|
||||
"reference": "ffe3aed36c4d60da2cf1b0a1cee6b8f2e5fa881b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/35f039df40a3b335ebf310f244cb242b3a83ac8d",
|
||||
"reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/ffe3aed36c4d60da2cf1b0a1cee6b8f2e5fa881b",
|
||||
"reference": "ffe3aed36c4d60da2cf1b0a1cee6b8f2e5fa881b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1|^3",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php73": "^1.8",
|
||||
"symfony/polyfill-php80": "^1.15",
|
||||
"symfony/service-contracts": "^1.1|^2",
|
||||
"symfony/string": "^5.1"
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/service-contracts": "^1.1|^2|^3",
|
||||
"symfony/string": "^5.1|^6.0"
|
||||
},
|
||||
"conflict": {
|
||||
"psr/log": ">=3",
|
||||
"symfony/dependency-injection": "<4.4",
|
||||
"symfony/dotenv": "<5.1",
|
||||
"symfony/event-dispatcher": "<4.4",
|
||||
|
@ -1092,16 +1098,16 @@
|
|||
"symfony/process": "<4.4"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0"
|
||||
"psr/log-implementation": "1.0|2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "^4.4|^5.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0",
|
||||
"symfony/event-dispatcher": "^4.4|^5.0",
|
||||
"symfony/lock": "^4.4|^5.0",
|
||||
"symfony/process": "^4.4|^5.0",
|
||||
"symfony/var-dumper": "^4.4|^5.0"
|
||||
"psr/log": "^1|^2",
|
||||
"symfony/config": "^4.4|^5.0|^6.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
|
||||
"symfony/event-dispatcher": "^4.4|^5.0|^6.0",
|
||||
"symfony/lock": "^4.4|^5.0|^6.0",
|
||||
"symfony/process": "^4.4|^5.0|^6.0",
|
||||
"symfony/var-dumper": "^4.4|^5.0|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "For using the console logger",
|
||||
|
@ -1140,20 +1146,37 @@
|
|||
"console",
|
||||
"terminal"
|
||||
],
|
||||
"time": "2021-03-28T09:42:18+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.4.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-12T16:02:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.2.0",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
|
||||
"reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1162,7 +1185,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.2-dev"
|
||||
"dev-main": "2.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
|
@ -1190,7 +1213,24 @@
|
|||
],
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2020-09-07T11:33:47+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-02T09:53:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
|
@ -1581,28 +1621,31 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
|
||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -1610,12 +1653,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
@ -1639,20 +1682,37 @@
|
|||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"time": "2021-01-07T16:49:33+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||
"reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170"
|
||||
"reference": "81b86b50cf841a64252b439e738e97f4a34e2783"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170",
|
||||
"reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783",
|
||||
"reference": "81b86b50cf841a64252b439e738e97f4a34e2783",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1664,7 +1724,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -1672,12 +1732,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
@ -1703,7 +1763,24 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2021-01-22T09:19:47+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-23T21:10:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-idn",
|
||||
|
@ -1794,16 +1871,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
|
||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
|
||||
"reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1815,7 +1892,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -1823,12 +1900,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
|
@ -1857,32 +1934,52 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2021-01-22T09:19:47+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
|
||||
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -1890,12 +1987,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
|
@ -1920,7 +2017,24 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2021-01-22T09:19:47+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-30T18:21:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php72",
|
||||
|
@ -2000,16 +2114,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php73",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||
"reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
|
||||
"reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
|
||||
"reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5",
|
||||
"reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2018,7 +2132,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -2026,12 +2140,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php73\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php73\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
|
@ -2058,20 +2172,37 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2021-01-07T16:49:33+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-05T21:20:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
|
||||
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
|
||||
"reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c",
|
||||
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2080,7 +2211,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
@ -2088,12 +2219,12 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
|
@ -2124,25 +2255,46 @@
|
|||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"time": "2021-01-07T16:49:33+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-04T08:16:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v2.2.0",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
|
||||
"reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
|
||||
"reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
|
||||
"reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/container": "^1.0"
|
||||
"psr/container": "^1.1",
|
||||
"symfony/deprecation-contracts": "^2.1|^3"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-psr": "<1.1|>=2"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/service-implementation": ""
|
||||
|
@ -2150,7 +2302,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.2-dev"
|
||||
"dev-main": "2.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
|
@ -2186,44 +2338,63 @@
|
|||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"time": "2020-09-07T11:33:47+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-13T20:07:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.2.6",
|
||||
"version": "v6.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572"
|
||||
"reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572",
|
||||
"reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/ac0aa5c2282e0de624c175b68d13f2c8f2e2649d",
|
||||
"reference": "ac0aa5c2282e0de624c175b68d13f2c8f2e2649d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"php": ">=8.0.2",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-intl-grapheme": "~1.0",
|
||||
"symfony/polyfill-intl-normalizer": "~1.0",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php80": "~1.15"
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/translation-contracts": "<2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/error-handler": "^4.4|^5.0",
|
||||
"symfony/http-client": "^4.4|^5.0",
|
||||
"symfony/translation-contracts": "^1.1|^2",
|
||||
"symfony/var-exporter": "^4.4|^5.0"
|
||||
"symfony/error-handler": "^5.4|^6.0",
|
||||
"symfony/http-client": "^5.4|^6.0",
|
||||
"symfony/translation-contracts": "^2.0|^3.0",
|
||||
"symfony/var-exporter": "^5.4|^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\String\\": ""
|
||||
},
|
||||
"files": [
|
||||
"Resources/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\String\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
|
@ -2252,7 +2423,24 @@
|
|||
"utf-8",
|
||||
"utf8"
|
||||
],
|
||||
"time": "2021-03-17T17:12:15+00:00"
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v6.0.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-04-22T08:18:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
|
|
|
@ -23,11 +23,6 @@
|
|||
border-left: 0.1mm solid #000000;
|
||||
border-right: 0.1mm solid #000000;
|
||||
}
|
||||
table thead th {
|
||||
background-color: #EEEEEE;
|
||||
text-align: center;
|
||||
border: 0.1mm solid #000000;
|
||||
}
|
||||
table tbody td {
|
||||
background-color: #FFF;
|
||||
border: 0.1mm solid #000000;
|
||||
|
@ -35,9 +30,26 @@
|
|||
padding-left: 2mm;
|
||||
}
|
||||
tbody td.td-3,
|
||||
tbody td.td-1 {
|
||||
tbody td.td-1,
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
/*tbody td.td-2,*/
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
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 {
|
||||
text-align: center!important;
|
||||
}
|
||||
/*
|
||||
tbody tr.tr-2 td,
|
||||
tbody tr.tr-3 td {
|
||||
background-color: #EEE;
|
||||
|
@ -46,32 +58,30 @@
|
|||
tbody tr.tr-3 td {
|
||||
font-weight: bold;
|
||||
}
|
||||
tbody td.td-2,
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.right {
|
||||
text-align: right;
|
||||
*/
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p class="center">My company name</br>My company address</br>123456789, mycompany@email.com</br>987654321</p>
|
||||
<p></p>
|
||||
<p>Other company</br>Other company address</br>1111 City</br>State</p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p class="right">City, on [[today]]</p>
|
||||
<p><strong>Honorarnote</br>Nummer [[number]]</strong></p>
|
||||
<p></p>
|
||||
<p>Für meine Tätigkeit Programmieren von [[date_start]] bis [[date_end]] erlaube ich mir, folgenden Betrag in Rechnung zu stellen:</p>
|
||||
[[table]]
|
||||
<p>Ich ersuche Sie höflich, den oben angeführt auf meine Kontonummer [[account_number]] mit der Bankleitzahl ABCDSI33 zu überweisen.</p>
|
||||
<p>Vielen Dank für den Auftrag,</br>
|
||||
<p class="center">Spletni razvoj, Liopold Doron Novelli s.p.,<br>Prešernova 23, 1236 Trzin<br>8638314000, liopold@drunomics.com<br>25752910</p>
|
||||
<p><br><br></p>
|
||||
<p>drunomics GmbH<br>Getriedmarkt 11/12<br>1060 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 SI56 6100 0002 3993 491 mit der Bankleitzahl HDELSI22 zu überweisen.<br></p>
|
||||
<p><br>Vielen Dank für den Auftrag,<br>
|
||||
mit besten Grüßen,</p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p>My name</p>
|
||||
<p><br><br></p>
|
||||
<p><br><br></p>
|
||||
<p>Liopold Doron Novelli</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -8,6 +8,8 @@ use RprtCli\Utils\Configuration\ConfigurationInterface;
|
|||
use RprtCli\Utils\Configuration\ConfigurationService;
|
||||
use RprtCli\Utils\CsvReport\CsvReport;
|
||||
use RprtCli\Utils\CsvReport\CsvReportInterface;
|
||||
use RprtCli\Utils\CsvReport\ReportCsv;
|
||||
use RprtCli\Utils\CsvReport\ReportCsvInterface;
|
||||
use GuzzleHttp\Client;
|
||||
use Mpdf\Mpdf;
|
||||
use RprtCli\Utils\Mailer\MailerInterface;
|
||||
|
@ -24,8 +26,6 @@ return [
|
|||
'config.file' => 'rprt.config.yml',
|
||||
'config.path' => '~/.config/rprt-cli/',
|
||||
'default_locale' => 'en',
|
||||
// 'translator' => ['default_path' => '%kernel.project_dir%/translations'],
|
||||
// 'guzzle' => create()->constructor(Client::class),
|
||||
'guzzle' => get(Client::class),
|
||||
'mpdf' => get(Mpdf::class),
|
||||
ConfigurationInterface::class => get(ConfigurationService::class),
|
||||
|
@ -49,11 +49,11 @@ return [
|
|||
// 'locale' => get('config.service')->method('get', 'en'),
|
||||
// Translator::class => create()->constructor('sl')->method('addLoader', 'po', new PoFileLoader),
|
||||
// 'translator' => get(Translator::class),
|
||||
CsvReportInterface::class => get(CsvReport::class),
|
||||
CsvReport::class => create()->constructor(
|
||||
ReportCsvInterface::class => get(ReportCsv::class),
|
||||
ReportCsv::class => create()->constructor(
|
||||
get('config.service')
|
||||
),
|
||||
'csv.report' => get(CsvReportInterface::class),
|
||||
'csv.report' => get(ReportCsvInterface::class),
|
||||
MailerInterface::class => get(MailerService::class),
|
||||
MailerService::class => create()->constructor(
|
||||
get('config.service'),
|
||||
|
|
|
@ -7,13 +7,16 @@ declare(strict_types=1);
|
|||
namespace RprtCli\Commands;
|
||||
|
||||
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||
use RprtCli\Utils\CsvReport\CsvReportInterface;
|
||||
use RprtCli\Utils\CsvReport\ReportCsvInterface;
|
||||
use RprtCli\Utils\Mailer\MailerInterface;
|
||||
use RprtCli\Utils\PdfExport\PdfExportInterface;
|
||||
use RprtCli\Utils\TimeTrackingServices\YoutrackInterface;
|
||||
use RprtCli\ValueObjects\Expenses;
|
||||
use RprtCli\ValueObjects\WorkInvoiceElement;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Helper\TableSeparator;
|
||||
use Symfony\Component\Console\Helper\TableCell;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -34,8 +37,11 @@ class RprtCommand extends Command
|
|||
|
||||
protected $pdfExport;
|
||||
|
||||
const TYPE_WORK = 1;
|
||||
const TYPE_EXPENSE = 2;
|
||||
|
||||
public function __construct(
|
||||
CsvReportInterface $csv,
|
||||
ReportCsvInterface $csv,
|
||||
ConfigurationInterface $configuration,
|
||||
YoutrackInterface $youtrack,
|
||||
PdfExportInterface $pdf_export,
|
||||
|
@ -81,7 +87,7 @@ class RprtCommand extends Command
|
|||
'test',
|
||||
't',
|
||||
InputOption::VALUE_NONE,
|
||||
'Test login into youtrack service.'
|
||||
'Test login into youtrack service. Prints out your name.'
|
||||
);
|
||||
$this->addOption(
|
||||
'output',
|
||||
|
@ -101,6 +107,20 @@ class RprtCommand extends Command
|
|||
InputOption::VALUE_REQUIRED,
|
||||
'Comma separated list of recipients that should get the exported pdf.'
|
||||
);
|
||||
$this->addOption(
|
||||
'expenses',
|
||||
'e',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'List of additional expenses in format expense1=value1;expenses2=value2... or empty for interactive output.',
|
||||
FALSE
|
||||
);
|
||||
$this->addOption(
|
||||
'custom',
|
||||
'c',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'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
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) : int
|
||||
|
@ -113,25 +133,38 @@ class RprtCommand extends Command
|
|||
$report_id = $this->youtrack->getReportId();
|
||||
$file = $this->youtrack->downloadReport($report_id);
|
||||
}
|
||||
if ($input->hasParameterOption('expenses') || $input->hasParameterOption('-e')) {
|
||||
$expenses = $this->getExpenses($input->getOption('expenses'));
|
||||
}
|
||||
if ($input->hasParameterOption('custom') || $input->hasParameterOption('-c')) {
|
||||
$custom = $this->getCustomWork($input->getOption('custom'));
|
||||
}
|
||||
if ($youtrack || $file = $input->getOption('file')) {
|
||||
// Youtrack can also provide a file name.
|
||||
var_dump($file);
|
||||
$data = $this->csv->getReportData($file);
|
||||
$table = $this->generateTable($output, $data);
|
||||
if (!empty($expenses)) {
|
||||
$data = array_merge($data, $expenses);
|
||||
}
|
||||
// $table = $this->generateTable($output, $data);
|
||||
$table = $this->getTable($output, $data);
|
||||
$table->render();
|
||||
|
||||
if ($pdf = $input->getOption('pdf')) {
|
||||
if ($input->getOption('pdf')) {
|
||||
$nice_data = $this->csv->arangeDataForDefaultPdfExport($data);
|
||||
// @TODO method gatherTokens();
|
||||
if ($output = $input->getOption('output')) {
|
||||
$this->pdfExport->setOutput($output);
|
||||
if ($out = $input->getOption('output')) {
|
||||
$this->pdfExport->setOutput($out);
|
||||
}
|
||||
$output_path = $this->pdfExport->fromDefaultDataToPdf($nice_data);
|
||||
// Notify the user where the file was generated to.
|
||||
$output->writeln("The file was generated at ${output_path}.");
|
||||
}
|
||||
|
||||
// return Command::SUCCESS;
|
||||
}
|
||||
if ($send = $input->getOption('send') && $output_path) {
|
||||
if ($input->getOption('send') && $output_path) {
|
||||
// @TODO If no output path print an error.
|
||||
// Send email to configured address.
|
||||
if ($recipients = $input->getOption('send-to')) {
|
||||
$this->mailer->setRecipients(explode(',', $recipients));
|
||||
|
@ -143,17 +176,35 @@ class RprtCommand extends Command
|
|||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
protected function getTable(OutputInterface $output, array $data) :Table {
|
||||
$rows = $this->csv->generateTable($data);
|
||||
$table = new Table($output);
|
||||
$table->setHeaders([
|
||||
'Project', 'Hours', 'Rate', 'Price',
|
||||
]);
|
||||
foreach ($rows as $key => $row) {
|
||||
if (!$row) {
|
||||
$rows[$key] = new TableSeparator();
|
||||
}
|
||||
elseif (is_array($row) && is_null($row[1]) && is_null($row[0])) {
|
||||
// Check which elements in array are null.
|
||||
$rows[$key] = [new TableCell($row[2], ['colspan' => 3]), $row[3]];
|
||||
}
|
||||
}
|
||||
$table->setRows($rows);
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create table from data that is already inline with configuration.
|
||||
*
|
||||
* @deprecated
|
||||
* This method was almost exact copy of CsvReport::arangeDataForDefaultPdfExport
|
||||
*/
|
||||
protected function generateTable(OutputInterface $output, array $data) : Table
|
||||
{
|
||||
$table = new Table($output);
|
||||
$table->setHeaders([
|
||||
// $this->translator->trans('Project', [], 'messages', 'sl_SI'),
|
||||
// $this->translator->trans('Hours', [], 'messages', 'sl_SI'),
|
||||
// $this->translator->trans('Rate'),
|
||||
// $this->translator->trans('Price'),
|
||||
'Project', 'Hours', 'Rate', 'Price',
|
||||
]);
|
||||
[$rows, $totalHours, $totalPrice] = [[], 0, 0];
|
||||
|
@ -168,7 +219,7 @@ class RprtCommand extends Command
|
|||
if ($config['time_format'] === 'm') {
|
||||
$hours /= 60;
|
||||
}
|
||||
$price = $hours * (int) $config['price'];
|
||||
$price = $hours * (float) $config['price'];
|
||||
$row = [
|
||||
$config['name'],
|
||||
$hours,
|
||||
|
@ -178,7 +229,15 @@ class RprtCommand extends Command
|
|||
$rows[] = $row;
|
||||
$totalHours += $hours;
|
||||
$totalPrice += $price;
|
||||
unset($data[$name]);
|
||||
}
|
||||
if (!empty($data)) {
|
||||
foreach ($data as $name => $value) {
|
||||
if (strpos(strtolower($name), 'expanses') !== FALSE) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = new TableSeparator();
|
||||
// @TODO Check rate in final result.
|
||||
// $rows[] = [$this->translator->trans('Sum'), $totalHours, $config['price'], $totalPrice];
|
||||
|
@ -206,4 +265,71 @@ class RprtCommand extends Command
|
|||
// $table->setStyle('borderless');
|
||||
$table->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expenses array.
|
||||
*
|
||||
* @return Expenses[]
|
||||
*/
|
||||
protected function getExpenses($expenses) {
|
||||
$output = [];
|
||||
if (is_string($expenses)) {
|
||||
foreach (explode(';', $expenses) as $expense) {
|
||||
[$name, $value] = explode('=', $expense);
|
||||
$output[] = new Expenses($name, (float) $value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$continue = TRUE;
|
||||
while ($continue) {
|
||||
$name = readline('Enter expenses name or leave empty to stop: ');
|
||||
$value = (float) readline('Enter expenses value: ');
|
||||
if (!empty($name)) {
|
||||
$output[] = new Expenses($name, $value);
|
||||
}
|
||||
else {
|
||||
$continue = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
protected function getCustomWorkOrExpenses($custom, $type) {
|
||||
$output = [];
|
||||
if (is_string($custom)) {
|
||||
foreach (explode(';', $custom) as $item) {
|
||||
[$name, $value] = explode('=', $item);
|
||||
$output[] = $this->createInvoiceElement($name, (float) $value, $type);
|
||||
}
|
||||
} else {
|
||||
$continue = TRUE;
|
||||
if ($type == self::TYPE_WORK) {
|
||||
$message_name = 'Enter project name or leave empty to stop: ';
|
||||
$message_value = 'Enter time spent of project: ';
|
||||
} elseif ($type == self::TYPE_EXPENSE) {
|
||||
$message_name = 'Enter expenses name or leave empty to stop: ';
|
||||
$message_value = 'Enter expenses value: ';
|
||||
}
|
||||
while ($continue) {
|
||||
$name = readline($message_name);
|
||||
$value = (float) readline($message_value);
|
||||
if (!empty($name)) {
|
||||
$output[] = $this->createInvoiceElement($name, $value, $type);
|
||||
} else {
|
||||
$continue = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
protected function createInvoiceElement(string $name, float $value, int $type) {
|
||||
if ($type == self::TYPE_WORK) {
|
||||
return new WorkInvoiceElement($name, (float) $value);
|
||||
} elseif ($type == self::TYPE_EXPENSE) {
|
||||
return new Expenses($name, (float) $value);
|
||||
}
|
||||
throw new \Exception('Unkown invoice element type.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ use function reset;
|
|||
|
||||
/**
|
||||
* Creates a report of projects and hours.
|
||||
*
|
||||
* @deprecated
|
||||
* Use ReportCsv class instead.
|
||||
*/
|
||||
class CsvReport implements CsvReportInterface
|
||||
{
|
||||
|
@ -90,7 +93,7 @@ class CsvReport implements CsvReportInterface
|
|||
}
|
||||
$hours = $data[$name];
|
||||
if ($config['time_format'] === 'm') {
|
||||
$hours /= 60;
|
||||
$hours /= 60.0;
|
||||
}
|
||||
$price = $hours * (int) $config['price'];
|
||||
$row = [
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\CsvReport;
|
||||
|
||||
use RprtCli\Utils\Configuration\ConfigurationInterface;
|
||||
use RprtCli\ValueObjects\ExpensesInterface;
|
||||
use RprtCli\ValueObjects\WorkInvoiceElement;
|
||||
use RprtCli\ValueObjects\WorkInvoiceElementInterface;
|
||||
|
||||
use function array_key_first;
|
||||
use function array_keys;
|
||||
use function fgetcsv;
|
||||
use function fopen;
|
||||
use function preg_match;
|
||||
use function reset;
|
||||
|
||||
/**
|
||||
* Creates a report of projects and hours.
|
||||
*
|
||||
* Uses value objects instead of arrays.
|
||||
*/
|
||||
class ReportCsv implements ReportCsvInterface
|
||||
{
|
||||
/**
|
||||
* A configuration service.
|
||||
*
|
||||
* @var ConfigurationInterface
|
||||
*/
|
||||
protected $configurationService;
|
||||
|
||||
public function __construct(ConfigurationInterface $config)
|
||||
{
|
||||
$this->configurationService = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getReportData(string $filePath) : array
|
||||
{
|
||||
$output = [];
|
||||
// @TODO replace with config service.
|
||||
// $config = $this->dummyConfig()['projects'];
|
||||
$config = $this->configurationService->get('projects');
|
||||
foreach (array_keys($config) as $key) {
|
||||
$output[$key] = 0;
|
||||
}
|
||||
if ($file = fopen($filePath, 'r')) {
|
||||
while (($line = fgetcsv($file)) !== false) {
|
||||
$parsed = $this->parseCsvFile($line);
|
||||
// $key = reset(array_keys($parsed));
|
||||
$key = array_key_first($parsed);
|
||||
if (isset($output[$key])) {
|
||||
$output[$key] += (float) reset($parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
$report_data = [];
|
||||
foreach ($output as $project => $hours) {
|
||||
$report_data[] = new WorkInvoiceElement($project, $hours);
|
||||
}
|
||||
return $report_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get correct values from the raw data lines of csv.
|
||||
*
|
||||
* @param array $rawData
|
||||
* Columns with data are specified in config.
|
||||
*
|
||||
* @return array
|
||||
* Project key and unit of time spent.
|
||||
*/
|
||||
protected function parseCsvFile(array $rawData) : array
|
||||
{
|
||||
$config = $this->configurationService->get('projects');
|
||||
foreach ($config as $key => $project) {
|
||||
if (preg_match('/' . $project['pattern'] . '/', $rawData[1])) {
|
||||
return [$key => $rawData[4]];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Input is array of Work elements and expenses.
|
||||
*/
|
||||
public function generateTable(array $data): array {
|
||||
[$rows, $totalHours, $totalPrice, $add_separator] = [[], 0, 0, FALSE];
|
||||
$projectsConfig = $this->configurationService->get('projects');
|
||||
// $header = $this->configurationService->get('export.labels', null);
|
||||
$header = null;
|
||||
if (is_array($header)) {
|
||||
$rows[] = $header;
|
||||
}
|
||||
// First only list work invoice elements.
|
||||
foreach ($data as $key => $invoice_element) {
|
||||
if ($invoice_element instanceof WorkInvoiceElementInterface) {
|
||||
$add_separator = TRUE;
|
||||
$project = $invoice_element->getName();
|
||||
$time = $invoice_element->getTime();
|
||||
$config = $projectsConfig[$project];
|
||||
$hours = $config['time_format'] == 'm' ? $time/60 : $time;
|
||||
$price = $hours * (float) $config['price'];
|
||||
$row = [
|
||||
$config['label'] ?? $project,
|
||||
number_format($hours, 2, ',', '.'),
|
||||
number_format($config['price'], 2, ',', '.'),
|
||||
number_format($price, 2, ',', '.'),
|
||||
];
|
||||
$totalHours += $hours;
|
||||
$totalPrice += $price;
|
||||
$rows[] = $row;
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
if ($add_separator) {
|
||||
$rows[] = null;
|
||||
$rows[] = ['Gesamt netto', number_format($totalHours, 2, ',', '.'), ' ', number_format($totalPrice, 2, ',', '.')];
|
||||
$add_separator = FALSE;
|
||||
}
|
||||
foreach ($data as $invoice_element) {
|
||||
if ($invoice_element instanceof ExpensesInterface) {
|
||||
if (!isset($added_expenses)) {
|
||||
// Make next line bold and centered.
|
||||
$rows[] = 0;
|
||||
$rows[] = [
|
||||
null,
|
||||
null,
|
||||
'Kosten',
|
||||
'EUR',
|
||||
];
|
||||
// Don't make next line bold. See RprtCli\PdfExport\PdfExportService::parsedDataToHtml.
|
||||
$rows[] = FALSE;
|
||||
$added_expenses = TRUE;
|
||||
}
|
||||
$add_separator = TRUE;
|
||||
$rows[] = [
|
||||
null,
|
||||
null,
|
||||
$invoice_element->getName(),
|
||||
number_format($invoice_element->getValue(), 2, ',', '.'),
|
||||
];
|
||||
$totalPrice += $invoice_element->getValue();
|
||||
}
|
||||
}
|
||||
if ($add_separator) {
|
||||
$rows[] = null;
|
||||
}
|
||||
$rows[] = [null, null, 'Gessamt brutto', number_format($totalPrice, 2, ',', '.')];
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function arangeDataForDefaultPdfExport(array $data) :array {
|
||||
$rows = $this->generateTable($data);
|
||||
$header = $this->configurationService->get('export.labels', null);
|
||||
array_unshift($rows, $header);
|
||||
foreach ($rows as $key => $row) {
|
||||
if (!$row) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\Utils\CsvReport;
|
||||
|
||||
/**
|
||||
* Handles creating report data from csv file downloaded from youtrack service.
|
||||
*/
|
||||
interface ReportCsvInterface
|
||||
{
|
||||
/**
|
||||
* Returns array of hours per configured projects.
|
||||
*
|
||||
* @todo - get data from variable.
|
||||
*
|
||||
*
|
||||
* Project key as key and number of hours as value.
|
||||
*/
|
||||
public function getReportData(string $filePath): array;
|
||||
|
||||
/**
|
||||
* Returns array of rows created from array of InvoiceElements.
|
||||
*
|
||||
* If row is null, it is meant to be a table separator.
|
||||
*/
|
||||
public function generateTable(array $data) :array;
|
||||
|
||||
/**
|
||||
* Data for default drunomics pdf export.
|
||||
*
|
||||
* @param array $data
|
||||
* Parsed data from csv report.
|
||||
*/
|
||||
public function arangeDataForDefaultPdfExport(array $data): array;
|
||||
}
|
|
@ -47,14 +47,26 @@ class PdfExportService implements PdfExportInterface {
|
|||
// @TODO move this method to CsvReport.
|
||||
// Add classes to table elements.
|
||||
// @TODO would it make sense to allow more per user extending?
|
||||
// @TODO - too much assumptions on data structure. Create a class with
|
||||
// precise data structure and use that to pass the data around.
|
||||
public function parsedDataToHtmlTable(array $data): ?string {
|
||||
$table = '<table><thead><tr class="tr-header">';
|
||||
$header = array_shift($data);
|
||||
$classes = '';
|
||||
foreach ($header as $index => $cell) {
|
||||
$table .= "<th class=\"th-{$index}\">{$cell}</th>";
|
||||
}
|
||||
$table .= '</tr></thead><tbody>';
|
||||
foreach ($data as $row_index => $row) {
|
||||
if (!$row) {
|
||||
if ($row === NULL) {
|
||||
$classes = 'bold';
|
||||
}
|
||||
elseif ($row === 0) {
|
||||
$classes = 'bold center';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
list($cells, $colspan) = [[], 0];
|
||||
foreach ($row as $index => $cell) {
|
||||
if (!$cell) {
|
||||
|
@ -70,10 +82,12 @@ class PdfExportService implements PdfExportInterface {
|
|||
$cells[] = "<td class=\"td-{$index}\">{$cell}</td>";
|
||||
}
|
||||
}
|
||||
$rows[] = "<tr class=\"tr-{$row_index}\">" . implode($cells) . '</tr>';
|
||||
$rows[] = "<tr class=\"tr-{$row_index} {$classes}\">" . implode($cells) . '</tr>';
|
||||
$classes = '';
|
||||
}
|
||||
$table .= implode('', $rows);
|
||||
$table .= '</tbody></table>';
|
||||
var_dump($table);
|
||||
return $table;
|
||||
}
|
||||
|
||||
|
@ -178,8 +192,8 @@ class PdfExportService implements PdfExportInterface {
|
|||
$tokens['today'] = date('j. m. y');
|
||||
$month_ago = strtotime('1 month ago');
|
||||
$tokens['number'] = date('m/y', $month_ago);
|
||||
$tokens['date_start'] = date('1. m. y', $month_ago);
|
||||
$tokens['date_end'] = date("Y-m-d", mktime(0, 0, 0, (int) date("m"), 0));
|
||||
$tokens['date_start'] = date('1. m. Y', $month_ago);
|
||||
$tokens['date_end'] = date("d. m. Y", mktime(0, 0, 0, (int) date("m"), 0));
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\ValueObjects;
|
||||
|
||||
class Expenses implements ExpensesInterface {
|
||||
|
||||
/**
|
||||
* Expenses in current currency.
|
||||
*/
|
||||
private float $value;
|
||||
|
||||
/**
|
||||
* Name of the expenses;
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
public function __construct(string $name, float $value) {
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getValue(): float
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\ValueObjects;
|
||||
|
||||
interface ExpensesInterface extends InvoiceElementInterface {
|
||||
|
||||
public function getValue() :float;
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\ValueObjects;
|
||||
|
||||
/**
|
||||
* Main interface for invoice elements.
|
||||
*/
|
||||
interface InvoiceElementInterface {
|
||||
|
||||
/**
|
||||
* Project or expenses name.
|
||||
*/
|
||||
public function getName() :string;
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\ValueObjects;
|
||||
|
||||
class WorkInvoiceElement implements WorkInvoiceElementInterface {
|
||||
|
||||
private float $time;
|
||||
|
||||
/**
|
||||
* Project name.
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
public function __construct(string $name, float $time) {
|
||||
$this->name = $name;
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
public function getTime() :float {
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
public function getName() :string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace RprtCli\ValueObjects;
|
||||
|
||||
interface WorkInvoiceElementInterface extends InvoiceElementInterface {
|
||||
|
||||
public function getTime() :float ;
|
||||
|
||||
/**
|
||||
* Get project name.
|
||||
*/
|
||||
public function getName() :string ;
|
||||
}
|
Loading…
Reference in New Issue