diff --git a/src/Command/KimaiImporterCommand.php b/src/Command/KimaiImporterCommand.php index b0187c4e..73a57bcb 100644 --- a/src/Command/KimaiImporterCommand.php +++ b/src/Command/KimaiImporterCommand.php @@ -763,7 +763,6 @@ class KimaiImporterCommand extends Command array $rates, ?Project $project ) { - $activityId = $oldActivity['activityID']; $projectId = null !== $project ? $project->getId() : null; diff --git a/src/Controller/Admin/ActivityController.php b/src/Controller/Admin/ActivityController.php index ca933d1d..d21c1323 100644 --- a/src/Controller/Admin/ActivityController.php +++ b/src/Controller/Admin/ActivityController.php @@ -13,7 +13,10 @@ use App\Controller\AbstractController; use App\Entity\Activity; use App\Form\ActivityEditForm; use App\Form\Toolbar\ActivityToolbarForm; +use App\Form\Type\ActivityType; +use App\Repository\ActivityRepository; use App\Repository\Query\ActivityQuery; +use Doctrine\ORM\ORMException; use Pagerfanta\Pagerfanta; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; @@ -105,6 +108,22 @@ class ActivityController extends AbstractController $stats = $this->getRepository()->getActivityStatistics($activity); $deleteForm = $this->createFormBuilder() + ->add('activity', ActivityType::class, [ + 'label' => 'label.activity', + 'query_builder' => function (ActivityRepository $repo) use ($activity) { + $query = new ActivityQuery(); + $query + ->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER) + ->setProject($activity->getProject()) + ->setOrderGlobalsFirst(true) + ->addIgnoredEntity($activity) + ->setGlobalsOnly(null === $activity->getProject()) + ; + + return $repo->findByQuery($query); + }, + 'required' => false, + ]) ->setAction($this->generateUrl('admin_activity_delete', ['id' => $activity->getId()])) ->setMethod('POST') ->getForm(); @@ -112,11 +131,12 @@ class ActivityController extends AbstractController $deleteForm->handleRequest($request); if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->remove($activity); - $entityManager->flush(); - - $this->flashSuccess('action.delete.success'); + try { + $this->getRepository()->deleteActivity($activity, $deleteForm->get('activity')->getData()); + $this->flashSuccess('action.delete.success'); + } catch (ORMException $ex) { + $this->flashError('action.delete.error'); + } return $this->redirectToRoute('admin_activity'); } diff --git a/src/Controller/Admin/CustomerController.php b/src/Controller/Admin/CustomerController.php index c667f097..7774a896 100644 --- a/src/Controller/Admin/CustomerController.php +++ b/src/Controller/Admin/CustomerController.php @@ -13,7 +13,10 @@ use App\Controller\AbstractController; use App\Entity\Customer; use App\Form\CustomerEditForm; use App\Form\Toolbar\CustomerToolbarForm; +use App\Form\Type\CustomerType; +use App\Repository\CustomerRepository; use App\Repository\Query\CustomerQuery; +use Doctrine\ORM\ORMException; use Pagerfanta\Pagerfanta; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\HttpFoundation\Request; @@ -140,6 +143,18 @@ class CustomerController extends AbstractController $stats = $this->getRepository()->getCustomerStatistics($customer); $deleteForm = $this->createFormBuilder() + ->add('customer', CustomerType::class, [ + 'label' => 'label.customer', + 'query_builder' => function (CustomerRepository $repo) use ($customer) { + $query = new CustomerQuery(); + $query + ->setResultType(CustomerQuery::RESULT_TYPE_QUERYBUILDER) + ->addIgnoredEntity($customer); + + return $repo->findByQuery($query); + }, + 'required' => false, + ]) ->setAction($this->generateUrl('admin_customer_delete', ['id' => $customer->getId()])) ->setMethod('POST') ->getForm(); @@ -147,11 +162,12 @@ class CustomerController extends AbstractController $deleteForm->handleRequest($request); if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->remove($customer); - $entityManager->flush(); - - $this->flashSuccess('action.delete.success'); + try { + $this->getRepository()->deleteCustomer($customer, $deleteForm->get('customer')->getData()); + $this->flashSuccess('action.delete.success'); + } catch (ORMException $ex) { + $this->flashError('action.delete.error'); + } return $this->redirectToRoute('admin_customer'); } diff --git a/src/Controller/Admin/ProjectController.php b/src/Controller/Admin/ProjectController.php index cf3948e4..ccfb6824 100644 --- a/src/Controller/Admin/ProjectController.php +++ b/src/Controller/Admin/ProjectController.php @@ -14,7 +14,10 @@ use App\Entity\Customer; use App\Entity\Project; use App\Form\ProjectEditForm; use App\Form\Toolbar\ProjectToolbarForm; +use App\Form\Type\ProjectType; +use App\Repository\ProjectRepository; use App\Repository\Query\ProjectQuery; +use Doctrine\ORM\ORMException; use Pagerfanta\Pagerfanta; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; @@ -99,6 +102,19 @@ class ProjectController extends AbstractController $stats = $this->getRepository()->getProjectStatistics($project); $deleteForm = $this->createFormBuilder() + ->add('project', ProjectType::class, [ + 'label' => 'label.project', + 'query_builder' => function (ProjectRepository $repo) use ($project) { + $query = new ProjectQuery(); + $query + ->setResultType(ProjectQuery::RESULT_TYPE_QUERYBUILDER) + ->setCustomer($project->getCustomer()) + ->addIgnoredEntity($project); + + return $repo->findByQuery($query); + }, + 'required' => false, + ]) ->setAction($this->generateUrl('admin_project_delete', ['id' => $project->getId()])) ->setMethod('POST') ->getForm(); @@ -106,11 +122,12 @@ class ProjectController extends AbstractController $deleteForm->handleRequest($request); if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { - $entityManager = $this->getDoctrine()->getManager(); - $entityManager->remove($project); - $entityManager->flush(); - - $this->flashSuccess('action.delete.success'); + try { + $this->getRepository()->deleteProject($project, $deleteForm->get('project')->getData()); + $this->flashSuccess('action.delete.success'); + } catch (ORMException $ex) { + $this->flashError('action.delete.error'); + } return $this->redirectToRoute('admin_project'); } diff --git a/src/Repository/ActivityRepository.php b/src/Repository/ActivityRepository.php index 684007b5..523f0135 100644 --- a/src/Repository/ActivityRepository.php +++ b/src/Repository/ActivityRepository.php @@ -15,6 +15,7 @@ use App\Entity\Timesheet; use App\Entity\User; use App\Model\ActivityStatistic; use App\Repository\Query\ActivityQuery; +use Doctrine\ORM\ORMException; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Pagerfanta\Pagerfanta; @@ -183,6 +184,11 @@ class ActivityRepository extends AbstractRepository $qb->setParameter('customer', $query->getCustomer()); } + if (!empty($query->getIgnoredEntities())) { + $qb->andWhere('a.id NOT IN(:ignored)'); + $qb->setParameter('ignored', $query->getIgnoredEntities()); + } + $or = $qb->expr()->orX(); // this must always be the last part before the or @@ -204,4 +210,38 @@ class ActivityRepository extends AbstractRepository return $this->getBaseQueryResult($qb, $query); } + + /** + * @param Activity $delete + * @param Activity|null $replace + * @throws \Doctrine\ORM\ORMException + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function deleteActivity(Activity $delete, ?Activity $replace = null) + { + $em = $this->getEntityManager(); + $em->beginTransaction(); + + try { + if (null !== $replace) { + $qb = $em->createQueryBuilder(); + $query = $qb + ->update(Timesheet::class, 't') + ->set('t.activity', ':replace') + ->where('t.activity = :delete') + ->setParameter('delete', $delete) + ->setParameter('replace', $replace) + ->getQuery(); + + $result = $query->execute(); + } + + $em->remove($delete); + $em->flush(); + $em->commit(); + } catch (ORMException $ex) { + $em->rollback(); + throw $ex; + } + } } diff --git a/src/Repository/CustomerRepository.php b/src/Repository/CustomerRepository.php index edff162c..b9c88322 100644 --- a/src/Repository/CustomerRepository.php +++ b/src/Repository/CustomerRepository.php @@ -15,6 +15,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Model\CustomerStatistic; use App\Repository\Query\CustomerQuery; +use Doctrine\ORM\ORMException; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Pagerfanta\Pagerfanta; @@ -121,6 +122,43 @@ class CustomerRepository extends AbstractRepository $qb->andWhere('c.visible = 0'); } + if (!empty($query->getIgnoredEntities())) { + $qb->andWhere('c.id NOT IN(:ignored)'); + $qb->setParameter('ignored', $query->getIgnoredEntities()); + } + return $this->getBaseQueryResult($qb, $query); } + + /** + * @param Customer $delete + * @param Customer|null $replace + * @throws \Doctrine\ORM\ORMException + */ + public function deleteCustomer(Customer $delete, ?Customer $replace = null) + { + $em = $this->getEntityManager(); + $em->beginTransaction(); + + try { + if (null !== $replace) { + $qb = $em->createQueryBuilder(); + $qb + ->update(Project::class, 'p') + ->set('p.customer', ':replace') + ->where('p.customer = :delete') + ->setParameter('delete', $delete) + ->setParameter('replace', $replace) + ->getQuery() + ->execute(); + } + + $em->remove($delete); + $em->flush(); + $em->commit(); + } catch (ORMException $ex) { + $em->rollback(); + throw $ex; + } + } } diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 96a8184f..9d5a1ae8 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -15,6 +15,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Model\ProjectStatistic; use App\Repository\Query\ProjectQuery; +use Doctrine\ORM\ORMException; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Pagerfanta\Pagerfanta; @@ -131,6 +132,53 @@ class ProjectRepository extends AbstractRepository ->setParameter('customer', $query->getCustomer()); } + if (!empty($query->getIgnoredEntities())) { + $qb->andWhere('p.id NOT IN(:ignored)'); + $qb->setParameter('ignored', $query->getIgnoredEntities()); + } + return $this->getBaseQueryResult($qb, $query); } + + /** + * @param Project $delete + * @param Project|null $replace + * @throws \Doctrine\ORM\ORMException + */ + public function deleteProject(Project $delete, ?Project $replace = null) + { + $em = $this->getEntityManager(); + $em->beginTransaction(); + + try { + if (null !== $replace) { + $qb = $em->createQueryBuilder(); + $qb + ->update(Timesheet::class, 't') + ->set('t.project', ':replace') + ->where('t.project = :delete') + ->setParameter('delete', $delete) + ->setParameter('replace', $replace) + ->getQuery() + ->execute(); + + $qb = $em->createQueryBuilder(); + $qb + ->update(Activity::class, 'a') + ->set('a.project', ':replace') + ->where('a.project = :delete') + ->setParameter('delete', $delete) + ->setParameter('replace', $replace) + ->getQuery() + ->execute(); + } + + $em->remove($delete); + $em->flush(); + $em->commit(); + } catch (ORMException $ex) { + $em->rollback(); + throw $ex; + } + } } diff --git a/src/Repository/Query/CustomerQuery.php b/src/Repository/Query/CustomerQuery.php index a0d120b4..a5a8b73a 100644 --- a/src/Repository/Query/CustomerQuery.php +++ b/src/Repository/Query/CustomerQuery.php @@ -9,9 +9,34 @@ namespace App\Repository\Query; +use App\Entity\Customer; + /** * Can be used for advanced queries with the: CustomerRepository */ class CustomerQuery extends VisibilityQuery { + /** + * @var array + */ + protected $ignored = []; + + /** + * @param Customer|int $customer + * @return $this + */ + public function addIgnoredEntity($customer) + { + $this->ignored[] = $customer; + + return $this; + } + + /** + * @return array + */ + public function getIgnoredEntities() + { + return $this->ignored; + } } diff --git a/src/Repository/Query/ProjectQuery.php b/src/Repository/Query/ProjectQuery.php index 9f00b1c0..aec5e0cb 100644 --- a/src/Repository/Query/ProjectQuery.php +++ b/src/Repository/Query/ProjectQuery.php @@ -21,6 +21,30 @@ class ProjectQuery extends VisibilityQuery */ protected $customer; + /** + * @var array + */ + protected $ignored = []; + + /** + * @param mixed $entity + * @return $this + */ + public function addIgnoredEntity($entity) + { + $this->ignored[] = $entity; + + return $this; + } + + /** + * @return array + */ + public function getIgnoredEntities() + { + return $this->ignored; + } + /** * @return Customer|int */ diff --git a/src/Repository/TimesheetRepository.php b/src/Repository/TimesheetRepository.php index 6e8efa62..b4cf727d 100644 --- a/src/Repository/TimesheetRepository.php +++ b/src/Repository/TimesheetRepository.php @@ -291,6 +291,7 @@ class TimesheetRepository extends AbstractRepository $qb->andWhere('t.begin >= :begin') ->setParameter('begin', $query->getBegin()); } + if (null !== $query->getEnd()) { $qb->andWhere('t.end <= :end') ->setParameter('end', $query->getEnd()); @@ -299,12 +300,16 @@ class TimesheetRepository extends AbstractRepository if (null !== $query->getActivity()) { $qb->andWhere('t.activity = :activity') ->setParameter('activity', $query->getActivity()); - } elseif (null !== $query->getProject()) { - $qb->andWhere('t.project = :project') - ->setParameter('project', $query->getProject()); - } elseif (null !== $query->getCustomer()) { - $qb->andWhere('p.customer = :customer') - ->setParameter('customer', $query->getCustomer()); + } + + if (null === $query->getActivity() || null === $query->getActivity()->getProject()) { + if (null !== $query->getProject()) { + $qb->andWhere('t.project = :project') + ->setParameter('project', $query->getProject()); + } elseif (null !== $query->getCustomer()) { + $qb->andWhere('p.customer = :customer') + ->setParameter('customer', $query->getCustomer()); + } } return $this->getBaseQueryResult($qb, $query); diff --git a/tests/Controller/Admin/ActivityControllerTest.php b/tests/Controller/Admin/ActivityControllerTest.php index f9fa4994..d08dcf71 100644 --- a/tests/Controller/Admin/ActivityControllerTest.php +++ b/tests/Controller/Admin/ActivityControllerTest.php @@ -9,8 +9,10 @@ namespace App\Tests\Controller\Admin; +use App\Entity\Timesheet; use App\Entity\User; use App\Tests\Controller\ControllerBaseTest; +use App\Tests\DataFixtures\ActivityFixtures; use App\Tests\DataFixtures\ProjectFixtures; use App\Tests\DataFixtures\TimesheetFixtures; @@ -127,6 +129,14 @@ class ActivityControllerTest extends ControllerBaseTest $fixture->setAmount(10); $this->importFixture($em, $fixture); + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(1, $entry->getActivity()->getId()); + } + $this->request($client, '/admin/activity/1/delete'); $this->assertTrue($client->getResponse()->isSuccessful()); @@ -139,6 +149,60 @@ class ActivityControllerTest extends ControllerBaseTest $this->assertHasDataTable($client); $this->assertHasFlashSuccess($client); + // SQLIte does not necessarly support onCascade delete, so these timesheet will stay after deletion + // $em->clear(); + // $timesheets = $em->getRepository(Timesheet::class)->findAll(); + // $this->assertEquals(0, count($timesheets)); + + $this->request($client, '/admin/activity/1/edit'); + $this->assertFalse($client->getResponse()->isSuccessful()); + } + + public function testDeleteActionWithTimesheetEntriesAndReplacement() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $fixture = new TimesheetFixtures(); + $fixture->setUser($this->getUserByRole($em, User::ROLE_USER)); + $fixture->setAmount(10); + $this->importFixture($em, $fixture); + $fixture = new ActivityFixtures(); + $fixture->setAmount(1)->setIsGlobal(true)->setIsVisible(true); + $this->importFixture($em, $fixture); + + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(1, $entry->getActivity()->getId()); + } + + $this->request($client, '/admin/activity/1/delete'); + $this->assertTrue($client->getResponse()->isSuccessful()); + + $form = $client->getCrawler()->filter('form[name=form]')->form(); + $this->assertStringEndsWith($this->createUrl('/admin/activity/1/delete'), $form->getUri()); + $client->submit($form, [ + 'form' => [ + 'activity' => 2 + ] + ]); + + $this->assertIsRedirect($client, $this->createUrl('/admin/activity/')); + $client->followRedirect(); + $this->assertHasDataTable($client); + $this->assertHasFlashSuccess($client); + + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(2, $entry->getActivity()->getId()); + } + $this->request($client, '/admin/activity/1/edit'); $this->assertFalse($client->getResponse()->isSuccessful()); } diff --git a/tests/Controller/Admin/CustomerControllerTest.php b/tests/Controller/Admin/CustomerControllerTest.php index ee8010e5..4acac03b 100644 --- a/tests/Controller/Admin/CustomerControllerTest.php +++ b/tests/Controller/Admin/CustomerControllerTest.php @@ -9,6 +9,7 @@ namespace App\Tests\Controller\Admin; +use App\Entity\Timesheet; use App\Entity\User; use App\Tests\Controller\ControllerBaseTest; use App\Tests\DataFixtures\CustomerFixtures; @@ -112,6 +113,14 @@ class CustomerControllerTest extends ControllerBaseTest $fixture->setAmount(10); $this->importFixture($em, $fixture); + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(1, $entry->getActivity()->getId()); + } + $this->request($client, '/admin/customer/1/delete'); $this->assertTrue($client->getResponse()->isSuccessful()); @@ -124,6 +133,60 @@ class CustomerControllerTest extends ControllerBaseTest $this->assertHasDataTable($client); $this->assertHasFlashSuccess($client); + // SQLIte does not necessarly support onCascade delete, so these timesheet will stay after deletion + // $em->clear(); + // $timesheets = $em->getRepository(Timesheet::class)->findAll(); + // $this->assertEquals(0, count($timesheets)); + + $this->request($client, '/admin/customer/1/edit'); + $this->assertFalse($client->getResponse()->isSuccessful()); + } + + public function testDeleteActionWithTimesheetEntriesAndReplacement() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $fixture = new TimesheetFixtures(); + $fixture->setUser($this->getUserByRole($em, User::ROLE_USER)); + $fixture->setAmount(10); + $this->importFixture($em, $fixture); + $fixture = new CustomerFixtures(); + $fixture->setAmount(1)->setIsVisible(true); + $this->importFixture($em, $fixture); + + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(1, $entry->getProject()->getCustomer()->getId()); + } + + $this->request($client, '/admin/customer/1/delete'); + $this->assertTrue($client->getResponse()->isSuccessful()); + + $form = $client->getCrawler()->filter('form[name=form]')->form(); + $this->assertStringEndsWith($this->createUrl('/admin/customer/1/delete'), $form->getUri()); + $client->submit($form, [ + 'form' => [ + 'customer' => 2 + ] + ]); + + $this->assertIsRedirect($client, $this->createUrl('/admin/customer/')); + $client->followRedirect(); + $this->assertHasDataTable($client); + $this->assertHasFlashSuccess($client); + + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(2, $entry->getProject()->getCustomer()->getId()); + } + $this->request($client, '/admin/customer/1/edit'); $this->assertFalse($client->getResponse()->isSuccessful()); } diff --git a/tests/Controller/Admin/ProjectControllerTest.php b/tests/Controller/Admin/ProjectControllerTest.php index f065abc5..8c3a17c3 100644 --- a/tests/Controller/Admin/ProjectControllerTest.php +++ b/tests/Controller/Admin/ProjectControllerTest.php @@ -9,6 +9,7 @@ namespace App\Tests\Controller\Admin; +use App\Entity\Timesheet; use App\Entity\User; use App\Tests\Controller\ControllerBaseTest; use App\Tests\DataFixtures\CustomerFixtures; @@ -135,6 +136,14 @@ class ProjectControllerTest extends ControllerBaseTest $fixture->setAmount(10); $this->importFixture($em, $fixture); + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(1, $entry->getActivity()->getId()); + } + $this->request($client, '/admin/project/1/delete'); $this->assertTrue($client->getResponse()->isSuccessful()); @@ -147,6 +156,60 @@ class ProjectControllerTest extends ControllerBaseTest $this->assertHasDataTable($client); $this->assertHasFlashSuccess($client); + // SQLIte does not necessarly support onCascade delete, so these timesheet will stay after deletion + // $em->clear(); + // $timesheets = $em->getRepository(Timesheet::class)->findAll(); + // $this->assertEquals(0, count($timesheets)); + + $this->request($client, '/admin/project/1/edit'); + $this->assertFalse($client->getResponse()->isSuccessful()); + } + + public function testDeleteActionWithTimesheetEntriesAndReplacement() + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $fixture = new TimesheetFixtures(); + $fixture->setUser($this->getUserByRole($em, User::ROLE_USER)); + $fixture->setAmount(10); + $this->importFixture($em, $fixture); + $fixture = new ProjectFixtures(); + $fixture->setAmount(1)->setIsVisible(true); + $this->importFixture($em, $fixture); + + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(1, $entry->getProject()->getId()); + } + + $this->request($client, '/admin/project/1/delete'); + $this->assertTrue($client->getResponse()->isSuccessful()); + + $form = $client->getCrawler()->filter('form[name=form]')->form(); + $this->assertStringEndsWith($this->createUrl('/admin/project/1/delete'), $form->getUri()); + $client->submit($form, [ + 'form' => [ + 'project' => 2 + ] + ]); + + $this->assertIsRedirect($client, $this->createUrl('/admin/project/')); + $client->followRedirect(); + $this->assertHasDataTable($client); + $this->assertHasFlashSuccess($client); + + $timesheets = $em->getRepository(Timesheet::class)->findAll(); + $this->assertEquals(10, count($timesheets)); + + /** @var Timesheet $entry */ + foreach($timesheets as $entry) { + $this->assertEquals(2, $entry->getProject()->getId()); + } + $this->request($client, '/admin/project/1/edit'); $this->assertFalse($client->getResponse()->isSuccessful()); } diff --git a/tests/DataFixtures/ActivityFixtures.php b/tests/DataFixtures/ActivityFixtures.php index f49884b5..b45a3273 100644 --- a/tests/DataFixtures/ActivityFixtures.php +++ b/tests/DataFixtures/ActivityFixtures.php @@ -25,6 +25,14 @@ class ActivityFixtures extends Fixture * @var int */ protected $amount = 0; + /** + * @var bool + */ + protected $isGlobal = false; + /** + * @var bool + */ + protected $isVisible = null; /** * @return int @@ -34,6 +42,28 @@ class ActivityFixtures extends Fixture return $this->amount; } + /** + * @param bool $global + * @return $this + */ + public function setIsGlobal(bool $global) + { + $this->isGlobal = $global; + + return $this; + } + + /** + * @param bool $visible + * @return $this + */ + public function setIsVisible(bool $visible) + { + $this->isVisible = $visible; + + return $this; + } + /** * @param int $amount * @return ActivityFixtures @@ -55,10 +85,17 @@ class ActivityFixtures extends Fixture // random amount of timesheet entries for every user for ($i = 0; $i < $this->amount; $i++) { + $project = null; + if (false === $this->isGlobal) { + $project = $projects[array_rand($projects)]; + } $visible = 0 != $i % 3; + if (null !== $this->isVisible) { + $visible = $this->isVisible; + } $entity = new Activity(); $entity - ->setProject($projects[array_rand($projects)]) + ->setProject($project) ->setName($faker->bs . ($visible ? '' : ' (x)')) ->setComment($faker->text) ->setVisible($visible) diff --git a/tests/DataFixtures/CustomerFixtures.php b/tests/DataFixtures/CustomerFixtures.php index 090baa80..a594f6c9 100644 --- a/tests/DataFixtures/CustomerFixtures.php +++ b/tests/DataFixtures/CustomerFixtures.php @@ -24,6 +24,10 @@ class CustomerFixtures extends Fixture * @var int */ protected $amount = 0; + /** + * @var bool + */ + protected $isVisible = null; /** * @return int @@ -44,6 +48,17 @@ class CustomerFixtures extends Fixture return $this; } + /** + * @param bool $visible + * @return $this + */ + public function setIsVisible(bool $visible) + { + $this->isVisible = $visible; + + return $this; + } + /** * {@inheritdoc} */ @@ -53,6 +68,9 @@ class CustomerFixtures extends Fixture for ($i = 0; $i < $this->amount; $i++) { $visible = 0 != $i % 3; + if (null !== $this->isVisible) { + $visible = $this->isVisible; + } $entity = new Customer(); $entity ->setCurrency($faker->currencyCode) diff --git a/tests/DataFixtures/ProjectFixtures.php b/tests/DataFixtures/ProjectFixtures.php index a18cbd8c..729a3b27 100644 --- a/tests/DataFixtures/ProjectFixtures.php +++ b/tests/DataFixtures/ProjectFixtures.php @@ -25,6 +25,10 @@ class ProjectFixtures extends Fixture * @var int */ protected $amount = 0; + /** + * @var bool + */ + protected $isVisible = null; /** * @return int @@ -45,6 +49,17 @@ class ProjectFixtures extends Fixture return $this; } + /** + * @param bool $visible + * @return $this + */ + public function setIsVisible(bool $visible) + { + $this->isVisible = $visible; + + return $this; + } + /** * {@inheritdoc} */ @@ -55,6 +70,9 @@ class ProjectFixtures extends Fixture for ($i = 0; $i < $this->amount; $i++) { $visible = 0 != $i % 3; + if (null !== $this->isVisible) { + $visible = $this->isVisible; + } $entity = new Project(); $entity ->setName($faker->catchPhrase . ($visible ? '' : ' (x)')) diff --git a/translations/messages.de.xliff b/translations/messages.de.xliff index 2158316e..1c55a73d 100644 --- a/translations/messages.de.xliff +++ b/translations/messages.de.xliff @@ -442,6 +442,7 @@ Momentan existieren für das Projekt %project% des Kunden %customer% insgesamt %activities% Tätigkeiten und %records% Zeiteinträge, welche sich auf eine Gesamtdauer von %duration% belaufen. Diese Tätigkeiten und Zeiteinträge werden ebenfalls mit gelöscht! + Alternativ können Sie ein Projekt auswählen, auf das alle existierenden Aktivitäten und Einträge umgebucht werden: @@ -462,6 +463,7 @@ Momentan existieren für die Tätigkeit %activity% im Projekt %project% für den Kunden %customer% insgesamt %records% Zeiteinträge, welche sich auf eine Gesamtdauer von %duration% belaufen. Diese Zeiteinträge werden ebenfalls mit gelöscht! + Alternativ können Sie eine Tätigkeit auswählen, auf die alle existierenden Einträge umgebucht werden: @@ -530,6 +532,7 @@ Momentan existieren für den Kunden %customer% insgesamt %project% Projekte mit %activity% Tätigkeiten, die sich in %records% Zeiteinträgen auf eine Gesamtdauer von %duration% belaufen. Diese Projekte, Tätigkeiten und Zeiteinträge werden ebenfalls mit gelöscht! + Alternativ können Sie einen Kunden auswählen, auf den alle Daten umgebucht werden: diff --git a/translations/messages.en.xliff b/translations/messages.en.xliff index 4132190d..ee86ba82 100644 --- a/translations/messages.en.xliff +++ b/translations/messages.en.xliff @@ -442,6 +442,7 @@ Currently the project %project% for the customer %customer% has %activities% activities and %records% time-records, which count up to a total of %duration%. These activities and time-records will be deleted as well! + Alternatively, you can select a project to which all existing activities and entries are transferred: @@ -462,6 +463,7 @@ Currently the activity %activity% in the project %project% for the customer %customer% has %records% time-records, which count up to a total of %duration%. These time-records will be deleted as well! + Alternatively, you can select an activity to which all existing entries are transferred: @@ -530,6 +532,7 @@ Currently the customer %customer% has %project% projects with %activity% activities, which count up in %records% time-records to a total of %duration%. These projects, activities and time-records will be deleted as well! + Alternatively, you can select a customer to whom all data is transferred: