monthly budget, monthly report, unified report calculation (#2684)
This commit is contained in:
@@ -61,6 +61,26 @@ class ActivityRepository extends EntityRepository
|
||||
return $this->findBy(['project' => $project]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $activityIds
|
||||
* @return Activity[]
|
||||
*/
|
||||
public function findByIds(array $activityIds)
|
||||
{
|
||||
$qb = $this->createQueryBuilder('a');
|
||||
$qb
|
||||
->where($qb->expr()->in('a.id', ':id'))
|
||||
->setParameter('id', $activityIds)
|
||||
;
|
||||
|
||||
$activities = $qb->getQuery()->getResult();
|
||||
|
||||
$loader = new ActivityLoader($qb->getEntityManager());
|
||||
$loader->loadResults($activities);
|
||||
|
||||
return $activities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @throws ORMException
|
||||
@@ -87,7 +107,8 @@ class ActivityRepository extends EntityRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves statistics for one activity.
|
||||
* @deprecated since 1.15 use ActivityStatisticService::getActivityStatistics() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Activity $activity
|
||||
* @return ActivityStatistic
|
||||
@@ -110,7 +131,7 @@ class ActivityRepository extends EntityRepository
|
||||
$stats = new ActivityStatistic();
|
||||
|
||||
if (null !== $timesheetResult) {
|
||||
$stats->setRecordAmount($timesheetResult['amount']);
|
||||
$stats->setCounter($timesheetResult['amount']);
|
||||
$stats->setRecordDuration($timesheetResult['duration']);
|
||||
$stats->setRecordRate($timesheetResult['rate']);
|
||||
$stats->setRecordInternalRate($timesheetResult['internal_rate']);
|
||||
@@ -194,6 +215,7 @@ class ActivityRepository extends EntityRepository
|
||||
|
||||
/**
|
||||
* @deprecated since 1.1 - use getQueryBuilderForFormType() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function builderForEntityType($activity, $project)
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ class CustomerRepository extends EntityRepository
|
||||
* @param null $lockVersion
|
||||
* @return Customer|null
|
||||
*/
|
||||
public function find($id, $lockMode = null, $lockVersion = null)
|
||||
public function find($id, $lockMode = null, $lockVersion = null): ?Customer
|
||||
{
|
||||
/** @var Customer|null $customer */
|
||||
$customer = parent::find($id, $lockMode, $lockVersion);
|
||||
@@ -56,28 +56,31 @@ class CustomerRepository extends EntityRepository
|
||||
/**
|
||||
* @param Customer $customer
|
||||
* @throws ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
*/
|
||||
public function saveCustomer(Customer $customer)
|
||||
public function saveCustomer(Customer $customer): void
|
||||
{
|
||||
$entityManager = $this->getEntityManager();
|
||||
$entityManager->persist($customer);
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|bool $visible
|
||||
* @return int
|
||||
*/
|
||||
public function countCustomer($visible = null)
|
||||
public function countCustomer(bool $visible = false): int
|
||||
{
|
||||
if (null !== $visible) {
|
||||
if ($visible) {
|
||||
return $this->count(['visible' => (bool) $visible]);
|
||||
}
|
||||
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 use CustomerStatisticService::getCustomerStatistics() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @return CustomerStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getCustomerStatistics(Customer $customer): CustomerStatistic
|
||||
{
|
||||
$stats = new CustomerStatistic();
|
||||
@@ -114,7 +117,7 @@ class CustomerRepository extends EntityRepository
|
||||
$stats->setRecordAmountBillable($resultRow['amount']);
|
||||
}
|
||||
}
|
||||
$stats->setRecordAmount($amount);
|
||||
$stats->setCounter($amount);
|
||||
$stats->setRecordDuration($duration);
|
||||
$stats->setRecordRate($rate);
|
||||
$stats->setRecordInternalRate($rateInternal);
|
||||
@@ -189,6 +192,7 @@ class CustomerRepository extends EntityRepository
|
||||
|
||||
/**
|
||||
* @deprecated since 1.1 - use getQueryBuilderForFormType() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function builderForEntityType($customer)
|
||||
{
|
||||
|
||||
@@ -14,14 +14,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class ActivityLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var ActivityIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->loader = new ActivityIdLoader($entityManager);
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +30,7 @@ final class ActivityLoader implements LoaderInterface
|
||||
return $activity->getId();
|
||||
}, $activities);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new ActivityIdLoader($this->entityManager);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class CustomerLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var CustomerIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->loader = new CustomerIdLoader($entityManager);
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +30,7 @@ final class CustomerLoader implements LoaderInterface
|
||||
return $customer->getId();
|
||||
}, $customers);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new CustomerIdLoader($this->entityManager);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class InvoiceLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var InvoiceIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->loader = new InvoiceIdLoader($entityManager);
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +30,7 @@ final class InvoiceLoader implements LoaderInterface
|
||||
return $invoice->getId();
|
||||
}, $invoices);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new InvoiceIdLoader($this->entityManager);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class ProjectLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var ProjectIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->loader = new ProjectIdLoader($entityManager);
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +30,7 @@ final class ProjectLoader implements LoaderInterface
|
||||
return $project->getId();
|
||||
}, $projects);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new ProjectIdLoader($this->entityManager);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,5 +45,13 @@ final class TeamIdLoader implements LoaderInterface
|
||||
->andWhere($qb->expr()->in('t.id', $ids))
|
||||
->getQuery()
|
||||
->execute();
|
||||
|
||||
$qb = $em->createQueryBuilder();
|
||||
$qb->select('PARTIAL t.{id}', 'projects')
|
||||
->from(Team::class, 't')
|
||||
->leftJoin('t.projects', 'projects')
|
||||
->andWhere($qb->expr()->in('t.id', $ids))
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class TeamLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var TeamIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->loader = new TeamIdLoader($entityManager);
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +30,7 @@ final class TeamLoader implements LoaderInterface
|
||||
return $team->getId();
|
||||
}, $teams);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new TeamIdLoader($this->entityManager);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,13 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class TimesheetLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var TimesheetIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
private $hydrateFullTree;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, bool $hydrateFullTree = false)
|
||||
{
|
||||
$this->loader = new TimesheetIdLoader($entityManager, $hydrateFullTree);
|
||||
$this->entityManager = $entityManager;
|
||||
$this->hydrateFullTree = $hydrateFullTree;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +32,7 @@ final class TimesheetLoader implements LoaderInterface
|
||||
return $timesheet->getId();
|
||||
}, $timesheets);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new TimesheetIdLoader($this->entityManager, $this->hydrateFullTree);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,11 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
final class UserLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var UserIdLoader
|
||||
*/
|
||||
private $loader;
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->loader = new UserIdLoader($entityManager);
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +30,7 @@ final class UserLoader implements LoaderInterface
|
||||
return $user->getId();
|
||||
}, $users);
|
||||
|
||||
$this->loader->loadResults($ids);
|
||||
$loader = new UserIdLoader($this->entityManager);
|
||||
$loader->loadResults($ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,26 @@ class ProjectRepository extends EntityRepository
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $projectIds
|
||||
* @return Project[]
|
||||
*/
|
||||
public function findByIds(array $projectIds)
|
||||
{
|
||||
$qb = $this->createQueryBuilder('p');
|
||||
$qb
|
||||
->where($qb->expr()->in('p.id', ':id'))
|
||||
->setParameter('id', $projectIds)
|
||||
;
|
||||
|
||||
$projects = $qb->getQuery()->getResult();
|
||||
|
||||
$loader = new ProjectLoader($qb->getEntityManager());
|
||||
$loader->loadResults($projects);
|
||||
|
||||
return $projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Project $project
|
||||
* @throws ORMException
|
||||
@@ -79,6 +99,16 @@ class ProjectRepository extends EntityRepository
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 use ProjectStatisticService::getProjectStatistics() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Project $project
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @return ProjectStatistic
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
public function getProjectStatistics(Project $project, ?DateTime $begin = null, ?DateTime $end = null): ProjectStatistic
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
@@ -103,7 +133,7 @@ class ProjectRepository extends EntityRepository
|
||||
$stats = new ProjectStatistic();
|
||||
|
||||
if (null !== $timesheetResult) {
|
||||
$stats->setRecordAmount($timesheetResult['amount']);
|
||||
$stats->setCounter($timesheetResult['amount']);
|
||||
$stats->setRecordDuration($timesheetResult['duration']);
|
||||
$stats->setRecordRate($timesheetResult['rate']);
|
||||
$stats->setRecordInternalRate($timesheetResult['internal_rate']);
|
||||
@@ -196,6 +226,7 @@ class ProjectRepository extends EntityRepository
|
||||
|
||||
/**
|
||||
* @deprecated since 1.1 - use getQueryBuilderForFormType() istead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function builderForEntityType($project, $customer)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,6 @@ use App\Repository\Query\TimesheetQuery;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@@ -46,8 +45,17 @@ class TimesheetRepository extends EntityRepository
|
||||
public const STATS_QUERY_USER = 'users';
|
||||
public const STATS_QUERY_AMOUNT = 'amount';
|
||||
public const STATS_QUERY_ACTIVE = 'active';
|
||||
/**
|
||||
* @deprecated since 1.15 - use TimesheetStatisticService::getMonthlyStats() instead - will be removed with 2.0
|
||||
*/
|
||||
public const STATS_QUERY_MONTHLY = 'monthly';
|
||||
|
||||
/**
|
||||
* Fetches the raw data of an timesheet, to allow comparison eg. of submitted and previously stored data.
|
||||
*
|
||||
* @param Timesheet $id
|
||||
* @return array
|
||||
*/
|
||||
public function getRawData(Timesheet $id): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('t');
|
||||
@@ -56,17 +64,18 @@ class TimesheetRepository extends EntityRepository
|
||||
't.rate',
|
||||
't.duration',
|
||||
't.hourlyRate',
|
||||
't.billable',
|
||||
'IDENTITY(p.customer) as customer',
|
||||
'IDENTITY(t.project) as project',
|
||||
'IDENTITY(t.activity) as activity',
|
||||
'IDENTITY(t.user) as user'
|
||||
])
|
||||
->leftJoin(Project::class, 'p', Join::WITH, 'p.id = t.project')
|
||||
->andWhere('t.id = :id')
|
||||
->andWhere($qb->expr()->eq('t.id', ':id'))
|
||||
->setParameter('id', $id)
|
||||
;
|
||||
|
||||
return $qb->getQuery()->getSingleResult(AbstractQuery::HYDRATE_ARRAY);
|
||||
return $qb->getQuery()->getOneOrNullResult();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +133,7 @@ class TimesheetRepository extends EntityRepository
|
||||
|
||||
/**
|
||||
* @deprecated since 1.11 use TimesheetService::stopTimesheet() instead
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function add(Timesheet $timesheet, int $maxRunningEntries)
|
||||
{
|
||||
@@ -194,13 +204,15 @@ class TimesheetRepository extends EntityRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.11 use TimesheetService::stopTimesheet() instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param Timesheet $entry
|
||||
* @param bool $flush
|
||||
* @return bool
|
||||
* @throws RepositoryException
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @deprecated since 1.11 use TimesheetService::stopTimesheet() instead
|
||||
*/
|
||||
public function stopRecording(Timesheet $entry, bool $flush = true)
|
||||
{
|
||||
@@ -264,39 +276,26 @@ class TimesheetRepository extends EntityRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $select
|
||||
* @param User $user
|
||||
* @return int
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
protected function queryThisMonth($select, User $user)
|
||||
{
|
||||
try {
|
||||
$timezone = new \DateTimeZone($user->getTimezone());
|
||||
$begin = new DateTime('first day of this month 00:00:00', $timezone);
|
||||
$end = new DateTime('last day of this month 23:59:59', $timezone);
|
||||
|
||||
return $this->queryTimeRange($select, $begin, $end, $user);
|
||||
} catch (\Exception $ex) {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $select
|
||||
* @param string|string[] $select
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @param User|null $user
|
||||
* @return int|mixed
|
||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||
*/
|
||||
protected function queryTimeRange(string $select, ?DateTime $begin, ?DateTime $end, ?User $user)
|
||||
protected function queryTimeRange($select, ?DateTime $begin, ?DateTime $end, ?User $user)
|
||||
{
|
||||
$selects = $select;
|
||||
if (!\is_array($select)) {
|
||||
$selects = [$select];
|
||||
}
|
||||
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select($select)
|
||||
->from(Timesheet::class, 't');
|
||||
$qb->from(Timesheet::class, 't');
|
||||
foreach ($selects as $s) {
|
||||
$qb->addSelect($s);
|
||||
}
|
||||
|
||||
if (!empty($begin)) {
|
||||
$qb
|
||||
@@ -315,36 +314,75 @@ class TimesheetRepository extends EntityRepository
|
||||
->setParameter('user', $user);
|
||||
}
|
||||
|
||||
if (\is_array($select)) {
|
||||
return $qb->getQuery()->getOneOrNullResult();
|
||||
}
|
||||
|
||||
$result = $qb->getQuery()->getSingleScalarResult();
|
||||
|
||||
return empty($result) ? 0 : $result;
|
||||
}
|
||||
|
||||
public function getUserStatistics(User $user): TimesheetStatistic
|
||||
/**
|
||||
* @param User $user
|
||||
* @param bool $bcSafe will be removed with 2.0
|
||||
* @return TimesheetStatistic
|
||||
*/
|
||||
public function getUserStatistics(User $user, bool $bcSafe = true): TimesheetStatistic
|
||||
{
|
||||
$durationTotal = $this->getStatistic(self::STATS_QUERY_DURATION, null, null, $user);
|
||||
$recordsTotal = $this->getStatistic(self::STATS_QUERY_AMOUNT, null, null, $user);
|
||||
$rateTotal = $this->getStatistic(self::STATS_QUERY_RATE, null, null, $user);
|
||||
$amountMonth = $this->queryThisMonth('COALESCE(SUM(t.rate), 0)', $user);
|
||||
$durationMonth = $this->queryThisMonth('COALESCE(SUM(t.duration), 0)', $user);
|
||||
$firstEntry = $this->getEntityManager()
|
||||
->createQuery('SELECT MIN(t.begin) FROM ' . Timesheet::class . ' t WHERE t.user = :user')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
$allTimeData = $this->queryTimeRange([
|
||||
'COALESCE(SUM(t.duration), 0) as duration',
|
||||
'COALESCE(SUM(t.rate), 0) as rate',
|
||||
'COUNT(t.id) as amount'
|
||||
], null, null, $user);
|
||||
|
||||
$timezone = new \DateTimeZone($user->getTimezone());
|
||||
$begin = new DateTime('first day of this month 00:00:00', $timezone);
|
||||
$end = new DateTime('last day of this month 23:59:59', $timezone);
|
||||
|
||||
$monthData = $this->queryTimeRange(
|
||||
[
|
||||
'COALESCE(SUM(t.rate), 0) as rate',
|
||||
'COALESCE(SUM(t.duration), 0) as duration'
|
||||
],
|
||||
$begin,
|
||||
$end,
|
||||
$user
|
||||
);
|
||||
|
||||
$stats = new TimesheetStatistic();
|
||||
$stats->setAmountTotal($rateTotal);
|
||||
$stats->setDurationTotal($durationTotal);
|
||||
$stats->setAmountThisMonth($amountMonth);
|
||||
$stats->setDurationThisMonth($durationMonth);
|
||||
$stats->setFirstEntry(new DateTime($firstEntry, new \DateTimeZone($user->getTimezone())));
|
||||
$stats->setRecordsTotal($recordsTotal);
|
||||
|
||||
if ($bcSafe) {
|
||||
$firstEntry = $this->getEntityManager()
|
||||
->createQuery('SELECT MIN(t.begin) FROM ' . Timesheet::class . ' t WHERE t.user = :user AND 1=12')
|
||||
->setParameter('user', $user)
|
||||
->getSingleScalarResult();
|
||||
|
||||
$timezone = new \DateTimeZone($user->getTimezone());
|
||||
|
||||
if ($firstEntry !== null) {
|
||||
$stats->setFirstEntry(new DateTime($firstEntry, $timezone));
|
||||
} else {
|
||||
@trigger_error(
|
||||
'TimesheetStatistic::getFirstEntry() returns a wrong result for users without record and will be removed with 2.0',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$stats->setFirstEntry(new DateTime('now', $timezone));
|
||||
}
|
||||
}
|
||||
|
||||
$stats->setAmountTotal($allTimeData['rate']);
|
||||
$stats->setDurationTotal($allTimeData['duration']);
|
||||
$stats->setAmountThisMonth($monthData['rate']);
|
||||
$stats->setDurationThisMonth($monthData['duration']);
|
||||
$stats->setRecordsTotal($allTimeData['amount']);
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Year statistics.
|
||||
* @deprecated since 1.15 - use TimesheetStatisticService::getMonthlyStats() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param DateTime $begin
|
||||
* @param DateTime $end
|
||||
@@ -353,6 +391,8 @@ class TimesheetRepository extends EntityRepository
|
||||
*/
|
||||
public function getMonthlyStats(DateTime $begin, DateTime $end, ?User $user = null): array
|
||||
{
|
||||
@trigger_error('TimesheetRepository::getMonthlyStats() is deprecated and will be removed with 2.0', E_USER_DEPRECATED);
|
||||
|
||||
/** @var Year[] $years */
|
||||
$years = [];
|
||||
|
||||
@@ -404,12 +444,25 @@ class TimesheetRepository extends EntityRepository
|
||||
return $years;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - use TimesheetStatisticService::getMonthlyStats() instead - will be removed with 2.0
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param DateTime|null $begin
|
||||
* @param DateTime|null $end
|
||||
* @param bool|null $billable
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
private function getMonthlyStatsQuery(User $user = null, ?DateTime $begin = null, ?DateTime $end = null, ?bool $billable = null): QueryBuilder
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->from(Timesheet::class, 't');
|
||||
$qb->select('COALESCE(SUM(t.rate), 0) as rate, COALESCE(SUM(t.duration), 0) as duration, MONTH(t.begin) as month, YEAR(t.begin) as year');
|
||||
$qb->select('COALESCE(SUM(t.rate), 0) as rate');
|
||||
$qb->addSelect('COALESCE(SUM(t.duration), 0) as duration');
|
||||
$qb->addSelect('MONTH(t.date) as month');
|
||||
$qb->addSelect('YEAR(t.date) as year');
|
||||
|
||||
if (!empty($begin)) {
|
||||
$qb->andWhere($qb->expr()->gte('t.begin', ':from'));
|
||||
@@ -465,8 +518,8 @@ class TimesheetRepository extends EntityRepository
|
||||
$or->add($qb->expr()->between('t.end', ':begin', ':end'));
|
||||
|
||||
$qb->select('t, p, a, c')
|
||||
->from(Timesheet::class, 't')
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
->from(Timesheet::class, 't')
|
||||
->andWhere($qb->expr()->isNotNull('t.end'))
|
||||
->andWhere($or)
|
||||
->orderBy('t.begin', 'DESC')
|
||||
->setParameter('begin', $begin)
|
||||
@@ -581,6 +634,9 @@ class TimesheetRepository extends EntityRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - use TimesheetStatisticService::getDailyStatistics() instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param DateTime $begin
|
||||
* @param DateTime $end
|
||||
@@ -646,6 +702,9 @@ class TimesheetRepository extends EntityRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.11 use TimesheetService::stopTimesheet() instead
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
* @param int $hardLimit
|
||||
* @param bool $flush
|
||||
@@ -653,7 +712,6 @@ class TimesheetRepository extends EntityRepository
|
||||
* @throws RepositoryException
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @deprecated since 1.11 use TimesheetService::stopTimesheet() instead
|
||||
*/
|
||||
public function stopActiveEntries(User $user, int $hardLimit, bool $flush = true)
|
||||
{
|
||||
@@ -753,8 +811,7 @@ class TimesheetRepository extends EntityRepository
|
||||
$qb
|
||||
->resetDQLPart('select')
|
||||
->resetDQLPart('orderBy')
|
||||
// faster then using "distinct id", as the user field is a separate (and smaller) index
|
||||
->select($qb->expr()->count('t.user'))
|
||||
->select($qb->expr()->count('t.id'))
|
||||
;
|
||||
$counter = (int) $qb->getQuery()->getSingleScalarResult();
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace App\Repository;
|
||||
|
||||
use App\Widget\Type\Counter;
|
||||
use App\Widget\Type\SimpleStatisticChart;
|
||||
use App\Widget\Type\YearChart;
|
||||
use App\Widget\WidgetException;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
@@ -342,46 +341,6 @@ class WidgetRepository
|
||||
'user' => false,
|
||||
'type' => Counter::class,
|
||||
],
|
||||
'userRecapThisYear' => [
|
||||
'title' => 'stats.yourWorkingHours',
|
||||
'query' => TimesheetRepository::STATS_QUERY_MONTHLY,
|
||||
'user' => true,
|
||||
'begin' => '01 january this year 00:00:00',
|
||||
'end' => '31 december this year 23:59:59',
|
||||
'color' => '',
|
||||
'icon' => '',
|
||||
'type' => YearChart::class,
|
||||
],
|
||||
'userRecapLastYear' => [
|
||||
'title' => 'stats.yourWorkingHours',
|
||||
'query' => TimesheetRepository::STATS_QUERY_MONTHLY,
|
||||
'user' => true,
|
||||
'begin' => '01 january last year 00:00:00',
|
||||
'end' => '31 december last year 23:59:59',
|
||||
'color' => 'rgba(0,115,183,0.7)|#3b8bba',
|
||||
'icon' => '',
|
||||
'type' => YearChart::class,
|
||||
],
|
||||
'userRecapTwoYears' => [
|
||||
'title' => 'stats.yourWorkingHours',
|
||||
'query' => TimesheetRepository::STATS_QUERY_MONTHLY,
|
||||
'user' => true,
|
||||
'begin' => '01 january last year 00:00:00',
|
||||
'end' => '31 december this year 23:59:59',
|
||||
'color' => 'rgba(0,115,183,0.6)|#3b8bba;rgba(233,233,233,0.8)|#ccc',
|
||||
'icon' => '',
|
||||
'type' => YearChart::class,
|
||||
],
|
||||
'userRecapThreeYears' => [
|
||||
'title' => 'stats.yourWorkingHours',
|
||||
'query' => TimesheetRepository::STATS_QUERY_MONTHLY,
|
||||
'user' => true,
|
||||
'begin' => '2 years ago first day of january 00:00:00',
|
||||
'end' => 'this year last day of december 23:59:59',
|
||||
'color' => 'rgba(0,115,183,0.4)|#3b8bba;rgba(233,233,233,0.7)|#ccc;rgba(210,214,222,0.9)|#c1c7d1',
|
||||
'icon' => '',
|
||||
'type' => YearChart::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user