* improved dev fixtures with more diversity, more data, better testcases, user avatars #42 * added customer stats to dashboard, fixed column length for 3 widgets #42 * fixed empty alias - display empty message for new user #42 * unified-ui for "new" toolbar icon #42 * use kimai2_ as database prefix #42 * added customer stats to dashboard, fixed column length for 3 widgets #42 * fix long project/customer names in "currently active" navbar flyout" #42 * added services config for dev environment #42 * added command to run unit tests #42 * added command to run integration tests #42 * added command to run code sniffer #42 * added phpcs and phpunit to composer #42 * created tests directory #42 * fixed code sniffer warnings #42 * added function to switch result type from Pagerfanta to QueryBuilder #42 * dramatically reduced database calls by using custom joined query #42 * added command to install kimai dependencies #42 * added dev:reset command #42
This commit is contained in:
@@ -44,7 +44,8 @@ class ActivityController extends Controller
|
||||
public function recentActivitiesAction()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$activeEntries = $this->getRepository()->getRecentActivities($user, new \DateTime('-30 days')); // TODO make days configurable
|
||||
// TODO make days configurable
|
||||
$activeEntries = $this->getRepository()->getRecentActivities($user, new \DateTime('-30 days'));
|
||||
|
||||
return $this->render(
|
||||
'TimesheetBundle:Navbar:recent-activities.html.twig',
|
||||
|
||||
@@ -14,14 +14,12 @@ namespace TimesheetBundle\Controller\Admin;
|
||||
use AppBundle\Controller\AbstractController;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
use TimesheetBundle\Form\ActivityEditForm;
|
||||
use TimesheetBundle\Repository\ActivityRepository;
|
||||
use TimesheetBundle\Repository\Query\ActivityQuery;
|
||||
|
||||
/**
|
||||
@@ -89,9 +87,7 @@ class ActivityController extends AbstractController
|
||||
|
||||
$this->flashSuccess('action.updated_successfully');
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'admin_activity', ['id' => $activity->getId()]
|
||||
);
|
||||
return $this->redirectToRoute('admin_activity', ['id' => $activity->getId()]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
@@ -105,7 +101,7 @@ class ActivityController extends AbstractController
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @return \Symfony\Component\Form\Form
|
||||
* @return \Symfony\Component\Form\FormInterface
|
||||
*/
|
||||
private function createEditForm(Activity $activity)
|
||||
{
|
||||
|
||||
@@ -87,9 +87,7 @@ class CustomerController extends AbstractController
|
||||
|
||||
$this->flashSuccess('action.updated_successfully');
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'admin_customer', ['id' => $customer->getId()]
|
||||
);
|
||||
return $this->redirectToRoute('admin_customer', ['id' => $customer->getId()]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
|
||||
@@ -88,9 +88,7 @@ class ProjectController extends AbstractController
|
||||
|
||||
$this->flashSuccess('action.updated_successfully');
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'admin_project', ['id' => $project->getId()]
|
||||
);
|
||||
return $this->redirectToRoute('admin_project', ['id' => $project->getId()]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
|
||||
@@ -34,10 +34,16 @@ class TimesheetController extends AbstractController
|
||||
use TimesheetControllerTrait;
|
||||
|
||||
/**
|
||||
* This route shows all users timesheet entries.
|
||||
*
|
||||
* @Route("/", defaults={"page": 1}, name="admin_timesheet")
|
||||
* @Route("/page/{page}", requirements={"page": "[1-9]\d*"}, name="admin_timesheet_paginated")
|
||||
* @Method("GET")
|
||||
* @Cache(smaxage="10")
|
||||
*
|
||||
* @param $page
|
||||
* @param Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function indexAction($page, Request $request)
|
||||
{
|
||||
@@ -62,10 +68,9 @@ class TimesheetController extends AbstractController
|
||||
* @Method({"GET"})
|
||||
*
|
||||
* @param Timesheet $entry
|
||||
* @param Request $request
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function stopAction(Timesheet $entry, Request $request)
|
||||
public function stopAction(Timesheet $entry)
|
||||
{
|
||||
try {
|
||||
$this->getRepository()->stopRecording($entry);
|
||||
|
||||
@@ -89,7 +89,13 @@ class TimesheetController extends AbstractController
|
||||
|
||||
// make sure only ADMIN can stop other users entries
|
||||
if ($user->getId() !== $entry->getUser()->getId()) {
|
||||
$this->denyUnlessGranted('ROLE_ADMIN', null, 'timesheet.access.denied', ['%user%' => $user->getId(), '%entry%' => $entry->getId()]);
|
||||
// TODO move me to a voter
|
||||
$this->denyUnlessGranted(
|
||||
'ROLE_ADMIN',
|
||||
null,
|
||||
'timesheet.access.denied',
|
||||
['%user%' => $user->getId(), '%entry%' => $entry->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -141,7 +147,13 @@ class TimesheetController extends AbstractController
|
||||
|
||||
// make sure only ADMIN can edit other users entries
|
||||
if ($user->getId() !== $entry->getUser()->getId()) {
|
||||
$this->denyUnlessGranted('ROLE_ADMIN', null, 'timesheet.access.denied', ['%user%' => $user->getId(), '%entry%' => $entry->getId()]);
|
||||
// TODO move me to a voter
|
||||
$this->denyUnlessGranted(
|
||||
'ROLE_ADMIN',
|
||||
null,
|
||||
'timesheet.access.denied',
|
||||
['%user%' => $user->getId(), '%entry%' => $entry->getId()]
|
||||
);
|
||||
}
|
||||
|
||||
$editForm = $this->createEditForm($entry, $request->get('page'));
|
||||
@@ -155,9 +167,7 @@ class TimesheetController extends AbstractController
|
||||
|
||||
$this->flashSuccess('action.updated_successfully');
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'timesheet_paginated', ['page' => $request->get('page')]
|
||||
);
|
||||
return $this->redirectToRoute('timesheet_paginated', ['page' => $request->get('page')]);
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
|
||||
@@ -71,7 +71,7 @@ trait TimesheetControllerTrait
|
||||
} else {
|
||||
$customer = null;
|
||||
}
|
||||
} else if ($customer !== null) {
|
||||
} elseif ($customer !== null) {
|
||||
$repo = $this->getDoctrine()->getRepository(Customer::class);
|
||||
$customer = $repo->getById($customer);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
namespace TimesheetBundle\DataFixtures\ORM;
|
||||
|
||||
use AppBundle\Entity\User;
|
||||
use Symfony\Component\Intl\Intl;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
use TimesheetBundle\Entity\Project;
|
||||
@@ -30,8 +29,7 @@ use AppBundle\DataFixtures\ORM\LoadFixtures as AppBundleLoadFixtures;
|
||||
*/
|
||||
class LoadFixtures extends AppBundleLoadFixtures
|
||||
{
|
||||
const AMOUNT_ACTIVITIES = 10; // maximum activites per project
|
||||
const AMOUNT_TIMESHEET = 1000; // timesheet entries total
|
||||
const AMOUNT_TIMESHEET = 5000; // timesheet entries total
|
||||
const RATE_MIN = 10; // minimum rate for one hour
|
||||
const RATE_MAX = 80; // maximum rate for one hour
|
||||
|
||||
@@ -109,9 +107,11 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
{
|
||||
$allUser = $this->getAllUsers($manager);
|
||||
$amountUser = count($allUser);
|
||||
|
||||
$allActivity = $this->getAllActivities($manager);
|
||||
|
||||
// by using array_pop we make sure that at least one activity has NO entry!
|
||||
array_pop($allActivity);
|
||||
|
||||
for ($i = 0; $i <= self::AMOUNT_TIMESHEET; $i++) {
|
||||
$entry = $this->createTimesheetEntry(
|
||||
$allUser[rand(1, $amountUser)],
|
||||
@@ -123,14 +123,18 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
$manager->persist($entry);
|
||||
}
|
||||
|
||||
// leave one running time entry for each user
|
||||
for ($i = 1; $i <= $amountUser; $i++) {
|
||||
$entry = $this->createTimesheetEntry(
|
||||
$allUser[$i],
|
||||
$allActivity[array_rand($allActivity)]
|
||||
);
|
||||
// by using array_pop we make sure that at least one user has NO running entry!
|
||||
array_pop($allUser);
|
||||
|
||||
$manager->persist($entry);
|
||||
// create active recodinge for test user
|
||||
foreach ($allUser as $id => $user) {
|
||||
for ($i = 0; $i < rand(1, 4); $i++) {
|
||||
$entry = $this->createTimesheetEntry(
|
||||
$user,
|
||||
$allActivity[array_rand($allActivity)]
|
||||
);
|
||||
$manager->persist($entry);
|
||||
}
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
@@ -174,17 +178,18 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
$amountTimezone = count($allTimezones);
|
||||
|
||||
$allCustomer = $this->getCustomers();
|
||||
$amountCustomer = count($allCustomer);
|
||||
shuffle($allCustomer);
|
||||
$i = 0;
|
||||
|
||||
for ($i = 0; $i < $amountCustomer; $i++) {
|
||||
foreach ($allCustomer as $customerName) {
|
||||
$entry = new Customer();
|
||||
$entry
|
||||
->setCurrency($this->getRandomCurrency())
|
||||
->setVat(rand(0, 30))
|
||||
->setName($allCustomer[$i])
|
||||
->setName($customerName)
|
||||
->setAddress($this->getRandomLocation())
|
||||
->setComment($this->getRandomPhrase())
|
||||
->setVisible($i % 3 != 0)
|
||||
->setVisible($i++ % 3 != 0)
|
||||
->setTimezone($allTimezones[rand(1, $amountTimezone)]);
|
||||
|
||||
$manager->persist($entry);
|
||||
@@ -195,19 +200,21 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
private function loadProjects(ObjectManager $manager)
|
||||
{
|
||||
$allCustomer = $this->getAllCustomers($manager);
|
||||
$amountCustomer = count($allCustomer);
|
||||
|
||||
for ($i = 0; $i < $amountCustomer * 2; $i++) {
|
||||
foreach ($allCustomer as $id => $customer) {
|
||||
$projectForCustomer = rand(0, 7);
|
||||
for ($i = 0; $i < $projectForCustomer; $i++) {
|
||||
$entry = new Project();
|
||||
|
||||
$entry = new Project();
|
||||
$entry
|
||||
->setName($this->getRandomProject())
|
||||
->setBudget(rand(1000, 100000))
|
||||
->setComment($this->getRandomPhrase())
|
||||
->setCustomer($allCustomer[($i % $amountCustomer) + 1])
|
||||
->setVisible($i % 3 != 0);
|
||||
$entry
|
||||
->setName($this->getRandomProject())
|
||||
->setBudget(rand(500, 100000))
|
||||
->setComment($this->getRandomPhrase())
|
||||
->setCustomer($customer)
|
||||
->setVisible($i % 3 != 0);
|
||||
|
||||
$manager->persist($entry);
|
||||
$manager->persist($entry);
|
||||
}
|
||||
}
|
||||
$manager->flush();
|
||||
}
|
||||
@@ -217,7 +224,7 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
$allProject = $this->getAllProjects($manager);
|
||||
|
||||
foreach ($allProject as $projectId => $project) {
|
||||
$activityCount = rand(1, self::AMOUNT_ACTIVITIES);
|
||||
$activityCount = rand(0, 10);
|
||||
for ($i = 0; $i < $activityCount; $i++) {
|
||||
$entry = new Activity();
|
||||
$entry
|
||||
@@ -238,12 +245,11 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
private function getActivities()
|
||||
{
|
||||
return [
|
||||
'Design',
|
||||
'Designing',
|
||||
'Programming',
|
||||
'Testing',
|
||||
'Documentation',
|
||||
'Pause',
|
||||
'Internal',
|
||||
'Research',
|
||||
'Meeting',
|
||||
'Hosting',
|
||||
@@ -256,6 +262,11 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
'Management',
|
||||
'Setup',
|
||||
'Planning',
|
||||
'Skiing',
|
||||
'Eating',
|
||||
'Watching TV',
|
||||
'Talking',
|
||||
'Cooking'
|
||||
];
|
||||
}
|
||||
|
||||
@@ -282,8 +293,10 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
'Hosting & Server',
|
||||
'Customer Relations',
|
||||
'Infrastructure',
|
||||
'Princess Cat',
|
||||
'Software Upgrade',
|
||||
'Office Management',
|
||||
'Project X',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -310,10 +323,20 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
'Amsterdam',
|
||||
'London',
|
||||
'San Francisco',
|
||||
'Tokio',
|
||||
'Tokyo',
|
||||
'Berlin',
|
||||
'Sao Paulo',
|
||||
'Mexico City',
|
||||
'Moscow',
|
||||
'Sankt Petersburg',
|
||||
'Taiwan',
|
||||
'Perth',
|
||||
'Sydney',
|
||||
'Mumbai',
|
||||
'Lagos',
|
||||
'Karachi',
|
||||
'Shanghai',
|
||||
'Delhi',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -342,18 +365,34 @@ class LoadFixtures extends AppBundleLoadFixtures
|
||||
'Twitter',
|
||||
'Zend',
|
||||
'SensioLabs',
|
||||
'Samsung',
|
||||
'Huawai',
|
||||
'Yandex',
|
||||
'Baidu',
|
||||
'Alphabet',
|
||||
'Amazon.com',
|
||||
'Berkshire Hathaway',
|
||||
'Facebook',
|
||||
'ExxonMobil',
|
||||
'Nestle',
|
||||
'Johnson & Johnson',
|
||||
'Alibaba',
|
||||
'General Electric',
|
||||
'Procter & Gamble',
|
||||
'Wal-Mart Stores',
|
||||
'Novartis',
|
||||
'Coca-Cola',
|
||||
'Wikipedia',
|
||||
'Walt Disney',
|
||||
'Merck',
|
||||
'Pfizer',
|
||||
"L'Oréal Group",
|
||||
"McDonald's",
|
||||
'China Petroleum & Chemical',
|
||||
'GlaxoSmithKline'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getRandomCustomer()
|
||||
{
|
||||
$all = $this->getCustomers();
|
||||
return $all[array_rand($all)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
|
||||
@@ -71,7 +71,11 @@ class Project
|
||||
/**
|
||||
* @var Activity[]
|
||||
*
|
||||
* @ORM\OneToMany(targetEntity="TimesheetBundle\Entity\Activity", mappedBy="project", cascade={"persist", "merge", "remove"})
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity="TimesheetBundle\Entity\Activity",
|
||||
* mappedBy="project",
|
||||
* cascade={"persist", "merge", "remove"}
|
||||
* )
|
||||
*/
|
||||
private $activities;
|
||||
|
||||
|
||||
@@ -18,7 +18,13 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
* Timesheet entity.
|
||||
*
|
||||
* @ORM\Entity(repositoryClass="TimesheetBundle\Repository\TimesheetRepository")
|
||||
* @ORM\Table(name="timesheet", indexes={@ORM\Index(columns={"user"}), @ORM\Index(name="activity", columns={"activity"})})
|
||||
* @ORM\Table(
|
||||
* name="timesheet",
|
||||
* indexes={
|
||||
* @ORM\Index(columns={"user"}),
|
||||
* @ORM\Index(name="activity", columns={"activity"})
|
||||
* }
|
||||
* )
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
|
||||
@@ -77,4 +77,4 @@ class Menu
|
||||
)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Repository\ActivityRepository;
|
||||
use TimesheetBundle\Repository\Query\ActivityQuery;
|
||||
|
||||
/**
|
||||
* Custom form field type to select an activity.
|
||||
@@ -24,17 +26,6 @@ use TimesheetBundle\Entity\Activity;
|
||||
class ActivityType extends AbstractType
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @param $key
|
||||
* @param $index
|
||||
* @return string
|
||||
*/
|
||||
public function groupBy(Activity $activity, $key, $index)
|
||||
{
|
||||
return $activity->getProject()->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -44,7 +35,14 @@ class ActivityType extends AbstractType
|
||||
'class' => 'TimesheetBundle:Activity',
|
||||
'choice_label' => 'name',
|
||||
'choice_value' => 'id',
|
||||
'group_by' => array($this, 'groupBy'),
|
||||
'group_by' => function (Activity $activity, $key, $index) {
|
||||
return $activity->getProject()->getName();
|
||||
},
|
||||
'query_builder' => function (ActivityRepository $repo) {
|
||||
$query = new ActivityQuery();
|
||||
$query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER);
|
||||
return $repo->findByQuery($query);
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Entity\Project;
|
||||
use TimesheetBundle\Repository\ProjectRepository;
|
||||
use TimesheetBundle\Repository\Query\ProjectQuery;
|
||||
|
||||
/**
|
||||
* Custom form field type to select a project.
|
||||
@@ -33,9 +35,14 @@ class ProjectType extends AbstractType
|
||||
'class' => 'TimesheetBundle:Project',
|
||||
'choice_label' => 'name',
|
||||
'choice_value' => 'id',
|
||||
'group_by' => function(Project $project, $key, $index) {
|
||||
'group_by' => function (Project $project, $key, $index) {
|
||||
return $project->getCustomer()->getName();
|
||||
},
|
||||
'query_builder' => function (ProjectRepository $repo) {
|
||||
$query = new ProjectQuery();
|
||||
$query->setResultType(ProjectQuery::RESULT_TYPE_QUERYBUILDER);
|
||||
return $repo->findByQuery($query);
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ class ActivityRepository extends AbstractRepository
|
||||
|
||||
$activities = [];
|
||||
/* @var Timesheet $entry */
|
||||
foreach($results as $entry) {
|
||||
foreach ($results as $entry) {
|
||||
$activities[] = $entry->getActivity();
|
||||
}
|
||||
|
||||
@@ -93,10 +93,9 @@ class ActivityRepository extends AbstractRepository
|
||||
return $stats;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ActivityQuery $query
|
||||
* @return \Pagerfanta\Pagerfanta
|
||||
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
|
||||
*/
|
||||
public function findByQuery(ActivityQuery $query)
|
||||
{
|
||||
@@ -116,6 +115,6 @@ class ActivityRepository extends AbstractRepository
|
||||
// TODO check for visibility of customer and project
|
||||
}
|
||||
|
||||
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
|
||||
return $this->getBaseQueryResult($qb, $query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class CustomerRepository extends AbstractRepository
|
||||
|
||||
/**
|
||||
* @param CustomerQuery $query
|
||||
* @return \Pagerfanta\Pagerfanta
|
||||
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
|
||||
*/
|
||||
public function findByQuery(CustomerQuery $query)
|
||||
{
|
||||
@@ -68,6 +68,6 @@ class CustomerRepository extends AbstractRepository
|
||||
$qb->andWhere('c.visible = 0');
|
||||
}
|
||||
|
||||
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
|
||||
return $this->getBaseQueryResult($qb, $query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,14 @@ class ProjectRepository extends AbstractRepository
|
||||
|
||||
/**
|
||||
* @param ProjectQuery $query
|
||||
* @return \Pagerfanta\Pagerfanta
|
||||
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
|
||||
*/
|
||||
public function findByQuery(ProjectQuery $query)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
// if we join activities, the maxperpage limit will limit the list due to the raised amount of rows by projects * activities
|
||||
// if we join activities, the maxperpage limit will limit the list
|
||||
// due to the raised amount of rows by projects * activities
|
||||
$qb->select('p', 'c')
|
||||
->from('TimesheetBundle:Project', 'p')
|
||||
->join('p.customer', 'c')
|
||||
@@ -72,6 +73,6 @@ class ProjectRepository extends AbstractRepository
|
||||
// TODO check for visibility of customer
|
||||
}
|
||||
|
||||
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
|
||||
return $this->getBaseQueryResult($qb, $query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,5 +158,4 @@ class TimesheetQuery extends BaseQuery
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,12 +12,10 @@
|
||||
namespace TimesheetBundle\Repository;
|
||||
|
||||
use AppBundle\Entity\User;
|
||||
use AppBundle\Repository\AbstractRepository;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Entity\Timesheet;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use TimesheetBundle\Model\Statistic\Month;
|
||||
use TimesheetBundle\Model\Statistic\Year;
|
||||
@@ -31,7 +29,7 @@ use TimesheetBundle\Repository\Query\TimesheetQuery;
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class TimesheetRepository extends EntityRepository
|
||||
class TimesheetRepository extends AbstractRepository
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -89,7 +87,7 @@ class TimesheetRepository extends EntityRepository
|
||||
$end = new DateTime('last day of this month');
|
||||
$end->setTime(23, 59, 59);
|
||||
$begin = new DateTime('first day of this month');
|
||||
$begin->setTime(0,0,0);
|
||||
$begin->setTime(0, 0, 0);
|
||||
|
||||
return $this->queryTimeRange($select, $begin, $end, $user);
|
||||
}
|
||||
@@ -168,7 +166,7 @@ class TimesheetRepository extends EntityRepository
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('SUM(t.rate) as totalRate, SUM(t.duration) as totalDuration, MONTH(t.begin) as month, YEAR(t.begin) as year')
|
||||
$qb->select('SUM(t.rate) as rate, SUM(t.duration) as duration, MONTH(t.begin) as month, YEAR(t.begin) as year')
|
||||
->from('TimesheetBundle:Timesheet', 't')
|
||||
->where($qb->expr()->gt('t.begin', '0'))
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
@@ -183,7 +181,7 @@ class TimesheetRepository extends EntityRepository
|
||||
}
|
||||
|
||||
$years = [];
|
||||
foreach($qb->getQuery()->execute() as $statRow) {
|
||||
foreach ($qb->getQuery()->execute() as $statRow) {
|
||||
$curYear = $statRow['year'];
|
||||
|
||||
if (!isset($years[$curYear])) {
|
||||
@@ -196,8 +194,8 @@ class TimesheetRepository extends EntityRepository
|
||||
}
|
||||
|
||||
$month = new Month($statRow['month']);
|
||||
$month->setTotalDuration($statRow['totalDuration'])
|
||||
->setTotalRate($statRow['totalRate']);
|
||||
$month->setTotalDuration($statRow['duration'])
|
||||
->setTotalRate($statRow['rate']);
|
||||
$years[$curYear]->setMonth($month);
|
||||
}
|
||||
|
||||
@@ -310,21 +308,6 @@ class TimesheetRepository extends EntityRepository
|
||||
->setParameter('customer', $query->getCustomer());
|
||||
}
|
||||
|
||||
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Query $query
|
||||
* @param int $page
|
||||
* @param int $maxPerPage
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
protected function getPager(Query $query, $page = 1, $maxPerPage = 25)
|
||||
{
|
||||
$paginator = new Pagerfanta(new DoctrineORMAdapter($query, false));
|
||||
$paginator->setMaxPerPage($maxPerPage);
|
||||
$paginator->setCurrentPage($page);
|
||||
|
||||
return $paginator;
|
||||
return $this->getBaseQueryResult($qb, $query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
'label.currency': 'hidden-xs',
|
||||
'label.visible': '',
|
||||
'label.actions': '',
|
||||
}, null, {'user-plus': path('admin_customer_create')}) }}
|
||||
}, null, {'plus-square': path('admin_customer_create')}) }}
|
||||
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
|
||||
@@ -74,6 +74,7 @@ class ActivityVoter extends AbstractVoter
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @param User $user
|
||||
* @param TokenInterface $token
|
||||
* @return bool
|
||||
*/
|
||||
protected function canView(Activity $activity, User $user, TokenInterface $token)
|
||||
@@ -88,6 +89,7 @@ class ActivityVoter extends AbstractVoter
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @param User $user
|
||||
* @param TokenInterface $token
|
||||
* @return bool
|
||||
*/
|
||||
protected function canEdit(Activity $activity, User $user, TokenInterface $token)
|
||||
|
||||
@@ -74,6 +74,7 @@ class CustomerVoter extends AbstractVoter
|
||||
/**
|
||||
* @param Customer $customer
|
||||
* @param User $user
|
||||
* @param TokenInterface $token
|
||||
* @return bool
|
||||
*/
|
||||
protected function canView(Customer $customer, User $user, TokenInterface $token)
|
||||
@@ -88,6 +89,7 @@ class CustomerVoter extends AbstractVoter
|
||||
/**
|
||||
* @param Customer $customer
|
||||
* @param User $user
|
||||
* @param TokenInterface $token
|
||||
* @return bool
|
||||
*/
|
||||
protected function canEdit(Customer $customer, User $user, TokenInterface $token)
|
||||
|
||||
@@ -74,6 +74,7 @@ class ProjectVoter extends AbstractVoter
|
||||
/**
|
||||
* @param Project $project
|
||||
* @param User $user
|
||||
* @param TokenInterface $token
|
||||
* @return bool
|
||||
*/
|
||||
protected function canView(Project $project, User $user, TokenInterface $token)
|
||||
@@ -88,6 +89,7 @@ class ProjectVoter extends AbstractVoter
|
||||
/**
|
||||
* @param Project $project
|
||||
* @param User $user
|
||||
* @param TokenInterface $token
|
||||
* @return bool
|
||||
*/
|
||||
protected function canEdit(Project $project, User $user, TokenInterface $token)
|
||||
|
||||
Reference in New Issue
Block a user