option to move entries from one entity to another upon deletion (#409)

This commit is contained in:
Kevin Papst
2018-11-12 00:01:49 +01:00
committed by GitHub
parent 1dabeedaa0
commit f9dc42d1f9
18 changed files with 524 additions and 23 deletions

View File

@@ -763,7 +763,6 @@ class KimaiImporterCommand extends Command
array $rates, array $rates,
?Project $project ?Project $project
) { ) {
$activityId = $oldActivity['activityID']; $activityId = $oldActivity['activityID'];
$projectId = null !== $project ? $project->getId() : null; $projectId = null !== $project ? $project->getId() : null;

View File

@@ -13,7 +13,10 @@ use App\Controller\AbstractController;
use App\Entity\Activity; use App\Entity\Activity;
use App\Form\ActivityEditForm; use App\Form\ActivityEditForm;
use App\Form\Toolbar\ActivityToolbarForm; use App\Form\Toolbar\ActivityToolbarForm;
use App\Form\Type\ActivityType;
use App\Repository\ActivityRepository;
use App\Repository\Query\ActivityQuery; use App\Repository\Query\ActivityQuery;
use Doctrine\ORM\ORMException;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
@@ -105,6 +108,22 @@ class ActivityController extends AbstractController
$stats = $this->getRepository()->getActivityStatistics($activity); $stats = $this->getRepository()->getActivityStatistics($activity);
$deleteForm = $this->createFormBuilder() $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()])) ->setAction($this->generateUrl('admin_activity_delete', ['id' => $activity->getId()]))
->setMethod('POST') ->setMethod('POST')
->getForm(); ->getForm();
@@ -112,11 +131,12 @@ class ActivityController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) {
$entityManager = $this->getDoctrine()->getManager(); try {
$entityManager->remove($activity); $this->getRepository()->deleteActivity($activity, $deleteForm->get('activity')->getData());
$entityManager->flush(); $this->flashSuccess('action.delete.success');
} catch (ORMException $ex) {
$this->flashSuccess('action.delete.success'); $this->flashError('action.delete.error');
}
return $this->redirectToRoute('admin_activity'); return $this->redirectToRoute('admin_activity');
} }

View File

@@ -13,7 +13,10 @@ use App\Controller\AbstractController;
use App\Entity\Customer; use App\Entity\Customer;
use App\Form\CustomerEditForm; use App\Form\CustomerEditForm;
use App\Form\Toolbar\CustomerToolbarForm; use App\Form\Toolbar\CustomerToolbarForm;
use App\Form\Type\CustomerType;
use App\Repository\CustomerRepository;
use App\Repository\Query\CustomerQuery; use App\Repository\Query\CustomerQuery;
use Doctrine\ORM\ORMException;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -140,6 +143,18 @@ class CustomerController extends AbstractController
$stats = $this->getRepository()->getCustomerStatistics($customer); $stats = $this->getRepository()->getCustomerStatistics($customer);
$deleteForm = $this->createFormBuilder() $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()])) ->setAction($this->generateUrl('admin_customer_delete', ['id' => $customer->getId()]))
->setMethod('POST') ->setMethod('POST')
->getForm(); ->getForm();
@@ -147,11 +162,12 @@ class CustomerController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) {
$entityManager = $this->getDoctrine()->getManager(); try {
$entityManager->remove($customer); $this->getRepository()->deleteCustomer($customer, $deleteForm->get('customer')->getData());
$entityManager->flush(); $this->flashSuccess('action.delete.success');
} catch (ORMException $ex) {
$this->flashSuccess('action.delete.success'); $this->flashError('action.delete.error');
}
return $this->redirectToRoute('admin_customer'); return $this->redirectToRoute('admin_customer');
} }

View File

