store search in session (#2735)
* allow to detect used query filters * allow to set label in action buttons * allow to reset last search from session * migrate invoice archive to new search system * display number of used search filters
This commit is contained in:
@@ -29,6 +29,16 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-left: 10px;
|
||||
.btn {
|
||||
span.label {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
text-align: center;
|
||||
font-size: 9px;
|
||||
padding: 2px 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-md-max) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@
|
||||
"build/app.e22732af.js"
|
||||
],
|
||||
"css": [
|
||||
"build/app.0b9e932d.css"
|
||||
"build/app.fdf3c5fb.css"
|
||||
]
|
||||
},
|
||||
"invoice": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"build/app.css": "build/app.0b9e932d.css",
|
||||
"build/app.css": "build/app.fdf3c5fb.css",
|
||||
"build/app.js": "build/app.e22732af.js",
|
||||
"build/invoice.css": "build/invoice.ff32661a.css",
|
||||
"build/invoice.js": "build/invoice.19f36eca.js",
|
||||
|
||||
@@ -238,7 +238,6 @@ class InvoiceCreateCommand extends Command
|
||||
// =============== VALIDATION END ===============
|
||||
|
||||
$defaultQuery = new InvoiceQuery();
|
||||
$defaultQuery->setOrder(InvoiceQuery::ORDER_ASC);
|
||||
$defaultQuery->setBegin($start);
|
||||
$defaultQuery->setEnd($end);
|
||||
$defaultQuery->setCurrentUser($user);
|
||||
|
||||
@@ -177,6 +177,15 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
return $this->get('session')->get($name);
|
||||
}
|
||||
|
||||
private function removeLastSearch(BaseQuery $query): void
|
||||
{
|
||||
$name = 'search_' . $this->getSearchName($query);
|
||||
|
||||
if ($this->get('session')->has($name)) {
|
||||
$this->get('session')->remove($name);
|
||||
}
|
||||
}
|
||||
|
||||
private function getSearchName(BaseQuery $query): string
|
||||
{
|
||||
return substr($query->getName(), 0, 50);
|
||||
@@ -198,6 +207,13 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
throw new \InvalidArgumentException('handleSearchForm() requires an instanceof BaseQuery as form data');
|
||||
}
|
||||
|
||||
if ($request->query->has('resetSearchFilter')) {
|
||||
$data->resetFilter();
|
||||
$this->removeLastSearch($data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$submitData = $request->query->all();
|
||||
// allow to use forms with block-prefix
|
||||
if (!empty($formName = $form->getConfig()->getName()) && $request->request->has($formName)) {
|
||||
@@ -228,6 +244,7 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
$submitData = array_merge($sessionSearch, $submitData);
|
||||
} elseif ($bookmark !== null && !$request->query->has('setDefaultQuery')) {
|
||||
$submitData = array_merge($bookmark->getContent(), $submitData);
|
||||
$data->flagAsBookmarkSearch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +276,9 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
}
|
||||
}
|
||||
|
||||
$this->get('session')->set('search_' . $searchName, $params);
|
||||
if ($request->query->has('performSearch')) {
|
||||
$this->get('session')->set('search_' . $searchName, $params);
|
||||
}
|
||||
|
||||
// filter stuff, that does not belong in a bookmark
|
||||
$filter = ['page'];
|
||||
@@ -270,6 +289,7 @@ abstract class AbstractController extends BaseAbstractController implements Serv
|
||||
}
|
||||
|
||||
if ($request->query->has('setDefaultQuery')) {
|
||||
$this->removeLastSearch($data);
|
||||
if ($bookmark === null) {
|
||||
$bookmark = new Bookmark();
|
||||
$bookmark->setType(Bookmark::SEARCH_DEFAULT);
|
||||
|
||||
@@ -134,11 +134,8 @@ class ExportController extends AbstractController
|
||||
$end = $this->getDateTimeFactory()->getEndOfMonth();
|
||||
|
||||
$query = new ExportQuery();
|
||||
$query->setOrder(ExportQuery::ORDER_ASC);
|
||||
$query->setBegin($begin);
|
||||
$query->setEnd($end);
|
||||
$query->setState(ExportQuery::STATE_STOPPED);
|
||||
$query->setExported(ExportQuery::STATE_NOT_EXPORTED);
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
return $query;
|
||||
|
||||
@@ -188,10 +188,8 @@ final class InvoiceController extends AbstractController
|
||||
$end = $factory->getEndOfMonth();
|
||||
|
||||
$query = new InvoiceQuery();
|
||||
$query->setOrder(InvoiceQuery::ORDER_ASC);
|
||||
$query->setBegin($begin);
|
||||
$query->setEnd($end);
|
||||
$query->setExported(InvoiceQuery::STATE_NOT_EXPORTED);
|
||||
// limit access to data from teams
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
@@ -301,11 +299,8 @@ final class InvoiceController extends AbstractController
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
$form = $this->getArchiveToolbarForm($query);
|
||||
$form->setData($query);
|
||||
$form->submit($request->query->all(), false);
|
||||
|
||||
if (!$form->isValid()) {
|
||||
$query->resetByFormError($form->getErrors());
|
||||
if ($this->handleSearch($form, $request)) {
|
||||
return $this->redirectToRoute('admin_invoice_list');
|
||||
}
|
||||
|
||||
$invoices = $this->invoiceRepository->getPagerfantaForQuery($query);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\Query\BaseQuery;
|
||||
|
||||
/**
|
||||
* This event is triggered once per side load.
|
||||
@@ -126,9 +127,18 @@ class PageActionsEvent extends ThemeEvent
|
||||
$this->payload['actions'][$key] = null;
|
||||
}
|
||||
|
||||
public function addSearchToggle(): void
|
||||
public function addSearchToggle(?BaseQuery $query = null): void
|
||||
{
|
||||
$this->addAction('search', ['modal' => '#modal_search']);
|
||||
$label = null;
|
||||
|
||||
if ($query !== null) {
|
||||
$label = $query->countFilter();
|
||||
if ($label < 1) {
|
||||
$label = null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->addAction('search', ['modal' => '#modal_search', 'label' => $label]);
|
||||
}
|
||||
|
||||
public function addQuickExport(string $url): void
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\ActivityQuery;
|
||||
|
||||
class ActivitiesSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,12 @@ class ActivitiesSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var ActivityQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
$event->addColumnToggle('#modal_activity_admin');
|
||||
$event->addQuickExport($this->path('activity_export'));
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\CustomerQuery;
|
||||
|
||||
class CustomersSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,12 @@ class CustomersSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var CustomerQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
$event->addColumnToggle('#modal_customer_admin');
|
||||
$event->addQuickExport($this->path('customer_export'));
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\InvoiceArchiveQuery;
|
||||
|
||||
class InvoiceArchiveSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,10 +21,16 @@ class InvoiceArchiveSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var InvoiceArchiveQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
if ($this->isGranted('view_invoice')) {
|
||||
$event->addBack($this->path('invoice'));
|
||||
}
|
||||
$event->addSearchToggle();
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
$event->addColumnToggle('#modal_invoices');
|
||||
$event->addQuickExport($this->path('invoice_export'));
|
||||
$event->addHelp($this->documentationLink('invoices.html'));
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\ProjectQuery;
|
||||
|
||||
class ProjectsSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,13 @@ class ProjectsSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var ProjectQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
|
||||
$event->addColumnToggle('#modal_project_admin');
|
||||
$event->addQuickExport($this->path('project_export'));
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\TagQuery;
|
||||
|
||||
class TagsSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,12 @@ class TagsSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var TagQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
|
||||
if ($this->isGranted('manage_tag')) {
|
||||
$event->addCreate($this->path('tags_create'));
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\TeamQuery;
|
||||
|
||||
class TeamsSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,12 @@ class TeamsSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var TeamQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
|
||||
if ($this->isGranted('create_team')) {
|
||||
$event->addCreate($this->path('admin_team_create'), false);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
|
||||
class TimesheetsSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,12 @@ class TimesheetsSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var TimesheetQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
$event->addColumnToggle('#modal_timesheet');
|
||||
|
||||
if ($this->isGranted('export_own_timesheet')) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
|
||||
class TimesheetsTeamSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,7 +21,12 @@ class TimesheetsTeamSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var TimesheetQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
$event->addColumnToggle('#modal_timesheet_admin');
|
||||
|
||||
if ($this->isGranted('export_other_timesheet')) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\EventSubscriber\Actions;
|
||||
|
||||
use App\Event\PageActionsEvent;
|
||||
use App\Repository\Query\UserQuery;
|
||||
|
||||
class UsersSubscriber extends AbstractActionsSubscriber
|
||||
{
|
||||
@@ -20,10 +21,17 @@ class UsersSubscriber extends AbstractActionsSubscriber
|
||||
|
||||
public function onActions(PageActionsEvent $event): void
|
||||
{
|
||||
$event->addSearchToggle();
|
||||
$payload = $event->getPayload();
|
||||
|
||||
/** @var UserQuery $query */
|
||||
$query = $payload['query'];
|
||||
|
||||
$event->addSearchToggle($query);
|
||||
|
||||
if ($event->isIndexView()) {
|
||||
$event->addColumnToggle('#modal_user_admin');
|
||||
}
|
||||
|
||||
$event->addQuickExport($this->path('user_export'));
|
||||
|
||||
if ($this->isGranted('create_user')) {
|
||||
|
||||
@@ -27,7 +27,10 @@ class InvoiceArchiveForm extends AbstractToolbarForm
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, ['required' => false, 'start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true, 'placeholder' => ''], true);
|
||||
$builder->add('status', InvoiceStatusType::class);
|
||||
$builder->add('status', InvoiceStatusType::class, ['required' => false]);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addOrder($builder);
|
||||
$this->addOrderBy($builder, InvoiceArchiveQuery::INVOICE_ARCHIVE_ORDER_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -185,12 +185,19 @@ class InvoiceRepository extends EntityRepository
|
||||
case 'date':
|
||||
$orderBy = 'i.createdAt';
|
||||
break;
|
||||
case 'customer':
|
||||
$orderBy = 'i.customer';
|
||||
case 'number':
|
||||
$orderBy = 'i.invoiceNumber';
|
||||
break;
|
||||
case 'total':
|
||||
case 'payed':
|
||||
$orderBy = 'i.paymentDate';
|
||||
break;
|
||||
case 'total_rate':
|
||||
$orderBy = 'i.total';
|
||||
break;
|
||||
case 'tax':
|
||||
case 'status':
|
||||
$orderBy = 'i.' . $orderBy;
|
||||
break;
|
||||
}
|
||||
|
||||
$qb->addOrderBy($orderBy, $query->getOrder());
|
||||
|
||||
@@ -19,7 +19,7 @@ class ActivityQuery extends ProjectQuery
|
||||
public const ACTIVITY_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'project', 'budget', 'timeBudget', 'visible'];
|
||||
|
||||
/**
|
||||
* @var Project[]|int[]
|
||||
* @var array<Project|int>
|
||||
*/
|
||||
private $projects = [];
|
||||
/**
|
||||
@@ -36,6 +36,9 @@ class ActivityQuery extends ProjectQuery
|
||||
parent::__construct();
|
||||
$this->setDefaults([
|
||||
'orderBy' => 'name',
|
||||
'projects' => [],
|
||||
'globalsOnly' => false,
|
||||
'excludeGlobals' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,13 +85,17 @@ class BaseQuery
|
||||
*/
|
||||
private $searchTerm;
|
||||
/**
|
||||
* @var Bookmark
|
||||
* @var Bookmark|null
|
||||
*/
|
||||
private $bookmark;
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $name;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bookmarkSearch = false;
|
||||
|
||||
/**
|
||||
* @param Team[] $teams
|
||||
@@ -258,13 +262,44 @@ class BaseQuery
|
||||
{
|
||||
$method = 'set' . ucfirst($name);
|
||||
if (method_exists($this, $method)) {
|
||||
$this->{$method}($value);
|
||||
} elseif (property_exists($this, $name)) {
|
||||
$this->$name = $value;
|
||||
\call_user_func([$this, $method], $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (substr($name, -1) === 's') {
|
||||
$method = 'add' . ucfirst(substr($name, 0, \strlen($name) - 1));
|
||||
if (method_exists($this, $method) && \is_array($value)) {
|
||||
foreach ($value as $v) {
|
||||
\call_user_func([$this, $method], $v);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (property_exists($this, $name)) {
|
||||
$this->{$name} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
protected function get($name)
|
||||
{
|
||||
$methods = ['get' . ucfirst($name), 'is' . ucfirst($name), 'has' . ucfirst($name)];
|
||||
foreach ($methods as $method) {
|
||||
if (method_exists($this, $method)) {
|
||||
return \call_user_func([$this, $method]);
|
||||
}
|
||||
}
|
||||
|
||||
if (property_exists($this, $name)) {
|
||||
return $this->{$name};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* You have to add ALL user facing form fields as default!
|
||||
*
|
||||
* @param array $defaults
|
||||
* @return self
|
||||
*/
|
||||
@@ -347,4 +382,66 @@ class BaseQuery
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function isDefaultFilter(string $filter): bool
|
||||
{
|
||||
if (!\array_key_exists($filter, $this->defaults)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expectedValue = $this->defaults[$filter];
|
||||
|
||||
return $this->matchesFilter($filter, $expectedValue);
|
||||
}
|
||||
|
||||
public function matchesFilter(string $filter, $expectedValue): bool
|
||||
{
|
||||
$currentValue = $this->get($filter);
|
||||
|
||||
if (\is_object($currentValue)) {
|
||||
if ($currentValue != $expectedValue) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ($currentValue !== $expectedValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function countFilter(): int
|
||||
{
|
||||
$filter = 0;
|
||||
|
||||
foreach (array_keys($this->defaults) as $key) {
|
||||
if ($key === 'page') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->isDefaultFilter($key)) {
|
||||
$filter++;
|
||||
}
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
public function resetFilter(): void
|
||||
{
|
||||
foreach ($this->defaults as $key => $value) {
|
||||
$this->set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function flagAsBookmarkSearch(): void
|
||||
{
|
||||
$this->bookmarkSearch = true;
|
||||
}
|
||||
|
||||
public function isBookmarkSearch(): bool
|
||||
{
|
||||
return $this->bookmarkSearch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class CustomerQuery extends BaseQuery implements VisibilityInterface
|
||||
{
|
||||
$this->setDefaults([
|
||||
'orderBy' => 'name',
|
||||
'visibility' => VisibilityInterface::SHOW_VISIBLE,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,16 @@ class ExportQuery extends TimesheetQuery
|
||||
*/
|
||||
private $markAsExported = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->setDefaults([
|
||||
'order' => ExportQuery::ORDER_ASC,
|
||||
'state' => ExportQuery::STATE_STOPPED,
|
||||
'exported' => ExportQuery::STATE_NOT_EXPORTED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getRenderer(): ?string
|
||||
{
|
||||
return $this->renderer;
|
||||
|
||||
@@ -21,7 +21,9 @@ class InvoiceArchiveQuery extends BaseQuery
|
||||
use DateRangeTrait;
|
||||
|
||||
public const INVOICE_ARCHIVE_ORDER_ALLOWED = [
|
||||
'date', 'customer', 'total'
|
||||
'date', 'total_rate',
|
||||
// TODO other fields have a problem with translation
|
||||
// 'number', 'tax', 'payed', 'status'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -40,6 +42,8 @@ class InvoiceArchiveQuery extends BaseQuery
|
||||
'orderBy' => 'date',
|
||||
'order' => self::ORDER_DESC,
|
||||
'dateRange' => new DateRange(),
|
||||
'customers' => [],
|
||||
'status' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ class InvoiceQuery extends TimesheetQuery
|
||||
{
|
||||
parent::__construct();
|
||||
$this->setDefaults([
|
||||
'order' => InvoiceQuery::ORDER_ASC,
|
||||
'exported' => InvoiceQuery::STATE_NOT_EXPORTED,
|
||||
'state' => self::STATE_STOPPED,
|
||||
'billable' => true,
|
||||
'markAsExported' => false,
|
||||
|
||||
@@ -23,15 +23,15 @@ class ProjectQuery extends BaseQuery implements VisibilityInterface
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var array<Customer|int>
|
||||
*/
|
||||
private $customers = [];
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTime|null
|
||||
*/
|
||||
private $projectStart;
|
||||
/**
|
||||
* @var \DateTime
|
||||
* @var \DateTime|null
|
||||
*/
|
||||
private $projectEnd;
|
||||
|
||||
@@ -39,6 +39,10 @@ class ProjectQuery extends BaseQuery implements VisibilityInterface
|
||||
{
|
||||
$this->setDefaults([
|
||||
'orderBy' => 'name',
|
||||
'customers' => [],
|
||||
'projectStart' => null,
|
||||
'projectEnd' => null,
|
||||
'visibility' => VisibilityInterface::SHOW_VISIBLE,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class TeamQuery extends BaseQuery
|
||||
{
|
||||
$this->setDefaults([
|
||||
'orderBy' => 'name',
|
||||
'users' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,13 @@ class TimesheetQuery extends ActivityQuery implements BillableInterface
|
||||
$this->setDefaults([
|
||||
'order' => self::ORDER_DESC,
|
||||
'orderBy' => 'begin',
|
||||
'dateRange' => new DateRange($resetTimes)
|
||||
'dateRange' => new DateRange($resetTimes),
|
||||
'exported' => self::STATE_ALL,
|
||||
'state' => self::STATE_ALL,
|
||||
'billable' => null,
|
||||
'tags' => [],
|
||||
'users' => [],
|
||||
'activities' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,17 @@ class UserQuery extends BaseQuery implements VisibilityInterface
|
||||
* @var string|null
|
||||
*/
|
||||
private $role;
|
||||
/**
|
||||
* @var Team[]
|
||||
*/
|
||||
private $searchTeams = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setDefaults([
|
||||
'orderBy' => 'username',
|
||||
'searchTeams' => [],
|
||||
'visibility' => VisibilityInterface::SHOW_VISIBLE,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro activities(view) %}
|
||||
{% macro activities(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'activities', view) %}
|
||||
{% set event = actions(app.user, 'activities', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
{% block page_title %}{{ 'admin_activity.title'|trans }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.activities('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.activities('index', query) }}{% endblock %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro customers(view) %}
|
||||
{% macro customers(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'customers', view) %}
|
||||
{% set event = actions(app.user, 'customers', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
{% block page_title %}{{ 'admin_customer.title'|trans }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.customers('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.customers('index', query) }}{% endblock %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
{{ widgets.table_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro invoice_listing(view) %}
|
||||
{% macro invoice_listing(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'invoice_details', view) %}
|
||||
{% set event = actions(app.user, 'invoice_details', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% import "macros/toolbar.html.twig" as toolbar %}
|
||||
{% import "macros/search.html.twig" as search %}
|
||||
{% import "macros/datatables.html.twig" as tables %}
|
||||
{% import "invoice/actions.html.twig" as actions %}
|
||||
{% import "invoice/macros.html.twig" as macros %}
|
||||
|
||||
{% set columns = {
|
||||
'date': {'class': 'alwaysVisible', 'orderBy': false},
|
||||
'date': {'class': 'alwaysVisible'},
|
||||
'user': {'class': 'hidden-xs hidden-sm text-nowrap hidden', 'orderBy': false},
|
||||
'customer': {'class': 'hidden-xs hidden-sm text-nowrap', 'orderBy': false},
|
||||
'invoice_number': {'class': 'hidden-xs hidden-sm w-min', 'title': 'invoice.number'|trans, 'orderBy': false},
|
||||
@@ -14,16 +14,16 @@
|
||||
'payment_date': {'class': 'hidden-xs hidden w-min', 'title': 'invoice.payment_date'|trans, 'orderBy': false},
|
||||
'status': {'class': 'alwaysVisible w-min', 'orderBy': false},
|
||||
'subtotal': {'class': 'hidden-xs text-right w-min hidden', 'title': 'invoice.subtotal'|trans, 'orderBy': false},
|
||||
'tax': {'class': 'hidden-xs text-right w-min hidden', 'title': 'invoice.tax'|trans, 'orderBy': false},
|
||||
'total_rate': {'class': 'hidden-xs text-right w-min', 'orderBy': false},
|
||||
'tax': {'class': 'hidden-xs text-right w-min hidden', 'title': 'invoice.tax'|trans},
|
||||
'total_rate': {'class': 'hidden-xs text-right w-min'},
|
||||
'actions': {'class': 'actions alwaysVisible', 'orderBy': false},
|
||||
} %}
|
||||
|
||||
{% set tableName = 'invoices' %}
|
||||
|
||||
{% block page_title %}{{ 'invoice.title'|trans }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.invoice_listing('index') }}{% endblock %}
|
||||
{% block page_search %}{{ toolbar.dropDownSearch(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.invoice_listing('index', query) }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
|
||||
@@ -43,6 +43,11 @@
|
||||
<div class="btn-group">
|
||||
<button type="submit" name="performSearch" value="performSearch" class="btn btn-primary pull-left" data-type="submit">{{ 'search'|trans }}</button>
|
||||
</div>
|
||||
{% if form.vars.data.countFilter() > 0 and not form.vars.data.isBookmarkSearch() %}
|
||||
<div class="btn-group">
|
||||
<button type="submit" id="resetSearchFilter" name="resetSearchFilter" class="btn btn-warning" title="{{ 'action.reset'|trans }}">{{ 'action.reset'|trans }}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="btn-group">
|
||||
{% if form.vars.data.bookmark %}
|
||||
<button type="submit" id="setDefaultQuery" name="setDefaultQuery" class="btn btn-default" title="{{ 'label.set_as_default'|trans }}"><i class="{{ 'bookmarked'|icon }}"></i></button>
|
||||
|
||||
@@ -359,6 +359,7 @@
|
||||
{% set title = null %}
|
||||
{% set disabled = false %}
|
||||
{% set attr = {} %}
|
||||
{% set label = null %}
|
||||
{% set translation_domain = 'messages' %}
|
||||
{% if type is same as (false) %}
|
||||
{% set class = "" %}
|
||||
@@ -369,6 +370,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if values is not iterable %}
|
||||
{% deprecated 'Passing a non iterable action_button is deprecated since 1.15 and will be removed with 2.0' %}
|
||||
{% set url = values %}
|
||||
{% if 'onclick:' in url %}
|
||||
{% set onclick = url|replace({'onclick:': ''}) %}
|
||||
@@ -393,6 +395,7 @@
|
||||
{% set title = values.title ?? null %}
|
||||
{% set class = class ~ ( values.class | default("")) %}
|
||||
{% set attr = values.attr ?? {} %}
|
||||
{% set label = values.label ?? null %}
|
||||
{% set translation_domain = values.translation_domain ?? translation_domain %}
|
||||
{% endif %}
|
||||
|
||||
@@ -412,7 +415,7 @@
|
||||
{{ ' ' ~ name }}={% if '"' in value %}'{{ value|raw }}'{% else %}"{{ value|raw }}"{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
>{% if title is not null %}{{ title|trans({}, translation_domain) }}{% else %}{{ _self.icon(icon) }}{% endif %}</a>
|
||||
>{% if title is not null %}{{ title|trans({}, translation_domain) }}{% else %}{{ _self.icon(icon) }}{% endif %}{% if label is not null %}<span class="label bg-yellow">{{ label }}</span>{% endif %}</a>
|
||||
{% endapply %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro projects(view) %}
|
||||
{% macro projects(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'projects', view) %}
|
||||
{% set event = actions(app.user, 'projects', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
{% block page_title %}{{ 'admin_project.title'|trans }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.projects('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.projects('index', query) }}{% endblock %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns) }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro tags(view) %}
|
||||
{% macro tags(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'tags', view) %}
|
||||
{% set event = actions(app.user, 'tags', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{% block page_title %}{{ 'menu.tags'|trans }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.tags('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.tags('index', query) }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro teams(view) %}
|
||||
{% macro teams(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'teams', view) %}
|
||||
{% set event = actions(app.user, 'teams', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{% block page_title %}{{ 'teams.title'|trans({}, 'teams') }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.teams('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.teams('index', query) }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro timesheets_team(view) %}
|
||||
{% macro timesheets_team(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'timesheets_team', view) %}
|
||||
{% set event = actions(app.user, 'timesheets_team', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% set allowMarkdown = false %}
|
||||
|
||||
{% block page_title %}{{ 'admin_timesheet.title'|trans }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.timesheets_team('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.timesheets_team('index', query) }}{% endblock %}
|
||||
|
||||
{% block row_action %}
|
||||
{{- actions.timesheet_team(entry, 'index') -}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro timesheets(view) %}
|
||||
{% macro timesheets(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'timesheets', view) %}
|
||||
{% set event = actions(app.user, 'timesheets', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% set allowMarkdown = true %}
|
||||
|
||||
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.timesheets('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.timesheets('index', query) }}{% endblock %}
|
||||
|
||||
{% block row_action %}
|
||||
{{- actions.timesheet(entry, 'index') -}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% macro users(view) %}
|
||||
{% macro users(view, query) %}
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
{% set event = actions(app.user, 'users', view) %}
|
||||
{% set event = actions(app.user, 'users', view, {'query': query}) %}
|
||||
{{ widgets.page_actions(event.actions) }}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
{% block page_title %}{{ 'admin_user.title'|trans }}{% endblock %}
|
||||
{% block page_search %}{{ search.searchModal(toolbarForm) }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.users('index') }}{% endblock %}
|
||||
{% block page_actions %}{{ actions.users('index', query) }}{% endblock %}
|
||||
|
||||
{% block main_before %}
|
||||
{{ tables.data_table_column_modal(tableName, columns, 'kimai.userUpdate') }}
|
||||
|
||||
@@ -95,7 +95,7 @@ class PageActionsEventTest extends TestCase
|
||||
$this->assertEquals(8, $sut->countActions());
|
||||
|
||||
$expected = [
|
||||
'search' => ['modal' => '#modal_search'],
|
||||
'search' => ['modal' => '#modal_search', 'label' => null],
|
||||
'divider0' => null,
|
||||
'back' => ['url' => 'foo1', 'translation_domain' => 'actions'],
|
||||
'visibility' => ['modal' => '#foo2'],
|
||||
|
||||
@@ -36,6 +36,7 @@ class BaseQueryTest extends TestCase
|
||||
{
|
||||
$this->assertBaseQuery(new BaseQuery());
|
||||
$this->assertResetByFormError(new BaseQuery());
|
||||
$this->assertFilter(new BaseQuery());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,6 +122,36 @@ class BaseQueryTest extends TestCase
|
||||
self::assertSame($team, $sut->getTeams()[0]);
|
||||
}
|
||||
|
||||
protected function assertFilter(BaseQuery $sut)
|
||||
{
|
||||
self::assertEquals(0, $sut->countFilter());
|
||||
$sut->setSearchTerm(new SearchTerm('sdfsdf'));
|
||||
self::assertEquals(1, $sut->countFilter());
|
||||
$sut->setPageSize(22);
|
||||
self::assertEquals(2, $sut->countFilter());
|
||||
$sut->setPage(2);
|
||||
self::assertEquals(2, $sut->countFilter());
|
||||
$sut->setOrderBy('foo');
|
||||
self::assertEquals(3, $sut->countFilter());
|
||||
$sut->setOrder(BaseQuery::ORDER_DESC);
|
||||
self::assertEquals(4, $sut->countFilter());
|
||||
$sut->setOrder(BaseQuery::ORDER_ASC);
|
||||
self::assertEquals(3, $sut->countFilter());
|
||||
$sut->setOrder(BaseQuery::ORDER_DESC);
|
||||
self::assertEquals(4, $sut->countFilter());
|
||||
|
||||
self::assertTrue($sut->matchesFilter('page', 2));
|
||||
self::assertFalse($sut->isDefaultFilter('page'));
|
||||
|
||||
$sut->resetFilter();
|
||||
self::assertEquals(0, $sut->countFilter());
|
||||
|
||||
self::assertEquals(1, $sut->getPage());
|
||||
self::assertTrue($sut->matchesFilter('page', 1));
|
||||
self::assertTrue($sut->isDefaultFilter('page'));
|
||||
self::assertFalse($sut->isDefaultFilter('foo'));
|
||||
}
|
||||
|
||||
protected function assertBookmark(BaseQuery $sut)
|
||||
{
|
||||
$bookmark = new Bookmark();
|
||||
@@ -129,6 +160,9 @@ class BaseQueryTest extends TestCase
|
||||
$sut->setBookmark($bookmark);
|
||||
self::assertSame($bookmark, $sut->getBookmark());
|
||||
self::assertTrue($sut->hasBookmark());
|
||||
self::assertFalse($sut->isBookmarkSearch());
|
||||
$sut->flagAsBookmarkSearch();
|
||||
self::assertTrue($sut->isBookmarkSearch());
|
||||
}
|
||||
|
||||
protected function assertPage(BaseQuery $sut)
|
||||
|
||||
@@ -24,14 +24,14 @@ class ExportQueryTest extends TimesheetQueryTest
|
||||
$this->assertPage($sut);
|
||||
$this->assertPageSize($sut);
|
||||
$this->assertOrderBy($sut, 'begin');
|
||||
$this->assertOrder($sut, ExportQuery::ORDER_DESC);
|
||||
$this->assertOrder($sut, ExportQuery::ORDER_ASC);
|
||||
|
||||
$this->assertUser($sut);
|
||||
$this->assertCustomer($sut);
|
||||
$this->assertProject($sut);
|
||||
$this->assertActivity($sut);
|
||||
$this->assertState($sut);
|
||||
$this->assertExported($sut);
|
||||
$this->assertStateWith($sut, ExportQuery::STATE_STOPPED);
|
||||
$this->assertExportedWith($sut, ExportQuery::STATE_NOT_EXPORTED);
|
||||
$this->assertRenderer($sut);
|
||||
$this->assertMarkAsExported($sut);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class InvoiceQueryTest extends TimesheetQueryTest
|
||||
$this->assertPage($sut);
|
||||
$this->assertPageSize($sut);
|
||||
$this->assertOrderBy($sut, 'begin');
|
||||
$this->assertOrder($sut, InvoiceQuery::ORDER_DESC);
|
||||
$this->assertOrder($sut, InvoiceQuery::ORDER_ASC);
|
||||
|
||||
$this->assertUser($sut);
|
||||
$this->assertCustomer($sut);
|
||||
@@ -35,7 +35,7 @@ class InvoiceQueryTest extends TimesheetQueryTest
|
||||
self::assertFalse($sut->isRunning());
|
||||
self::assertTrue($sut->isStopped());
|
||||
|
||||
$this->assertExported($sut);
|
||||
$this->assertExportedWith($sut, InvoiceQuery::STATE_NOT_EXPORTED);
|
||||
$this->assertMarkAsExported($sut);
|
||||
$this->assertModifiedAfter($sut);
|
||||
|
||||
|
||||
@@ -98,8 +98,13 @@ class TimesheetQueryTest extends BaseQueryTest
|
||||
self::assertFalse($sut->isRunning());
|
||||
self::assertFalse($sut->isStopped());
|
||||
|
||||
$this->assertStateWith($sut, TimesheetQuery::STATE_ALL);
|
||||
}
|
||||
|
||||
protected function assertStateWith(TimesheetQuery $sut, int $defaultState)
|
||||
{
|
||||
self::assertInstanceOf(TimesheetQuery::class, $sut->setState(PHP_INT_MAX));
|
||||
self::assertEquals(TimesheetQuery::STATE_ALL, $sut->getState());
|
||||
self::assertEquals($defaultState, $sut->getState());
|
||||
|
||||
$sut->setState(TimesheetQuery::STATE_STOPPED);
|
||||
self::assertEquals(TimesheetQuery::STATE_STOPPED, $sut->getState());
|
||||
@@ -121,8 +126,13 @@ class TimesheetQueryTest extends BaseQueryTest
|
||||
self::assertFalse($sut->isExported());
|
||||
self::assertFalse($sut->isNotExported());
|
||||
|
||||
$this->assertExportedWith($sut, TimesheetQuery::STATE_ALL);
|
||||
}
|
||||
|
||||
protected function assertExportedWith(TimesheetQuery $sut, int $defaultState)
|
||||
{
|
||||
self::assertInstanceOf(TimesheetQuery::class, $sut->setExported(PHP_INT_MAX));
|
||||
self::assertEquals(TimesheetQuery::STATE_ALL, $sut->getExported());
|
||||
self::assertEquals($defaultState, $sut->getExported());
|
||||
|
||||
$sut->setExported(TimesheetQuery::STATE_EXPORTED);
|
||||
self::assertEquals(TimesheetQuery::STATE_EXPORTED, $sut->getExported());
|
||||
|
||||
Reference in New Issue
Block a user