Release 2.0.14 (#3963)

- show customer totals in single-user reports
- support custom fonts in export / invoice templates
- require daterange for export and invoice searches
- improve help label for user locale
- show decimal formats in help controller
- cache tag amount per request
- composer updates
- improved code styles
- added default label to date picker
- new macros & use own macro
This commit is contained in:
Kevin Papst
2023-04-05 19:14:49 +02:00
committed by GitHub
parent 01226a1243
commit 0c397e5080
21 changed files with 458 additions and 493 deletions

View File

@@ -14,7 +14,11 @@ $fixer
'encoding' => true, 'encoding' => true,
'full_opening_tag' => true, 'full_opening_tag' => true,
'blank_line_after_namespace' => true, 'blank_line_after_namespace' => true,
'braces' => true, 'control_structure_braces' => true,
'control_structure_continuation_position' => ['position' => 'same_line'],
'declare_parentheses' => true,
'no_multiple_statements_per_line' => true,
'statement_indentation' => true,
'class_definition' => true, 'class_definition' => true,
'elseif' => true, 'elseif' => true,
'function_declaration' => true, 'function_declaration' => true,

535
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2270,16 +2270,6 @@ parameters:
count: 1 count: 1
path: src/Event/AbstractTimesheetMultipleEvent.php path: src/Event/AbstractTimesheetMultipleEvent.php
-
message: "#^Method App\\\\Event\\\\ActivityDetailControllerEvent\\:\\:getController\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Event/ActivityDetailControllerEvent.php
-
message: "#^Property App\\\\Event\\\\ActivityDetailControllerEvent\\:\\:\\$controller has no type specified\\.$#"
count: 1
path: src/Event/ActivityDetailControllerEvent.php
- -
message: "#^Method App\\\\Event\\\\CalendarConfigurationEvent\\:\\:__construct\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Event\\\\CalendarConfigurationEvent\\:\\:__construct\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -2300,16 +2290,6 @@ parameters:
count: 1 count: 1
path: src/Event/CalendarConfigurationEvent.php path: src/Event/CalendarConfigurationEvent.php
-
message: "#^Method App\\\\Event\\\\CustomerDetailControllerEvent\\:\\:getController\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Event/CustomerDetailControllerEvent.php
-
message: "#^Property App\\\\Event\\\\CustomerDetailControllerEvent\\:\\:\\$controller has no type specified\\.$#"
count: 1
path: src/Event/CustomerDetailControllerEvent.php
- -
message: "#^Cannot access offset 'actions' on mixed\\.$#" message: "#^Cannot access offset 'actions' on mixed\\.$#"
count: 11 count: 11
@@ -2395,16 +2375,6 @@ parameters:
count: 1 count: 1
path: src/Event/PermissionsEvent.php path: src/Event/PermissionsEvent.php
-
message: "#^Method App\\\\Event\\\\ProjectDetailControllerEvent\\:\\:getController\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Event/ProjectDetailControllerEvent.php
-
message: "#^Property App\\\\Event\\\\ProjectDetailControllerEvent\\:\\:\\$controller has no type specified\\.$#"
count: 1
path: src/Event/ProjectDetailControllerEvent.php
- -
message: "#^Method App\\\\Event\\\\RevenueStatisticEvent\\:\\:getRevenue\\(\\) return type has no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Event\\\\RevenueStatisticEvent\\:\\:getRevenue\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3455,21 +3425,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php path: src/Form/Toolbar/AbstractToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/AbstractToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\AbstractToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3520,21 +3480,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php path: src/Form/Toolbar/ActivityToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ActivityToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\ActivityToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3585,21 +3535,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php path: src/Form/Toolbar/CustomerToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/CustomerToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\CustomerToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3650,21 +3590,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/ExportToolbarForm.php path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/ExportToolbarForm.php path: src/Form/Toolbar/ExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ExportToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\ExportToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3715,21 +3645,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php path: src/Form/Toolbar/InvoiceArchiveForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceArchiveForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceArchiveForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3780,21 +3700,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php path: src/Form/Toolbar/InvoiceToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/InvoiceToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\InvoiceToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3845,21 +3755,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php path: src/Form/Toolbar/ProjectToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/ProjectToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\ProjectToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3910,21 +3810,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/TagToolbarForm.php path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/TagToolbarForm.php path: src/Form/Toolbar/TagToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TagToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TagToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -3975,21 +3865,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/TeamToolbarForm.php path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/TeamToolbarForm.php path: src/Form/Toolbar/TeamToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TeamToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TeamToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -4040,21 +3920,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php path: src/Form/Toolbar/TimesheetExportToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetExportToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetExportToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -4105,21 +3975,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php path: src/Form/Toolbar/TimesheetToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/TimesheetToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\TimesheetToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1
@@ -4170,21 +4030,11 @@ parameters:
count: 1 count: 1
path: src/Form/Toolbar/UserToolbarForm.php path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$allowEmpty with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#"
count: 1 count: 1
path: src/Form/Toolbar/UserToolbarForm.php path: src/Form/Toolbar/UserToolbarForm.php
-
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addDateRange\\(\\) has parameter \\$required with no type specified\\.$#"
count: 1
path: src/Form/Toolbar/UserToolbarForm.php
- -
message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#" message: "#^Method App\\\\Form\\\\Toolbar\\\\UserToolbarForm\\:\\:addOrderBy\\(\\) has parameter \\$allowedColumns with no value type specified in iterable type array\\.$#"
count: 1 count: 1

View File

@@ -17,11 +17,11 @@ class Constants
/** /**
* The current release version * The current release version
*/ */
public const VERSION = '2.0.13'; public const VERSION = '2.0.14';
/** /**
* The current release: major * 10000 + minor * 100 + patch * The current release: major * 10000 + minor * 100 + patch
*/ */
public const VERSION_ID = 20013; public const VERSION_ID = 20014;
/** /**
* The software name * The software name
*/ */

View File

@@ -307,8 +307,8 @@ final class DoctorController extends AbstractController
} }
$phpInfo = $phpinfo['phpinfo']; $phpInfo = $phpinfo['phpinfo'];
unset($phpInfo[0]); array_pop($phpInfo);
unset($phpInfo[1]); array_shift($phpInfo);
return $phpInfo; return $phpInfo;
} }

