Fixes for 1.12 (#4655)
* fix missing locales * bump composer packages * changed translations * assert timezone on customer * include user accountNumber in excel export * improve next customer number calculation * fix z-index of contextmenu / action dropdown
This commit is contained in:
@@ -168,12 +168,14 @@ final class RegenerateLocalesCommand extends Command
|
||||
$removableDuplicates[] = $locale;
|
||||
}
|
||||
|
||||
$io->title('Redundant locales that will be skipped');
|
||||
/*
|
||||
$io->title('Redundant locales that could be skipped');
|
||||
$io->writeln(implode('|', $removableDuplicates));
|
||||
|
||||
foreach ($removableDuplicates as $duplicate) {
|
||||
unset($appLocales[$duplicate]);
|
||||
}
|
||||
*/
|
||||
|
||||
// in the future this list should be reduced to the list of available translations, but for a long time users
|
||||
// could choose from the entire list of all locales, so we likely have to keep that forever ...
|
||||
|
||||
@@ -28,9 +28,9 @@ use Symfony\Component\Security\Http\LoginLink\LoginLinkHandlerInterface;
|
||||
final class UserLoginLinkCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private LoginLinkHandlerInterface $loginLink,
|
||||
private UserRepository $userRepository,
|
||||
private RequestStack $requestStack
|
||||
private readonly LoginLinkHandlerInterface $loginLink,
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly RequestStack $requestStack
|
||||
)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
@@ -28,10 +28,10 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
final class CustomerService
|
||||
{
|
||||
public function __construct(
|
||||
private CustomerRepository $repository,
|
||||
private SystemConfiguration $configuration,
|
||||
private ValidatorInterface $validator,
|
||||
private EventDispatcherInterface $dispatcher
|
||||
private readonly CustomerRepository $repository,
|
||||
private readonly SystemConfiguration $configuration,
|
||||
private readonly ValidatorInterface $validator,
|
||||
private readonly EventDispatcherInterface $dispatcher
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ final class CustomerService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Customer $customer
|
||||
* @param string[] $groups
|
||||
* @throws ValidationFailedException
|
||||
*/
|
||||
@@ -122,15 +121,28 @@ final class CustomerService
|
||||
}
|
||||
|
||||
public function calculateNextCustomerNumber(): string
|
||||
{
|
||||
// we cannot use max(number) because a varchar column returns unexpected results
|
||||
$start = $this->repository->countCustomer();
|
||||
|
||||
do {
|
||||
$number = $this->getNextNumber($start++);
|
||||
$customer = $this->findCustomerByNumber($number);
|
||||
} while ($customer !== null);
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
private function getNextNumber(int $counter): string
|
||||
{
|
||||
$format = $this->configuration->find('customer.number_format');
|
||||
if (empty($format) || !\is_string($format)) {
|
||||
$format = '{cc,4}';
|
||||
}
|
||||
|
||||
$numberGenerator = new NumberGenerator($format, function (string $originalFormat, string $format, int $increaseBy): string|int {
|
||||
$numberGenerator = new NumberGenerator($format, function (string $originalFormat, string $format, int $increaseBy) use ($counter): string|int {
|
||||
return match ($format) {
|
||||
'cc' => $this->repository->count([]) + $increaseBy,
|
||||
'cc' => $counter + $increaseBy,
|
||||
default => $originalFormat,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -146,6 +146,7 @@ class Customer implements EntityWithMetaFields, EntityWithBudget
|
||||
*/
|
||||
#[ORM\Column(name: 'timezone', type: 'string', length: 64, nullable: false)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Timezone]
|
||||
#[Assert\Length(max: 64)]
|
||||
#[Serializer\Expose]
|
||||
#[Serializer\Groups(['Customer_Entity'])]
|
||||
|
||||
@@ -80,6 +80,9 @@ abstract class AbstractSpreadsheetRenderer
|
||||
'label' => 'name'
|
||||
],
|
||||
'username' => [],
|
||||
'accountNumber' => [
|
||||
'label' => 'account_number'
|
||||
],
|
||||
'customer' => [],
|
||||
'project' => [],
|
||||
'activity' => [],
|
||||
@@ -315,6 +318,18 @@ abstract class AbstractSpreadsheetRenderer
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($columns['accountNumber'])) {
|
||||
if (!isset($columns['accountNumber']['render'])) {
|
||||
$columns['accountNumber']['render'] = function (Worksheet $sheet, int $row, int $column, ExportableItem $entity) {
|
||||
$accountNumber = '';
|
||||
if (null !== $entity->getUser()) {
|
||||
$accountNumber = $entity->getUser()->getAccountNumber();
|
||||
}
|
||||
$sheet->setCellValue(CellAddress::fromColumnAndRow($column, $row), $accountNumber);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($columns['customer']) && !isset($columns['customer']['render'])) {
|
||||
$columns['customer']['render'] = function (Worksheet $sheet, int $row, int $column, ExportableItem $entity) {
|
||||
$customer = '';
|
||||
|
||||
@@ -45,11 +45,11 @@ use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
class ProjectStatisticService
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectRepository $projectRepository,
|
||||
private ActivityRepository $activityRepository,
|
||||
private TimesheetRepository $timesheetRepository,
|
||||
private EventDispatcherInterface $dispatcher,
|
||||
private UserRepository $userRepository
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly ActivityRepository $activityRepository,
|
||||
private readonly TimesheetRepository $timesheetRepository,
|
||||
private readonly EventDispatcherInterface $dispatcher,
|
||||
private readonly UserRepository $userRepository
|
||||
)
|
||||
{
|
||||
}
|
||||
@@ -729,7 +729,7 @@ class ProjectStatisticService
|
||||
->addSelect('COALESCE(SUM(t.rate), 0) AS rate')
|
||||
->andWhere($tplQb->expr()->in('t.project', ':project'))
|
||||
->groupBy('t.project')
|
||||
->setParameter('project', array_values($projectIds))
|
||||
->setParameter('project', $projectIds)
|
||||
;
|
||||
|
||||
// find the most recent timesheet for each project
|
||||
|
||||
@@ -29,7 +29,7 @@ use Doctrine\ORM\Query\Expr\Andx;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
/**
|
||||
* @extends \Doctrine\ORM\EntityRepository<Customer>
|
||||
* @extends EntityRepository<Customer>
|
||||
*/
|
||||
class CustomerRepository extends EntityRepository
|
||||
{
|
||||
@@ -75,7 +75,7 @@ class CustomerRepository extends EntityRepository
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
private function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = [])
|
||||
private function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = []): void
|
||||
{
|
||||
$permissions = $this->getPermissionCriteria($qb, $user, $teams);
|
||||
if ($permissions->count() > 0) {
|
||||
@@ -249,7 +249,6 @@ class CustomerRepository extends EntityRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerQuery $query
|
||||
* @return Customer[]
|
||||
*/
|
||||
public function getCustomersForQuery(CustomerQuery $query): iterable
|
||||
@@ -261,12 +260,7 @@ class CustomerRepository extends EntityRepository
|
||||
return $paginator->getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Customer $delete
|
||||
* @param Customer|null $replace
|
||||
* @throws \Doctrine\ORM\Exception\ORMException
|
||||
*/
|
||||
public function deleteCustomer(Customer $delete, ?Customer $replace = null)
|
||||
public function deleteCustomer(Customer $delete, ?Customer $replace = null): void
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$em->beginTransaction();
|
||||
@@ -308,14 +302,14 @@ class CustomerRepository extends EntityRepository
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function saveComment(CustomerComment $comment)
|
||||
public function saveComment(CustomerComment $comment): void
|
||||
{
|
||||
$entityManager = $this->getEntityManager();
|
||||
$entityManager->persist($comment);
|
||||
$entityManager->flush();
|
||||
}
|
||||
|
||||
public function deleteComment(CustomerComment $comment)
|
||||
public function deleteComment(CustomerComment $comment): void
|
||||
{
|
||||
$entityManager = $this->getEntityManager();
|
||||
$entityManager->remove($comment);
|
||||
|
||||
@@ -100,7 +100,7 @@ final class WorkingTimeService
|
||||
return $year->getMonth($monthDate);
|
||||
}
|
||||
|
||||
public function approveMonth(User $user, Month $month, \DateTimeInterface $approvalDate, User $approver): void
|
||||
public function approveMonth(User $user, Month $month, \DateTimeInterface $approvalDate, User $approvedBy): void
|
||||
{
|
||||
foreach ($month->getDays() as $day) {
|
||||
$workingTime = $day->getWorkingTime();
|
||||
@@ -116,7 +116,7 @@ final class WorkingTimeService
|
||||
continue;
|
||||
}
|
||||
|
||||
$workingTime->setApprovedBy($approver);
|
||||
$workingTime->setApprovedBy($approvedBy);
|
||||
// FIXME see calling method
|
||||
$workingTime->setApprovedAt(\DateTimeImmutable::createFromInterface($approvalDate));
|
||||
$this->workingTimeRepository->scheduleWorkingTimeUpdate($workingTime);
|
||||
@@ -124,7 +124,7 @@ final class WorkingTimeService
|
||||
|
||||
$this->workingTimeRepository->persistScheduledWorkingTimes();
|
||||
|
||||
$this->eventDispatcher->dispatch(new WorkingTimeApproveMonthEvent($user, $month, $approvalDate, $approver));
|
||||
$this->eventDispatcher->dispatch(new WorkingTimeApproveMonthEvent($user, $month, $approvalDate, $approvedBy));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user