Release 1.6.2 (#1289)
* include user teams in user entity * prevent unauthorized access via API * improve teamlead permission handling in team timesheets * add team data to user entity * add security tests * highlight menu for invoice template copy * unified handling of invoice data across all templates * access to the current users data in invoice templates * permission improvement in invoice form * allow to skip record rows * allow to add new invoice locations without overwriting the global ones * allow to order user preferences * change permission for normal users with access to view_other_timesheets * properly validate invoice template field length * allow to replace multiple variables in cell values text * upgraded office invoice template * doctrine deprecation fix * upgrade phpoffice/phpword * fix future begin check for default rounding rules * dashboard widget counter: respect visibility and teams - fixes #1161 * fix future begin check for default rounding rules * added new events for pre and post invoice rendering * fix permission issue for users without team seeing all records * prevent error in spreadsheet renderer for empty invoices
This commit is contained in:
@@ -10,8 +10,11 @@
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\InvoiceTemplate;
|
||||
use App\Event\InvoicePostRenderEvent;
|
||||
use App\Event\InvoicePreRenderEvent;
|
||||
use App\Form\InvoiceTemplateForm;
|
||||
use App\Form\Toolbar\InvoiceToolbarForm;
|
||||
use App\Invoice\InvoiceFormatter;
|
||||
use App\Invoice\InvoiceItemInterface;
|
||||
use App\Invoice\InvoiceModel;
|
||||
use App\Invoice\ServiceInvoice;
|
||||
@@ -25,6 +28,7 @@ use Symfony\Component\Form\SubmitButton;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Controller used to create invoices and manage invoice templates.
|
||||
@@ -32,26 +36,36 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
* @Route(path="/invoice")
|
||||
* @Security("is_granted('view_invoice')")
|
||||
*/
|
||||
class InvoiceController extends AbstractController
|
||||
final class InvoiceController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var ServiceInvoice
|
||||
*/
|
||||
protected $service;
|
||||
private $service;
|
||||
/**
|
||||
* @var InvoiceTemplateRepository
|
||||
*/
|
||||
protected $invoiceRepository;
|
||||
private $invoiceRepository;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTimeFactory;
|
||||
private $dateTimeFactory;
|
||||
/**
|
||||
* @var InvoiceFormatter
|
||||
*/
|
||||
private $formatter;
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $invoice, UserDateTimeFactory $dateTimeFactory)
|
||||
public function __construct(ServiceInvoice $service, InvoiceTemplateRepository $invoice, UserDateTimeFactory $dateTimeFactory, InvoiceFormatter $formatter, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->invoiceRepository = $invoice;
|
||||
$this->dateTimeFactory = $dateTimeFactory;
|
||||
$this->formatter = $formatter;
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,8 +129,14 @@ class InvoiceController extends AbstractController
|
||||
$query->setEnd($end);
|
||||
$query->setExported(InvoiceQuery::STATE_NOT_EXPORTED);
|
||||
$query->setState(InvoiceQuery::STATE_STOPPED);
|
||||
// limit access to data from teams
|
||||
$query->setCurrentUser($this->getUser());
|
||||
|
||||
if (!$this->isGranted('view_other_timesheet')) {
|
||||
// limit access to own data
|
||||
$query->setUser($this->getUser());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
@@ -135,11 +155,15 @@ class InvoiceController extends AbstractController
|
||||
|
||||
foreach ($this->service->getRenderer() as $renderer) {
|
||||
if ($renderer->supports($document)) {
|
||||
$this->dispatcher->dispatch(new InvoicePreRenderEvent($model, $document, $renderer));
|
||||
|
||||
$response = $renderer->render($document, $model);
|
||||
if ($query->isMarkAsExported()) {
|
||||
$this->markEntriesAsExported($entries);
|
||||
}
|
||||
|
||||
$this->dispatcher->dispatch(new InvoicePostRenderEvent($model, $document, $renderer, $response));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -216,9 +240,10 @@ class InvoiceController extends AbstractController
|
||||
*/
|
||||
protected function prepareModel(InvoiceQuery $query): InvoiceModel
|
||||
{
|
||||
$model = new InvoiceModel();
|
||||
$model = new InvoiceModel($this->formatter);
|
||||
$model
|
||||
->setQuery($query)
|
||||
->setUser($this->getUser())
|
||||
->setCustomer($query->getCustomer())
|
||||
;
|
||||
|
||||
@@ -340,6 +365,7 @@ class InvoiceController extends AbstractController
|
||||
return $this->createForm(InvoiceToolbarForm::class, $query, [
|
||||
'action' => $this->generateUrl('invoice', []),
|
||||
'method' => $method,
|
||||
'include_user' => $this->isGranted('view_other_timesheet'),
|
||||
'attr' => [
|
||||
'id' => 'invoice-print-form'
|
||||
],
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserPreference;
|
||||
use App\Event\PrepareUserEvent;
|
||||
use App\Form\UserApiTokenType;
|
||||
use App\Form\UserEditType;
|
||||
@@ -20,6 +21,7 @@ use App\Form\UserTeamsType;
|
||||
use App\Repository\TeamRepository;
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Voter\UserVoter;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
@@ -210,6 +212,13 @@ class ProfileController extends AbstractController
|
||||
$event = new PrepareUserEvent($profile);
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
/** @var \ArrayIterator $iterator */
|
||||
$iterator = $profile->getPreferences()->getIterator();
|
||||
$iterator->uasort(function (UserPreference $a, UserPreference $b) {
|
||||
return ($a->getOrder() < $b->getOrder()) ? -1 : 1;
|
||||
});
|
||||
$profile->setPreferences(new ArrayCollection(iterator_to_array($iterator)));
|
||||
|
||||
$original = [];
|
||||
foreach ($profile->getPreferences() as $preference) {
|
||||
$original[$preference->getName()] = $preference;
|
||||
|
||||
@@ -402,7 +402,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
'action' => $this->generateUrl($this->getMultiUpdateRoute(), []),
|
||||
'method' => 'POST',
|
||||
'include_exported' => $this->isGranted($this->getPermissionEditExport()),
|
||||
'include_user' => $this->includeUserInForms(),
|
||||
'include_user' => $this->includeUserInForms('multi'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
'action' => $this->generateUrl($this->getCreateRoute()),
|
||||
'include_rate' => $this->isGranted('edit_rate', $entry),
|
||||
'include_exported' => $this->isGranted('edit_export', $entry),
|
||||
'include_user' => $this->includeUserInForms(),
|
||||
'include_user' => $this->includeUserInForms('create'),
|
||||
'allow_begin_datetime' => $mode->canEditBegin(),
|
||||
'allow_end_datetime' => $mode->canEditEnd(),
|
||||
'allow_duration' => $mode->canEditDuration(),
|
||||
@@ -450,7 +450,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
]),
|
||||
'include_rate' => $this->isGranted('edit_rate', $entry),
|
||||
'include_exported' => $this->isGranted('edit_export', $entry),
|
||||
'include_user' => $this->includeUserInForms(),
|
||||
'include_user' => $this->includeUserInForms('edit'),
|
||||
'allow_begin_datetime' => $mode->canEditBegin(),
|
||||
'allow_end_datetime' => $mode->canEditEnd(),
|
||||
'allow_duration' => $mode->canEditDuration(),
|
||||
@@ -469,7 +469,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
'page' => $query->getPage(),
|
||||
]),
|
||||
'method' => 'GET',
|
||||
'include_user' => $this->includeUserInForms(),
|
||||
'include_user' => $this->includeUserInForms('toolbar'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
return (bool) $this->getUser()->getPreferenceValue('timesheet.daily_stats', false);
|
||||
}
|
||||
|
||||
protected function includeUserInForms(): bool
|
||||
protected function includeUserInForms(string $formName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -118,9 +118,13 @@ class TimesheetTeamController extends TimesheetAbstractController
|
||||
return TimesheetAdminEditForm::class;
|
||||
}
|
||||
|
||||
protected function includeUserInForms(): bool
|
||||
protected function includeUserInForms(string $formName): bool
|
||||
{
|
||||
return true;
|
||||
if ($formName === 'toolbar') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->isGranted('edit_other_timesheet');
|
||||
}
|
||||
|
||||
protected function getTimesheetRoute(): string
|
||||
|
||||
Reference in New Issue
Block a user