View File

@@ -36,6 +36,7 @@ final class HelpController extends AbstractController
$table->addColumn('date', ['class' => 'alwaysVisible w-min', 'orderBy' => false]); $table->addColumn('date', ['class' => 'alwaysVisible w-min', 'orderBy' => false]);
$table->addColumn('time', ['class' => 'alwaysVisible w-min text-center', 'orderBy' => false]); $table->addColumn('time', ['class' => 'alwaysVisible w-min text-center', 'orderBy' => false]);
$table->addColumn('duration', ['class' => 'd-none w-min text-end', 'orderBy' => false]); $table->addColumn('duration', ['class' => 'd-none w-min text-end', 'orderBy' => false]);
$table->addColumn('decimal', ['class' => 'd-none w-min text-end', 'orderBy' => false, 'title' => 'Decimal']);
$table->addColumn('money', ['class' => 'w-min text-end', 'orderBy' => false, 'title' => 'rate']); $table->addColumn('money', ['class' => 'w-min text-end', 'orderBy' => false, 'title' => 'rate']);
$table->addColumn('hour_24', ['class' => 'alwaysVisible w-min text-center', 'orderBy' => false]); $table->addColumn('hour_24', ['class' => 'alwaysVisible w-min text-center', 'orderBy' => false]);
$table->addColumn('rtl', ['class' => 'd-none w-min text-center', 'orderBy' => false, 'title' => 'RTL']); $table->addColumn('rtl', ['class' => 'd-none w-min text-center', 'orderBy' => false, 'title' => 'RTL']);
@@ -75,6 +76,7 @@ final class HelpController extends AbstractController
'date' => $formatter->dateShort($now), 'date' => $formatter->dateShort($now),
'time' => $formatter->time($now), 'time' => $formatter->time($now),
'duration' => $formatter->duration(46120), 'duration' => $formatter->duration(46120),
'decimal' => $formatter->durationDecimal(46120),
'money' => $formatter->money(2794.83, 'EUR'), 'money' => $formatter->money(2794.83, 'EUR'),
'hour_24' => $service->is24Hour($locale), 'hour_24' => $service->is24Hour($locale),
'rtl' => $service->isRightToLeft($locale), 'rtl' => $service->isRightToLeft($locale),

