This commit is contained in:
Alexey Skobkin 2020-01-31 04:04:13 +03:00
commit b49d840391
No known key found for this signature in database
GPG key ID: 5D5CEF6F221278E7
15 changed files with 1328 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
vendor/
config/parameters.yaml
.idea/

28
composer.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "skobkin/bilingual-invoice-generator",
"type": "project",
"license": "MIT",
"authors": [
{
"name": "Alexey Skobkin",
"email": "skobkin-ru@ya.ru"
}
],
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"require": {
"php": ">=7.4",
"asika/simple-console": "^1.0",
"kwn/number-to-words": "^1.9",
"mpdf/mpdf": "^8.0",
"symfony/yaml": "^5.0",
"twig/html-extra": "^3.0",
"twig/twig": "^3.0"
},
"config": {
"sort-packages": true
}
}

842
composer.lock generated Normal file
View file

@ -0,0 +1,842 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c214995f6d2d08b81315006f8b75ff8f",
"packages": [
{
"name": "asika/simple-console",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/asika32764/php-simple-console.git",
"reference": "0b624c1a999849dc6481a47182e58d593bf65068"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/asika32764/php-simple-console/zipball/0b624c1a999849dc6481a47182e58d593bf65068",
"reference": "0b624c1a999849dc6481a47182e58d593bf65068",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"Asika\\SimpleConsole\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Simon Asika",
"email": "asika32764@gmail.com"
}
],
"description": "One file console framework to help you write build scripts.",
"time": "2018-03-08T12:05:40+00:00"
},
{
"name": "kwn/number-to-words",
"version": "1.9.2",
"source": {
"type": "git",
"url": "https://github.com/kwn/number-to-words.git",
"reference": "1cbfbeb3fb2083c2af9aa47de20d9357b12eccb3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kwn/number-to-words/zipball/1cbfbeb3fb2083c2af9aa47de20d9357b12eccb3",
"reference": "1cbfbeb3fb2083c2af9aa47de20d9357b12eccb3",
"shasum": ""
},
"require": {
"php": ">=5.6|>=7.1"
},
"require-dev": {
"phpunit/phpunit": "^5.7.9",
"squizlabs/php_codesniffer": "^3.3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"NumberToWords\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Karol Wnuk",
"email": "k.wnuk@ascetic.pl"
}
],
"description": "Multi language standalone PHP number to words converter. Fully tested, open for extensions and new languages.",
"keywords": [
"currency",
"money",
"number",
"numbers",
"string",
"to",
"words"
],
"time": "2020-01-16T15:22:16+00:00"
},
{
"name": "mpdf/mpdf",
"version": "v8.0.4",
"source": {
"type": "git",
"url": "https://github.com/mpdf/mpdf.git",
"reference": "d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mpdf/mpdf/zipball/d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f",
"reference": "d3147a0d790b6d11936fd9c73fa31a7ed45e3f6f",
"shasum": ""
},
"require": {
"ext-gd": "*",
"ext-mbstring": "*",
"myclabs/deep-copy": "^1.7",
"paragonie/random_compat": "^1.4|^2.0|9.99.99",
"php": "^5.6 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0",
"psr/log": "^1.0",
"setasign/fpdi": "^2.1"
},
"require-dev": {
"mockery/mockery": "^0.9.5",
"mpdf/qrcode": "^1.0.0",
"phpunit/phpunit": "^5.0",
"squizlabs/php_codesniffer": "^3.5.0",
"tracy/tracy": "^2.4"
},
"suggest": {
"ext-bcmath": "Needed for generation of some types of barcodes",
"ext-xml": "Needed mainly for SVG manipulation",
"ext-zlib": "Needed for compression of embedded resources, such as fonts"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-development": "7.x-dev"
}
},
"autoload": {
"psr-4": {
"Mpdf\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-only"
],
"authors": [
{
"name": "Matěj Humpál",
"role": "Developer, maintainer"
},
{
"name": "Ian Back",
"role": "Developer (retired)"
}
],
"description": "PHP library generating PDF files from UTF-8 encoded HTML",
"homepage": "https://mpdf.github.io",
"keywords": [
"pdf",
"php",
"utf-8"
],
"time": "2019-11-28T09:39:33+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.9.5",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
"reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"replace": {
"myclabs/deep-copy": "self.version"
},
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
"phpunit/phpunit": "^7.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
},
"files": [
"src/DeepCopy/deep_copy.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Create deep copies (clones) of your objects",
"keywords": [
"clone",
"copy",
"duplicate",
"object",
"object graph"
],
"time": "2020-01-17T21:11:47+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.99",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"shasum": ""
},
"require": {
"php": "^7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"time": "2018-07-02T15:55:56+00:00"
},
{
"name": "psr/log",
"version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2019-11-01T11:05:21+00:00"
},
{
"name": "setasign/fpdi",
"version": "v2.2.0",
"source": {
"type": "git",
"url": "https://github.com/Setasign/FPDI.git",
"reference": "3c266002f8044f61b17329f7cd702d44d73f0f7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Setasign/FPDI/zipball/3c266002f8044f61b17329f7cd702d44d73f0f7f",
"reference": "3c266002f8044f61b17329f7cd702d44d73f0f7f",
"shasum": ""
},
"require": {
"ext-zlib": "*",
"php": "^5.6 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7",
"setasign/fpdf": "~1.8",
"setasign/tfpdf": "1.25",
"tecnickcom/tcpdf": "~6.2"
},
"suggest": {
"setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured.",
"setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.",
"setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF.",
"setasign/fpdi-tfpdf": "Use this package to automatically evaluate dependencies to tFPDF."
},
"type": "library",
"autoload": {
"psr-4": {
"setasign\\Fpdi\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Slabon",
"email": "jan.slabon@setasign.com",
"homepage": "https://www.setasign.com"
},
{
"name": "Maximilian Kresse",
"email": "maximilian.kresse@setasign.com",
"homepage": "https://www.setasign.com"
}
],
"description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.",
"homepage": "https://www.setasign.com/fpdi",
"keywords": [
"fpdf",
"fpdi",
"pdf"
],
"time": "2019-01-30T14:11:19+00:00"
},
{
"name": "symfony/mime",
"version": "v5.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/2a3c7fee1f1a0961fa9cf360d5da553d05095e59",
"reference": "2a3c7fee1f1a0961fa9cf360d5da553d05095e59",
"shasum": ""
},
"require": {
"php": "^7.2.5",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "^1.0"
},
"conflict": {
"symfony/mailer": "<4.4"
},
"require-dev": {
"egulias/email-validator": "^2.1.10",
"symfony/dependency-injection": "^4.4|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Mime\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A library to manipulate MIME messages",
"homepage": "https://symfony.com",
"keywords": [
"mime",
"mime-type"
],
"time": "2020-01-04T14:08:26+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6f9c239e61e1b0c9229a28ff89a812dc449c3d46",
"reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php72": "^1.9"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2019-11-27T14:18:11+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "66fea50f6cb37a35eea048d75a7d99a45b586038"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038",
"reference": "66fea50f6cb37a35eea048d75a7d99a45b586038",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/yaml",
"version": "v5.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "69b44e3b8f90949aee2eb3aa9b86ceeb01cbf62a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/69b44e3b8f90949aee2eb3aa9b86ceeb01cbf62a",
"reference": "69b44e3b8f90949aee2eb3aa9b86ceeb01cbf62a",
"shasum": ""
},
"require": {
"php": "^7.2.5",
"symfony/polyfill-ctype": "~1.8"
},
"conflict": {
"symfony/console": "<4.4"
},
"require-dev": {
"symfony/console": "^4.4|^5.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2020-01-21T11:12:28+00:00"
},
{
"name": "twig/html-extra",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/html-extra.git",
"reference": "4ddd2b6500d074d71930a46a6ba315f31515b6f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/html-extra/zipball/4ddd2b6500d074d71930a46a6ba315f31515b6f3",
"reference": "4ddd2b6500d074d71930a46a6ba315f31515b6f3",
"shasum": ""
},
"require": {
"php": "^7.1.3",
"symfony/mime": "^4.3|^5.0",
"twig/twig": "^2.4|^3.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Twig\\Extra\\Html\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
}
],
"description": "A Twig extension for HTML",
"homepage": "https://twig.symfony.com",
"keywords": [
"html",
"twig"
],
"time": "2019-12-28T07:09:27+00:00"
},
{
"name": "twig/twig",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "28f856a4c57eeb24485916e8a68403f41a133616"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/28f856a4c57eeb24485916e8a68403f41a133616",
"reference": "28f856a4c57eeb24485916e8a68403f41a133616",
"shasum": ""
},
"require": {
"php": "^7.2.5",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Twig\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "https://twig.symfony.com",
"keywords": [
"templating"
],
"time": "2019-12-28T07:17:28+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.4"
},
"platform-dev": []
}

