From 9f6079ad8a13196f5dcc7a9952f1a16fa4787326 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sun, 2 Sep 2018 11:36:04 +0200 Subject: [PATCH] added option to delete invoice template (#294) --- config/services.yaml | 5 ++ src/Controller/Admin/ActivityController.php | 4 +- src/Controller/Admin/CustomerController.php | 4 +- src/Controller/Admin/ProjectController.php | 4 +- src/Controller/Admin/TimesheetController.php | 4 +- src/Controller/Admin/UserController.php | 4 +- src/Controller/InvoiceController.php | 75 +++++++++++++------- src/Controller/ProfileController.php | 10 +-- src/Controller/TimesheetController.php | 4 +- src/Controller/TimesheetControllerTrait.php | 4 +- src/Repository/InvoiceTemplateRepository.php | 31 ++++++++ templates/invoice/templates.html.twig | 1 + tests/Controller/InvoiceControllerTest.php | 55 +++++++++++++- tests/Controller/ProfileControllerTest.php | 2 +- tests/DataFixtures/InvoiceFixtures.php | 61 ++++++++++++++++ translations/flashmessages.de.xliff | 16 +++-- translations/flashmessages.en.xliff | 16 +++-- translations/flashmessages.es.xliff | 16 +++-- translations/flashmessages.fr.xliff | 16 +++-- translations/flashmessages.it.xliff | 16 +++-- translations/flashmessages.ru.xliff | 16 +++-- 21 files changed, 281 insertions(+), 83 deletions(-) create mode 100644 tests/DataFixtures/InvoiceFixtures.php diff --git a/config/services.yaml b/config/services.yaml index 769a476f..017b5339 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -141,3 +141,8 @@ services: class: Doctrine\ORM\EntityRepository factory: ['@doctrine.orm.entity_manager', getRepository] arguments: ['App\Entity\Customer'] + + App\Repository\InvoiceTemplateRepository: + class: Doctrine\ORM\EntityRepository + factory: ['@doctrine.orm.entity_manager', getRepository] + arguments: ['App\Entity\InvoiceTemplate'] diff --git a/src/Controller/Admin/ActivityController.php b/src/Controller/Admin/ActivityController.php index e2466857..fd8b1da2 100644 --- a/src/Controller/Admin/ActivityController.php +++ b/src/Controller/Admin/ActivityController.php @@ -115,7 +115,7 @@ class ActivityController extends AbstractController $entityManager->remove($activity); $entityManager->flush(); - $this->flashSuccess('action.deleted_successfully'); + $this->flashSuccess('action.delete.success'); return $this->redirectToRoute('admin_activity'); } @@ -146,7 +146,7 @@ class ActivityController extends AbstractController $entityManager->persist($activity); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); if ($editForm->has('create_more') && $editForm->get('create_more')->getData() === true) { $newActivity = new Activity(); diff --git a/src/Controller/Admin/CustomerController.php b/src/Controller/Admin/CustomerController.php index a6b36b2b..eb7634a4 100644 --- a/src/Controller/Admin/CustomerController.php +++ b/src/Controller/Admin/CustomerController.php @@ -95,7 +95,7 @@ class CustomerController extends AbstractController $entityManager->persist($customer); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute('admin_customer'); } @@ -132,7 +132,7 @@ class CustomerController extends AbstractController $entityManager->remove($customer); $entityManager->flush(); - $this->flashSuccess('action.deleted_successfully'); + $this->flashSuccess('action.delete.success'); return $this->redirectToRoute('admin_customer'); } diff --git a/src/Controller/Admin/ProjectController.php b/src/Controller/Admin/ProjectController.php index fd5bbeb3..536c2fe4 100644 --- a/src/Controller/Admin/ProjectController.php +++ b/src/Controller/Admin/ProjectController.php @@ -109,7 +109,7 @@ class ProjectController extends AbstractController $entityManager->remove($project); $entityManager->flush(); - $this->flashSuccess('action.deleted_successfully'); + $this->flashSuccess('action.delete.success'); return $this->redirectToRoute('admin_project'); } @@ -137,7 +137,7 @@ class ProjectController extends AbstractController $entityManager->persist($project); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); if ($editForm->has('create_more') && $editForm->get('create_more')->getData() === true) { $newProject = new Project(); diff --git a/src/Controller/Admin/TimesheetController.php b/src/Controller/Admin/TimesheetController.php index 308a77a9..458c240a 100644 --- a/src/Controller/Admin/TimesheetController.php +++ b/src/Controller/Admin/TimesheetController.php @@ -132,9 +132,9 @@ class TimesheetController extends AbstractController $entityManager->remove($entry); $entityManager->flush(); - $this->flashSuccess('action.deleted_successfully'); + $this->flashSuccess('action.delete.success'); } catch (\Exception $ex) { - $this->flashError('action.deleted.error', ['%reason%' => $ex->getMessage()]); + $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]); } return $this->redirectToRoute('admin_timesheet_paginated', ['page' => $request->get('page')]); diff --git a/src/Controller/Admin/UserController.php b/src/Controller/Admin/UserController.php index 45694538..10e9003c 100644 --- a/src/Controller/Admin/UserController.php +++ b/src/Controller/Admin/UserController.php @@ -102,7 +102,7 @@ class UserController extends AbstractController $entityManager->persist($user); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); if ($editForm->get('create_more')->getData() !== true) { return $this->redirectToRoute('user_profile_edit', ['username' => $user->getUsername()]); @@ -149,7 +149,7 @@ class UserController extends AbstractController $entityManager->remove($userToDelete); $entityManager->flush(); - $this->flashSuccess('action.deleted_successfully'); + $this->flashSuccess('action.delete.success'); return $this->redirectToRoute('admin_user'); } diff --git a/src/Controller/InvoiceController.php b/src/Controller/InvoiceController.php index b352825d..b59fbacb 100644 --- a/src/Controller/InvoiceController.php +++ b/src/Controller/InvoiceController.php @@ -15,6 +15,7 @@ use App\Form\InvoiceTemplateForm; use App\Form\Toolbar\InvoiceToolbarForm; use App\Invoice\ServiceInvoice; use App\Model\InvoiceModel; +use App\Repository\InvoiceTemplateRepository; use App\Repository\Query\BaseQuery; use App\Repository\Query\InvoiceQuery; use App\Repository\Query\TimesheetQuery; @@ -35,13 +36,25 @@ class InvoiceController extends AbstractController * @var ServiceInvoice */ protected $service; + /** + * @var InvoiceTemplateRepository + */ + protected $invoiceRepository; + /** + * @var TimesheetRepository + */ + protected $timesheetRepository; /** * @param ServiceInvoice $service + * @param InvoiceTemplateRepository $invoice + * @param TimesheetRepository $timesheet */ - public function __construct(ServiceInvoice $service) + public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $invoice, TimesheetRepository $timesheet) { $this->service = $service; + $this->invoiceRepository = $invoice; + $this->timesheetRepository = $timesheet; } /** @@ -62,14 +75,6 @@ class InvoiceController extends AbstractController return $query; } - /** - * @return \App\Repository\InvoiceTemplateRepository - */ - protected function getRepository() - { - return $this->getDoctrine()->getRepository(InvoiceTemplate::class); - } - /** * @Route(path="/", name="invoice", methods={"GET", "POST"}) * @@ -79,7 +84,7 @@ class InvoiceController extends AbstractController */ public function indexAction(Request $request) { - if (!$this->getRepository()->hasTemplate()) { + if (!$this->invoiceRepository->hasTemplate()) { return $this->redirectToRoute('admin_invoice_template_create'); } @@ -99,9 +104,7 @@ class InvoiceController extends AbstractController $query->getBegin()->setTime(0, 0, 0); $query->getEnd()->setTime(23, 59, 59); - /* @var TimesheetRepository $timeRepo */ - $timeRepo = $this->getDoctrine()->getRepository(Timesheet::class); - $queryBuilder = $timeRepo->findByQuery($query); + $queryBuilder = $this->timesheetRepository->findByQuery($query); $entries = $queryBuilder->getQuery()->getResult(); } } @@ -147,7 +150,7 @@ class InvoiceController extends AbstractController */ public function listTemplateAction($page) { - $templates = $this->getRepository()->findByQuery(new BaseQuery()); + $templates = $this->invoiceRepository->findByQuery(new BaseQuery()); return $this->render('invoice/templates.html.twig', [ 'entries' => $templates, @@ -156,10 +159,11 @@ class InvoiceController extends AbstractController } /** - * @Route(path="/{id}/edit", name="admin_invoice_template_edit", methods={"GET", "POST"}) + * @Route(path="/template/{id}/edit", name="admin_invoice_template_edit", methods={"GET", "POST"}) * * TODO permission * + * @param InvoiceTemplate $template * @param Request $request * @return \Symfony\Component\HttpFoundation\Response * @throws \Exception @@ -170,7 +174,7 @@ class InvoiceController extends AbstractController } /** - * @Route(path="/create", name="admin_invoice_template_create", methods={"GET", "POST"}) + * @Route(path="/template/create", name="admin_invoice_template_create", methods={"GET", "POST"}) * * TODO permission * @@ -180,13 +184,36 @@ class InvoiceController extends AbstractController */ public function createTemplateAction(Request $request) { - if (!$this->getRepository()->hasTemplate()) { + if (!$this->invoiceRepository->hasTemplate()) { $this->flashWarning('invoice.first_template'); } return $this->renderTemplateForm(new InvoiceTemplate(), $request); } + /** + * The route to delete an existing template. + * + * TODO permission + * + * @Route(path="/template/{id}/delete", name="admin_invoice_template_delete", methods={"GET", "POST"}) + * + * @param InvoiceTemplate $template + * @param Request $request + * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response + */ + public function deleteTemplate(InvoiceTemplate $template, Request $request) + { + try { + $this->invoiceRepository->removeTemplate($template); + $this->flashSuccess('action.delete.success'); + } catch (\Exception $ex) { + $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]); + } + + return $this->redirectToRoute('admin_invoice_template_paginated', ['page' => $request->get('page')]); + } + /** * @param InvoiceTemplate $template * @param Request $request @@ -199,13 +226,13 @@ class InvoiceController extends AbstractController $editForm->handleRequest($request); if ($editForm->isSubmitted() && $editForm->isValid()) { - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->persist($template); - $entityManager->flush(); - - $this->flashSuccess('action.updated_successfully'); - - return $this->redirectToRoute('admin_invoice_template'); + try { + $this->invoiceRepository->saveTemplate($template); + $this->flashSuccess('action.update.success'); + return $this->redirectToRoute('admin_invoice_template'); + } catch (\Exception $ex) { + $this->flashError('action.update.error', ['%reason%' => $ex->getMessage()]); + } } return $this->render('invoice/template_edit.html.twig', [ diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index 58c1feb4..4a630f9a 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -68,7 +68,7 @@ class ProfileController extends AbstractController $entityManager->persist($profile); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]); } @@ -93,7 +93,7 @@ class ProfileController extends AbstractController $entityManager->persist($profile); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]); } @@ -118,7 +118,7 @@ class ProfileController extends AbstractController $entityManager->persist($profile); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]); } @@ -140,7 +140,7 @@ class ProfileController extends AbstractController $entityManager->persist($profile); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]); } @@ -184,7 +184,7 @@ class ProfileController extends AbstractController $entityManager->persist($profile); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute('user_profile', ['username' => $profile->getUsername()]); } diff --git a/src/Controller/TimesheetController.php b/src/Controller/TimesheetController.php index ad95505e..a1b76091 100644 --- a/src/Controller/TimesheetController.php +++ b/src/Controller/TimesheetController.php @@ -169,9 +169,9 @@ class TimesheetController extends AbstractController $entityManager->remove($entry); $entityManager->flush(); - $this->flashSuccess('action.deleted_successfully'); + $this->flashSuccess('action.delete.success'); } catch (\Exception $ex) { - $this->flashError('action.deleted.error', ['%reason%' => $ex->getMessage()]); + $this->flashError('action.delete.error', ['%reason%' => $ex->getMessage()]); } return $this->redirectToRoute('timesheet_paginated', ['page' => $request->get('page')]); diff --git a/src/Controller/TimesheetControllerTrait.php b/src/Controller/TimesheetControllerTrait.php index 7a7da18b..b0aa2ada 100644 --- a/src/Controller/TimesheetControllerTrait.php +++ b/src/Controller/TimesheetControllerTrait.php @@ -98,7 +98,7 @@ trait TimesheetControllerTrait $entityManager->persist($entry); $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute($redirectRoute, ['page' => $request->get('page')]); } @@ -177,7 +177,7 @@ trait TimesheetControllerTrait $entityManager->flush(); - $this->flashSuccess('action.updated_successfully'); + $this->flashSuccess('action.update.success'); return $this->redirectToRoute($redirectRoute); } diff --git a/src/Repository/InvoiceTemplateRepository.php b/src/Repository/InvoiceTemplateRepository.php index 0c597c08..2535d464 100644 --- a/src/Repository/InvoiceTemplateRepository.php +++ b/src/Repository/InvoiceTemplateRepository.php @@ -54,4 +54,35 @@ class InvoiceTemplateRepository extends AbstractRepository return $this->getBaseQueryResult($qb, $query); } + + /** + * @param InvoiceTemplate $template + * @return InvoiceTemplate + * @throws RepositoryException + */ + public function saveTemplate(InvoiceTemplate $template) + { + try { + $this->getEntityManager()->persist($template); + $this->getEntityManager()->flush(); + } catch (\Exception $ex) { + throw new RepositoryException('Could not save InvoiceTemplate'); + } + + return $template; + } + + /** + * @param InvoiceTemplate $template + * @throws RepositoryException + */ + public function removeTemplate(InvoiceTemplate $template) + { + try { + $this->getEntityManager()->remove($template); + $this->getEntityManager()->flush(); + } catch (\Exception $ex) { + throw new RepositoryException('Could not remove InvoiceTemplate'); + } + } } diff --git a/templates/invoice/templates.html.twig b/templates/invoice/templates.html.twig index f40b02c4..7729905f 100644 --- a/templates/invoice/templates.html.twig +++ b/templates/invoice/templates.html.twig @@ -29,6 +29,7 @@ {{ "invoice_renderer.#{entry.renderer}"|trans }} {% set actionButtons = {'edit': path('admin_invoice_template_edit', {'id' : entry.id, 'page': page})} %} + {% set actionButtons = actionButtons|merge({'trash': path('admin_invoice_template_delete', {'id' : entry.id, 'page': page})}) %} {{ widgets.button_group(actionButtons) }} diff --git a/tests/Controller/InvoiceControllerTest.php b/tests/Controller/InvoiceControllerTest.php index 54a35a19..43256eb5 100644 --- a/tests/Controller/InvoiceControllerTest.php +++ b/tests/Controller/InvoiceControllerTest.php @@ -9,7 +9,9 @@ namespace App\Tests\Controller; +use App\Entity\InvoiceTemplate; use App\Entity\User; +use App\Tests\DataFixtures\InvoiceFixtures; /** * @coversDefaultClass \App\Controller\InvoiceController @@ -23,12 +25,59 @@ class InvoiceControllerTest extends ControllerBaseTest $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/invoice/'); } - public function testIndexAction() + public function testIndexActionRedirectsToCreateTemplate() { - $this->markTestSkipped('create invoice template before this test case'); $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); + + $this->request($client, '/invoice/'); + $this->assertIsRedirect($client, '/invoice/template/create'); + } + + public function testIndexActionHasErrorMessageOnEmptyQuery() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $fixture = new InvoiceFixtures(); + $this->importFixture($em, $fixture); + $this->request($client, '/invoice/'); $this->assertTrue($client->getResponse()->isSuccessful()); - $this->assertMainContentClass($client, 'dashboard'); + + $node = $client->getCrawler()->filter('div.callout.callout-warning.lead'); + $this->assertNotEmpty($node->text()); + $this->assertContains('Before you can create an invoice, you have to select at least a customer filter.', $node->text()); + } + + public function testListTemplateAction() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $fixture = new InvoiceFixtures(); + $this->importFixture($em, $fixture); + + $this->request($client, '/invoice/template'); + + $this->assertTrue($client->getResponse()->isSuccessful()); + $this->assertHasDataTable($client); + } + + public function testDeleteTemplateAction() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $fixture = new InvoiceFixtures(); + $this->importFixture($em, $fixture); + + $this->request($client, '/invoice/template/1/delete?page=1'); + $this->assertIsRedirect($client, '/invoice/template/page/1'); + $client->followRedirect(); + + $this->assertTrue($client->getResponse()->isSuccessful()); + $this->assertHasFlashSuccess($client); + + $this->assertEquals(0, $em->getRepository(InvoiceTemplate::class)->count([])); } } diff --git a/tests/Controller/ProfileControllerTest.php b/tests/Controller/ProfileControllerTest.php index 9bbaf885..996bdbea 100644 --- a/tests/Controller/ProfileControllerTest.php +++ b/tests/Controller/ProfileControllerTest.php @@ -14,7 +14,7 @@ use App\Entity\User; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; /** - * @coversDefaultClass \App\Controller\InvoiceController + * @coversDefaultClass \App\Controller\ProfileController * @group integration */ class ProfileControllerTest extends ControllerBaseTest diff --git a/tests/DataFixtures/InvoiceFixtures.php b/tests/DataFixtures/InvoiceFixtures.php new file mode 100644 index 00000000..8fa400a4 --- /dev/null +++ b/tests/DataFixtures/InvoiceFixtures.php @@ -0,0 +1,61 @@ +loadInvoiceTemplates($manager); + } + + /** + * @param ObjectManager $manager + */ + private function loadInvoiceTemplates(ObjectManager $manager) + { + $faker = Factory::create(); + + $template = new InvoiceTemplate(); + $template + ->setName('Invoice') + ->setTitle('Your company name') + ->setCompany($faker->company) + ->setVat(19) + ->setDueDays(14) + ->setPaymentTerms( + 'I would like to thank you for your confidence and will gladly be there for you in the future.' . + PHP_EOL . + 'Please transfer the total amount within 14 days to the given account and use the invoice number ' . + 'as reference.' + ) + ->setAddress( + $faker->streetAddress . PHP_EOL . + $faker->city . ', ' . $faker->stateAbbr . ' ' . $faker->postcode . PHP_EOL . + 'Phone: ' . $faker->phoneNumber . PHP_EOL . + 'Email: ' . $faker->safeEmail + ) + ; + + $manager->persist($template); + $manager->flush(); + } +} diff --git a/translations/flashmessages.de.xliff b/translations/flashmessages.de.xliff index 6cd0a126..2b978ca8 100644 --- a/translations/flashmessages.de.xliff +++ b/translations/flashmessages.de.xliff @@ -18,16 +18,20 @@ timesheet.start.error Zeitmessung konnte nicht gestartet werden: %reason% - - action.updated_successfully + + action.update.success Änderungen erfolgreich gespeichert - - action.deleted_successfully + + action.update.error + Änderungen konnten nicht gespeichert werden: %reason% + + + action.delete.success Eintrag wurde erfolgreich gelöscht - - action.deleted.error + + action.delete.error Eintrag konnte nicht gelöscht werden: %reason% diff --git a/translations/flashmessages.en.xliff b/translations/flashmessages.en.xliff index 55c22773..0ab57997 100644 --- a/translations/flashmessages.en.xliff +++ b/translations/flashmessages.en.xliff @@ -18,16 +18,20 @@ timesheet.start.error Time-recording could not be started: %reason% - - action.updated_successfully + + action.update.success Saved changes successful - - action.deleted_successfully + + action.update.error + Changes could not be saved: %reason% + + + action.delete.success Entry was deleted successful - - action.deleted.error + + action.delete.error Entry could not be deleted: %reason% diff --git a/translations/flashmessages.es.xliff b/translations/flashmessages.es.xliff index cc54a7e6..bd16e8b1 100644 --- a/translations/flashmessages.es.xliff +++ b/translations/flashmessages.es.xliff @@ -18,16 +18,20 @@ timesheet.start.error No se pudo iniciar el registro de tiempo: %reason% - - action.updated_successfully + + action.update.success Cambios guardados! - - action.deleted_successfully + + action.update.error + Changes could not be saved: %reason% + + + action.delete.success Registro borrado! - - action.deleted.error + + action.delete.error No se pudo borrar el registro: %reason% diff --git a/translations/flashmessages.fr.xliff b/translations/flashmessages.fr.xliff index d8693178..8c1c09e4 100644 --- a/translations/flashmessages.fr.xliff +++ b/translations/flashmessages.fr.xliff @@ -18,16 +18,20 @@ timesheet.start.error Le chronométrage ne peut pas être lancé : %reason% - - action.updated_successfully + + action.update.success Modifications enregistrées - - action.deleted_successfully + + action.update.error + Changes could not be saved: %reason% + + + action.delete.success L'entrée a été supprimée - - action.deleted.error + + action.delete.error L'entrée ne peut pas être supprimée : %reason% diff --git a/translations/flashmessages.it.xliff b/translations/flashmessages.it.xliff index 1f4ed71f..4f35499e 100755 --- a/translations/flashmessages.it.xliff +++ b/translations/flashmessages.it.xliff @@ -18,16 +18,20 @@ timesheet.start.error Time-recording non può essere avviata: %reason% - - action.updated_successfully + + action.update.success Salvataggio avvenuto - - action.deleted_successfully + + action.update.error + Changes could not be saved: %reason% + + + action.delete.success Informazioni cancellate - - action.deleted.error + + action.delete.error Le informazioni non possono essere cancellate: %reason% diff --git a/translations/flashmessages.ru.xliff b/translations/flashmessages.ru.xliff index f22280d0..8bc42d4c 100644 --- a/translations/flashmessages.ru.xliff +++ b/translations/flashmessages.ru.xliff @@ -18,16 +18,20 @@ timesheet.start.error Ошибка - Хронометраж не был начат: %reason% - - action.updated_successfully + + action.update.success Изменения успешно сохранены - - action.deleted_successfully + + action.update.error + Changes could not be saved: %reason% + + + action.delete.success Запись успешно удалена - - action.deleted.error + + action.delete.error Ошибка - Запись не была удалена: %reason%