View File

@@ -113,6 +113,24 @@ abstract class AbstractUserReportController extends AbstractController
$data[$project->getId()]['project'] = $project; $data[$project->getId()]['project'] = $project;
} }
return $data; $customers = [];
foreach ($data as $id => $row) {
$customerId = (string) $row['project']->getCustomer()->getId();
if (!\array_key_exists($customerId, $customers)) {
$customers[$customerId] = [
'customer' => $row['project']->getCustomer(),
'projects' => [],
'duration' => 0,
'rate' => 0.0,
'internalRate' => 0.0,
];
}
$customers[$customerId]['projects'][$id] = $row;
$customers[$customerId]['duration'] += $row['duration'];
$customers[$customerId]['rate'] += $row['rate'];
$customers[$customerId]['internalRate'] += $row['internalRate'];
}
return $customers;
} }
} }

View File

@@ -16,13 +16,19 @@ namespace App\Event;
*/ */
final class ActivityDetailControllerEvent extends AbstractActivityEvent final class ActivityDetailControllerEvent extends AbstractActivityEvent
{ {
private $controller = []; /**
* @var array<string>
*/
private array $controller = [];
public function addController(string $controller): void public function addController(string $controller): void
{ {
$this->controller[] = $controller; $this->controller[] = $controller;
} }
/**
* @return string[]
*/
public function getController(): array public function getController(): array
{ {
return $this->controller; return $this->controller;

View File

@@ -16,13 +16,19 @@ namespace App\Event;
*/ */
final class CustomerDetailControllerEvent extends AbstractCustomerEvent final class CustomerDetailControllerEvent extends AbstractCustomerEvent
{ {
private $controller = []; /**
* @var array<string>
*/
private array $controller = [];
public function addController(string $controller): void public function addController(string $controller): void
{ {
$this->controller[] = $controller; $this->controller[] = $controller;
} }
/**
* @return string[]
*/
public function getController(): array public function getController(): array
{ {
return $this->controller; return $this->controller;

View File

@@ -16,13 +16,19 @@ namespace App\Event;
*/ */
final class ProjectDetailControllerEvent extends AbstractProjectEvent final class ProjectDetailControllerEvent extends AbstractProjectEvent
{ {
private $controller = []; /**
* @var array<string>
*/
private array $controller = [];
public function addController(string $controller): void public function addController(string $controller): void
{ {
$this->controller[] = $controller; $this->controller[] = $controller;
} }
/**
* @return string[]
*/
public function getController(): array public function getController(): array
{ {
return $this->controller; return $this->controller;

View File

@@ -25,7 +25,7 @@ final class ExportToolbarForm extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$this->addSearchTermInputField($builder); $this->addSearchTermInputField($builder);
$this->addDateRange($builder, ['timezone' => $options['timezone']]); $this->addDateRange($builder, ['timezone' => $options['timezone']], false, true);
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true); $this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true); $this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
$this->addActivitySelect($builder, [], true, true, false); $this->addActivitySelect($builder, [], true, true, false);

View File

@@ -26,7 +26,7 @@ final class InvoiceToolbarForm extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$this->addSearchTermInputField($builder); $this->addSearchTermInputField($builder);
$this->addDateRange($builder, ['timezone' => $options['timezone']]); $this->addDateRange($builder, ['timezone' => $options['timezone']], false, true);
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true); $this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true); $this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
$this->addActivitySelect($builder, [], true, true, false); $this->addActivitySelect($builder, [], true, true, false);

View File

@@ -155,7 +155,7 @@ trait ToolbarFormTrait
]); ]);
} }
protected function addDateRange(FormBuilderInterface $builder, array $options, $allowEmpty = true, $required = false): void protected function addDateRange(FormBuilderInterface $builder, array $options, bool $allowEmpty = true, bool $required = false): void
{ {
$params = [ $params = [
'required' => $required, 'required' => $required,

View File

@@ -57,6 +57,7 @@ class DatePickerType extends AbstractType
$formFormat = $converter->convert($format); $formFormat = $converter->convert($format);
$resolver->setDefaults([ $resolver->setDefaults([
'label' => 'date',
'widget' => 'single_text', 'widget' => 'single_text',
'html5' => false, 'html5' => false,
'format' => $formFormat, 'format' => $formFormat,

View File

@@ -16,6 +16,8 @@ use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
final class TagsType extends AbstractType final class TagsType extends AbstractType
{ {
private ?int $count = null;
public function __construct( public function __construct(
private AuthorizationCheckerInterface $auth, private AuthorizationCheckerInterface $auth,
private TagRepository $repository private TagRepository $repository
@@ -31,7 +33,11 @@ final class TagsType extends AbstractType
public function getParent(): string public function getParent(): string
{ {
if ($this->repository->count([]) > TagRepository::MAX_AMOUNT_SELECT) { if ($this->count === null) {
$this->count = $this->repository->count([]);
}
if ($this->count > TagRepository::MAX_AMOUNT_SELECT) {
return TagsInputType::class; return TagsInputType::class;
} }

View File

@@ -27,10 +27,11 @@ final class UserLanguageType extends AbstractType
{ {
$route = $this->router->generate('help_locales'); $route = $this->router->generate('help_locales');
$message = $this->translator->trans('user.language.help'); $message = $this->translator->trans('user.language.help');
$moreLink = $this->translator->trans('help_locales');
$resolver->setDefaults([ $resolver->setDefaults([
'help_html' => true, 'help_html' => true,
'help' => sprintf('<a href="%s" target="help_locales">%s</a>', $route, $message) 'help' => sprintf('%2$s <a href="%1$s" target="help_locales">%3$s</a>', $route, $message, $moreLink)
]); ]);
} }

View File

@@ -28,7 +28,7 @@ final class MPdfConverter implements HtmlToPdfConverter
$fonts = new FontVariables(); $fonts = new FontVariables();
$allowed = [ $allowed = [
'mode', 'format', 'default_font_size', 'default_font', 'margin_left', 'margin_right', 'margin_top', 'mode', 'format', 'default_font_size', 'default_font', 'margin_left', 'margin_right', 'margin_top',
'margin_bottom', 'margin_header', 'margin_footer', 'orientation', 'margin_bottom', 'margin_header', 'margin_footer', 'orientation', 'fonts',
]; ];
$filtered = array_filter($options, function ($key) use ($allowed, $configs, $fonts): bool { $filtered = array_filter($options, function ($key) use ($allowed, $configs, $fonts): bool {
@@ -61,15 +61,7 @@ final class MPdfConverter implements HtmlToPdfConverter
['tempDir' => $this->cacheDirectory, 'exposeVersion' => false] ['tempDir' => $this->cacheDirectory, 'exposeVersion' => false]
); );
$mpdf = new Mpdf($sanitized); $mpdf = $this->initMpdf($sanitized);
$mpdf->creator = Constants::SOFTWARE;
if (\array_key_exists('fonts', $options)) {
$mpdf->AddFontDirectory($this->fileHelper->getDataDirectory('fonts'));
foreach ($options['fonts'] as $fontName => $fontConfig) {
$mpdf->AddFont($fontName, $fontConfig);
}
}
// some OS'es do not follow the PHP default settings // some OS'es do not follow the PHP default settings
if ((int) \ini_get('pcre.backtrack_limit') < 1000000) { if ((int) \ini_get('pcre.backtrack_limit') < 1000000) {
@@ -99,4 +91,47 @@ final class MPdfConverter implements HtmlToPdfConverter
return $mpdf->Output('', Destination::STRING_RETURN); return $mpdf->Output('', Destination::STRING_RETURN);
} }
/**
* @param array<string, array<mixed>> $options
* @return Mpdf
*/
private function initMpdf(array $options): Mpdf
{
$options['fontDir'] = $this->getFontDirectories();
$options['fontdata'] = $this->mergeFontData($options);
$mpdf = new Mpdf($options);
$mpdf->creator = Constants::SOFTWARE;
return $mpdf;
}
/**
* @return array<string>
*/
private function getFontDirectories(): array
{
$defaultConfig = (new ConfigVariables())->getDefaults();
$fontDirectories = $defaultConfig['fontDir'];
$fontDirectories[] = rtrim($this->fileHelper->getDataDirectory('fonts'), DIRECTORY_SEPARATOR);
return $fontDirectories;
}
/**
* @param array<string, array<mixed>> $options
* @return array<string, array<mixed>>
*/
private function mergeFontData(array $options): array
{
$defaultFontConfig = (new FontVariables())->getDefaults();
$fontData = $defaultFontConfig['fontdata'];
if (\array_key_exists('fonts', $options)) {
$fontData = array_merge($fontData, $options['fonts']);
}
return $fontData;
}
} }

View File

@@ -14,6 +14,8 @@
{{ entry.time }} {{ entry.time }}
{% elseif column == 'duration' %} {% elseif column == 'duration' %}
{{ entry.duration }} {{ entry.duration }}
{% elseif column == 'decimal' %}
{{ entry.decimal }}
{% elseif column == 'money' %} {% elseif column == 'money' %}
{{ entry.money }} {{ entry.money }}
{% elseif column == 'hour_24' %} {% elseif column == 'hour_24' %}

View File

@@ -122,6 +122,14 @@
{%- if tooltip is not same as null %} data-toggle="tooltip" data-placement="top" title="{{ tooltip }}"{% endif -%}>{{ initials }}</span> {%- if tooltip is not same as null %} data-toggle="tooltip" data-placement="top" title="{{ tooltip }}"{% endif -%}>{{ initials }}</span>
{% endmacro %} {% endmacro %}
{% macro avatar_deleted(tooltip) %}
{% set initials = tooltip ?? '??' %}
{% set tooltip = 'deleted'|trans %}
{% set color = null|colorize(initials) %}
<span class="avatar {{ class ?? '' }}" style="background-color: {{ color }}; color: {{ color|font_contrast }}"
{%- if tooltip is not same as null %} data-toggle="tooltip" data-placement="top" title="{{ tooltip }}"{% endif -%}>{{ initials }}</span>
{% endmacro %}
{# {#
options = {'inherit': true} options = {'inherit': true}
#} #}
@@ -263,6 +271,10 @@
{{ macro.action_cardtoolbutton(icon, values) }} {{ macro.action_cardtoolbutton(icon, values) }}
{% endmacro %} {% endmacro %}
{% macro card_tool_create(create_url) %}
{{ _self.card_tool_button('create', {'url': create_url, 'class': 'modal-ajax-form open-edit', 'title': 'create'|trans}) }}
{% endmacro %}
{% macro action_button(icon, values, type) %} {% macro action_button(icon, values, type) %}
{% import '@theme/components/button.html.twig' as macro %} {% import '@theme/components/button.html.twig' as macro %}
{% set values = values|merge({'tooltip_attr': 'data-toggle'}) %} {% set values = values|merge({'tooltip_attr': 'data-toggle'}) %}

View File

@@ -12,7 +12,7 @@
{% else %} {% else %}
{% set dataTypeTitle = 'stats.durationTotal' %} {% set dataTypeTitle = 'stats.durationTotal' %}
{% endif %} {% endif %}
{% set columns = 2 %} {% set columns = 0 %}
{% set totalCurrency = false %} {% set totalCurrency = false %}
<table class="table table-hover dataTable"> <table class="table table-hover dataTable">
<thead> <thead>
@@ -30,82 +30,90 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% set oldCustomer = null %} {% for customerId, data in rows %}
{% for project in rows|sort((a,b) => a.project.customer.id <=> b.project.customer.id) %} {% set currency = data.customer.currency %}
{% set currency = project.project.customer.currency %} {% if totalCurrency is same as (false) %}
{% if oldCustomer is null or oldCustomer != project.project.customer.id %} {% set totalCurrency = currency %}
{% set oldCustomer = project.project.customer.id %} {% elseif data.customer != totalCurrency %}
{% if totalCurrency is same as (false) %} {% set totalCurrency = null %}
{% set totalCurrency = currency %}
{% elseif project.project.customer.currency != totalCurrency %}
{% set totalCurrency = null %}
{% endif %}
<tr class="summary">
<td colspan="{{ columns }}">{{ widgets.label_customer(project.project.customer) }}</td>
</tr>
{% endif %} {% endif %}
{% set absoluteDuration = absoluteDuration + project.duration %} <tr class="summary">
{% set absoluteInternalRate = absoluteInternalRate + project.internalRate %} <td>{{ widgets.label_customer(data.customer) }}</td>
{% set absoluteRate = absoluteRate + project.rate %} <td class="text-center total">
<tr class="project">
<td class="text-nowrap">
<strong>{{ widgets.label_project(project.project) }}</strong>
</td>
<td class="text-nowrap text-center total">
{% if dataType == 'rate' %} {% if dataType == 'rate' %}
{{ project.rate|money(currency) }} {{ data.rate|money(currency) }}
{% elseif dataType == 'internalRate' %} {% elseif dataType == 'internalRate' %}
{{ project.internalRate|money(currency) }} {{ data.internalRate|money(currency) }}
{% else %} {% else %}
{{ project.duration|duration(decimal) }} {{ data.duration|duration(decimal) }}
{% endif %} {% endif %}
</td> </td>
{% for column in project.data.data %} <td colspan="{{ columns }}"></td>
{% set dateKey = column.date|report_date %}
<td class="text-nowrap text-center day-total {% block column_classes_project %}{% endblock %}">
{% if column.duration > 0 or column.rate > 0 or column.internalRate > 0 %}
{% if dataType == 'rate' %}
{% set totalsRate = totalsRate|merge({(dateKey): (totalsRate[dateKey] + column.rate)}) %}
<strong>{{ column.rate|money(currency) }}</strong>
{% elseif dataType == 'internalRate' %}
{% set totalsInternalRate = totalsInternalRate|merge({(dateKey): (totalsInternalRate[dateKey] + column.internalRate)}) %}
<strong>{{ column.internalRate|money(currency) }}</strong>
{% else %}
{% set totalsDuration = totalsDuration|merge({(dateKey): (totalsDuration[dateKey] + column.duration)}) %}
<strong>{{ column.duration|duration(decimal) }}</strong>
{% endif %}
{% endif %}
</td>
{% endfor %}
</tr> </tr>
{% for activity in project.activities %} {% for project in data.projects %}
<tr class="activity"> {% set absoluteDuration = absoluteDuration + project.duration %}
{% set absoluteInternalRate = absoluteInternalRate + project.internalRate %}
{% set absoluteRate = absoluteRate + project.rate %}
<tr class="project">
<td class="text-nowrap"> <td class="text-nowrap">
{{ widgets.label_activity(activity.activity) }} <strong>{{ widgets.label_project(project.project) }}</strong>
</td> </td>
<td class="text-nowrap text-center"> <td class="text-nowrap text-center total">
{% if dataType == 'rate' %} {% if dataType == 'rate' %}
{{ activity.rate|money(currency) }} {{ project.rate|money(currency) }}
{% elseif dataType == 'internalRate' %} {% elseif dataType == 'internalRate' %}
{{ activity.internalRate|money(currency) }} {{ project.internalRate|money(currency) }}
{% else %} {% else %}
{{ activity.duration|duration(decimal) }} {{ project.duration|duration(decimal) }}
{% endif %} {% endif %}
</td> </td>
{% for column in activity.data.data %} {% for column in project.data.data %}
<td class="text-nowrap text-center day-total {% block column_classes_activity %}{% endblock %}"> {% set dateKey = column.date|report_date %}
<td class="text-nowrap text-center day-total {% block column_classes_project %}{% endblock %}">
{% if column.duration > 0 or column.rate > 0 or column.internalRate > 0 %} {% if column.duration > 0 or column.rate > 0 or column.internalRate > 0 %}
{% if dataType == 'rate' %} {% if dataType == 'rate' %}
{{ column.rate|money(currency) }} {% set totalsRate = totalsRate|merge({(dateKey): (totalsRate[dateKey] + column.rate)}) %}
<strong>{{ column.rate|money(currency) }}</strong>
{% elseif dataType == 'internalRate' %} {% elseif dataType == 'internalRate' %}
{{ column.internalRate|money(currency) }} {% set totalsInternalRate = totalsInternalRate|merge({(dateKey): (totalsInternalRate[dateKey] + column.internalRate)}) %}
<strong>{{ column.internalRate|money(currency) }}</strong>
{% else %} {% else %}
{{ column.duration|duration(decimal) }} {% set totalsDuration = totalsDuration|merge({(dateKey): (totalsDuration[dateKey] + column.duration)}) %}
<strong>{{ column.duration|duration(decimal) }}</strong>
{% endif %} {% endif %}
{% endif %} {% endif %}
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>
{% for activity in project.activities %}
<tr class="activity">
<td class="text-nowrap">
{{ widgets.label_activity(activity.activity) }}
</td>
<td class="text-nowrap text-center">
{% if dataType == 'rate' %}
{{ activity.rate|money(currency) }}
{% elseif dataType == 'internalRate' %}
{{ activity.internalRate|money(currency) }}
{% else %}
{{ activity.duration|duration(decimal) }}
{% endif %}
</td>
{% for column in activity.data.data %}
<td class="text-nowrap text-center day-total {% block column_classes_activity %}{% endblock %}">
{% if column.duration > 0 or column.rate > 0 or column.internalRate > 0 %}
{% if dataType == 'rate' %}
{{ column.rate|money(currency) }}
{% elseif dataType == 'internalRate' %}
{{ column.internalRate|money(currency) }}
{% else %}
{{ column.duration|duration(decimal) }}
{% endif %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -85,8 +85,7 @@
{%- endblock %} {%- endblock %}
{% block box_tools %} {% block box_tools %}
{%- if canSeeReport -%} {%- if canSeeReport -%}
{% from '@theme/components/buttons.html.twig' import action_cardtoolbutton %} {{ macros.card_tool_button('reporting', {'url': path('report_user_year', {'user': user.id, 'date': year ~ '-01-01'})}) }}
{{ action_cardtoolbutton('reporting', {'url': path('report_user_year', {'user': user.id, 'date': year ~ '-01-01'})}) }}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block box_body %} {% block box_body %}