add user preference: default report (#2430)
This commit is contained in:
@@ -14,6 +14,7 @@ use App\Reporting\MonthByUser;
|
||||
use App\Reporting\MonthByUserForm;
|
||||
use App\Reporting\MonthlyUserList;
|
||||
use App\Reporting\MonthlyUserListForm;
|
||||
use App\Reporting\ReportingService;
|
||||
use App\Reporting\WeekByUser;
|
||||
use App\Reporting\WeekByUserForm;
|
||||
use App\Repository\Query\UserQuery;
|
||||
@@ -54,9 +55,33 @@ final class ReportingController extends AbstractController
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function defaultReport(): Response
|
||||
public function defaultReport(ReportingService $reportingService): Response
|
||||
{
|
||||
return $this->redirectToRoute('report_user_week');
|
||||
$user = $this->getUser();
|
||||
$route = null;
|
||||
|
||||
$defaultReport = $user->getPreferenceValue('reporting.initial_view', ReportingService::DEFAULT_VIEW);
|
||||
$allReports = $reportingService->getAvailableReports($user);
|
||||
|
||||
foreach ($allReports as $report) {
|
||||
if ($report->getId() === $defaultReport) {
|
||||
$route = $report->getRoute();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback, if the configured report could not be found
|
||||
// eg. when it was deleted or replaced by an enhanced version with a new id
|
||||
if ($route === null && \count($allReports) > 0) {
|
||||
$report = $allReports[array_keys($allReports)[0]];
|
||||
$route = $report->getRoute();
|
||||
}
|
||||
|
||||
if ($route === null) {
|
||||
throw $this->createNotFoundException('Unknown default report');
|
||||
}
|
||||
|
||||
return $this->redirectToRoute($route);
|
||||
}
|
||||
|
||||
private function canSelectUser(): bool
|
||||
|
||||
@@ -18,8 +18,10 @@ use App\Form\Type\CalendarViewType;
|
||||
use App\Form\Type\FirstWeekDayType;
|
||||
use App\Form\Type\InitialViewType;
|
||||
use App\Form\Type\LanguageType;
|
||||
use App\Form\Type\ReportType;
|
||||
use App\Form\Type\SkinType;
|
||||
use App\Form\Type\ThemeLayoutType;
|
||||
use App\Reporting\ReportingService;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
@@ -30,24 +32,15 @@ use Symfony\Component\Validator\Constraints\Range;
|
||||
|
||||
final class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
/**
|
||||
* @var AuthorizationCheckerInterface
|
||||
*/
|
||||
private $voter;
|
||||
/**
|
||||
* @var SystemConfiguration
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher, AuthorizationCheckerInterface $voter, SystemConfiguration $formConfig)
|
||||
public function __construct(EventDispatcherInterface $eventDispatcher, AuthorizationCheckerInterface $voter, SystemConfiguration $systemConfiguration)
|
||||
{
|
||||
$this->eventDispatcher = $dispatcher;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->voter = $voter;
|
||||
$this->configuration = $formConfig;
|
||||
$this->configuration = $systemConfiguration;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
@@ -153,6 +146,13 @@ final class UserPreferenceSubscriber implements EventSubscriberInterface
|
||||
->setSection('behaviour')
|
||||
->setType(CalendarViewType::class),
|
||||
|
||||
(new UserPreference())
|
||||
->setName('reporting.initial_view')
|
||||
->setValue(ReportingService::DEFAULT_VIEW)
|
||||
->setOrder(650)
|
||||
->setSection('behaviour')
|
||||
->setType(ReportType::class),
|
||||
|
||||
(new UserPreference())
|
||||
->setName('login.initial_view')
|
||||
->setValue(InitialViewType::DEFAULT_VIEW)
|
||||
|
||||
58
src/Form/Type/ReportType.php
Normal file
58
src/Form/Type/ReportType.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Reporting\ReportingService;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to select a report.
|
||||
*/
|
||||
class ReportType extends AbstractType
|
||||
{
|
||||
private $reportingService;
|
||||
|
||||
public function __construct(ReportingService $reportingService)
|
||||
{
|
||||
$this->reportingService = $reportingService;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefault('required', true);
|
||||
$resolver->setDefault('translation_domain', 'reporting');
|
||||
$resolver->setDefault('choices', function (Options $options) {
|
||||
/** @var User $user */
|
||||
$user = $options['user'];
|
||||
|
||||
$choices = [];
|
||||
foreach ($this->reportingService->getAvailableReports($user) as $report) {
|
||||
$choices[$report->getLabel()] = $report->getId();
|
||||
}
|
||||
|
||||
return $choices;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,28 @@ class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
||||
$total = $model->getCalculator()->getTotal();
|
||||
$subtotal = $model->getCalculator()->getSubtotal();
|
||||
$formatter = $model->getFormatter();
|
||||
$entries = $model->getCalculator()->getEntries();
|
||||
|
||||
return [
|
||||
$begin = null;
|
||||
if ($model->getQuery()->getBegin() !== null) {
|
||||
$begin = $model->getQuery()->getBegin();
|
||||
} elseif (!empty($entries)) {
|
||||
$begin = $entries[0];
|
||||
}
|
||||
|
||||
$end = null;
|
||||
if ($model->getQuery()->getEnd() !== null) {
|
||||
$end = $model->getQuery()->getEnd();
|
||||
} elseif (!empty($entries)) {
|
||||
$end = array_keys($entries)[\count($entries) - 1];
|
||||
}
|
||||
|
||||
$values = [
|
||||
'invoice.due_date' => $formatter->getFormattedDateTime($model->getDueDate()),
|
||||
'invoice.date' => $formatter->getFormattedDateTime($model->getInvoiceDate()),
|
||||
'invoice.number' => $model->getInvoiceNumber(),
|
||||
'invoice.currency' => $currency,
|
||||
'invoice.language' => $model->getTemplate()->getLanguage(), // since 1.9
|
||||
'invoice.language' => $model->getTemplate()->getLanguage(), // since 1.9
|
||||
'invoice.currency_symbol' => $formatter->getCurrencySymbol($currency),
|
||||
'invoice.vat' => $model->getCalculator()->getVat(),
|
||||
'invoice.tax' => $formatter->getFormattedMoney($tax, $currency),
|
||||
@@ -52,20 +67,41 @@ class InvoiceModelDefaultHydrator implements InvoiceModelHydrator
|
||||
'template.contact' => $model->getTemplate()->getContact(),
|
||||
'template.payment_details' => $model->getTemplate()->getPaymentDetails(),
|
||||
|
||||
'query.begin' => $formatter->getFormattedDateTime($model->getQuery()->getBegin()),
|
||||
'query.day' => $model->getQuery()->getBegin()->format('d'), // @deprecated
|
||||
'query.month' => $formatter->getFormattedMonthName($model->getQuery()->getBegin()), // @deprecated
|
||||
'query.month_number' => $model->getQuery()->getBegin()->format('m'), // @deprecated
|
||||
'query.year' => $model->getQuery()->getBegin()->format('Y'), // @deprecated
|
||||
'query.begin_day' => $model->getQuery()->getBegin()->format('d'),
|
||||
'query.begin_month' => $formatter->getFormattedMonthName($model->getQuery()->getBegin()),
|
||||
'query.begin_month_number' => $model->getQuery()->getBegin()->format('m'),
|
||||
'query.begin_year' => $model->getQuery()->getBegin()->format('Y'),
|
||||
'query.end' => $formatter->getFormattedDateTime($model->getQuery()->getEnd()), // since 1.9
|
||||
'query.end_day' => $model->getQuery()->getEnd()->format('d'), // since 1.9
|
||||
'query.end_month' => $formatter->getFormattedMonthName($model->getQuery()->getEnd()), // since 1.9
|
||||
'query.end_month_number' => $model->getQuery()->getEnd()->format('m'), // since 1.9
|
||||
'query.end_year' => $model->getQuery()->getEnd()->format('Y'), // since 1.9
|
||||
'query.begin' => '',
|
||||
'query.day' => '', // @deprecated
|
||||
'query.month' => '', // @deprecated
|
||||
'query.month_number' => '', // @deprecated
|
||||
'query.year' => '', // @deprecated
|
||||
'query.begin_day' => '',
|
||||
'query.begin_month' => '',
|
||||
'query.begin_month_number' => '',
|
||||
'query.begin_year' => '',
|
||||
'query.end' => '', // since 1.9
|
||||
'query.end_day' => '', // since 1.9
|
||||
'query.end_month' => '', // since 1.9
|
||||
'query.end_month_number' => '', // since 1.9
|
||||
'query.end_year' => '', // since 1.9
|
||||
];
|
||||
|
||||
if ($begin !== null) {
|
||||
$values = array_merge($values, [
|
||||
'query.begin' => $formatter->getFormattedDateTime($begin),
|
||||
'query.day' => $begin->format('d'), // @deprecated
|
||||
'query.month' => $formatter->getFormattedMonthName($begin), // @deprecated
|
||||
'query.month_number' => $begin->format('m'), // @deprecated
|
||||
'query.year' => $begin->format('Y'), // @deprecated
|
||||
'query.begin_day' => $begin->format('d'),
|
||||
'query.begin_month' => $formatter->getFormattedMonthName($begin),
|
||||
'query.begin_month_number' => $begin->format('m'),
|
||||
'query.begin_year' => $begin->format('Y'),
|
||||
'query.end' => $formatter->getFormattedDateTime($end), // since 1.9
|
||||
'query.end_day' => $end->format('d'), // since 1.9
|
||||
'query.end_month' => $formatter->getFormattedMonthName($end), // since 1.9
|
||||
'query.end_month_number' => $end->format('m'), // since 1.9
|
||||
'query.end_year' => $end->format('Y'), // since 1.9
|
||||
]);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,12 @@ final class ProjectViewService
|
||||
}
|
||||
|
||||
if (!$query->isIncludeNoBudget()) {
|
||||
$qb->andWhere($qb->expr()->gt('p.timeBudget', 0));
|
||||
$qb->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->gt('p.budget', 0),
|
||||
$qb->expr()->gt('p.timeBudget', 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->repository->addPermissionCriteria($qb, $user);
|
||||
|
||||
@@ -16,6 +16,8 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
||||
final class ReportingService
|
||||
{
|
||||
public const DEFAULT_VIEW = 'week_by_user';
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
@@ -40,7 +42,7 @@ final class ReportingService
|
||||
$event = new ReportingEvent($user);
|
||||
|
||||
if ($this->security->isGranted('view_reporting')) {
|
||||
$event->addReport(new Report('week_by_user', 'report_user_week', 'report_user_week'));
|
||||
$event->addReport(new Report(self::DEFAULT_VIEW, 'report_user_week', 'report_user_week'));
|
||||
$event->addReport(new Report('month_by_user', 'report_user_month', 'report_user_month'));
|
||||
if ($this->security->isGranted('budget_project')) {
|
||||
$event->addReport(new Report('project_view', 'report_project_view', 'report_project_view'));
|
||||
@@ -48,9 +50,9 @@ final class ReportingService
|
||||
if ($this->security->isGranted('view_other_timesheet')) {
|
||||
$event->addReport(new Report('monthly_users_list', 'report_monthly_users', 'report_monthly_users'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch($event);
|
||||
$this->dispatcher->dispatch($event);
|
||||
}
|
||||
|
||||
return $event->getReports();
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{% set columns = columns|merge({
|
||||
'budget': {'class': 'hidden-xs hidden-sm hidden', 'title': 'label.budget'|trans},
|
||||
'timeBudget': {'class': 'hidden-xs hidden-sm hidden', 'title': 'label.timeBudget'|trans},
|
||||
'budget': {'class': 'hidden-xs hidden-sm hidden text-center w-min', 'title': 'label.budget'|trans},
|
||||
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-center w-min', 'title': 'label.timeBudget'|trans},
|
||||
'team': {'class': 'text-center w-min', 'orderBy': false},
|
||||
'visible': {'class': 'text-center hidden w-min'},
|
||||
'actions': {'class': 'actions alwaysVisible'},
|
||||
@@ -60,8 +60,20 @@
|
||||
{{ tables.datatable_meta_column(entry, field) }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">{{ entry.budget|money((entry.project is null ? defaultCurrency : entry.project.customer.currency)) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">{{ entry.timeBudget|duration }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
|
||||
{% if entry.hasBudget() %}
|
||||
{{ entry.budget|money((entry.project is null ? defaultCurrency : entry.project.customer.currency)) }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
|
||||
{% if entry.hasTimeBudget() %}
|
||||
{{ entry.timeBudget|duration }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'team') }}">{{ widgets.badge_team_access(entry.teams) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'visible') }}">{{ widgets.label_visible(entry.visible) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'actions') }}">{{ actions.activity(entry, 'index') }}</td>
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{% set columns = columns|merge({
|
||||
'budget': {'class': 'hidden-xs hidden-sm hidden', 'title': 'label.budget'|trans},
|
||||
'timeBudget': {'class': 'hidden-xs hidden-sm hidden', 'title': 'label.timeBudget'|trans},
|
||||
'budget': {'class': 'hidden-xs hidden-sm hidden text-center w-min', 'title': 'label.budget'|trans},
|
||||
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-center w-min', 'title': 'label.timeBudget'|trans},
|
||||
'team': {'class': 'text-center w-min', 'orderBy': false},
|
||||
'visible': {'class': 'text-center hidden w-min'},
|
||||
'actions': {'class': 'actions alwaysVisible'},
|
||||
@@ -71,8 +71,20 @@
|
||||
{{ tables.datatable_meta_column(entry, field) }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">{{ entry.budget|money(entry.currency) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">{{ entry.timeBudget|duration }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
|
||||
{% if entry.hasBudget() %}
|
||||
{{ entry.budget|money(entry.currency) }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
|
||||
{% if entry.hasBudget() %}
|
||||
{{ entry.timeBudget|duration }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'team') }}">{{ widgets.badge_team_access(entry.teams) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'visible') }}">{{ widgets.label_visible(entry.visible) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'actions') }}">{{ actions.customer(entry, 'index') }}</td>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
}) %}
|
||||
{% endfor %}
|
||||
{% set columns = columns|merge({
|
||||
'budget': {'class': 'hidden-xs hidden-sm hidden', 'title': 'label.budget'|trans},
|
||||
'timeBudget': {'class': 'hidden-xs hidden-sm hidden', 'title': 'label.timeBudget'|trans},
|
||||
'budget': {'class': 'hidden-xs hidden-sm hidden text-center w-min', 'title': 'label.budget'|trans},
|
||||
'timeBudget': {'class': 'hidden-xs hidden-sm hidden text-center w-min', 'title': 'label.timeBudget'|trans},
|
||||
'team': {'class': 'text-center w-min', 'orderBy': false},
|
||||
'visible': {'class': 'text-center hidden w-min'},
|
||||
'actions': {'class': 'actions alwaysVisible'},
|
||||
@@ -57,8 +57,20 @@
|
||||
{{ tables.datatable_meta_column(entry, field) }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">{{ entry.budget|money(entry.customer.currency) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">{{ entry.timeBudget|duration }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'budget') }}">
|
||||
{% if entry.hasBudget() %}
|
||||
{{ entry.budget|money(entry.customer.currency) }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'timeBudget') }}">
|
||||
{% if entry.hasBudget() %}
|
||||
{{ entry.timeBudget|duration }}
|
||||
{% else %}
|
||||
–
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'team') }}">{{ widgets.badge_team_access(entry.teams) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'visible') }}">{{ widgets.label_visible(entry.visible) }}</td>
|
||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'actions') }}">{{ actions.project(entry, 'index') }}</td>
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
{% block report_title %}{{ 'report_project_view'|trans({}, 'reporting') }}{% endblock %}
|
||||
|
||||
{% set columns = {
|
||||
'name': {'class': 'alwaysVisible'},
|
||||
'today': {'class': 'text-nowrap text-right', 'title': 'daterangepicker.today'|trans({}, 'daterangepicker')},
|
||||
'week': {'class': 'text-nowrap text-right', 'title': 'agendaWeek'|trans},
|
||||
'month': {'class': 'text-nowrap text-right', 'title': 'month'|trans},
|
||||
'durationTotal': {'class': 'hidden-md hidden-sm hidden-xs text-nowrap text-right', 'title': 'stats.durationTotal'|trans},
|
||||
'name': {'class': 'alwaysVisible w-min'},
|
||||
'today': {'class': 'text-nowrap text-center w-min', 'title': 'daterangepicker.today'|trans({}, 'daterangepicker')},
|
||||
'week': {'class': 'text-nowrap text-center w-min', 'title': 'agendaWeek'|trans},
|
||||
'month': {'class': 'text-nowrap text-center w-min', 'title': 'month'|trans},
|
||||
'durationTotal': {'class': 'hidden-md hidden-sm hidden-xs text-nowrap text-center w-min', 'title': 'label.total'|trans},
|
||||
'timeBudget': {'class': 'text-nowrap', 'title': 'label.timeBudget'|trans},
|
||||
'budget': {'class': 'text-nowrap', 'title': 'label.budget'|trans},
|
||||
'stateDuration': {'class': 'hidden-sm hidden-xs text-nowrap text-right', 'title': 'entryState.not_exported'|trans},
|
||||
'stateMoney': {'class': 'hidden-sm hidden-xs text-nowrap text-right', 'title': 'entryState.not_exported'|trans},
|
||||
'projectEnd': {'class': 'hidden-md hidden-sm hidden-xs hidden text-nowrap text-center', 'title': 'label.project_end'|trans},
|
||||
'stateDuration': {'class': 'hidden-sm hidden-xs text-nowrap text-center w-min', 'title': 'label.not_exported'|trans},
|
||||
'stateMoney': {'class': 'hidden-sm hidden-xs text-nowrap text-center w-min', 'title': 'label.not_invoiced'|trans},
|
||||
'projectEnd': {'class': 'hidden-md hidden-sm hidden-xs hidden text-nowrap text-center w-min', 'title': 'label.project_end'|trans},
|
||||
'comment': {'class': 'hidden-md hidden-sm hidden-xs hidden', 'title': 'label.comment'|trans},
|
||||
} %}
|
||||
{% set tableName = 'project_view_reporting' %}
|
||||
|
||||
@@ -23,6 +23,23 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
*/
|
||||
class UserPreferenceSubscriberTest extends TestCase
|
||||
{
|
||||
public const EXPECTED_PREFERENCES = [
|
||||
'hourly_rate',
|
||||
'internal_rate',
|
||||
'timezone',
|
||||
'language',
|
||||
'first_weekday',
|
||||
'skin',
|
||||
'theme.layout',
|
||||
'theme.collapsed_sidebar',
|
||||
'theme.update_browser_title',
|
||||
'calendar.initial_view',
|
||||
'reporting.initial_view',
|
||||
'login.initial_view',
|
||||
'timesheet.daily_stats',
|
||||
'timesheet.export_decimal',
|
||||
];
|
||||
|
||||
public function testGetSubscribedEvents()
|
||||
{
|
||||
$events = UserPreferenceSubscriber::getSubscribedEvents();
|
||||
@@ -40,7 +57,11 @@ class UserPreferenceSubscriberTest extends TestCase
|
||||
self::assertSame($user, $event->getUser());
|
||||
|
||||
$prefs = $sut->getDefaultPreferences($user);
|
||||
self::assertCount(13, $prefs);
|
||||
foreach ($prefs as $pref) {
|
||||
$this->assertTrue(\in_array($pref->getName(), self::EXPECTED_PREFERENCES), 'Unknown user preference: ' . $pref->getName());
|
||||
}
|
||||
|
||||
self::assertCount(\count(self::EXPECTED_PREFERENCES), $prefs);
|
||||
|
||||
foreach ($prefs as $pref) {
|
||||
switch ($pref->getName()) {
|
||||
@@ -70,7 +91,7 @@ class UserPreferenceSubscriberTest extends TestCase
|
||||
// TODO test merging values
|
||||
$sut->loadUserPreferences($event);
|
||||
$prefs = $event->getUser()->getPreferences();
|
||||
self::assertCount(13, $prefs);
|
||||
self::assertCount(\count(self::EXPECTED_PREFERENCES), $prefs);
|
||||
|
||||
foreach ($prefs as $pref) {
|
||||
switch ($pref->getName()) {
|
||||
|
||||
@@ -24,7 +24,7 @@ class ReportingServiceTest extends TestCase
|
||||
protected function getSut(bool $isGranted = false): ReportingService
|
||||
{
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$dispatcher->expects($this->once())->method('dispatch')->willReturnCallback(function ($event) {
|
||||
$dispatcher->expects($this->exactly($isGranted ? 1 : 0))->method('dispatch')->willReturnCallback(function ($event) {
|
||||
$this->assertInstanceOf(ReportingEvent::class, $event);
|
||||
});
|
||||
|
||||
|
||||
@@ -1146,6 +1146,14 @@
|
||||
<source>label.includeNoBudget</source>
|
||||
<target>Einträge ohne Budget anzeigen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.not_exported">
|
||||
<source>label.not_exported</source>
|
||||
<target>Nicht exportiert</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.not_invoiced">
|
||||
<source>label.not_invoiced</source>
|
||||
<target>Nicht abgerechnet</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -1166,6 +1166,14 @@
|
||||
<source>label.includeNoBudget</source>
|
||||
<target>Show entries without budget</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.not_exported">
|
||||
<source>label.not_exported</source>
|
||||
<target>Not exported</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.not_invoiced">
|
||||
<source>label.not_invoiced</source>
|
||||
<target>Not billed</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
<source>report_project_view</source>
|
||||
<target>Projektübersicht</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="reporting.initial_view">
|
||||
<source>reporting.initial_view</source>
|
||||
<target>Initialer Bericht</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
<source>report_project_view</source>
|
||||
<target>Project overview</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="reporting.initial_view">
|
||||
<source>reporting.initial_view</source>
|
||||
<target>Initial report</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
Reference in New Issue
Block a user