@@ -14,7 +14,10 @@ use App\Entity\Customer;
use App\Entity\Project; use App\Entity\Project;
use App\Form\ProjectEditForm; use App\Form\ProjectEditForm;
use App\Form\Toolbar\ProjectToolbarForm; use App\Form\Toolbar\ProjectToolbarForm;
use App\Form\Type\ProjectType;
use App\Repository\ProjectRepository;
use App\Repository\Query\ProjectQuery; use App\Repository\Query\ProjectQuery;
use Doctrine\ORM\ORMException;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
@@ -99,6 +102,19 @@ class ProjectController extends AbstractController
$stats = $this->getRepository()->getProjectStatistics($project); $stats = $this->getRepository()->getProjectStatistics($project);
$deleteForm = $this->createFormBuilder() $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()])) ->setAction($this->generateUrl('admin_project_delete', ['id' => $project->getId()]))
->setMethod('POST') ->setMethod('POST')
->getForm(); ->getForm();
@@ -106,11 +122,12 @@ class ProjectController extends AbstractController
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) { if (0 == $stats->getRecordAmount() || ($deleteForm->isSubmitted() && $deleteForm->isValid())) {
$entityManager = $this->getDoctrine()->getManager(); try {
$entityManager->remove($project); $this->getRepository()->deleteProject($project, $deleteForm->get('project')->getData());
$entityManager->flush(); $this->flashSuccess('action.delete.success');
} catch (ORMException $ex) {
$this->flashSuccess('action.delete.success'); $this->flashError('action.delete.error');
}
return $this->redirectToRoute('admin_project'); return $this->redirectToRoute('admin_project');
} }

View File

@@ -15,6 +15,7 @@ use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Model\ActivityStatistic; use App\Model\ActivityStatistic;
use App\Repository\Query\ActivityQuery; use App\Repository\Query\ActivityQuery;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
@@ -183,6 +184,11 @@ class ActivityRepository extends AbstractRepository
$qb->setParameter('customer', $query->getCustomer()); $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(); $or = $qb->expr()->orX();
// this must always be the last part before the or // this must always be the last part before the or
@@ -204,4 +210,38 @@ class ActivityRepository extends AbstractRepository
return $this->getBaseQueryResult($qb, $query); 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;
}
}
} }

View File

@@ -15,6 +15,7 @@ use App\Entity\Project;
use App\Entity\Timesheet; use App\Entity\Timesheet;
use App\Model\CustomerStatistic; use App\Model\CustomerStatistic;
use App\Repository\Query\CustomerQuery; use App\Repository\Query\CustomerQuery;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
@@ -121,6 +122,43 @@ class CustomerRepository extends AbstractRepository
$qb->andWhere('c.visible = 0'); $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); 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;
}
}
} }

View File

@@ -15,6 +15,7 @@ use App\Entity\Project;
use App\Entity\Timesheet; use App\Entity\Timesheet;
use App\Model\ProjectStatistic; use App\Model\ProjectStatistic;
use App\Repository\Query\ProjectQuery; use App\Repository\Query\ProjectQuery;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
@@ -131,6 +132,53 @@ class ProjectRepository extends AbstractRepository
->setParameter('customer', $query->getCustomer()); ->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); 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;
}
}
} }

View File

@@ -9,9 +9,34 @@
namespace App\Repository\Query; namespace App\Repository\Query;
use App\Entity\Customer;
/** /**
* Can be used for advanced queries with the: CustomerRepository * Can be used for advanced queries with the: CustomerRepository
*/ */
class CustomerQuery extends VisibilityQuery 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;
}
} }

View File

@@ -21,6 +21,30 @@ class ProjectQuery extends VisibilityQuery
*/ */
protected $customer; 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 * @return Customer|int
*/ */

View File

@@ -291,6 +291,7 @@ class TimesheetRepository extends AbstractRepository
$qb->andWhere('t.begin >= :begin') $qb->andWhere('t.begin >= :begin')
->setParameter('begin', $query->getBegin()); ->setParameter('begin', $query->getBegin());
} }
if (null !== $query->getEnd()) { if (null !== $query->getEnd()) {
$qb->andWhere('t.end <= :end') $qb->andWhere('t.end <= :end')
->setParameter('end', $query->getEnd()); ->setParameter('end', $query->getEnd());
@@ -299,12 +300,16 @@ class TimesheetRepository extends AbstractRepository
if (null !== $query->getActivity()) { if (null !== $query->getActivity()) {
$qb->andWhere('t.activity = :activity') $qb->andWhere('t.activity = :activity')
->setParameter('activity', $query->getActivity()); ->setParameter('activity', $query->getActivity());
} elseif (null !== $query->getProject()) { }
$qb->andWhere('t.project = :project')
->setParameter('project', $query->getProject()); if (null === $query->getActivity() || null === $query->getActivity()->getProject()) {
} elseif (null !== $query->getCustomer()) { if (null !== $query->getProject()) {
$qb->andWhere('p.customer = :customer') $qb->andWhere('t.project = :project')
->setParameter('customer', $query->getCustomer()); ->setParameter('project', $query->getProject());
} elseif (null !== $query->getCustomer()) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
}
} }
return $this->getBaseQueryResult($qb, $query); return $this->getBaseQueryResult($qb, $query);

View File

@@ -9,8 +9,10 @@
namespace App\Tests\Controller\Admin; namespace App\Tests\Controller\Admin;
use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\Controller\ControllerBaseTest; use App\Tests\Controller\ControllerBaseTest;
use App\Tests\DataFixtures\ActivityFixtures;
use App\Tests\DataFixtures\ProjectFixtures; use App\Tests\DataFixtures\ProjectFixtures;
use App\Tests\DataFixtures\TimesheetFixtures; use App\Tests\DataFixtures\TimesheetFixtures;
@@ -127,6 +129,14 @@ class ActivityControllerTest extends ControllerBaseTest
$fixture->setAmount(10); $fixture->setAmount(10);
$this->importFixture($em, $fixture); $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->request($client, '/admin/activity/1/delete');
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
@@ -139,6 +149,60 @@ class ActivityControllerTest extends ControllerBaseTest
$this->assertHasDataTable($client); $this->assertHasDataTable($client);
$this->assertHasFlashSuccess($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->request($client, '/admin/activity/1/edit');
$this->assertFalse($client->getResponse()->isSuccessful()); $this->assertFalse($client->getResponse()->isSuccessful());
} }

