Release 1.19.5 (#3265)
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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"
|
||||
*/
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
37
src/Event/InvoiceCreatedMultipleEvent.php
Normal file
37
src/Event/InvoiceCreatedMultipleEvent.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user