From 246840921232dec3d18c3fe3e994f35dc230abfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Mon, 12 Aug 2019 23:45:21 +0200 Subject: [PATCH] Added an admin page for currencies. --- composer.json | 2 + composer.lock | 116 +++++++++++++++++- config/bundles.php | 1 + config/packages/twig.yaml | 1 + .../AdminPages/CurrencyController.php | 112 +++++++++++++++++ src/Entity/Parts/MeasurementUnit.php | 11 ++ src/Entity/PriceInformations/Currency.php | 17 ++- src/Form/AdminPages/BaseEntityAdminForm.php | 8 +- src/Form/AdminPages/CurrencyAdminForm.php | 57 +++++++++ src/Migrations/Version20190812154222.php | 2 +- src/Security/Voter/StructureVoter.php | 6 +- src/Services/EntityURLGenerator.php | 15 ++- src/Services/ToolsTreeBuilder.php | 3 + symfony.lock | 6 + templates/AdminPages/CurrencyAdmin.html.twig | 26 ++++ 15 files changed, 374 insertions(+), 9 deletions(-) create mode 100644 src/Controller/AdminPages/CurrencyController.php create mode 100644 src/Form/AdminPages/CurrencyAdminForm.php create mode 100644 templates/AdminPages/CurrencyAdmin.html.twig diff --git a/composer.json b/composer.json index 75c2d78d..7faac738 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,8 @@ "symfony/webpack-encore-bundle": "^1.1", "symfony/yaml": "4.3.*", "twig/extensions": "^1.5", + "twig/extra-bundle": "3.x-dev", + "twig/intl-extra": "3.x-dev", "webmozart/assert": "^1.4" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 7f9348d5..bf944ff3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c4b42f65037fd3b81886b8d9c98deb32", + "content-hash": "4efee728068f5b14ad389d7a4a75131b", "packages": [ { "name": "doctrine/annotations", @@ -6371,6 +6371,118 @@ ], "time": "2018-12-05T18:34:18+00:00" }, + { + "name": "twig/extra-bundle", + "version": "3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/twigphp/twig-extra-bundle.git", + "reference": "695a3d1749265340642681fd48e30795752f7126" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/695a3d1749265340642681fd48e30795752f7126", + "reference": "695a3d1749265340642681fd48e30795752f7126", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/framework-bundle": "^4.3|^5.0", + "symfony/twig-bundle": "^4.3|^5.0", + "twig/twig": "^2.4|^3.0" + }, + "require-dev": { + "twig/cssinliner-extra": "^2.12|^3.0@dev", + "twig/html-extra": "^2.12@dev|^3.0@dev", + "twig/inky-extra": "^2.12@dev|^3.0@dev", + "twig/intl-extra": "^2.12@dev|^3.0@dev", + "twig/markdown-extra": "^2.12@dev|^3.0@dev" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\Extra\\TwigExtraBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "role": "Lead Developer", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org" + } + ], + "description": "A Symfony bundle for extra Twig extensions", + "homepage": "https://twig.symfony.com", + "keywords": [ + "bundle", + "extra", + "twig" + ], + "time": "2019-08-12T07:27:28+00:00" + }, + { + "name": "twig/intl-extra", + "version": "3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "6330d1cf99976605aa06c709fc382868b038ae9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/6330d1cf99976605aa06c709fc382868b038ae9a", + "reference": "6330d1cf99976605aa06c709fc382868b038ae9a", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/intl": "^4.3", + "twig/twig": "^2.4|^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\Extra\\Intl\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "role": "Lead Developer", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org" + } + ], + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", + "keywords": [ + "intl", + "twig" + ], + "time": "2019-08-12T07:27:28+00:00" + }, { "name": "twig/twig", "version": "v2.11.3", @@ -7526,6 +7638,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "twig/extra-bundle": 20, + "twig/intl-extra": 20, "roave/security-advisories": 20 }, "prefer-stable": false, diff --git a/config/bundles.php b/config/bundles.php index 1b1310cc..1cb665bc 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -19,4 +19,5 @@ return [ Shivas\VersioningBundle\ShivasVersioningBundle::class => ['all' => true], FOS\CKEditorBundle\FOSCKEditorBundle::class => ['all' => true], Translation\Bundle\TranslationBundle::class => ['all' => true], + Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], ]; diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index a0bb844b..c4a7d885 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -6,3 +6,4 @@ twig: globals: partdb_title: '%partdb_title%' + default_currency: '%default_currency%' diff --git a/src/Controller/AdminPages/CurrencyController.php b/src/Controller/AdminPages/CurrencyController.php new file mode 100644 index 00000000..8702c147 --- /dev/null +++ b/src/Controller/AdminPages/CurrencyController.php @@ -0,0 +1,112 @@ +_edit($entity, $request, $em); + } + + /** + * @Route("/new", name="currency_new") + * @Route("/") + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer) + { + return $this->_new($request, $em, $importer); + } + + /** + * @Route("/{id}", name="currency_delete", methods={"DELETE"}) + */ + public function delete(Request $request, Currency $entity, StructuralElementRecursionHelper $recursionHelper) + { + return $this->_delete($request, $entity, $recursionHelper); + } + + /** + * @Route("/export", name="currency_export_all") + * @param Request $request + * @param SerializerInterface $serializer + * @param EntityManagerInterface $em + * @return Response + */ + public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request) + { + return $this->_exportAll($em, $exporter, $request); + } + + /** + * @Route("/{id}/export", name="currency_export") + * @param Request $request + * @param AttachmentType $entity + */ + public function exportEntity(Currency $entity, EntityExporter $exporter, Request $request) + { + return $this->_exportEntity($entity, $exporter, $request); + } +} \ No newline at end of file diff --git a/src/Entity/Parts/MeasurementUnit.php b/src/Entity/Parts/MeasurementUnit.php index 9b681d88..2a75fa5b 100644 --- a/src/Entity/Parts/MeasurementUnit.php +++ b/src/Entity/Parts/MeasurementUnit.php @@ -69,6 +69,17 @@ class MeasurementUnit extends StructuralDBElement */ protected $usesSIPrefixes; + /** + * @ORM\OneToMany(targetEntity="MeasurementUnit", mappedBy="parent", cascade={"persist"}) + */ + protected $children; + + /** + * @ORM\ManyToOne(targetEntity="MeasurementUnit", inversedBy="children") + * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") + */ + protected $parent; + /** * Returns the ID as an string, defined by the element class. * This should have a form like P000014, for a part with ID 14. diff --git a/src/Entity/PriceInformations/Currency.php b/src/Entity/PriceInformations/Currency.php index 95c0282f..931e3a43 100644 --- a/src/Entity/PriceInformations/Currency.php +++ b/src/Entity/PriceInformations/Currency.php @@ -49,7 +49,7 @@ class Currency extends StructuralDBElement /** * @var string The 3 letter ISO code of the currency. - * @ORM\Column(type="string", unique=true, nullable=true) + * @ORM\Column(type="string", unique=true) * @Assert\Currency() */ protected $iso_code; @@ -57,16 +57,27 @@ class Currency extends StructuralDBElement /** * @var float|null The exchange rate between this currency and the base currency * (how many base units the current currency is worth) - * @ORM\Column(type="decimal", precision=11, scale=5) + * @ORM\Column(type="decimal", precision=11, scale=5, nullable=true) * @Assert\Positive() */ protected $exchange_rate; + /** + * @ORM\OneToMany(targetEntity="Currency", mappedBy="parent", cascade={"persist"}) + */ + protected $children; + + /** + * @ORM\ManyToOne(targetEntity="Currency", inversedBy="children") + * @ORM\JoinColumn(name="parent_id", referencedColumnName="id") + */ + protected $parent; + /** * Returns the 3 letter ISO code of this currency * @return string */ - public function getIsoCode(): string + public function getIsoCode(): ?string { return $this->iso_code; } diff --git a/src/Form/AdminPages/BaseEntityAdminForm.php b/src/Form/AdminPages/BaseEntityAdminForm.php index e9df8e96..005c6a0c 100644 --- a/src/Form/AdminPages/BaseEntityAdminForm.php +++ b/src/Form/AdminPages/BaseEntityAdminForm.php @@ -36,6 +36,8 @@ use App\Entity\Base\NamedDBElement; use App\Entity\Base\StructuralDBElement; use FOS\CKEditorBundle\Form\Type\CKEditorType; use Symfony\Bridge\Doctrine\Form\Type\EntityType; +use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ResetType; @@ -48,10 +50,12 @@ class BaseEntityAdminForm extends AbstractType { protected $security; + protected $params; - public function __construct(Security $security) + public function __construct(Security $security, ParameterBagInterface $params) { $this->security = $security; + $this->params = $params; } @@ -74,7 +78,7 @@ class BaseEntityAdminForm extends AbstractType 'label' => 'not_selectable.label', 'help' => 'not_selectable.help', 'label_attr'=> ['class' => 'checkbox-custom'], 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity) ]) - ->add('comment', CKEditorType::class, ['required' => false, + ->add('comment', CKEditorType::class, ['required' => false, 'empty_data' => '', 'label' => 'comment.label', 'attr' => ['rows' => 4], 'help' => 'bbcode.hint', 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity)]); diff --git a/src/Form/AdminPages/CurrencyAdminForm.php b/src/Form/AdminPages/CurrencyAdminForm.php new file mode 100644 index 00000000..75323b2f --- /dev/null +++ b/src/Form/AdminPages/CurrencyAdminForm.php @@ -0,0 +1,57 @@ +getID() === null; + + $builder->add('iso_code', CurrencyType::class , ['required' => true, + 'label' => 'currency.iso_code.label', + 'preferred_choices' => ['EUR', 'USD', 'GBP', 'JPY', 'CNY'], + 'attr' => ['class' => 'selectpicker', 'data-live-search' => true], + 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity)]); + + $builder->add('exchange_rate', MoneyType::class, ['required' => false, + 'label' => 'currency.exchange_rate.label', 'currency' => $this->params->get('default_currency'), + 'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity)]); + } +} \ No newline at end of file diff --git a/src/Migrations/Version20190812154222.php b/src/Migrations/Version20190812154222.php index 64a11126..dea338c0 100644 --- a/src/Migrations/Version20190812154222.php +++ b/src/Migrations/Version20190812154222.php @@ -24,7 +24,7 @@ final class Version20190812154222 extends AbstractMigration $this->addSql('CREATE TABLE `measurement_units` (id INT AUTO_INCREMENT NOT NULL, unit VARCHAR(255) NOT NULL, is_integer TINYINT(1) NOT NULL, use_si_prefix TINYINT(1) NOT NULL, comment LONGTEXT NOT NULL, parent_id INT DEFAULT NULL, not_selectable TINYINT(1) NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME NOT NULL, datetime_added DATETIME NOT NULL, UNIQUE INDEX UNIQ_F5AF83CFDCBB0C53 (unit), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); $this->addSql('CREATE TABLE part_lots (id INT AUTO_INCREMENT NOT NULL, id_store_location INT DEFAULT NULL, id_part INT DEFAULT NULL, description LONGTEXT NOT NULL, comment LONGTEXT NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown TINYINT(1) NOT NULL, instock INT DEFAULT NULL, amount DOUBLE PRECISION DEFAULT NULL, needs_refill TINYINT(1) NOT NULL, last_modified DATETIME NOT NULL, datetime_added DATETIME NOT NULL, INDEX IDX_EBC8F9435D8F4B37 (id_store_location), INDEX IDX_EBC8F943C22F6CC4 (id_part), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); - $this->addSql('CREATE TABLE currencies (id INT AUTO_INCREMENT NOT NULL, iso_code VARCHAR(255) DEFAULT NULL, exchange_rate NUMERIC(11, 5) NOT NULL, comment LONGTEXT NOT NULL, parent_id INT DEFAULT NULL, not_selectable TINYINT(1) NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME NOT NULL, datetime_added DATETIME NOT NULL, UNIQUE INDEX UNIQ_37C4469362B6A45E (iso_code), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); + $this->addSql('CREATE TABLE currencies (id INT AUTO_INCREMENT NOT NULL, iso_code VARCHAR(255) NOT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL, comment LONGTEXT NOT NULL, parent_id INT DEFAULT NULL, not_selectable TINYINT(1) NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME NOT NULL, datetime_added DATETIME NOT NULL, UNIQUE INDEX UNIQ_37C4469362B6A45E (iso_code), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB'); $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES `storelocations` (id)'); $this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES `parts` (id)'); diff --git a/src/Security/Voter/StructureVoter.php b/src/Security/Voter/StructureVoter.php index 6b0b3904..8f0ea031 100644 --- a/src/Security/Voter/StructureVoter.php +++ b/src/Security/Voter/StructureVoter.php @@ -33,12 +33,13 @@ namespace App\Security\Voter; use App\Entity\Attachments\AttachmentType; -use App\Entity\Device\Device; +use App\Entity\Devices\Device; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\Storelocation; use App\Entity\Parts\Supplier; +use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\User; class StructureVoter extends ExtendedVoter @@ -83,6 +84,9 @@ class StructureVoter extends ExtendedVoter return 'storelocations'; case Supplier::class: return 'suppliers'; + case Currency::class: + //TODO: Implement own permission + return 'suppliers'; } //When the class is not supported by this class return null return null; diff --git a/src/Services/EntityURLGenerator.php b/src/Services/EntityURLGenerator.php index 110549d8..15f77c70 100644 --- a/src/Services/EntityURLGenerator.php +++ b/src/Services/EntityURLGenerator.php @@ -39,6 +39,7 @@ use App\Entity\Base\NamedDBElement; use App\Entity\Parts\Part; use App\Entity\Parts\Storelocation; use App\Entity\Parts\Supplier; +use App\Entity\PriceInformations\Currency; use App\Entity\UserSystem\User; use App\Exceptions\EntityNotSupported; use Symfony\Component\HttpKernel\HttpCache\Store; @@ -176,10 +177,14 @@ class EntityURLGenerator return $this->urlGenerator->generate("footprint_edit", ['id' => $entity->getID()]); } - if($entity instanceof User) { + if ($entity instanceof User) { return $this->urlGenerator->generate('user_edit', ['id' => $entity->getID()]); } + if ($entity instanceof Currency) { + return $this->urlGenerator->generate('currency_edit', ['id' => $entity->getID()]); + } + //Otherwise throw an error throw new EntityNotSupported('The given entity is not supported yet!'); } @@ -229,6 +234,10 @@ class EntityURLGenerator return $this->urlGenerator->generate('user_new'); } + if ($entity instanceof Currency) { + return $this->urlGenerator->generate('currency_new'); + } + throw new EntityNotSupported('The given entity is not supported yet!'); } @@ -299,6 +308,10 @@ class EntityURLGenerator return $this->urlGenerator->generate('user_delete', ['id' => $entity->getID()]); } + if ($entity instanceof Currency) { + return $this->urlGenerator->generate('currency_delete', ['id' => $entity->getID()]); + } + throw new EntityNotSupported('The given entity is not supported yet!'); } diff --git a/src/Services/ToolsTreeBuilder.php b/src/Services/ToolsTreeBuilder.php index a3b042ba..9dbcb424 100644 --- a/src/Services/ToolsTreeBuilder.php +++ b/src/Services/ToolsTreeBuilder.php @@ -76,6 +76,9 @@ class ToolsTreeBuilder $nodes[] = new TreeViewNode($this->translator->trans('tree.tools.edit.footprint'), $this->urlGenerator->generate('footprint_new')); + $nodes[] = new TreeViewNode($this->translator->trans('tree.tools.edit.currency'), + $this->urlGenerator->generate('currency_new')); + $nodes[] = new TreeViewNode($this->translator->trans('tree.tools.edit.part'), $this->urlGenerator->generate('part_new')); diff --git a/symfony.lock b/symfony.lock index f9bc906d..e1b7ca09 100644 --- a/symfony.lock +++ b/symfony.lock @@ -602,6 +602,12 @@ "./config/packages/twig_extensions.yaml" ] }, + "twig/extra-bundle": { + "version": "3.x-dev" + }, + "twig/intl-extra": { + "version": "3.x-dev" + }, "twig/twig": { "version": "v2.6.2" }, diff --git a/templates/AdminPages/CurrencyAdmin.html.twig b/templates/AdminPages/CurrencyAdmin.html.twig new file mode 100644 index 00000000..283e7ec9 --- /dev/null +++ b/templates/AdminPages/CurrencyAdmin.html.twig @@ -0,0 +1,26 @@ +{% extends "AdminPages/EntityAdminBase.html.twig" %} + +{% block card_title %} + {% trans %}currency.caption{% endtrans %} +{% endblock %} + +{% block additional_controls %} + {{ form_row(form.iso_code) }} + {% if entity.isoCode %} +
+ + {% trans %}currency.iso_code.caption{% endtrans %}: {{ entity.isoCode }} + + + {% trans %}currency.symbol.caption{% endtrans %}: {{ entity.isoCode | currency_symbol }} + +
+ {% endif %} + + {{ form_row(form.exchange_rate) }} + {% if entity.inverseExchangeRate %} + + {{ '1'|format_currency(default_currency) }} = {{ entity.inverseExchangeRate | format_currency(entity.isoCode) }} + + {% endif %} +{% endblock %} \ No newline at end of file