View File

@@ -9,6 +9,7 @@
namespace App\Tests\Controller\Admin; namespace App\Tests\Controller\Admin;
use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\Controller\ControllerBaseTest; use App\Tests\Controller\ControllerBaseTest;
use App\Tests\DataFixtures\CustomerFixtures; use App\Tests\DataFixtures\CustomerFixtures;
@@ -112,6 +113,14 @@ class CustomerControllerTest extends ControllerBaseTest
$fixture->setAmount(10); $fixture->setAmount(10);
$this->importFixture($em, $fixture); $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->request($client, '/admin/customer/1/delete');
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
@@ -124,6 +133,60 @@ class CustomerControllerTest extends ControllerBaseTest
$this->assertHasDataTable($client); $this->assertHasDataTable($client);
$this->assertHasFlashSuccess($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->request($client, '/admin/customer/1/edit');
$this->assertFalse($client->getResponse()->isSuccessful()); $this->assertFalse($client->getResponse()->isSuccessful());
} }

View File

@@ -9,6 +9,7 @@
namespace App\Tests\Controller\Admin; namespace App\Tests\Controller\Admin;
use App\Entity\Timesheet;
use App\Entity\User; use App\Entity\User;
use App\Tests\Controller\ControllerBaseTest; use App\Tests\Controller\ControllerBaseTest;
use App\Tests\DataFixtures\CustomerFixtures; use App\Tests\DataFixtures\CustomerFixtures;
@@ -135,6 +136,14 @@ class ProjectControllerTest extends ControllerBaseTest
$fixture->setAmount(10); $fixture->setAmount(10);
$this->importFixture($em, $fixture); $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->request($client, '/admin/project/1/delete');
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
@@ -147,6 +156,60 @@ class ProjectControllerTest extends ControllerBaseTest
$this->assertHasDataTable($client); $this->assertHasDataTable($client);
$this->assertHasFlashSuccess($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->request($client, '/admin/project/1/edit');
$this->assertFalse($client->getResponse()->isSuccessful()); $this->assertFalse($client->getResponse()->isSuccessful());
} }

View File

