Release 1.19.5 (#3265)

This commit is contained in:
Kevin Papst
2022-04-26 18:48:43 +02:00
committed by GitHub
parent 41e8e196ec
commit 76899d2fdb
133 changed files with 849 additions and 683 deletions

View File

@@ -388,7 +388,7 @@ class TranslationCommand extends Command
if (!isset($unit['resname'])) {
$unit['resname'] = $source;
}
$unit['id'] = $this->generateId($source);
$unit['id'] = $this->generateId($unit['resname']);
}
$xmlDocument = new \DOMDocument('1.0');

View File

@@ -17,11 +17,11 @@ class Constants
/**
* The current release version
*/
public const VERSION = '1.19.4';
public const VERSION = '1.19.5';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 11904;
public const VERSION_ID = 11905;
/**
* The current release status, either "stable" or "dev"
*/

View File

@@ -14,6 +14,7 @@ use App\Entity\Customer;
use App\Entity\Invoice;
use App\Entity\InvoiceTemplate;
use App\Entity\MetaTableTypeInterface;
use App\Event\InvoiceCreatedMultipleEvent;
use App\Event\InvoiceDocumentsEvent;
use App\Event\InvoiceMetaDefinitionEvent;
use App\Event\InvoiceMetaDisplayEvent;
@@ -170,12 +171,12 @@ final class InvoiceController extends AbstractController
return $this->service->renderInvoiceWithModel($model, $this->dispatcher);
} catch (Exception $ex) {
$this->logException($ex);
$this->flashError('action.update.error', ['%reason%' => 'Failed generating invoice preview: ' . $ex->getMessage()]);
$this->flashError('action.update.error', ['%reason%' => $ex->getMessage()]);
}
} else {
$this->flashFormError($form);
}
$this->flashFormError($form);
return $this->redirectToRoute('invoice');
}
@@ -615,6 +616,8 @@ final class InvoiceController extends AbstractController
if (\count($invoices) === 1) {
return $this->redirectToRoute('admin_invoice_list', ['id' => $invoices[0]->getId()]);
} elseif (\count($invoices) > 1) {
$this->dispatcher->dispatch(new InvoiceCreatedMultipleEvent($invoices));
}
return $this->redirectToRoute('admin_invoice_list');

View File

