Release 2.0.11 (#3932)
- added "today" as selector in date-range dropdown - added feature to prevent auto-select of dropdowns with only one entry - added hint that no changes were detected in batch update - added negative invoice sums are possible (e.g. for credit notes) - fix project list is expanded after submission - fix invalid date parsing causes 500 - fix: prevent auto-select of activities in export and invoice form (in case only one global activity exists) - fix team assignments for customer and project were not saved (using API now) - fix form fieldset with legend styling (e.g. team project assignment) - fix required meta-field were forced to have a value in batch update - fix tomselect meta-field was not disabled in batch update - fix unset internal rate is shown as 0 - fix one minute rounding problem in duration-only mode with "now" being default time - fix column width and label for duration-only mode - tech debt: cleanup invoice template (remove invoice layout) - tech debt: reorder for simpler comparison with invoice form - possible BC for devs: remove unused methods from form trait - bump composer packages (includes new translations for auth screens)
This commit is contained in:
@@ -68,7 +68,7 @@ final class CalendarController extends AbstractController
|
||||
$defaultStart = null;
|
||||
if ($this->configuration->getTimesheetDefaultBeginTime() !== 'now') {
|
||||
$defaultStart = $factory->createDateTime($this->configuration->getTimesheetDefaultBeginTime());
|
||||
$defaultStart = $defaultStart->format('h:i:s');
|
||||
$defaultStart = $defaultStart->format('H:i:s');
|
||||
}
|
||||
|
||||
$config = $this->calendarService->getConfiguration();
|
||||
|
||||
@@ -36,10 +36,17 @@ final class QuickEntryController extends AbstractController
|
||||
public function quickEntry(Request $request, ?string $begin = null)
|
||||
{
|
||||
$factory = $this->getDateTimeFactory();
|
||||
|
||||
if ($begin !== null) {
|
||||
try {
|
||||
$begin = $factory->createDateTime($begin);
|
||||
} catch (\Exception $ex) {
|
||||
$begin = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($begin === null) {
|
||||
$begin = $factory->createDateTime();
|
||||
} else {
|
||||
$begin = $factory->createDateTime($begin);
|
||||
}
|
||||
|
||||
$startWeek = $factory->getStartOfWeek($begin);
|
||||
|
||||
@@ -77,7 +77,7 @@ final class TagController extends AbstractController
|
||||
|
||||
#[Route(path: '/{id}/edit', name: 'tags_edit', methods: ['GET', 'POST'])]
|
||||
#[IsGranted('manage_tag')]
|
||||
public function editAction(Tag $tag, TagRepository $repository, Request $request)
|
||||
public function editAction(Tag $tag, TagRepository $repository, Request $request): Response
|
||||
{
|
||||
$editForm = $this->createForm(TagEditForm::class, $tag, [
|
||||
'action' => $this->generateUrl('tags_edit', ['id' => $tag->getId()]),
|
||||
@@ -109,7 +109,7 @@ final class TagController extends AbstractController
|
||||
|
||||
#[Route(path: '/create', name: 'tags_create', methods: ['GET', 'POST'])]
|
||||
#[IsGranted('manage_tag')]
|
||||
public function createAction(TagRepository $repository, Request $request)
|
||||
public function createAction(TagRepository $repository, Request $request): Response
|
||||
{
|
||||
$tag = new Tag();
|
||||
|
||||
@@ -143,7 +143,7 @@ final class TagController extends AbstractController
|
||||
|
||||
#[Route(path: '/multi-delete', name: 'tags_multi_delete', methods: ['POST'])]
|
||||
#[IsGranted('delete_tag')]
|
||||
public function multiDelete(TagRepository $repository, Request $request)
|
||||
public function multiDelete(TagRepository $repository, Request $request): Response
|
||||
{
|
||||
$form = $this->getMultiUpdateForm($repository);
|
||||
$form->handleRequest($request);
|
||||
|
||||
@@ -10,14 +10,15 @@
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Team;
|
||||
use App\Form\TeamCustomerForm;
|
||||
use App\Form\TeamEditForm;
|
||||
use App\Form\TeamProjectForm;
|
||||
use App\Form\Toolbar\TeamToolbarForm;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Repository\Query\TeamQuery;
|
||||
use App\Repository\TeamRepository;
|
||||
use App\Utils\DataTable;
|
||||
use App\Utils\PageSetup;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -90,7 +91,7 @@ final class TeamController extends AbstractController
|
||||
#[Route(path: '/{id}/duplicate', name: 'team_duplicate', methods: ['GET', 'POST'])]
|
||||
#[IsGranted('create_team')]
|
||||
#[IsGranted('edit', 'team')]
|
||||
public function duplicateTeam(Team $team, Request $request)
|
||||
public function duplicateTeam(Team $team, Request $request): Response
|
||||
{
|
||||
$newTeam = clone $team;
|
||||
|
||||
@@ -105,14 +106,14 @@ final class TeamController extends AbstractController
|
||||
|
||||
#[Route(path: '/{id}/edit', name: 'admin_team_edit', methods: ['GET', 'POST'])]
|
||||
#[IsGranted('edit', 'team')]
|
||||
public function editAction(Team $team, Request $request)
|
||||
public function editAction(Team $team, Request $request): Response
|
||||
{
|
||||
return $this->renderEditScreen($team, $request);
|
||||
}
|
||||
|
||||
#[Route(path: '/{id}/edit_member', name: 'admin_team_member', methods: ['GET', 'POST'])]
|
||||
#[IsGranted('edit', 'team')]
|
||||
public function editMemberAction(Team $team, Request $request)
|
||||
public function editMemberAction(Team $team, Request $request): Response
|
||||
{
|
||||
$editForm = $this->createForm(TeamEditForm::class, $team, [
|
||||
'action' => $this->generateUrl('admin_team_member', ['id' => $team->getId()]),
|
||||
@@ -176,37 +177,21 @@ final class TeamController extends AbstractController
|
||||
}
|
||||
|
||||
if (null !== $team->getId()) {
|
||||
$customerForm = $this->createForm(TeamCustomerForm::class, $team, [
|
||||
'method' => 'POST',
|
||||
]);
|
||||
$customerForm->handleRequest($request);
|
||||
$customerForm = $this->createFormWithName('team_customer_form', FormType::class, $team)
|
||||
->add('customers', CustomerType::class, [
|
||||
'label' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'query_builder_for_user' => false,
|
||||
]);
|
||||
|
||||
if ($customerForm->isSubmitted() && $customerForm->isValid()) {
|
||||
try {
|
||||
$this->repository->saveTeam($team);
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute('admin_team_edit', ['id' => $team->getId()]);
|
||||
} catch (\Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
}
|
||||
|
||||
$projectForm = $this->createForm(TeamProjectForm::class, $team, [
|
||||
'method' => 'POST',
|
||||
]);
|
||||
$projectForm->handleRequest($request);
|
||||
|
||||
if ($projectForm->isSubmitted() && $projectForm->isValid()) {
|
||||
try {
|
||||
$this->repository->saveTeam($team);
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute('admin_team_edit', ['id' => $team->getId()]);
|
||||
} catch (\Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
}
|
||||
$projectForm = $this->createFormWithName('team_project_form', FormType::class, $team)
|
||||
->add('projects', ProjectType::class, [
|
||||
'label' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'query_builder_for_user' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
$page = new PageSetup('teams');
|
||||
|
||||
@@ -280,15 +280,19 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
protected function multiUpdate(Request $request)
|
||||
protected function multiUpdate(Request $request): Response
|
||||
{
|
||||
$dto = new TimesheetMultiUpdateDTO();
|
||||
|
||||
// initial request from the listing posts a different form
|
||||
$form = $this->getMultiUpdateActionForm();
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$dto->setEntities($form->getData()->getEntities());
|
||||
$data = $form->getData();
|
||||
if ($data instanceof MultiUpdateTableDTO) {
|
||||
$dto->setEntities($data->getEntities());
|
||||
}
|
||||
}
|
||||
|
||||
// using a new timesheet to make sure we ONLY use meta-fields which are registered via events
|
||||
@@ -321,14 +325,14 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
|
||||
$dto->setEntities($timesheets);
|
||||
|
||||
if (\count($dto->getEntities()) === 0) {
|
||||
if (\count($timesheets) === 0) {
|
||||
return $this->redirectToRoute($this->getTimesheetRoute());
|
||||
}
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
/** @var Timesheet $timesheet */
|
||||
$execute = false;
|
||||
foreach ($dto->getEntities() as $timesheet) {
|
||||
/** @var Timesheet $timesheet */
|
||||
foreach ($timesheets as $timesheet) {
|
||||
if ($dto->isReplaceTags()) {
|
||||
foreach ($timesheet->getTags() as $tag) {
|
||||
$timesheet->removeTag($tag);
|
||||
@@ -391,13 +395,17 @@ abstract class TimesheetAbstractController extends AbstractController
|
||||
|
||||
if ($execute) {
|
||||
try {
|
||||
$this->service->updateMultipleTimesheets($dto->getEntities());
|
||||
$this->service->updateMultipleTimesheets($timesheets);
|
||||
$this->flashSuccess('action.update.success');
|
||||
|
||||
return $this->redirectToRoute($this->getTimesheetRoute());
|
||||
} catch (\Exception $ex) {
|
||||
$this->flashUpdateException($ex);
|
||||
}
|
||||
} else {
|
||||
$this->flashSuccess(sprintf('No changes for %s entries detected.', \count($timesheets)));
|
||||
|
||||
return $this->redirectToRoute($this->getTimesheetRoute());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user