Release 2.0.12 (#3947)

- added submenus in action drodowns, to shorten them
- show all user-edit-screens in action dropdown
- fix cascade delete teams through customer
- fix cascade delete customer/project/activity through teams
- fix responsive classes for "internal rate" column
- clarify error message if invoice number generator or calculator is missing
This commit is contained in:
Kevin Papst
2023-03-24 00:47:36 +01:00
committed by GitHub
parent 3a5d7a62de
commit 04b5eb4c38
19 changed files with 54 additions and 49 deletions

View File

@@ -76,7 +76,10 @@ export default class KimaiContextMenu {
dropdownElement.style.top = (event.clientY) + 'px'; dropdownElement.style.top = (event.clientY) + 'px';
dropdownElement.style.left = (event.clientX) + 'px'; dropdownElement.style.left = (event.clientX) + 'px';
const dropdownListener = () => { const dropdownListener = (event) => {
if (event.target.classList.contains('dropdown-toggle') || event.target.classList.contains('dropdown-divider')) {
return;
}
dropdownElement.classList.remove('d-block'); dropdownElement.classList.remove('d-block');
if (!dropdownElement.classList.contains('d-none')) { if (!dropdownElement.classList.contains('d-none')) {
dropdownElement.classList.add('d-none'); dropdownElement.classList.add('d-none');

View File

@@ -34,7 +34,7 @@
"friendsofsymfony/rest-bundle": "^3.0", "friendsofsymfony/rest-bundle": "^3.0",
"gedmo/doctrine-extensions": "^3.6", "gedmo/doctrine-extensions": "^3.6",
"jms/serializer-bundle": "^5.0", "jms/serializer-bundle": "^5.0",
"kevinpapst/tabler-bundle": "^0.13", "kevinpapst/tabler-bundle": "dev-main",
"league/csv": "^9.4", "league/csv": "^9.4",
"mpdf/mpdf": "^8.0", "mpdf/mpdf": "^8.0",
"nelmio/api-doc-bundle": "^4.0", "nelmio/api-doc-bundle": "^4.0",

19
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "42733cc81b93bcae0a70454796d6c486", "content-hash": "dfc85100677cacc71615a6442646ff39",
"packages": [ "packages": [
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@@ -2415,16 +2415,16 @@
}, },
{ {
"name": "kevinpapst/tabler-bundle", "name": "kevinpapst/tabler-bundle",
"version": "0.13", "version": "dev-main",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/kevinpapst/TablerBundle.git", "url": "https://github.com/kevinpapst/TablerBundle.git",
"reference": "28287dc810186ef3188811af071cb67832e7c7f0" "reference": "f0a8cdae6b4a243ee9acce60e2ffbb5f0c107c03"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/28287dc810186ef3188811af071cb67832e7c7f0", "url": "https://api.github.com/repos/kevinpapst/TablerBundle/zipball/f0a8cdae6b4a243ee9acce60e2ffbb5f0c107c03",
"reference": "28287dc810186ef3188811af071cb67832e7c7f0", "reference": "f0a8cdae6b4a243ee9acce60e2ffbb5f0c107c03",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2454,6 +2454,7 @@
"suggest": { "suggest": {
"knplabs/knp-menu-bundle": "Allows easy menu integration" "knplabs/knp-menu-bundle": "Allows easy menu integration"
}, },
"default-branch": true,
"type": "symfony-bundle", "type": "symfony-bundle",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@@ -2473,7 +2474,7 @@
"description": "Admin/Backend theme bundle for Symfony based on Tabler.io", "description": "Admin/Backend theme bundle for Symfony based on Tabler.io",
"support": { "support": {
"issues": "https://github.com/kevinpapst/TablerBundle/issues", "issues": "https://github.com/kevinpapst/TablerBundle/issues",
"source": "https://github.com/kevinpapst/TablerBundle/tree/0.13" "source": "https://github.com/kevinpapst/TablerBundle/tree/main"
}, },
"funding": [ "funding": [
{ {
@@ -2485,7 +2486,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-03-21T09:35:32+00:00" "time": "2023-03-23T14:18:33+00:00"
}, },
{ {
"name": "laminas/laminas-escaper", "name": "laminas/laminas-escaper",
@@ -13344,7 +13345,9 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {
"kevinpapst/tabler-bundle": 20
},
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {

View File

@@ -7150,11 +7150,6 @@ parameters:
count: 1 count: 1
path: src/Validator/Constraints/UserValidator.php path: src/Validator/Constraints/UserValidator.php
-
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\User\\|null\\.$#"
count: 1
path: src/Voter/TimesheetVoter.php
- -
message: "#^Cannot call method isVisible\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#" message: "#^Cannot call method isVisible\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
count: 1 count: 1

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"app": { "app": {
"js": [ "js": [
"/build/runtime.f0079159.js", "/build/runtime.f0079159.js",
"/build/app.4ca3f16e.js" "/build/app.a6b63f34.js"
], ],
"css": [ "css": [
"/build/app.b10b16e5.css" "/build/app.b10b16e5.css"
@@ -45,7 +45,7 @@
"calendar": { "calendar": {
"js": [ "js": [
"/build/runtime.f0079159.js", "/build/runtime.f0079159.js",
"/build/calendar.94fe23d5.js" "/build/calendar.8577d9cd.js"
], ],
"css": [ "css": [
"/build/calendar.bb473428.css" "/build/calendar.bb473428.css"
@@ -63,7 +63,7 @@
}, },
"integrity": { "integrity": {
"/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV", "/build/runtime.f0079159.js": "sha384-H22sAW1aTvyIPqvHOvGXWSWTxf0y6mptp+MsVmyXCfjx/WJjBbhX9gbUZ+qIuihV",
"/build/app.4ca3f16e.js": "sha384-4VmfUWQxO4OK+j7bPm7p+B8b/krMYs5T+JLrU6DPiTep/LNoMLq11DGP7QL0j7Pu", "/build/app.a6b63f34.js": "sha384-gYC58lsaQMGHSI811cimIwsqDcwhnKasGWsvFQODG0FrxVb2dDV9VvtFo9V5I8JL",
"/build/app.b10b16e5.css": "sha384-dwEgmHAQRm4PP7XTYkJfIoB+Iumd+0WuH5FyoC/+qC3WQ/e1gCTmlx9M2mIBxPuK", "/build/app.b10b16e5.css": "sha384-dwEgmHAQRm4PP7XTYkJfIoB+Iumd+0WuH5FyoC/+qC3WQ/e1gCTmlx9M2mIBxPuK",
"/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW", "/build/export-pdf.587575e7.js": "sha384-J50GStmmfVwUTN4dIRQ02eg9hyzGFPSzpTtpPody92j0V6zCqw+s5l8+ZhVTugeW",
"/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg", "/build/export-pdf.d8a6c23b.css": "sha384-ztepocHE4rnGE9eKZ4kL6jTKaePUyiwiB9TjJjstjpf/ckcKg1HedrEOOk/8ElJg",
@@ -72,7 +72,7 @@
"/build/invoice-pdf.d86b82ee.js": "sha384-A0HJqP+MvEqQr1uG8wViCeEWxBRKyS6l8D+Ao4pFYHUA12gCC1gRYhk9I+SJPvZq", "/build/invoice-pdf.d86b82ee.js": "sha384-A0HJqP+MvEqQr1uG8wViCeEWxBRKyS6l8D+Ao4pFYHUA12gCC1gRYhk9I+SJPvZq",
"/build/invoice-pdf.c88953bb.css": "sha384-ZvSi1e+ZKGzvZJUtAPLjzOSTh13N9zRevq44GKdYdBja/DAplGE55saY2Ur+83yv", "/build/invoice-pdf.c88953bb.css": "sha384-ZvSi1e+ZKGzvZJUtAPLjzOSTh13N9zRevq44GKdYdBja/DAplGE55saY2Ur+83yv",
"/build/chart.f5becfac.js": "sha384-GSqETm8wULiVXyizvwRompfwu63r/C0Qd/AvrHDE4cqAKiIGCssb3QyBtGu1WN+W", "/build/chart.f5becfac.js": "sha384-GSqETm8wULiVXyizvwRompfwu63r/C0Qd/AvrHDE4cqAKiIGCssb3QyBtGu1WN+W",
"/build/calendar.94fe23d5.js": "sha384-7STPDBwT7nqGf6WWeoWJ2/KyDsf8J7cs9hw/zcYQefgKlzfexbxdZsIJxFxoK1Bu", "/build/calendar.8577d9cd.js": "sha384-1qlF+4tr7/zve+wvZef7sVcbUxhK0/yEm5Hbuct8K/ZV4JtENdNY7YRLqOcZ/8Y3",
"/build/calendar.bb473428.css": "sha384-900W9o8666Qpw21rLP7hn5Ql8tWd40k0IcE3QpFm7E7V6bKva7xQgGlyzVuuh4GK", "/build/calendar.bb473428.css": "sha384-900W9o8666Qpw21rLP7hn5Ql8tWd40k0IcE3QpFm7E7V6bKva7xQgGlyzVuuh4GK",
"/build/dashboard.6774a712.js": "sha384-lBwkNqUPv+IBbznagiFakY2BOIueq/Bg89wHO2XeM9YAZ51rPkrvdMd+XmFupHsM", "/build/dashboard.6774a712.js": "sha384-lBwkNqUPv+IBbznagiFakY2BOIueq/Bg89wHO2XeM9YAZ51rPkrvdMd+XmFupHsM",
"/build/dashboard.18f5a8b7.css": "sha384-PBD8ftb2yBoSjRe2sN5Xb4dEz045kOEUfcIufdSis+tWoWdAx5j4Yic+F/6CYbrP" "/build/dashboard.18f5a8b7.css": "sha384-PBD8ftb2yBoSjRe2sN5Xb4dEz045kOEUfcIufdSis+tWoWdAx5j4Yic+F/6CYbrP"

View File

@@ -1,6 +1,6 @@
{ {
"build/app.css": "/build/app.b10b16e5.css", "build/app.css": "/build/app.b10b16e5.css",
"build/app.js": "/build/app.4ca3f16e.js", "build/app.js": "/build/app.a6b63f34.js",
"build/export-pdf.css": "/build/export-pdf.d8a6c23b.css", "build/export-pdf.css": "/build/export-pdf.d8a6c23b.css",
"build/export-pdf.js": "/build/export-pdf.587575e7.js", "build/export-pdf.js": "/build/export-pdf.587575e7.js",
"build/invoice.css": "/build/invoice.3c80ee80.css", "build/invoice.css": "/build/invoice.3c80ee80.css",
@@ -9,7 +9,7 @@
"build/invoice-pdf.js": "/build/invoice-pdf.d86b82ee.js", "build/invoice-pdf.js": "/build/invoice-pdf.d86b82ee.js",
"build/chart.js": "/build/chart.f5becfac.js", "build/chart.js": "/build/chart.f5becfac.js",
"build/calendar.css": "/build/calendar.bb473428.css", "build/calendar.css": "/build/calendar.bb473428.css",
"build/calendar.js": "/build/calendar.94fe23d5.js", "build/calendar.js": "/build/calendar.8577d9cd.js",
"build/dashboard.css": "/build/dashboard.18f5a8b7.css", "build/dashboard.css": "/build/dashboard.18f5a8b7.css",
"build/dashboard.js": "/build/dashboard.6774a712.js", "build/dashboard.js": "/build/dashboard.6774a712.js",
"build/runtime.js": "/build/runtime.f0079159.js" "build/runtime.js": "/build/runtime.f0079159.js"

View File

@@ -17,11 +17,11 @@ class Constants
/** /**
* The current release version * The current release version
*/ */
public const VERSION = '2.0.11'; public const VERSION = '2.0.12';
/** /**
* The current release: major * 10000 + minor * 100 + patch * The current release: major * 10000 + minor * 100 + patch
*/ */
public const VERSION_ID = 20011; public const VERSION_ID = 20012;
/** /**
* The software name * The software name
*/ */

View File

@@ -88,7 +88,7 @@ abstract class TimesheetAbstractController extends AbstractController
if ($canSeeRate) { if ($canSeeRate) {
$table->addColumn('hourlyRate', ['class' => 'text-end d-none text-nowrap']); $table->addColumn('hourlyRate', ['class' => 'text-end d-none text-nowrap']);
$table->addColumn('internalRate', ['class' => 'text-end text-nowrap']); $table->addColumn('internalRate', ['class' => 'text-end text-nowrap d-none d-md-table-cell']);
$table->addColumn('rate', ['class' => 'text-end text-nowrap']); $table->addColumn('rate', ['class' => 'text-end text-nowrap']);
} }

View File

@@ -99,10 +99,10 @@ class Activity implements EntityWithMetaFields, EntityWithBudget
* *
* @var Collection<Team> * @var Collection<Team>
*/ */
#[ORM\ManyToMany(targetEntity: 'App\Entity\Team', cascade: ['persist'], inversedBy: 'activities')]
#[ORM\JoinTable(name: 'kimai2_activities_teams')] #[ORM\JoinTable(name: 'kimai2_activities_teams')]
#[ORM\JoinColumn(name: 'activity_id', referencedColumnName: 'id', onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'activity_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[ORM\InverseJoinColumn(name: 'team_id', referencedColumnName: 'id', onDelete: 'CASCADE')] #[ORM\InverseJoinColumn(name: 'team_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[ORM\ManyToMany(targetEntity: 'App\Entity\Team', cascade: ['persist'], inversedBy: 'activities')]
#[Serializer\Expose] #[Serializer\Expose]
#[Serializer\Groups(['Activity'])] #[Serializer\Groups(['Activity'])]
#[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Team'))] #[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Team'))]

View File

@@ -171,7 +171,7 @@ class Customer implements EntityWithMetaFields, EntityWithBudget
#[ORM\JoinTable(name: 'kimai2_customers_teams')] #[ORM\JoinTable(name: 'kimai2_customers_teams')]
#[ORM\JoinColumn(name: 'customer_id', referencedColumnName: 'id', onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'customer_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[ORM\InverseJoinColumn(name: 'team_id', referencedColumnName: 'id', onDelete: 'CASCADE')] #[ORM\InverseJoinColumn(name: 'team_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[ORM\ManyToMany(targetEntity: 'App\Entity\Team', cascade: ['persist', 'remove'], inversedBy: 'customers')] #[ORM\ManyToMany(targetEntity: 'App\Entity\Team', cascade: ['persist'], inversedBy: 'customers')]
#[Serializer\Expose] #[Serializer\Expose]
#[Serializer\Groups(['Customer'])] #[Serializer\Groups(['Customer'])]
#[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Team'))] #[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Team'))]

View File

@@ -47,7 +47,7 @@ class Team
* *
* @var Collection<TeamMember> * @var Collection<TeamMember>
*/ */
#[ORM\OneToMany(targetEntity: 'App\Entity\TeamMember', mappedBy: 'team', fetch: 'LAZY', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: TeamMember::class, mappedBy: 'team', fetch: 'LAZY', cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\JoinColumn(onDelete: 'CASCADE')] #[ORM\JoinColumn(onDelete: 'CASCADE')]
#[Assert\Count(min: 1)] #[Assert\Count(min: 1)]
#[Serializer\Expose] #[Serializer\Expose]
@@ -59,7 +59,7 @@ class Team
* *
* @var Collection<Customer> * @var Collection<Customer>
*/ */
#[ORM\ManyToMany(targetEntity: 'App\Entity\Customer', mappedBy: 'teams', fetch: 'EXTRA_LAZY', cascade: ['persist', 'remove'])] #[ORM\ManyToMany(targetEntity: Customer::class, mappedBy: 'teams', fetch: 'EXTRA_LAZY', cascade: ['persist'])]
#[Serializer\Expose] #[Serializer\Expose]
#[Serializer\Groups(['Team_Entity'])] #[Serializer\Groups(['Team_Entity'])]
#[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Customer'))] #[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Customer'))]
@@ -69,7 +69,7 @@ class Team
* *
* @var Collection<Project> * @var Collection<Project>
*/ */
#[ORM\ManyToMany(targetEntity: 'App\Entity\Project', mappedBy: 'teams', fetch: 'EXTRA_LAZY', cascade: ['persist', 'remove'])] #[ORM\ManyToMany(targetEntity: Project::class, mappedBy: 'teams', fetch: 'EXTRA_LAZY', cascade: ['persist'])]
#[Serializer\Expose] #[Serializer\Expose]
#[Serializer\Groups(['Team_Entity', 'Expanded'])] #[Serializer\Groups(['Team_Entity', 'Expanded'])]
#[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Project'))] #[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Project'))]
@@ -79,7 +79,7 @@ class Team
* *
* @var Collection<Activity> * @var Collection<Activity>
*/ */
#[ORM\ManyToMany(targetEntity: 'App\Entity\Activity', mappedBy: 'teams', fetch: 'EXTRA_LAZY', cascade: ['persist', 'remove'])] #[ORM\ManyToMany(targetEntity: Activity::class, mappedBy: 'teams', fetch: 'EXTRA_LAZY', cascade: ['persist'])]
#[Serializer\Expose] #[Serializer\Expose]
#[Serializer\Groups(['Team_Entity', 'Expanded'])] #[Serializer\Groups(['Team_Entity', 'Expanded'])]
#[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Activity'))] #[OA\Property(type: 'array', items: new OA\Items(ref: '#/components/schemas/Activity'))]

View File

@@ -11,9 +11,17 @@ namespace App\EventSubscriber\Actions;
use App\Entity\User; use App\Entity\User;
use App\Event\PageActionsEvent; use App\Event\PageActionsEvent;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
final class UserSubscriber extends AbstractActionsSubscriber final class UserSubscriber extends AbstractActionsSubscriber
{ {
public function __construct(AuthorizationCheckerInterface $auth, UrlGeneratorInterface $urlGenerator, private EventDispatcherInterface $eventDispatcher)
{
parent::__construct($auth, $urlGenerator);
}
public static function getActionName(): string public static function getActionName(): string
{ {
return 'user'; return 'user';
@@ -23,10 +31,7 @@ final class UserSubscriber extends AbstractActionsSubscriber
{ {
$payload = $event->getPayload(); $payload = $event->getPayload();
/** @var User $user */ if (($user = $payload['user']) === null || !$user instanceof User || $user->getId() === null) {
$user = $payload['user'];
if ($user->getId() === null) {
return; return;
} }
@@ -35,21 +40,20 @@ final class UserSubscriber extends AbstractActionsSubscriber
$event->addDivider(); $event->addDivider();
} }
if ($this->isGranted('edit', $user)) { $subEvent = new PageActionsEvent($user, ['user' => $user], 'user_forms', 'index');
$event->addAction('edit', ['url' => $this->path('user_profile_edit', ['username' => $user->getUserIdentifier()]), 'title' => 'edit', 'translation_domain' => 'actions']); $this->eventDispatcher->dispatch($subEvent, $subEvent->getEventName());
foreach ($subEvent->getActions() as $id => $action) {
$event->addActionToSubmenu('edit', $id, $action);
} }
if ($this->isGranted('preferences', $user)) { if (($event->getUser()->getId() === $user->getId() && $this->isGranted('report:user')) || $this->isGranted('report:other')) {
$event->addConfig($this->path('user_profile_preferences', ['username' => $user->getUserIdentifier()]));
}
if ($this->isGranted('report:other') || ($this->isGranted('report:user') && $event->getUser()->getId() === $user->getId())) {
$event->addActionToSubmenu('report', 'weekly', ['url' => $this->path('report_user_week', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_week']); $event->addActionToSubmenu('report', 'weekly', ['url' => $this->path('report_user_week', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_week']);
$event->addActionToSubmenu('report', 'monthly', ['url' => $this->path('report_user_month', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_month']); $event->addActionToSubmenu('report', 'monthly', ['url' => $this->path('report_user_month', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_month']);
$event->addActionToSubmenu('report', 'yearly', ['url' => $this->path('report_user_year', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_year']); $event->addActionToSubmenu('report', 'yearly', ['url' => $this->path('report_user_year', ['user' => $user->getId()]), 'translation_domain' => 'reporting', 'title' => 'report_user_year']);
} }
if ($this->isGranted('view_other_timesheet') && $user->isEnabled()) { if ($user->isEnabled() && $this->isGranted('view_other_timesheet')) {
$event->addActionToSubmenu('filter', 'timesheet', ['url' => $this->path('admin_timesheet', ['users[]' => $user->getId()]), 'title' => 'timesheet.filter', 'translation_domain' => 'actions']); $event->addActionToSubmenu('filter', 'timesheet', ['url' => $this->path('admin_timesheet', ['users[]' => $user->getId()]), 'title' => 'timesheet.filter', 'translation_domain' => 'actions']);
} }

View File

@@ -421,12 +421,12 @@ final class ServiceInvoice
$generator = $this->getNumberGeneratorByName($template->getNumberGenerator()); $generator = $this->getNumberGeneratorByName($template->getNumberGenerator());
if (null === $generator) { if (null === $generator) {
throw new \Exception('Unknown number generator: ' . $template->getNumberGenerator()); throw new \Exception('Please adjust your invoice template, the number generator is invalid: ' . $template->getNumberGenerator());
} }
$calculator = $this->getCalculatorByName($template->getCalculator()); $calculator = $this->getCalculatorByName($template->getCalculator());
if (null === $calculator) { if (null === $calculator) {
throw new \Exception('Unknown invoice calculator: ' . $template->getCalculator()); throw new \Exception('Please adjust your invoice template, the invoice calculator is invalid: ' . $template->getCalculator());
} }
$model->setCalculator($calculator); $model->setCalculator($calculator);

View File

@@ -128,7 +128,7 @@ final class TimesheetVoter extends Voter
$permission .= '_'; $permission .= '_';
// extend me for "team" support later on // extend me for "team" support later on
if ($subject->getUser()->getId() === $user->getId()) { if ($subject->getUser()?->getId() === $user->getId()) {
$permission .= 'own'; $permission .= 'own';
} else { } else {
$permission .= 'other'; $permission .= 'other';