@@ -243,6 +243,7 @@ abstract class TimesheetAbstractController extends AbstractController
protected function export(Request $request, ServiceExport $serviceExport): Response
{
$query = $this->createDefaultQuery();
$query->setOrder(TimesheetQuery::ORDER_ASC);
$form = $this->getExportForm($query);

View File

@@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Event;
use App\Entity\Invoice;
use Symfony\Contracts\EventDispatcher\Event;
final class InvoiceCreatedMultipleEvent extends Event
{
/**
* @param Invoice[] $invoices
*/
private $invoices;
/**
* @param Invoice[] $invoices
*/
public function __construct(array $invoices)
{
$this->invoices = $invoices;
}
/**
* @return Invoice[]
*/
public function getInvoices(): array
{
return $this->invoices;
}
}

View File

@@ -28,6 +28,7 @@ trait RendererTrait
$customerId = 'none';
$projectId = 'none';
$activityId = 'none';
$userId = 'none';
$customer = null;
$project = null;
$activity = null;
@@ -44,6 +45,10 @@ trait RendererTrait
$activityId = $exportItem->getActivity()->getId();
}
if (null !== ($user = $exportItem->getUser())) {
$userId = $user->getId();
}
$id = $customerId . '_' . $projectId;
$type = $exportItem->getType();
$category = $exportItem->getCategory();
@@ -59,6 +64,7 @@ trait RendererTrait
'duration' => 0,
'type' => [],
'types' => [],
'users' => []
];
if ($project !== null) {
@@ -90,6 +96,7 @@ trait RendererTrait
'rate' => 0,
'rate_internal' => 0,
'duration' => 0,
'users' => []
];
if ($activity !== null) {
@@ -97,31 +104,57 @@ trait RendererTrait
}
}
if (!isset($summary[$id]['users'][$userId])) {
$summary[$id]['users'][$userId] = [
'user' => $user,
'rate' => 0,
'rate_internal' => 0,
'duration' => 0,
];
}
if (!isset($summary[$id]['activities'][$activityId]['users'][$userId])) {
$summary[$id]['activities'][$activityId]['users'][$userId] = [
'user' => $user,
'rate' => 0,
'rate_internal' => 0,
'duration' => 0,
];
}
$duration = $exportItem->getDuration();
if (null === $duration) {
$duration = 0;
}
$summary[$id]['rate'] += $exportItem->getRate();
$summary[$id]['type'][$type]['rate'] += $exportItem->getRate();
$summary[$id]['types'][$type][$category]['rate'] += $exportItem->getRate();
$rate = $exportItem->getRate();
$internalRate = $rate;
if (method_exists($exportItem, 'getInternalRate')) {
$summary[$id]['rate_internal'] += $exportItem->getInternalRate();
$summary[$id]['type'][$type]['rate_internal'] += $exportItem->getInternalRate();
$summary[$id]['types'][$type][$category]['rate_internal'] += $exportItem->getInternalRate();
} else {
$summary[$id]['rate_internal'] += $exportItem->getRate();
$summary[$id]['type'][$type]['rate_internal'] += $exportItem->getRate();
$summary[$id]['types'][$type][$category]['rate_internal'] += $exportItem->getRate();
$internalRate = $exportItem->getInternalRate();
}
// rate
$summary[$id]['rate'] += $rate;
$summary[$id]['type'][$type]['rate'] += $rate;
$summary[$id]['types'][$type][$category]['rate'] += $rate;
$summary[$id]['users'][$userId]['rate'] += $rate;
$summary[$id]['activities'][$activityId]['rate'] += $rate;
$summary[$id]['activities'][$activityId]['users'][$userId]['rate'] += $rate;
// internal rate
$summary[$id]['rate_internal'] += $internalRate;
$summary[$id]['type'][$type]['rate_internal'] += $internalRate;
$summary[$id]['types'][$type][$category]['rate_internal'] += $internalRate;
$summary[$id]['users'][$userId]['rate_internal'] += $internalRate;
$summary[$id]['activities'][$activityId]['rate_internal'] += $internalRate;
$summary[$id]['activities'][$activityId]['users'][$userId]['rate_internal'] += $internalRate;
// duration
$summary[$id]['duration'] += $duration;
$summary[$id]['type'][$type]['duration'] += $duration;
$summary[$id]['types'][$type][$category]['duration'] += $duration;
$summary[$id]['activities'][$activityId]['rate'] += $exportItem->getRate();
$summary[$id]['users'][$userId]['duration'] += $duration;
$summary[$id]['activities'][$activityId]['duration'] += $duration;
$summary[$id]['activities'][$activityId]['users'][$userId]['duration'] += $duration;
}
asort($summary);
@@ -226,7 +259,6 @@ trait RendererTrait
foreach ($exportItems as $exportItem) {
$customerId = 'none';
$projectId = 'none';
$customer = null;
$project = null;
$activity = null;
@@ -261,6 +293,8 @@ trait RendererTrait
'money' => $activity->getBudget(),
'time_left' => null,
'money_left' => null,
'time_left_total' => null,
'money_left_total' => null,
];
}
}
@@ -273,10 +307,12 @@ trait RendererTrait
$project = $statisticModel->getActivity()->getProject();
$id = $project->getCustomer()->getId() . '_' . $project->getId();
if ($statisticModel->hasTimeBudget()) {
$summary[$id][$activityId]['time_left'] = $statisticModel->getTimeBudgetOpen();
$summary[$id][$activityId]['time_left'] = $statisticModel->getTimeBudgetOpenRelative();
$summary[$id][$activityId]['time_left_total'] = $statisticModel->getTimeBudgetOpen();
}
if ($statisticModel->hasBudget()) {
$summary[$id][$activityId]['money_left'] = $statisticModel->getBudgetOpen();
$summary[$id][$activityId]['money_left'] = $statisticModel->getBudgetOpenRelative();
$summary[$id][$activityId]['money_left_total'] = $statisticModel->getBudgetOpen();
}
}

View File

@@ -45,6 +45,8 @@ class TimesheetExportToolbarForm extends AbstractToolbarForm
$this->addTimesheetStateChoice($builder);
$this->addBillableChoice($builder);
$this->addExportStateChoice($builder);
$this->addOrder($builder);
$this->addOrderBy($builder, TimesheetQuery::TIMESHEET_ORDER_ALLOWED);
}
/**

View File

@@ -37,11 +37,18 @@ abstract class AbstractTwigRenderer implements RendererInterface
$language = $model->getTemplate()->getLanguage();
$formatLocale = $model->getFormatter()->getLocale();
$template = '@invoice/' . basename($document->getFilename());
$entries = [];
foreach ($model->getCalculator()->getEntries() as $entry) {
$entries[] = $model->itemToArray($entry);
}
$options = [
// model should not be used in the future, but we can likely not remove it
'model' => $model,
// new since 1.16.7 - templates should only use the pre-generated values
'invoice' => $model->toArray(),
// new since 1.19.5 - templates should only use the pre-generated values
'entries' => $entries
];
return $this->renderTwigTemplateWithLanguage($this->twig, $template, $options, $language, $formatLocale);

View File

@@ -378,6 +378,8 @@ final class TimesheetStatisticService
if (!isset($stats[$projectId])) {
$stats[$projectId] = [
'id' => $projectId,
'customer' => '',
'customer_id' => null,
'name' => null,
'activities' => [],
'duration' => 0,
@@ -451,6 +453,8 @@ final class TimesheetStatisticService
foreach (array_keys($stats) as $pid) {
$stats[$pid]['name'] = $projects[$pid]['name'];
$stats[$pid]['customer'] = $projects[$pid]['customer'];
$stats[$pid]['customer_id'] = $projects[$pid]['customer_id'];
foreach (array_keys($stats[$pid]['activities']) as $aid) {
$stats[$pid]['activities'][$aid]['name'] = $activities[$aid]['name'];
foreach (array_keys($stats[$pid]['activities'][$aid]['users']) as $uid) {