@@ -25,6 +25,14 @@ class ActivityFixtures extends Fixture
* @var int * @var int
*/ */
protected $amount = 0; protected $amount = 0;
/**
* @var bool
*/
protected $isGlobal = false;
/**
* @var bool
*/
protected $isVisible = null;
/** /**
* @return int * @return int
@@ -34,6 +42,28 @@ class ActivityFixtures extends Fixture
return $this->amount; 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 * @param int $amount
* @return ActivityFixtures * @return ActivityFixtures
@@ -55,10 +85,17 @@ class ActivityFixtures extends Fixture
// random amount of timesheet entries for every user // random amount of timesheet entries for every user
for ($i = 0; $i < $this->amount; $i++) { for ($i = 0; $i < $this->amount; $i++) {
$project = null;
if (false === $this->isGlobal) {
$project = $projects[array_rand($projects)];
}
$visible = 0 != $i % 3; $visible = 0 != $i % 3;
if (null !== $this->isVisible) {
$visible = $this->isVisible;
}
$entity = new Activity(); $entity = new Activity();
$entity $entity
->setProject($projects[array_rand($projects)]) ->setProject($project)
->setName($faker->bs . ($visible ? '' : ' (x)')) ->setName($faker->bs . ($visible ? '' : ' (x)'))
->setComment($faker->text) ->setComment($faker->text)
->setVisible($visible) ->setVisible($visible)

View File

@@ -24,6 +24,10 @@ class CustomerFixtures extends Fixture
* @var int * @var int
*/ */
protected $amount = 0; protected $amount = 0;
/**
* @var bool
*/
protected $isVisible = null;
/** /**
* @return int * @return int
@@ -44,6 +48,17 @@ class CustomerFixtures extends Fixture
return $this; return $this;
} }
/**
* @param bool $visible
* @return $this
*/
public function setIsVisible(bool $visible)
{
$this->isVisible = $visible;
return $this;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@@ -53,6 +68,9 @@ class CustomerFixtures extends Fixture
for ($i = 0; $i < $this->amount; $i++) { for ($i = 0; $i < $this->amount; $i++) {
$visible = 0 != $i % 3; $visible = 0 != $i % 3;
if (null !== $this->isVisible) {
$visible = $this->isVisible;
}
$entity = new Customer(); $entity = new Customer();
$entity $entity
->setCurrency($faker->currencyCode) ->setCurrency($faker->currencyCode)

View File

@@ -25,6 +25,10 @@ class ProjectFixtures extends Fixture
* @var int * @var int
*/ */
protected $amount = 0; protected $amount = 0;
/**
* @var bool
*/
protected $isVisible = null;
/** /**
* @return int * @return int
@@ -45,6 +49,17 @@ class ProjectFixtures extends Fixture
return $this; return $this;
} }
/**
* @param bool $visible
* @return $this
*/
public function setIsVisible(bool $visible)
{
$this->isVisible = $visible;
return $this;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@@ -55,6 +70,9 @@ class ProjectFixtures extends Fixture
for ($i = 0; $i < $this->amount; $i++) { for ($i = 0; $i < $this->amount; $i++) {
$visible = 0 != $i % 3; $visible = 0 != $i % 3;
if (null !== $this->isVisible) {
$visible = $this->isVisible;
}
$entity = new Project(); $entity = new Project();
$entity $entity
->setName($faker->catchPhrase . ($visible ? '' : ' (x)')) ->setName($faker->catchPhrase . ($visible ? '' : ' (x)'))

View File

@@ -442,6 +442,7 @@
Momentan existieren für das Projekt %project% des Kunden %customer% insgesamt %activities% 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. 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! 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:
</target> </target>
</trans-unit> </trans-unit>
@@ -462,6 +463,7 @@
Momentan existieren für die Tätigkeit %activity% im Projekt %project% für den Kunden %customer% 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 insgesamt %records% Zeiteinträge, welche sich auf eine Gesamtdauer von %duration% belaufen. Diese
Zeiteinträge werden ebenfalls mit gelöscht! Zeiteinträge werden ebenfalls mit gelöscht!
Alternativ können Sie eine Tätigkeit auswählen, auf die alle existierenden Einträge umgebucht werden:
</target> </target>
</trans-unit> </trans-unit>
@@ -530,6 +532,7 @@
Momentan existieren für den Kunden %customer% insgesamt %project% Projekte mit %activity% 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 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! 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:
</target> </target>
</trans-unit> </trans-unit>

View File

@@ -442,6 +442,7 @@
Currently the project %project% for the customer %customer% has %activities% Currently the project %project% for the customer %customer% has %activities%
activities and %records% time-records, which count up to a total of %duration%. activities and %records% time-records, which count up to a total of %duration%.
These activities and time-records will be deleted as well! 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:
</target> </target>
</trans-unit> </trans-unit>
@@ -462,6 +463,7 @@
Currently the activity %activity% in the project %project% for the customer %customer% Currently the activity %activity% in the project %project% for the customer %customer%
has %records% time-records, which count up to a total of %duration%. has %records% time-records, which count up to a total of %duration%.
These time-records will be deleted as well! These time-records will be deleted as well!
Alternatively, you can select an activity to which all existing entries are transferred:
</target> </target>
</trans-unit> </trans-unit>
@@ -530,6 +532,7 @@
Currently the customer %customer% has %project% projects with %activity% activities, which count up Currently the customer %customer% has %project% projects with %activity% activities, which count up
in %records% time-records to a total of %duration%. in %records% time-records to a total of %duration%.
These projects, activities and time-records will be deleted as well! These projects, activities and time-records will be deleted as well!
Alternatively, you can select a customer to whom all data is transferred:
</target> </target>
</trans-unit> </trans-unit>