View file

@ -0,0 +1,70 @@
configuration:
date_format:
source: ''
target: ''
locales:
source: ru
target: en
images:
signature: ''
stamp: ''
services:
-
name:
source: 'Работа'
target: 'Work'
amount: 1000
units:
source: 'часа'
target: 'hour'
price: 1000
translations:
source:
variables:
title: 'Счёт на оплату №%invoice_number% от %invoice_date%'
currency: '%currency%'
rows:
supplier: 'Поставщик'
buyer: 'Покупатель'
# service table
th_number: '№'
th_name: 'Наименование работ, услуг'
th_amount: 'Кол-во'
th_units: 'Ед.'
th_price: 'Цена'
th_sum: 'Сумма'
tf_total: 'Итого'
# bank data
account_data: 'Банковские реквизиты поставщика'
account_number: 'Номер счёта'
bank: 'Банк'
bank_address: 'Адрес'
swift: 'SWIFT'
corr_bank: 'Банк-посредник'
total_to_pay: 'Всего к оплате'
signature: 'Подпись'
supplier:
title: '123456'
short_title: '123'
address: 'some address'
extra: []
bank:
account: '9999999999999999999'
name: 'Some Bank of some Country'
address: 'Some address'
swift: 'SOMESWIFT'
corr_bank:
name: 'SOME CORR BANK'
swift: 'SOMESWIFT2'
buyer:
title: '"Some Client" LLC'
address: 'Some Address'
extra:
- 'VAT: EU12313123'
- 'IBAN: EU123131231231232112'
static_substitutions:
'%contract_date%': '01.01.1970'
'%currency%': 'EUR'
#target:

