mirror of
https://github.com/kevinpapst/kimai2.git
synced 2025-04-11 00:00:26 +00:00
Release 2.29 (#5325)
* bump composer packages * fixes #5329 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:
parent
8444928ae4
commit
b42c77a2a1
106 changed files with 617 additions and 1068 deletions
composer.jsoncomposer.lock
migrations
phpstan.neonsrc
Constants.php
Controller
EventSubscriber/Actions
Export
Base
Package
Form
Invoice/Hydrator
Reporting/YearlyUserList
templates
tests
API
ActivityControllerTest.phpCustomerControllerTest.phpProjectControllerTest.phpRateControllerTestTrait.php
Export
Base
Package
Invoice
phpstan.neontranslations
messages.ar.xlfmessages.cs.xlfmessages.da.xlfmessages.de.xlfmessages.de_CH.xlfmessages.el.xlfmessages.en.xlfmessages.eo.xlfmessages.es.xlfmessages.eu.xlfmessages.fa.xlfmessages.fi.xlfmessages.fr.xlfmessages.he.xlfmessages.hr.xlfmessages.hu.xlfmessages.it.xlfmessages.ko.xlfmessages.nb_NO.xlfmessages.nl.xlfmessages.pl.xlfmessages.pt.xlfmessages.pt_BR.xlfmessages.ro.xlfmessages.ru.xlfmessages.sk.xlfmessages.sv.xlfmessages.ta.xlfmessages.tr.xlfmessages.uk.xlfmessages.vi.xlfmessages.zh_CN.xlfmessages.zh_Hant.xlfsystem-configuration.ar.xlfsystem-configuration.cs.xlfsystem-configuration.da.xlfsystem-configuration.de.xlfsystem-configuration.de_CH.xlfsystem-configuration.el.xlfsystem-configuration.en.xlfsystem-configuration.eo.xlfsystem-configuration.es.xlfsystem-configuration.eu.xlfsystem-configuration.fa.xlfsystem-configuration.fi.xlfsystem-configuration.fr.xlfsystem-configuration.he.xlfsystem-configuration.hr.xlfsystem-configuration.hu.xlfsystem-configuration.id.xlfsystem-configuration.it.xlfsystem-configuration.ja.xlfsystem-configuration.ko.xlfsystem-configuration.nb_NO.xlfsystem-configuration.nl.xlfsystem-configuration.pl.xlfsystem-configuration.pt.xlfsystem-configuration.pt_BR.xlfsystem-configuration.ro.xlfsystem-configuration.ru.xlfsystem-configuration.sk.xlfsystem-configuration.sv.xlf
|
@ -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
683
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
10
phpstan.neon
10
phpstan.neon
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
19
src/Export/Package/ColumnWidth.php
Normal file
19
src/Export/Package/ColumnWidth.php
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,7 +28,7 @@ final class Configuration
|
|||
*/
|
||||
private array $constraints = [];
|
||||
|
||||
public function __construct(private string $name)
|
||||
public function __construct(private readonly string $name)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }} – {{ 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) }}
|
||||
–
|
||||
{{ '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) }}
|
||||
–
|
||||
{{ '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) }}
|
||||
–
|
||||
{{ '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') }}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue