0
0
Fork 0
mirror of https://github.com/kevinpapst/kimai2.git synced 2025-04-11 00:00:26 +00:00

Release 2.29 ()

* bump composer packages
* fixes  quotes for ANSI_MODE
* improve year selection
* improve year selection via dropdown
* added range selector in month-picker
* fix week number if week starts with sunday
* fix first day of month in URL
* predefined options for week chooser
* z-index issue with sticky table header
* replace duplicated translations
* add logout button to allow user switch without having to re-login in "remember me" login
* new flag to detect if invoice entry is a fixed rate
* improve export column lengths
This commit is contained in:
Kevin Papst 2025-02-09 00:16:03 +01:00 committed by GitHub
parent 8444928ae4
commit b42c77a2a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 617 additions and 1068 deletions
composer.jsoncomposer.lock
migrations
phpstan.neon
src
templates
tests
translations

View file

@ -28,7 +28,7 @@
"azuyalabs/yasumi": "^2.6",
"composer/semver": "^3.3",
"doctrine/doctrine-bundle": "^2.7",
"doctrine/doctrine-migrations-bundle": "3.3.1",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^2.8",
"endroid/qr-code": "^4.8",
"erusev/parsedown": "^1.6",

683
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -23,9 +23,9 @@ final class Version20190219200020 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('DELETE FROM kimai2_user_preferences WHERE name = "theme.fixed_layout"');
$this->addSql('DELETE FROM kimai2_user_preferences WHERE name = "theme.boxed_layout"');
$this->addSql('DELETE FROM kimai2_user_preferences WHERE name = "theme.mini_sidebar"');
$this->addSql("DELETE FROM kimai2_user_preferences WHERE name = 'theme.fixed_layout'");
$this->addSql("DELETE FROM kimai2_user_preferences WHERE name = 'theme.boxed_layout'");
$this->addSql("DELETE FROM kimai2_user_preferences WHERE name = 'theme.mini_sidebar'");
}
public function down(Schema $schema): void

View file

@ -2892,16 +2892,6 @@ parameters:
count: 1
path: src/Form/Type/MetaFieldsCollectionType.php
-
message: "#^Cannot call method modify\\(\\) on mixed\\.$#"
count: 2
path: src/Form/Type/MonthPickerType.php
-
message: "#^Cannot clone non\\-object variable \\$date of type mixed\\.$#"
count: 2
path: src/Form/Type/MonthPickerType.php
-
message: "#^Cannot call method modify\\(\\) on DateTime\\|null\\.$#"
count: 1

View file

@ -17,11 +17,11 @@ final class Constants
/**
* The current release version
*/
public const VERSION = '2.28.0';
public const VERSION = '2.29.0';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
public const VERSION_ID = 22800;
public const VERSION_ID = 22900;
/**
* The software name
*/

View file

@ -61,11 +61,9 @@ final class ReportUsersYearController extends AbstractController
$dateTimeFactory = $this->getDateTimeFactory();
$defaultDate = $dateTimeFactory->createStartOfYear();
$isFinancialYear = false;
if (null !== ($financialYear = $systemConfiguration->getFinancialYearStart())) {
$defaultDate = $this->getDateTimeFactory()->createStartOfFinancialYear($financialYear);
$isFinancialYear = true;
}
$values = new YearlyUserList();
@ -74,7 +72,6 @@ final class ReportUsersYearController extends AbstractController
$form = $this->createFormForGetRequest(YearlyUserListForm::class, $values, [
'timezone' => $dateTimeFactory->getTimezone()->getName(),
'start_date' => $values->getDate(),
'show_range' => $isFinancialYear,
]);
$form->submit($request->query->all(), false);

View file