9
generate Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env php
<?php
include_once __DIR__.'/vendor/autoload.php';
use App\Command\InvoiceGeneratorCommand;
$generator = new InvoiceGeneratorCommand();
$generator->execute();

2
images/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

19
show.php Normal file
View file

@ -0,0 +1,19 @@
<?php
require_once __DIR__.'/vendor/autoload.php';
use App\Generator\InvoiceGenerator;
use Mpdf\Mpdf;
use Symfony\Component\Yaml\Yaml;
$config = Yaml::parseFile(__DIR__.'/config/parameters.yaml');
$pdf = $_GET['pdf'] ?? false;
if ($pdf) {
$mpdf = new Mpdf();
$mpdf->WriteHTML(InvoiceGenerator::generate($config));
$mpdf->Output();
} else {
echo InvoiceGenerator::generate($config);
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Command;
use Asika\SimpleConsole\Console;
class InvoiceGeneratorCommand extends Console
{
protected function doExecute(): int
{
return 0;
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace App\Generator;
use App\Kernel;
use App\Twig\NumberToWordsExtension;
use App\Util\StringReplacer;
use Twig\Environment as Twig;
use Twig\Extra\Html\HtmlExtension;
use Twig\Loader\FilesystemLoader;
class InvoiceGenerator
{
public static function generate(array $config): string
{
$twig = static::createTwig();
$sourceStaticSubstitutions = $config['translations']['source']['static_substitutions'];
//$sourceStaticSubstitutions = $config['translations']['target']['static_substitutions'];
$source = StringReplacer::recursiveReplace(
$config['translations']['source']['variables'],
$sourceStaticSubstitutions
);
//$target = StringReplacer::recursiveReplace($config['translations']['target']['variables']);
// @TODO fix multilingual substitution depending on the context
$services = StringReplacer::recursiveReplace($config['services'], $sourceStaticSubstitutions);
$images = static::getImagesContent($config['configuration']['images']);
return $twig->render('invoice.html.twig', [
'configuration' => $config['configuration'],
'trans_data' => [
'source' => $source,
//'target' => $target,
],
'services' => $services,
'images' => $images,
]);
}
private static function getImagesContent(array $paths): array
{
$new = [];
foreach ($paths as $key => $path) {
$new[$key] = file_get_contents(Kernel::getProjectRoot().'/'.$path);
}
return $new;
}
private static function createTwig(): Twig
{
$loader = new FilesystemLoader(__DIR__.'/../../templates');
$twig = new Twig($loader);
$twig->addExtension(new NumberToWordsExtension());
$twig->addExtension(new HtmlExtension());
return $twig;
}
}

11
src/Kernel.php Normal file
View file

@ -0,0 +1,11 @@
<?php
namespace App;
class Kernel
{
public static function getProjectRoot(): string
{
return dirname(__DIR__);
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Twig;
use NumberToWords\NumberToWords;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class NumberToWordsExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('ntw', [$this, 'numberToWords'])
];
}
public function numberToWords(int $number, string $locale): string
{
$ntw = new NumberToWords();
return $ntw->getNumberTransformer($locale)->toWords($number);
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Util;
class StringReplacer
{
/**
* @param array $variables
* @param string[]|array $staticReplaces
* @param callable[]|array $dynamicReplaces
*
* @return array
*/
public static function recursiveReplace(
array $variables,
array $staticReplaces = [],
array $dynamicReplaces = []
): array {
foreach ($variables as $key => &$value) {
if (is_string($value)) {
$value = static::replaceString($value, $staticReplaces);
} elseif (is_int($value)) {
continue;
} elseif (is_array($value)) {
$value = static::recursiveReplace($value, $staticReplaces);
} else {
throw new \InvalidArgumentException(sprintf(
'Invalid value. string/array allowed, %s (%s) given.',
gettype($value),
print_r($value, true)
));
}
}
return $variables;
}
private static function replaceString(
string $string,
array $staticReplaces
): string {
// Process static replaces
$string = str_replace(
array_keys($staticReplaces),
array_values($staticReplaces),
$string
);
// TBI
// ...
return $string;
}
}

27
templates/invoice.css Normal file
View file

@ -0,0 +1,27 @@
table.fat
{
width: 100%;
}
.title {
text-align: center;
}
table.bordered,
tr.bordered td,
tr.bordered th
{
border: 1px solid;
border-collapse: collapse;
}
table.bordered-fully td,
table.bordered-fully th
{
border: 1px solid;
border-collapse: collapse;
}
table#invoice td {
vertical-align: top;
}

View file

@ -0,0 +1,40 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Invoice</title>
{#<meta name="description" content="">#}
</head>
<style>
{{ include ('invoice.css') }}
</style>
<body>
<table width="1400px" id="invoice">
<tr>
<td width="50%">
{{ include ('invoice_side.html.twig', {
t: trans_data.source,
services: services,
images: images,
context: 'source',
locale: configuration.locales.source
}, with_context = false) }}
</td>
<td width="50%">
{{ include ('invoice_side.html.twig', {
t: trans_data.source,
services: services,
images: images,
context: 'target',
locale: configuration.locales.target
}, with_context = false) }}
</td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,125 @@
<h2 class="title">{{ t.title }}</h2>
<!-- Party attributes -->
<table class="bordered fat">
<tr>
<td colspan="3">&nbsp;</td>
</tr>
<tr>
<td rowspan="2">{{ t.rows.supplier }}</td>
<td colspan="2">{{ t.supplier.title }}</td>
</tr>
<tr>
<td colspan="2">{{ t.supplier.address }}</td>
</tr>
<tr>
<td rowspan="{{ 2 + t.buyer.extra|length }}">{{ t.rows.buyer }}</td>
<td colspan="2">{{ t.buyer.title }}</td>
</tr>
<tr>
<td colspan="2">{{ t.buyer.address }}</td>
</tr>
{% for item in t.buyer.extra %}
<tr>
<td colspan="2">{{ item }}</td>
</tr>
{% endfor %}
</table>
<!-- Services table -->
<table class="bordered bordered-fully fat">
<thead>
<tr>
<th>{{ t.rows.th_number }}</th>
<th>{{ t.rows.th_name }}</th>
<th>{{ t.rows.th_amount }}</th>
<th>{{ t.rows.th_units }}</th>
<th>{{ t.rows.th_price }}</th>
<th>{{ t.rows.th_sum }}</th>
</tr>
</thead>
<tbody>
{% set total = 0 %}
{% for service in services %}
<tr>
<td>{{ loop.index }}</td>
<td width="60%">{{ service.name[context] }}</td>
<td>{{ service.amount }}</td>
<td>{{ service.units[context] }}</td>
<td>{{ service.amount }} {{ t.currency }}</td>
{% set sum = service.amount * service.price %}
<td>{{ sum }} {{ t.currency }}</td>
</tr>
{% set total = total + sum %}
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="5" align="right">{{ t.rows.tf_total }}</td>
<td>{{ total }} {{ t.currency }}</td>
</tr>
</tfoot>
</table>
<table class="fat">
<tr>
<td colspan="3">
{{ t.rows.total_to_pay }}: {{ total|ntw(locale) }}
</td>
</tr>
</table>
<!-- Bank account data -->
<table id="bank-details" class="fat">
<tr class="bordered">
<td colspan="3"><strong>{{ t.rows.account_data }}:</strong></td>
</tr>
<tr>
<td>{{ t.rows.account_number }}</td>
<td colspan="2">{{ t.supplier.bank.account }}</td>
</tr>
<tr>
<td colspan="3">{{ t.rows.bank }}</td>
</tr>
<tr>
<td colspan="3">{{ t.supplier.bank.name }}</td>
</tr>
<tr>
<td>{{ t.rows.bank_address }}</td>
<td colspan="2">{{ t.supplier.bank.address }}</td>
</tr>
<tr>
<td>{{ t.rows.swift }}</td>
<td colspan="2">{{ t.supplier.bank.swift }}</td>
</tr>
<tr>
<td><strong>{{ t.rows.corr_bank }}</strong></td>
<td colspan="2">{{ t.supplier.bank.corr_bank.name }}</td>
</tr>
<tr>
<td>{{ t.rows.swift }}</td>
<td colspan="2">{{ t.supplier.bank.corr_bank.swift }}</td>
</tr>
<tr>
<td colspan="3">&nbsp;</td>
</tr>
<tr>
<td colspan="3">{{ t.supplier.short_title }}</td>
</tr>
<tr>
<td colspan="3">&nbsp;</td>
</tr>
<tr>
<td height="50px">{{ t.rows.signature }}</td>
<td colspan="2" valign="bottom">
<img src="{{ images.signature|data_uri(mime='image/png') }}" alt="Signature">
</td>
</tr>
{% if images.stamp is defined %}
<tr>
<td colspan="3">
<img src="{{ images.stamp|data_uri(mime='image/png') }}" alt="Stamp">
</td>
</tr>
{% endif %}
</table>