@ -294,6 +294,8 @@ final class SystemConfigurationController extends AbstractController
return [
(new SystemConfigurationModel('timesheet'))
->setTranslation('time_tracking')
->setTranslationDomain('messages')
->setConfiguration([
(new Configuration('timesheet.mode'))
->setType(TrackingModeType::class)

View file

@ -61,11 +61,11 @@ final class CustomerSubscriber extends AbstractActionsSubscriber
}
if ($this->isGranted('view_project') || $this->isGranted('view_teamlead_project') || $this->isGranted('view_team_project')) {
$event->addActionToSubmenu('filter', 'project', ['title' => 'project.filter', 'url' => $this->path('admin_project', ['customers[]' => $customer->getId()])]);
$event->addActionToSubmenu('filter', 'project', ['title' => 'projects', 'url' => $this->path('admin_project', ['customers[]' => $customer->getId()])]);
}
if ($this->isGranted('view_activity')) {
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activity.filter', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId()])]);
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activities', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId()])]);
}
if ($this->isGranted('view_other_timesheet')) {

View file

@ -51,7 +51,7 @@ final class ProjectSubscriber extends AbstractActionsSubscriber
}
if ($this->isGranted('view_activity')) {
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activity.filter', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId(), 'projects[]' => $project->getId()])]);
$event->addActionToSubmenu('filter', 'activity', ['title' => 'activities', 'url' => $this->path('admin_activity', ['customers[]' => $customer->getId(), 'projects[]' => $project->getId()])]);
}
if ($this->isGranted('view_other_timesheet')) {

View file

@ -35,7 +35,13 @@ final class UserSubscriber extends AbstractActionsSubscriber
{
$payload = $event->getPayload();
if (($user = $payload['user']) === null || !$user instanceof User || $user->getId() === null) {
if (!\array_key_exists('user', $payload)) {
return;
}
$user = $payload['user'];
if (!$user instanceof User || $user->getId() === null) {
return;
}

View file

@ -19,12 +19,16 @@ use App\Repository\Query\TimesheetQuery;
use OpenSpout\Writer\CSV\Options;
use OpenSpout\Writer\CSV\Writer;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
final class CsvRenderer implements RendererInterface, TimesheetExportInterface
{
use ExportTrait;
public function __construct(private readonly SpreadsheetRenderer $spreadsheetRenderer)
public function __construct(
private readonly SpreadsheetRenderer $spreadsheetRenderer,
private readonly TranslatorInterface $translator
)
{
}
@ -63,7 +67,7 @@ final class CsvRenderer implements RendererInterface, TimesheetExportInterface
$options = new Options();
$options->SHOULD_ADD_BOM = false;
$spreadsheet = new SpoutSpreadsheet(new Writer($options));
$spreadsheet = new SpoutSpreadsheet(new Writer($options), $this->translator);
$spreadsheet->open($filename);
$this->spreadsheetRenderer->registerFormatter('date', new DateStringFormatter());

View file

@ -27,6 +27,7 @@ use App\Export\Package\CellFormatter\RateFormatter;
use App\Export\Package\CellFormatter\TextFormatter;
use App\Export\Package\CellFormatter\TimeFormatter;
use App\Export\Package\Column;
use App\Export\Package\ColumnWidth;
use App\Export\Package\SpreadsheetPackage;
use App\Repository\Query\ActivityQuery;
use App\Repository\Query\CustomerQuery;
@ -34,7 +35,6 @@ use App\Repository\Query\ProjectQuery;
use App\Repository\Query\TimesheetQuery;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal means no BC promise whatsoever!
@ -47,7 +47,6 @@ final class SpreadsheetRenderer
private array $formatter = [];
public function __construct(
protected TranslatorInterface $translator,
protected EventDispatcherInterface $dispatcher,
protected Security $voter
) {
@ -83,12 +82,7 @@ final class SpreadsheetRenderer
public function writeSpreadsheet(SpreadsheetPackage $spreadsheetPackage, array $exportItems, TimesheetQuery $query): void
{
$columns = $this->getColumns($query);
$headerRow = [];
foreach ($columns as $column) {
$headerRow[] = $this->translator->trans($column->getHeader());
}
$spreadsheetPackage->setHeader($headerRow);
$spreadsheetPackage->setColumns($columns);
$currentRow = 1;
foreach ($exportItems as $exportItem) {
@ -150,26 +144,26 @@ final class SpreadsheetRenderer
$columns = [];
$columns[] = (new Column('date', $this->getFormatter('date')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getBegin());
$columns[] = (new Column('begin', $this->getFormatter('time')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getBegin());
$columns[] = (new Column('end', $this->getFormatter('time')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getEnd());
$columns[] = (new Column('duration', $this->getFormatter('duration')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getDuration());
$columns[] = (new Column('begin', $this->getFormatter('time')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getBegin())->withColumnWidth(ColumnWidth::SMALL);
$columns[] = (new Column('end', $this->getFormatter('time')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getEnd())->withColumnWidth(ColumnWidth::SMALL);
$columns[] = (new Column('duration', $this->getFormatter('duration')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getDuration())->withColumnWidth(ColumnWidth::SMALL);
if ($showRates) {
$columns[] = (new Column('currency', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getProject()?->getCustomer()?->getCurrency());
$columns[] = (new Column('currency', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getProject()?->getCustomer()?->getCurrency())->withColumnWidth(ColumnWidth::SMALL);
$columns[] = (new Column('rate', new RateFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getRate());
$columns[] = (new Column('internalRate', new RateFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getInternalRate());
$columns[] = (new Column('hourlyRate', new RateFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getHourlyRate());
$columns[] = (new Column('fixedRate', new RateFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getFixedRate());
}
$columns[] = (new Column('username', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getUser()?->getDisplayName());
$columns[] = (new Column('username', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getUser()?->getDisplayName())->withColumnWidth(ColumnWidth::MEDIUM);
$columns[] = (new Column('account_number', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getUser()?->getAccountNumber());
$columns[] = (new Column('customer', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getProject()?->getCustomer()?->getName());
$columns[] = (new Column('project', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getProject()?->getName());
$columns[] = (new Column('activity', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getActivity()?->getName());
$columns[] = (new Column('description', new TextFormatter(true)))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getDescription());
$columns[] = (new Column('customer', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getProject()?->getCustomer()?->getName())->withColumnWidth(ColumnWidth::MEDIUM);
$columns[] = (new Column('project', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getProject()?->getName())->withColumnWidth(ColumnWidth::MEDIUM);
$columns[] = (new Column('activity', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getActivity()?->getName())->withColumnWidth(ColumnWidth::MEDIUM);
$columns[] = (new Column('description', new TextFormatter(true)))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getDescription())->withColumnWidth(ColumnWidth::LARGE);
//$columns[] = (new Column('exported', new BooleanFormatter()))->withExtractor(fn(ExportableItem $exportableItem) => $exportableItem->isExported());
$columns[] = (new Column('billable', new BooleanFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->isBillable());
$columns[] = (new Column('billable', new BooleanFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->isBillable())->withColumnWidth(ColumnWidth::SMALL);
$columns[] = (new Column('tags', new ArrayFormatter()))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getTagsAsArray());
$columns[] = (new Column('type', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getType());
$columns[] = (new Column('category', $this->getFormatter('default')))->withExtractor(fn (ExportableItem $exportableItem) => $exportableItem->getCategory());

View file

@ -17,12 +17,16 @@ use App\Export\TimesheetExportInterface;
use App\Repository\Query\TimesheetQuery;
use OpenSpout\Writer\XLSX\Writer;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
final class XlsxRenderer implements RendererInterface, TimesheetExportInterface
{
use ExportTrait;
public function __construct(private readonly SpreadsheetRenderer $spreadsheetRenderer)
public function __construct(
private readonly SpreadsheetRenderer $spreadsheetRenderer,
private readonly TranslatorInterface $translator,
)
{
}
@ -58,7 +62,7 @@ final class XlsxRenderer implements RendererInterface, TimesheetExportInterface
throw new \Exception('Could not open temporary file');
}
$spreadsheet = new SpoutSpreadsheet(new Writer());
$spreadsheet = new SpoutSpreadsheet(new Writer(), $this->translator);
$spreadsheet->open($filename);
$this->spreadsheetRenderer->writeSpreadsheet($spreadsheet, $exportItems, $query);

View file

@ -16,6 +16,7 @@ class Column
{
private ?string $header = null;
private \Closure|null $extractor = null;
private ColumnWidth $columnWidth = ColumnWidth::DEFAULT;
public function __construct(private readonly string $name, private readonly CellFormatterInterface $formatter)
{
@ -33,6 +34,18 @@ class Column
return $this;
}
public function withColumnWidth(ColumnWidth $columnWidth): Column
{
$this->columnWidth = $columnWidth;
return $this;
}
public function getColumnWidth(): ColumnWidth
{
return $this->columnWidth;
}
public function withExtractor(\Closure $extractor): Column
{
$this->extractor = $extractor;

View file

@ -0,0 +1,19 @@
<?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\Export\Package;
enum ColumnWidth
{
case EXTRA_SMALL;
case SMALL;
case DEFAULT;
case MEDIUM;
case LARGE;
}

View file

@ -15,6 +15,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Symfony\Contracts\Translation\TranslatorInterface;
final class PhpOfficeSpreadsheet implements SpreadsheetPackage
{
@ -23,7 +24,7 @@ final class PhpOfficeSpreadsheet implements SpreadsheetPackage
private int $currentRow = 1;
private ?string $filename = null;
public function __construct()
public function __construct(private readonly TranslatorInterface $translator)
{
$this->spreadsheet = new Spreadsheet();
$this->worksheet = $this->spreadsheet->getActiveSheet();
@ -82,9 +83,9 @@ final class PhpOfficeSpreadsheet implements SpreadsheetPackage
}
/**
* @param array<string> $columns
* @param array<Column> $columns
*/
public function setHeader(array $columns): void
public function setColumns(array $columns): void
{
if ($this->worksheet === null) {
throw new \Exception('Cannot re-use spreadsheet after calling save()');
@ -92,8 +93,9 @@ final class PhpOfficeSpreadsheet implements SpreadsheetPackage
$counter = 1;
foreach ($columns as $column) {
$title = $this->translator->trans($column->getHeader());
$pos = CellAddress::fromColumnAndRow($counter, 1);
$this->worksheet->setCellValue($pos, $column);
$this->worksheet->setCellValue($pos, $title);
$style = $this->worksheet->getStyle($pos);
$style->getBorders()->getBottom()->setBorderStyle(Border::BORDER_THIN);
$style->getFont()->setBold(true);

View file

@ -17,28 +17,40 @@ use OpenSpout\Common\Entity\Style\BorderPart;
use OpenSpout\Common\Entity\Style\Color;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\AbstractWriterMultiSheets;
use OpenSpout\Writer\CSV\Writer;
use OpenSpout\Writer\AutoFilter;
use OpenSpout\Writer\CSV\Writer as CSVWriter;
use OpenSpout\Writer\WriterInterface;
use OpenSpout\Writer\XLSX\Entity\SheetView;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;
use Symfony\Contracts\Translation\TranslatorInterface;
class SpoutSpreadsheet implements SpreadsheetPackage
{
private Style $dateStyle;
public function __construct(private readonly WriterInterface $writer)
public function __construct(
private readonly WriterInterface $writer,
private readonly TranslatorInterface $translator
)
{
$this->writer->setCreator(Constants::SOFTWARE);
$this->dateStyle = (new Style())->setFormat('yyyy-mm-dd');
}
/**
* @param array<string> $columns
* @param array<Column> $columns
*/
public function setHeader(array $columns): void
public function setColumns(array $columns): void
{
$columnCounter = \count($columns);
if ($columnCounter === 0) {
throw new \InvalidArgumentException('At least one column is required');
}
$tmp = [];
foreach ($columns as $column) {
$tmp[] = Cell::fromValue($column);
$title = $this->translator->trans($column->getHeader());
$tmp[] = Cell::fromValue($title);
}
$style = new Style();
@ -48,6 +60,24 @@ class SpoutSpreadsheet implements SpreadsheetPackage
$style->setBorder(new Border(new BorderPart(Border::BOTTOM, Color::BLACK, Border::WIDTH_THIN, Border::STYLE_SOLID)));
$style->setFontBold();
if ($this->writer instanceof XLSXWriter) {
$autoFilter = new AutoFilter(0, 1, $columnCounter - 1, 1);
$this->writer->getCurrentSheet()->setAutoFilter($autoFilter);
$options = $this->writer->getOptions();
$i = 1;
foreach ($columns as $column) {
$width = match($column->getColumnWidth()) {
ColumnWidth::SMALL => 10,
ColumnWidth::MEDIUM => 30,
ColumnWidth::LARGE => 50,
default => 15,
};
$options->setColumnWidth($width, $i++);
}
}
$this->writer->addRow(new Row($tmp, $style));
}
@ -62,7 +92,7 @@ class SpoutSpreadsheet implements SpreadsheetPackage
$style->setShouldShrinkToFit(true);
if (\array_key_exists('totals', $options) && $options['totals'] === true) {
if ($this->writer instanceof Writer) {
if ($this->writer instanceof CSVWriter) {
return;
}
$style->setBorder(new Border(new BorderPart(Border::TOP, Color::BLACK, Border::WIDTH_THIN, Border::STYLE_SOLID)));

View file

@ -19,9 +19,9 @@ interface SpreadsheetPackage
public function save(): void;
/**
* @param array<string> $columns
* @param array<Column> $columns
*/
public function setHeader(array $columns): void;
public function setColumns(array $columns): void;
/**
* @param array<int, mixed> $columns

View file

@ -28,7 +28,7 @@ final class Configuration
*/
private array $constraints = [];
public function __construct(private string $name)
public function __construct(private readonly string $name)
{
}

View file

@ -28,22 +28,41 @@ final class MonthPickerType extends AbstractType
'widget' => 'single_text',
'html5' => false,
'format' => DateType::HTML5_FORMAT,
'start_date' => new \DateTime(),
'start_date' => new \DateTimeImmutable(),
]);
}
public function buildView(FormView $view, FormInterface $form, array $options): void
{
/** @var \DateTime|null $date */
if (!$options['start_date'] instanceof \DateTimeInterface) {
throw new \InvalidArgumentException('Start date needs to be a DateTime object');
}
/** @var \DateTimeInterface|null $date */
$date = $form->getData();
if (null === $date) {
/** @var \DateTimeImmutable $date */
$date = $options['start_date'];
}
$date = \DateTimeImmutable::createFromInterface($date);
$start = \DateTimeImmutable::createFromInterface($options['start_date']);
$start = $start->setDate((int) $start->format('Y'), (int) $start->format('m'), 1);
$range = [];
$end = $start->modify('-2 year');
$end = $end->setDate((int) $end->format('Y'), 1, 1);
$i = 1;
while ($i++ < 36 && $end <= $start) {
$range[] = $start;
$start = $start->modify('- 1 month');
}
$view->vars['month'] = $date;
$view->vars['previousMonth'] = (clone $date)->modify('-1 month');
$view->vars['nextMonth'] = (clone $date)->modify('+1 month');
$view->vars['range'] = $range;
$view->vars['previousMonth'] = $date->modify('-1 month');
$view->vars['nextMonth'] = $date->modify('+1 month');
}
public function getParent(): string

View file

@ -34,6 +34,10 @@ final class WeekPickerType extends AbstractType
public function buildView(FormView $view, FormInterface $form, array $options): void
{
if (!$options['start_date'] instanceof \DateTimeInterface) {
throw new \InvalidArgumentException('Start date needs to be a DateTime object');
}
/** @var \DateTimeInterface|null $date */
$date = $form->getData();
@ -42,11 +46,31 @@ final class WeekPickerType extends AbstractType
$date = $options['start_date'];
}
$date = \DateTime::createFromInterface($date);
$date = \DateTimeImmutable::createFromInterface($date);
$start = \DateTimeImmutable::createFromInterface($options['start_date']);
$view->vars['week'] = $date;
$view->vars['previousWeek'] = (clone $date)->modify('-1 week');
$view->vars['nextWeek'] = (clone $date)->modify('+1 week');
$week = $date;
if ($date->format('N') === '7') {
$week = $date->modify('+1 day');
}
if ($start->format('N') === '7') {
$start = $start->modify('+1 day');
}
$range = [];
$end = $start->modify('-1 year');
$end = $end->setDate((int) $end->format('Y'), 1, 1);
$i = 1;
while ($i++ < 106 && $end <= $start) {
$range[] = $start;
$start = $start->modify('- 1 week');
}
$view->vars['range'] = $range;
$view->vars['weekNumber'] = $week->format('W');
$view->vars['week'] = $week;
$view->vars['previousWeek'] = $week->modify('-1 week');
$view->vars['nextWeek'] = $week->modify('+1 week');
}
public function getParent(): string

View file

@ -29,7 +29,6 @@ final class YearPickerType extends AbstractType
'html5' => false,
'format' => DateType::HTML5_FORMAT,
'start_date' => new \DateTimeImmutable(),
'show_range' => false,
]);
}
@ -43,12 +42,19 @@ final class YearPickerType extends AbstractType
$date = $options['start_date'];
}
$date = \DateTime::createFromInterface($date);
$date = \DateTimeImmutable::createFromInterface($date);
$range = [];
$start = new \DateTimeImmutable();
$start = $start->setDate((int) $start->format('Y'), (int) $date->format('m'), (int) $date->format('d'));
for ($i = 0; $i < 6; $i++) {
$range[] = $start->modify('-' . $i . ' year');
}
$view->vars['year'] = $date;
$view->vars['show_range'] = $options['show_range'];
$view->vars['previousYear'] = (clone $date)->modify('-1 year');
$view->vars['nextYear'] = (clone $date)->modify('+1 year');
$view->vars['range'] = $range;
$view->vars['previousYear'] = $date->modify('-1 year');
$view->vars['nextYear'] = $date->modify('+1 year');
}
public function getParent(): string

View file

@ -66,6 +66,7 @@ final class InvoiceItemDefaultHydrator implements InvoiceItemHydrator
'entry.rate_internal' => $formatter->getFormattedMoney($internalRate, $currency),
'entry.rate_internal_nc' => $formatter->getFormattedMoney($internalRate, $currency, false),
'entry.rate_internal_plain' => $internalRate,
'entry.rate_fixed' => ($item->isFixedRate() ? $item->getFixedRate() : null),
'entry.total' => $formatter->getFormattedMoney($rate, $currency),
'entry.total_nc' => $formatter->getFormattedMoney($rate, $currency, false),
'entry.total_plain' => $rate,

View file

@ -28,7 +28,6 @@ final class YearlyUserListForm extends AbstractType
'model_timezone' => $options['timezone'],
'view_timezone' => $options['timezone'],
'start_date' => $options['start_date'],
'show_range' => $options['show_range'],
]);
$builder->add('team', TeamType::class, [
'multiple' => false,
@ -51,7 +50,6 @@ final class YearlyUserListForm extends AbstractType
'start_date' => new \DateTime(),
'csrf_protection' => false,
'method' => 'GET',
'show_range' => false,
]);
}
}

View file

@ -291,7 +291,7 @@
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ previousYear|report_date }}" data-event="change">
{{ icon('left') }}
</a>
{% if show_range %}
{% if range is not defined or range is not iterable or range is empty %}
<a class="btn" href="#" onclick="return false;">
<span id="{{ form.vars.id }}_month_name">{{ year|date_short }} &ndash; {{ nextYear|date_short }}</span>
</a>
@ -299,10 +299,9 @@
<button type="button" class="btn btn-default dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ yearInt }}
</button>
<ul class="dropdown-menu dropdown-menu-start pre-scrollable">
{% for i in (yearInt - 5)..(yearInt + 5) %}
{% set tmpYear = report_date(i) %}
<li><a href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ tmpYear|report_date }}" data-event="change" class="dropdown-item">{{ tmpYear|date_format('Y') }}</a></li>
<ul class="dropdown-menu dropdown-menu-start pre-scrollable" style="z-index: 1021">
{% for rangeDate in range %}
<li><a href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ rangeDate|report_date }}" data-event="change" class="dropdown-item {% if rangeDate|date_format('Y') == yearInt %}active{% endif %}" title="{{ rangeDate|date_short }}">{{ rangeDate|date_format('Y') }}</a></li>
{% endfor %}
</ul>
{% endif %}
@ -321,9 +320,20 @@
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ previousMonth|report_date }}" data-event="change">
{{ icon('left') }}
</a>
<a class="btn" href="#" onclick="return false;">
<span id="{{ form.vars.id }}_month_name">{{ month|month_name(true) }}</span>
</a>
{% if range is not defined or range is not iterable or range is empty %}
<a class="btn" href="#" onclick="return false;">
<span id="{{ form.vars.id }}_month_name">{{ month|month_name(true) }}</span>
</a>
{% else %}
<button type="button" class="btn btn-default dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ month|month_name(true) }}
</button>
<ul class="dropdown-menu dropdown-menu-start pre-scrollable" style="z-index: 1021">
{% for rangeDate in range %}
<li><a href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ rangeDate|report_date }}" data-event="change" class="dropdown-item {% if rangeDate|date_format('Y-m') == month|date_format('Y-m') %}active{% endif %}" title="{{ rangeDate|date_short }}">{{ rangeDate|month_name(true) }}</a></li>
{% endfor %}
</ul>
{% endif %}
<a class="btn btn-right btn-icon" href="#" data-toggle="tooltip" data-placement="top" title="{{ nextMonth|month_name(true) }}"
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ nextMonth|report_date }}" data-event="change">
{{ icon('right') }}
@ -339,13 +349,30 @@
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ previousWeek|report_date }}" data-event="change">
{{ icon('left') }}
</a>
<a class="btn btn-default" href="#" onclick="return false;">
<span id="{{ form.vars.id }}_week_number" data-toggle="tooltip" data-placement="top" title="{{ 'stats.workingTimeWeek'|trans({'%week%': week|date_format('W')}) }}">
{% if range is not defined or range is not iterable or range is empty %}
<a class="btn btn-default" href="#" onclick="return false;">
<span id="{{ form.vars.id }}_week_number" data-toggle="tooltip" data-placement="top" title="{{ 'stats.workingTimeWeek'|trans({'%week%': weekNumber}) }}">
{{ week|month_name(true) }}
&ndash;
{{ 'stats.workingTimeWeekShort'|trans({'%week%': week|date_format('W')}) }}
{{ 'stats.workingTimeWeekShort'|trans({'%week%': weekNumber}) }}
</span>
</a>
</a>
{% else %}
<button type="button" class="btn btn-default dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ week|month_name(true) }}
&ndash;
{{ 'stats.workingTimeWeekShort'|trans({'%week%': weekNumber}) }}
</button>
<ul class="dropdown-menu dropdown-menu-start pre-scrollable" style="z-index: 1021">
{% for rangeDate in range %}
<li><a href="#" data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ rangeDate|report_date }}" data-event="change" class="dropdown-item {% if rangeDate|date_format('Y-m-W') == week|date_format('Y-m-W') %}active{% endif %}">
{{ rangeDate|month_name(true) }}
&ndash;
{{ 'stats.workingTimeWeekShort'|trans({'%week%': rangeDate|date_format('W')}) }}
</a></li>
{% endfor %}
</ul>
{% endif %}
<a class="btn btn-right btn-icon" href="#" data-toggle="tooltip" data-placement="top" title="{{ 'stats.workingTimeWeek'|trans({'%week%': nextWeek|date_format('W')}) }}"
data-form-widget="copy-data" data-target="#{{ form.vars.id }}" data-value="{{ nextWeek|report_date }}" data-event="change">
{{ icon('right') }}

View file

@ -36,5 +36,6 @@
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}"/>
</form>
{% endif %}
<a class="btn w-100 mt-2" href="{{ path('logout') }}">{{ 'logout'|trans }}</a>
{% endblock %}
{% endblock %}

View file

@ -32,13 +32,12 @@ class ActivityControllerTest extends APIControllerBaseTestCase
{
use RateControllerTestTrait;
/**
* @param ActivityRate $rate
* @param bool $isCollection
* @return string
*/
protected function getRateUrlByRate(RateInterface $rate, bool $isCollection): string
{
self::assertInstanceOf(ActivityRate::class, $rate);
self::assertNotNull($rate->getActivity());
self::assertNotNull($rate->getActivity()->getId());
if ($isCollection) {
return $this->getRateUrl($rate->getActivity()->getId());
}
@ -46,7 +45,7 @@ class ActivityControllerTest extends APIControllerBaseTestCase
return $this->getRateUrl($rate->getActivity()->getId(), $rate->getId());
}
protected function getRateUrl($id = '1', $rateId = null): string
protected function getRateUrl(?int $id = 1, ?int $rateId = null): string
{
if (null !== $rateId) {
return \sprintf('/api/activities/%s/rates/%s', $id, $rateId);

View file

@ -32,13 +32,12 @@ class CustomerControllerTest extends APIControllerBaseTestCase
{
use RateControllerTestTrait;
/**
* @param CustomerRate $rate
* @param bool $isCollection
* @return string
*/
protected function getRateUrlByRate(RateInterface $rate, bool $isCollection): string
{
self::assertInstanceOf(CustomerRate::class, $rate);
self::assertNotNull($rate->getCustomer());
self::assertNotNull($rate->getCustomer()->getId());
if ($isCollection) {
return $this->getRateUrl($rate->getCustomer()->getId());
}
@ -46,7 +45,7 @@ class CustomerControllerTest extends APIControllerBaseTestCase
return $this->getRateUrl($rate->getCustomer()->getId(), $rate->getId());
}
protected function getRateUrl($id = '1', $rateId = null): string
protected function getRateUrl(?int $id = 1, ?int $rateId = null): string
{
if (null !== $rateId) {
return \sprintf('/api/customers/%s/rates/%s', $id, $rateId);

View file

@ -32,13 +32,12 @@ class ProjectControllerTest extends APIControllerBaseTestCase
{
use RateControllerTestTrait;
/**
* @param ProjectRate $rate
* @param bool $isCollection
* @return string
*/
protected function getRateUrlByRate(RateInterface $rate, bool $isCollection): string
{
self::assertInstanceOf(ProjectRate::class, $rate);
self::assertNotNull($rate->getProject());
self::assertNotNull($rate->getProject()->getId());
if ($isCollection) {
return $this->getRateUrl($rate->getProject()->getId());
}
@ -46,7 +45,7 @@ class ProjectControllerTest extends APIControllerBaseTestCase
return $this->getRateUrl($rate->getProject()->getId(), $rate->getId());
}
protected function getRateUrl($id = '1', $rateId = null): string
protected function getRateUrl(?int $id = 1, ?int $rateId = null): string
{
if (null !== $rateId) {
return \sprintf('/api/projects/%s/rates/%s', $id, $rateId);

View file

@ -20,7 +20,7 @@ use App\Entity\User;
*/
trait RateControllerTestTrait
{
abstract protected function getRateUrl(string|int $id = '1', string|int|null $rateId = null): string;
abstract protected function getRateUrl(?int $id = 1, ?int $rateId = null): string;
abstract protected function getRateUrlByRate(RateInterface $rate, bool $isCollection): string;

View file

@ -39,7 +39,7 @@ class CsvRendererTest extends AbstractRendererTestCase
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new MetaFieldColumnSubscriber());
return new CsvRenderer(new SpreadsheetRenderer($translator, $dispatcher, $security));
return new CsvRenderer(new SpreadsheetRenderer($dispatcher, $security), $translator);
}
public function testConfiguration(): void

View file

@ -26,23 +26,17 @@ class SpreadsheetRendererTest extends AbstractRendererTestCase
{
public function testWriteSpreadsheetCreatesSpreadsheetWithCorrectHeaders(): void
{
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturnArgument(0);
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$security = $this->createMock(Security::class);
$spreadsheetPackage = $this->createMock(SpreadsheetPackage::class);
$spreadsheetPackage->expects(self::once())->method('setHeader')->with([
'date', 'begin', 'end', 'duration', 'currency', 'rate', 'internalRate', 'hourlyRate', 'fixedRate', 'username', 'account_number', 'customer', 'project', 'activity', 'description', 'billable', 'tags', 'type', 'category', 'number', 'project_number', 'vat_id', 'orderNumber'
]);
$spreadsheetPackage->expects(self::once())->method('setColumns');
$renderer = new SpreadsheetRenderer($translator, $dispatcher, $security);
$renderer = new SpreadsheetRenderer($dispatcher, $security);
$renderer->writeSpreadsheet($spreadsheetPackage, [], new TimesheetQuery());
}
public function testWriteSpreadsheetAddsRowsForExportItems(): void
{
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturnArgument(0);
$dispatcher = $this->createMock(EventDispatcherInterface::class);
$security = $this->createMock(Security::class);
$spreadsheetPackage = $this->createMock(SpreadsheetPackage::class);
@ -66,7 +60,7 @@ class SpreadsheetRendererTest extends AbstractRendererTestCase
$exportItem->method('getType')->willReturn('type');
$exportItem->method('getCategory')->willReturn('category');
$renderer = new SpreadsheetRenderer($translator, $dispatcher, $security);
$renderer = new SpreadsheetRenderer($dispatcher, $security);
$renderer->writeSpreadsheet($spreadsheetPackage, [$exportItem], new TimesheetQuery());
}
@ -96,7 +90,7 @@ class SpreadsheetRendererTest extends AbstractRendererTestCase
$exportItem->method('getType')->willReturn('type');
$exportItem->method('getCategory')->willReturn('category');
$renderer = new SpreadsheetRenderer($translator, $dispatcher, $security);
$renderer = new SpreadsheetRenderer($dispatcher, $security);
$renderer->writeSpreadsheet($spreadsheetPackage, [$exportItem, $exportItem], new TimesheetQuery());
}
}

View file

@ -35,11 +35,12 @@ class XlsxRendererTest extends AbstractRendererTestCase
$security->expects($this->any())->method('isGranted')->willReturn(true);
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturnArgument(0);
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new MetaFieldColumnSubscriber());
return new XlsxRenderer(new SpreadsheetRenderer($translator, $dispatcher, $security));
return new XlsxRenderer(new SpreadsheetRenderer($dispatcher, $security), $translator);
}
public function testConfiguration(): void

View file

@ -9,9 +9,12 @@
namespace App\Tests\Export\Package;
use App\Export\Package\CellFormatter\DefaultFormatter;
use App\Export\Package\Column;
use App\Export\Package\PhpOfficeSpreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @covers \App\Export\Package\PhpOfficeSpreadsheet
@ -33,9 +36,17 @@ class PhpOfficeSpreadsheetTest extends TestCase
}
}
private function createSut(): PhpOfficeSpreadsheet
{
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturnArgument(0);
return new PhpOfficeSpreadsheet($translator);
}
public function testopenSetsFilename(): void
{
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->open($this->filename);
$reflection = new \ReflectionClass($spreadsheetPackage);
$property = $reflection->getProperty('filename');
@ -48,15 +59,15 @@ class PhpOfficeSpreadsheetTest extends TestCase
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Need to call open() first before save()');
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->save();
}
public function testsaveWritesFile(): void
{
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->open($this->filename);
$spreadsheetPackage->setHeader(['Foo', 'Bar']);
$spreadsheetPackage->setColumns([new Column('Foo', new DefaultFormatter()), new Column('Bar', new DefaultFormatter())]);
$spreadsheetPackage->addRow(['Data1', 'Data2']);
$spreadsheetPackage->addRow(['Data3', 'Data4']);
$spreadsheetPackage->save();
@ -69,7 +80,7 @@ class PhpOfficeSpreadsheetTest extends TestCase
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Cannot re-use spreadsheet after calling save()');
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->open($this->filename);
$spreadsheetPackage->save();
$spreadsheetPackage->save();
@ -77,9 +88,9 @@ class PhpOfficeSpreadsheetTest extends TestCase
public function testsetHeaderSetsHeaderRow(): void
{
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->open($this->filename);
$spreadsheetPackage->setHeader(['Column1', 'Column2']);
$spreadsheetPackage->setColumns([new Column('Column1', new DefaultFormatter()), new Column('Column2', new DefaultFormatter())]);
$reflection = new \ReflectionClass($spreadsheetPackage);
$property = $reflection->getProperty('worksheet');
@ -94,7 +105,7 @@ class PhpOfficeSpreadsheetTest extends TestCase
public function testaddRowAddsDataRow(): void
{
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->open($this->filename);
$spreadsheetPackage->addRow(['Data1', 'Data2']);
@ -113,7 +124,7 @@ class PhpOfficeSpreadsheetTest extends TestCase
$this->expectException(\Exception::class);
$this->expectExceptionMessage('Cannot re-use spreadsheet after calling save()');
$spreadsheetPackage = new PhpOfficeSpreadsheet();
$spreadsheetPackage = $this->createSut();
$spreadsheetPackage->open($this->filename);
$spreadsheetPackage->save();
$spreadsheetPackage->addRow(['Data1', 'Data2']);

View file

@ -9,9 +9,12 @@
namespace App\Tests\Export\Package;
use App\Export\Package\CellFormatter\DefaultFormatter;
use App\Export\Package\Column;
use App\Export\Package\SpoutSpreadsheet;
use OpenSpout\Writer\CSV\Writer;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @covers \App\Export\Package\SpoutSpreadsheet
@ -35,9 +38,12 @@ class SpoutSpreadsheetTest extends TestCase
public function testsaveWritesFile(): void
{
$spreadsheetPackage = new SpoutSpreadsheet(new Writer());
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturnArgument(0);
$spreadsheetPackage = new SpoutSpreadsheet(new Writer(), $translator);
$spreadsheetPackage->open($this->filename);
$spreadsheetPackage->setHeader(['Foo', 'Bar']);
$spreadsheetPackage->setColumns([new Column('Foo', new DefaultFormatter()), new Column('Bar', new DefaultFormatter())]);
$spreadsheetPackage->addRow(['Data1', 'Data2']);
$spreadsheetPackage->addRow(['Data3', 'Data4']);
$spreadsheetPackage->save();

View file

@ -57,6 +57,7 @@ class InvoiceItemDefaultHydratorTest extends TestCase
'entry.description_safe',
'entry.amount',
'entry.rate',
'entry.rate_fixed',
'entry.rate_nc',
'entry.rate_plain',
'entry.rate_internal',

View file

@ -271,6 +271,7 @@ class DebugRendererTest extends TestCase
'entry.description_safe',
'entry.amount',
'entry.rate',
'entry.rate_fixed',
'entry.rate_nc',
'entry.rate_plain',
'entry.rate_internal',

View file

@ -142,11 +142,6 @@ parameters:
count: 4
path: API/ActionsControllerTest.php
-
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Activity\\|null\\.$#"
count: 2
path: API/ActivityControllerTest.php
-
message: "#^Cannot call method getValue\\(\\) on App\\\\Entity\\\\MetaTableTypeInterface\\|null\\.$#"
count: 1
@ -162,26 +157,11 @@ parameters:
count: 1
path: API/ActivityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ActivityControllerTest\\:\\:getRateUrl\\(\\) has parameter \\$id with no type specified\\.$#"
count: 1
path: API/ActivityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ActivityControllerTest\\:\\:getRateUrl\\(\\) has parameter \\$rateId with no type specified\\.$#"
count: 1
path: API/ActivityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ActivityControllerTest\\:\\:importTestRates\\(\\) has parameter \\$id with no type specified\\.$#"
count: 1
path: API/ActivityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ActivityControllerTest\\:\\:importTestRates\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: API/ActivityControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ActivityControllerTest\\:\\:testGetCollection\\(\\) has parameter \\$expected with no type specified\\.$#"
count: 1
@ -242,11 +222,6 @@ parameters:
count: 1
path: API/ApiDocControllerTest.php
-
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Customer\\|null\\.$#"
count: 2
path: API/CustomerControllerTest.php
-
message: "#^Cannot call method getValue\\(\\) on App\\\\Entity\\\\MetaTableTypeInterface\\|null\\.$#"
count: 1
@ -262,36 +237,16 @@ parameters:
count: 1
path: API/CustomerControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\CustomerControllerTest\\:\\:getRateUrl\\(\\) has parameter \\$id with no type specified\\.$#"
count: 1
path: API/CustomerControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\CustomerControllerTest\\:\\:getRateUrl\\(\\) has parameter \\$rateId with no type specified\\.$#"
count: 1
path: API/CustomerControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\CustomerControllerTest\\:\\:importTestRates\\(\\) has parameter \\$id with no type specified\\.$#"
count: 1
path: API/CustomerControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\CustomerControllerTest\\:\\:importTestRates\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: API/CustomerControllerTest.php
-
message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTestCase\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#"
count: 13
path: API/CustomerControllerTest.php
-
message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Project\\|null\\.$#"
count: 2
path: API/ProjectControllerTest.php
-
message: "#^Cannot call method getValue\\(\\) on App\\\\Entity\\\\MetaTableTypeInterface\\|null\\.$#"
count: 1
@ -307,26 +262,11 @@ parameters:
count: 1
path: API/ProjectControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:getRateUrl\\(\\) has parameter \\$id with no type specified\\.$#"
count: 1
path: API/ProjectControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:getRateUrl\\(\\) has parameter \\$rateId with no type specified\\.$#"
count: 1
path: API/ProjectControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:importTestRates\\(\\) has parameter \\$id with no type specified\\.$#"
count: 1
path: API/ProjectControllerTest.php
-
message: "#^Method App\\\\Tests\\\\API\\\\ProjectControllerTest\\:\\:importTestRates\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: API/ProjectControllerTest.php
-
message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTestCase\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#"
count: 15

View file

@ -1686,18 +1686,6 @@
<source>Filter timesheets</source>
<target>الجداول الزمنية</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>فلتر العملاء</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>فلتر المشاريع</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>فلتر الأنشطة</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>إنشاء نسخة</target>

View file

@ -1826,18 +1826,6 @@
<source>Filter timesheets</source>
<target>Pracovní výkaz</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtr zákazníků</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtr projektů</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtr aktivit</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Vytvořit kopii</target>

View file

@ -1422,18 +1422,6 @@
<source>Filter timesheets</source>
<target>Timeseddel</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrer kunder</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrer projekter</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrer aktiviteter</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Kopier</target>

View file

@ -1840,19 +1840,7 @@
</trans-unit>
<trans-unit id="anCjtwe" resname="timesheet.filter">
<source>Filter timesheets</source>
<target>Stundenzettel filtern</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Kunden filtern</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Projekte filtern</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Tätigkeiten filtern</target>
<target>Stundenzettel</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>

View file

@ -1218,14 +1218,6 @@
<source>Filter timesheets</source>
<target state="translated">Arbeitszeittabellen filtern</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">Projekte filtern</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Tätigkeiten filtern</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">Kopie erstellen</target>
@ -1274,10 +1266,6 @@
<source>create-project</source>
<target state="translated">Projekt erstellen</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">Kunden filtern</target>
</trans-unit>
<trans-unit id="9M4b2ZN" resname="invoice.paid">
<source>invoice.paid</source>
<target state="translated">Rechnung bezahlt</target>

View file

@ -1198,18 +1198,6 @@
<source>Filter timesheets</source>
<target>Φιλτράρισμα χρονοδιαγραμμάτων</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Φίλτρο πελατών</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Φίλτρο έργων</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Φίλτρο δραστηριοτήτων</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Δημιουργία αντίγραφου</target>

View file

@ -1840,19 +1840,7 @@
</trans-unit>
<trans-unit id="anCjtwe" resname="timesheet.filter">
<source>Filter timesheets</source>
<target>Filter timesheets</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filter customers</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filter projects</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filter activities</target>
<target>Timesheets</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>

View file

@ -1190,18 +1190,6 @@
<source>Filter timesheets</source>
<target>Temposlipoj</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtri klientojn</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtri projektojn</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtri aktivaĵojn</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Krei kopion</target>

View file

@ -1830,18 +1830,6 @@
<source>Filter timesheets</source>
<target>Partes de horas</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrar clientes</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrar proyectos</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrar actividades</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Copiar</target>

View file

@ -1010,18 +1010,6 @@
<source>Filter timesheets</source>
<target>Ordu taula</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Bezeroak aukeratu</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Proiektuak aukeratu</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Jarduerak aukeratu</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Kopiatu</target>

View file

@ -1442,18 +1442,6 @@
<source>Filter timesheets</source>
<target state="translated">فیلتر تایم شیت ها</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">فیلتر مشتریان</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">فیلتر پروژه ها</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">فیلتر فعالیت ها</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">ایجاد کپی</target>

View file

@ -1802,18 +1802,6 @@
<source>Filter timesheets</source>
<target state="translated">Työajanseuranta</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">Suodata asiakkaita</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">Suodata projekteja</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Suodata työtehtäviä</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">Luo kopio</target>

View file

@ -1770,18 +1770,6 @@
<source>Filter timesheets</source>
<target state="translated">Filtrer les feuille de temps</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrer les clients</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrer les projets</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrer les activités</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Créer une copie</target>

View file

@ -1826,18 +1826,6 @@
<source>Filter timesheets</source>
<target>גיליונות שעות</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter" xml:space="preserve" approved="yes">
<source>Filter customers</source>
<target state="final">סינון לקוחות</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter" xml:space="preserve" approved="yes">
<source>Filter projects</source>
<target state="final">סינון מיזמים</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter" xml:space="preserve">
<source>Filter activities</source>
<target state="translated">סינון פעילויות</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy" xml:space="preserve">
<source>copy</source>
<target state="translated">יצירת עותק</target>

View file

@ -1794,18 +1794,6 @@
<source>Filter timesheets</source>
<target state="translated">Filtriraj vremenske tablice</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtriraj kupce</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtriraj projekte</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtriraj aktivnosti</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Stvori kopiju</target>

View file

@ -1218,18 +1218,6 @@
<source>Filter timesheets</source>
<target>Rögzített órák</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Ügyfelek szűrése</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Projektek szűrése</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Tevékenységek szűrése</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Másolat készítése</target>

View file

@ -1830,18 +1830,6 @@
<source>Filter timesheets</source>
<target state="final">Filtra fogli di presenza</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter" approved="yes">
<source>Filter customers</source>
<target state="final">Filtra clienti</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter" approved="yes">
<source>Filter projects</source>
<target state="final">Filtra progetti</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter" approved="yes">
<source>Filter activities</source>
<target state="final">Filtra attività</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy" approved="yes">
<source>copy</source>
<target state="final">Crea copia</target>

View file

@ -1258,18 +1258,6 @@
<source>Filter timesheets</source>
<target state="translated">타임시트 필터링</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">고객 필터링</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">프로젝트 필터링</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">활동 필터링</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">사본 만들기</target>

View file

@ -1466,18 +1466,6 @@
<source>Filter timesheets</source>
<target>Filtrer timesedler</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrer kunder</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrer prosjekter</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrer aktiviteter</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Opprett kopi</target>

View file

@ -1830,18 +1830,6 @@
<source>Filter timesheets</source>
<target>Tijden</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filter op klant</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filter op project</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filter op activiteiten</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Maak een kopie</target>

View file

@ -1782,18 +1782,6 @@
<source>Filter timesheets</source>
<target state="translated">Filtruj ewidencję pomiarów</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">Filtruj klientów</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">Filtruj projekty</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Filtruj aktywności</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Utwórz kopię</target>

View file

@ -1830,18 +1830,6 @@
<source>Filter timesheets</source>
<target>Quadro de horários</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrar clientes</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrar projetos</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrar atividades</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Criar cópia</target>

View file

@ -1830,18 +1830,6 @@
<source>Filter timesheets</source>
<target>Horários</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrar clientes</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrar projetos</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrar atividades</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Criar cópia</target>

View file

@ -1774,18 +1774,6 @@
<source>Filter timesheets</source>
<target>Fișe de pontaj</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Filtrează clienții</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Filtrează proiectele</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Filtrează activitățile</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Copiază</target>

View file

@ -1754,18 +1754,6 @@
<source>Filter timesheets</source>
<target state="translated">Фильтр табелей</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Фильтр клиентов</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Фильтр проектов</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Фильтр активности</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Создать копию</target>

View file

@ -1586,18 +1586,6 @@
<source>Filter timesheets</source>
<target>Pracovné výkazy</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Zákazník</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Projekt</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Činnosť</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Kópia</target>

View file

@ -1778,18 +1778,6 @@
<source>Filter timesheets</source>
<target state="translated">Tidrapport</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">Filtrera kunder</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">Filtrera projekt</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Filtrera aktiviter</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">Skapa kopia</target>

View file

@ -1878,18 +1878,6 @@
<source>Filter timesheets</source>
<target state="final">நேர அட்டவணைகளை வடிகட்டவும்</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter" xml:space="preserve" approved="yes">
<source>Filter customers</source>
<target state="final">வாடிக்கையாளர்களை வடிகட்டவும்</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter" xml:space="preserve" approved="yes">
<source>Filter projects</source>
<target state="final">வடிகட்டித் திட்டங்கள்</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter" xml:space="preserve" approved="yes">
<source>Filter activities</source>
<target state="final">வடிகட்டி நடவடிக்கைகள்</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy" xml:space="preserve" approved="yes">
<source>copy</source>
<target state="final">நகலை உருவாக்கவும்</target>

View file

@ -1826,18 +1826,6 @@
<source>Filter timesheets</source>
<target state="translated">Çizelgeler</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">Müşterileri filtrele</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">Projeleri filtrele</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Etkinlikleri filtrele</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">Kopya oluştur</target>

View file

@ -1830,18 +1830,6 @@
<source>Filter timesheets</source>
<target state="translated">Фільтр табелів</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target state="translated">Фільтр клієнтів</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target state="translated">Фільтрувати проєкти</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target state="translated">Фільтри діяльності</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target state="translated">Створити копію</target>

View file

@ -1766,18 +1766,6 @@
<source>Filter timesheets</source>
<target state="translated">Lọc bảng chấm công</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>Lọc khách hàng</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>Lọc dự án</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>Lọc hoạt động</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>Tạo bản sao</target>

View file

@ -1794,18 +1794,6 @@
<source>Filter timesheets</source>
<target>时间表</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter">
<source>Filter customers</source>
<target>筛选用户</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter">
<source>Filter projects</source>
<target>筛选项目</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter">
<source>Filter activities</source>
<target>筛选活动</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy">
<source>copy</source>
<target>创建复制</target>

View file

@ -1814,10 +1814,6 @@
<source>profile-stats</source>
<target state="final">資料</target>
</trans-unit>
<trans-unit id="5G3QNHA" resname="activity.filter" xml:space="preserve" approved="yes">
<source>Filter activities</source>
<target state="final">篩選活動</target>
</trans-unit>
<trans-unit id="b1pgNOd" resname="copy" xml:space="preserve" approved="yes">
<source>copy</source>
<target state="final">建立複本</target>
@ -1830,10 +1826,6 @@
<source>invoice.paid</source>
<target state="final">收支單已付款</target>
</trans-unit>
<trans-unit id="xY9_cU2" resname="customer.filter" xml:space="preserve" approved="yes">
<source>Filter customers</source>
<target state="final">篩選客戶</target>
</trans-unit>
<trans-unit id="SEyZupT" resname="invoice.cancel" xml:space="preserve" approved="yes">
<source>invoice.cancel</source>
<target state="final">取消收支單</target>
@ -1870,10 +1862,6 @@
<source>download</source>
<target state="final">下載</target>
</trans-unit>
<trans-unit id="UFgOnc6" resname="project.filter" xml:space="preserve" approved="yes">
<source>Filter projects</source>
<target state="final">篩選專案</target>
</trans-unit>
<trans-unit id="hF6RgxM" resname="report" xml:space="preserve" approved="yes">
<source>report</source>
<target state="final">報表</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="ar" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>تتبع الوقت</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>موضوع</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="cs" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Sledování času</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Vzhled</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="da" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Tidsregistrering</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="de" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet" approved="yes">
<source>Time tracking</source>
<target state="final">Zeiterfassung</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Darstellung</target>

View file

@ -166,10 +166,6 @@
<source>theme</source>
<target state="translated">Thema</target>
</trans-unit>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Zeiterfassung</target>
</trans-unit>
<trans-unit id="nwuLBP4" resname="branding">
<source>branding</source>
<target state="translated">Meine Firma</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="el" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Ιχνηλάτηση Χρόνου</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Θέμα</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="en" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Time tracking</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Theme</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="eo" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Tempo-registrado</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Etoso</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="es" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Parte de horas</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="eu" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Ordu taula</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Itxura</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="fa" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">پیگیری زمان</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target state="translated">تم</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="fi" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Työajanseuranta</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target state="translated">Teema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="fr" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Suivi du temps</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Thème</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="he" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>גיליון שעות</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>ערכת עיצוב</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="hr" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Evidentiranje vremena</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="hu" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Rögzített órák</target>
</trans-unit>
<trans-unit id="k4pl27g" resname="timesheet.rules.allow_future_times">
<source>timesheet.rules.allow_future_times</source>
<target>Jövőbeli rögzítések engedélyezése</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="id" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Pelacakan waktu</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target state="translated">Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="it" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet" approved="yes">
<source>Time tracking</source>
<target state="final">Monitoraggio del tempo</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme" approved="yes">
<source>theme</source>
<target state="final">Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="ja" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>タイムシート</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>テーマ</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="ko" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">시간 추적</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target state="translated">테마</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="nb-NO" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Tidsregistrering</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Drakt</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="nl" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet" xml:space="preserve">
<source>Time tracking</source>
<target state="translated">Tijdsregistratie</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Thema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="pl" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Śledzenie czasu</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target state="translated">Motyw graficzny</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="pt" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Rastreamento de tempo</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="pt-BR" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Rastreamento de tempo</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="ro" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target state="translated">Timpul de urmărire</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Tema</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="ru" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Учет времени</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target>Тема</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="sk" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet">
<source>Time tracking</source>
<target>Pracovný výkaz</target>
</trans-unit>
<trans-unit id="FaGqoTT" resname="timesheet.mode">
<source>timesheet.mode</source>
<target state="translated">Režim sledovania času</target>

View file

@ -2,10 +2,6 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="sv" datatype="plaintext" original="system-configuration.en.xlf">
<body>
<trans-unit id="lt2Y2LO" resname="timesheet" xml:space="preserve">
<source>Time tracking</source>
<target state="translated">Tidrapport</target>
</trans-unit>
<trans-unit id="PLggHn." resname="theme">
<source>theme</source>
<target state="translated">Tema</target>

Some files were not shown because too many files have changed in this diff Show more