default value for billable flag and support in batch update (#2851)

This commit is contained in:
Kevin Papst
2021-10-13 15:49:20 +02:00
committed by GitHub
parent 9226daa891
commit 6b066046c6
12 changed files with 92 additions and 51 deletions

View File

@@ -232,6 +232,11 @@ class SystemConfiguration implements SystemBundleConfiguration
return (string) $this->find('timesheet.default_begin'); return (string) $this->find('timesheet.default_begin');
} }
public function getTimesheetDefaultBillable(): bool
{
return (bool) $this->find('defaults.timesheet.billable');
}
public function isTimesheetAllowFutureTimes(): bool public function isTimesheetAllowFutureTimes(): bool
{ {
return (bool) $this->find('timesheet.rules.allow_future_times'); return (bool) $this->find('timesheet.rules.allow_future_times');

View File

@@ -382,6 +382,10 @@ final class SystemConfigurationController extends AbstractController
->setConstraints([ ->setConstraints([
new GreaterThanOrEqual(['value' => 0]) new GreaterThanOrEqual(['value' => 0])
]), ]),
(new Configuration())
->setName('defaults.timesheet.billable')
->setType(YesNoType::class)
->setOptions(['help' => 'default_value_new', 'label' => 'label.billable']),
]), ]),
(new SystemConfigurationModel()) (new SystemConfigurationModel())
->setSection(SystemConfigurationModel::SECTION_LOCKDOWN) ->setSection(SystemConfigurationModel::SECTION_LOCKDOWN)

View File

@@ -364,6 +364,10 @@ abstract class TimesheetAbstractController extends AbstractController
$timesheet->setExported($dto->isExported()); $timesheet->setExported($dto->isExported());
$execute = true; $execute = true;
} }
if (null !== $dto->isBillable()) {
$timesheet->setBillable($dto->isBillable());
$execute = true;
}
if ($dto->isRecalculateRates()) { if ($dto->isRecalculateRates()) {
$timesheet->setFixedRate(null); $timesheet->setFixedRate(null);

View File

@@ -635,6 +635,12 @@ class Configuration implements ConfigurationInterface
->scalarNode('currency')->defaultValue(Customer::DEFAULT_CURRENCY)->end() ->scalarNode('currency')->defaultValue(Customer::DEFAULT_CURRENCY)->end()
->end() ->end()
->end() ->end()
->arrayNode('timesheet')
->addDefaultsIfNotSet()
->children()
->booleanNode('billable')->defaultTrue()->end()
->end()
->end()
->arrayNode('user') ->arrayNode('user')
->addDefaultsIfNotSet() ->addDefaultsIfNotSet()
->children() ->children()

View File

@@ -207,6 +207,17 @@ class TimesheetMultiUpdate extends AbstractType
]); ]);
} }
if ($options['include_billable']) {
$builder->add('billable', ChoiceType::class, [
'label' => 'label.billable',
'choices' => [
'' => null,
'yes' => true,
'no' => false,
],
]);
}
if ($options['include_rate']) { if ($options['include_rate']) {
$builder $builder
->add('recalculateRates', YesNoType::class, [ ->add('recalculateRates', YesNoType::class, [
@@ -279,6 +290,7 @@ class TimesheetMultiUpdate extends AbstractType
'include_user' => false, 'include_user' => false,
'include_rate' => false, 'include_rate' => false,
'include_exported' => false, 'include_exported' => false,
'include_billable' => true,
]); ]);
} }
} }

View File

@@ -22,6 +22,7 @@ use Doctrine\Common\Collections\Collection;
/** /**
* @App\Validator\Constraints\TimesheetMultiUpdate * @App\Validator\Constraints\TimesheetMultiUpdate
* @internal
*/ */
class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithMetaFields class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithMetaFields
{ {
@@ -57,6 +58,10 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
* @var bool|null * @var bool|null
*/ */
private $exported = null; private $exported = null;
/**
* @var bool|null
*/
private $billable = null;
/** /**
* @var float|null * @var float|null
*/ */
@@ -84,11 +89,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->customer; return $this->customer;
} }
public function setCustomer(Customer $customer): TimesheetMultiUpdateDTO public function setCustomer(Customer $customer): void
{ {
$this->customer = $customer; $this->customer = $customer;
return $this;
} }
public function getProject(): ?Project public function getProject(): ?Project
@@ -96,11 +99,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->project; return $this->project;
} }
public function setProject(Project $project): TimesheetMultiUpdateDTO public function setProject(Project $project): void
{ {
$this->project = $project; $this->project = $project;
return $this;
} }
public function getActivity(): ?Activity public function getActivity(): ?Activity
@@ -108,11 +109,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->activity; return $this->activity;
} }
public function setActivity(Activity $activity): TimesheetMultiUpdateDTO public function setActivity(Activity $activity): void
{ {
$this->activity = $activity; $this->activity = $activity;
return $this;
} }
/** /**
@@ -123,11 +122,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->tags; return $this->tags;
} }
public function setTags(iterable $tags): TimesheetMultiUpdateDTO public function setTags(iterable $tags): void
{ {
$this->tags = $tags; $this->tags = $tags;
return $this;
} }
public function getUser(): ?User public function getUser(): ?User
@@ -135,11 +132,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->user; return $this->user;
} }
public function setUser(User $user): TimesheetMultiUpdateDTO public function setUser(User $user): void
{ {
$this->user = $user; $this->user = $user;
return $this;
} }
public function isExported(): ?bool public function isExported(): ?bool
@@ -147,11 +142,19 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->exported; return $this->exported;
} }
public function setExported(bool $exported): TimesheetMultiUpdateDTO public function setExported(?bool $exported): void
{ {
$this->exported = $exported; $this->exported = $exported;
}
return $this; public function isBillable(): ?bool
{
return $this->billable;
}
public function setBillable(?bool $billable): void
{
$this->billable = $billable;
} }
public function isRecalculateRates(): bool public function isRecalculateRates(): bool
@@ -159,11 +162,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->recalculateRates; return $this->recalculateRates;
} }
public function setRecalculateRates(bool $recalculateRates): TimesheetMultiUpdateDTO public function setRecalculateRates(bool $recalculateRates): void
{ {
$this->recalculateRates = $recalculateRates; $this->recalculateRates = $recalculateRates;
return $this;
} }
public function isReplaceTags(): bool public function isReplaceTags(): bool
@@ -171,11 +172,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->replaceTags; return $this->replaceTags;
} }
public function setReplaceTags(bool $replaceTags): TimesheetMultiUpdateDTO public function setReplaceTags(bool $replaceTags): void
{ {
$this->replaceTags = $replaceTags; $this->replaceTags = $replaceTags;
return $this;
} }
public function getFixedRate(): ?float public function getFixedRate(): ?float
@@ -183,11 +182,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->fixedRate; return $this->fixedRate;
} }
public function setFixedRate(?float $fixedRate): TimesheetMultiUpdateDTO public function setFixedRate(?float $fixedRate): void
{ {
$this->fixedRate = $fixedRate; $this->fixedRate = $fixedRate;
return $this;
} }
public function getHourlyRate(): ?float public function getHourlyRate(): ?float
@@ -195,11 +192,9 @@ class TimesheetMultiUpdateDTO extends MultiUpdateTableDTO implements EntityWithM
return $this->hourlyRate; return $this->hourlyRate;
} }
public function setHourlyRate(?float $hourlyRate): TimesheetMultiUpdateDTO public function setHourlyRate(?float $hourlyRate): void
{ {
$this->hourlyRate = $hourlyRate; $this->hourlyRate = $hourlyRate;
return $this;
} }
/** /**

View File

@@ -110,6 +110,8 @@ final class TimesheetService
$mode = $this->trackingModeService->getActiveMode(); $mode = $this->trackingModeService->getActiveMode();
$mode->create($timesheet, $request); $mode->create($timesheet, $request);
$timesheet->setBillable($this->configuration->getTimesheetDefaultBillable());
return $timesheet; return $timesheet;
} }

View File

@@ -36,6 +36,11 @@
{{ form_row(form.exported) }} {{ form_row(form.exported) }}
</fieldset> </fieldset>
{% endif %} {% endif %}
{% if form.billable is defined %}
<fieldset>
{{ form_row(form.billable) }}
</fieldset>
{% endif %}
{% if form.recalculateRates is defined %} {% if form.recalculateRates is defined %}
<fieldset> <fieldset>
{{ form_row(form.recalculateRates) }} {{ form_row(form.recalculateRates) }}

View File

@@ -140,6 +140,9 @@ class AppExtensionTest extends TestCase
'templates/invoice/renderer/', 'templates/invoice/renderer/',
], ],
'kimai.defaults' => [ 'kimai.defaults' => [
'timesheet' => [
'billable' => true,
],
'customer' => [ 'customer' => [
'timezone' => null, 'timezone' => null,
'country' => 'DE', 'country' => 'DE',

View File

@@ -395,6 +395,9 @@ class ConfigurationTest extends TestCase
'dashboard' => [], 'dashboard' => [],
'widgets' => [], 'widgets' => [],
'defaults' => [ 'defaults' => [
'timesheet' => [
'billable' => true,
],
'customer' => [ 'customer' => [
'timezone' => null, 'timezone' => null,
'country' => 'DE', 'country' => 'DE',

View File

@@ -32,6 +32,7 @@ class TimesheetMultiUpdateDTOTest extends TestCase
self::assertNull($sut->getAction()); self::assertNull($sut->getAction());
self::assertNull($sut->isExported()); self::assertNull($sut->isExported());
self::assertNull($sut->isBillable());
self::assertNull($sut->getProject()); self::assertNull($sut->getProject());
self::assertNull($sut->getAction()); self::assertNull($sut->getAction());
self::assertNull($sut->getCustomer()); self::assertNull($sut->getCustomer());
@@ -70,37 +71,44 @@ class TimesheetMultiUpdateDTOTest extends TestCase
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setEntities($entities)); self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setEntities($entities));
self::assertEquals($entities, $sut->getEntities()); self::assertEquals($entities, $sut->getEntities());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setExported(true)); self::assertNull($sut->isExported());
$sut->setExported(true);
self::assertTrue($sut->isExported()); self::assertTrue($sut->isExported());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setExported(false)); $sut->setExported(false);
self::assertFalse($sut->isExported()); self::assertFalse($sut->isExported());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setTags(['foo', '0815'])); self::assertNull($sut->isBillable());
$sut->setBillable(true);
self::assertTrue($sut->isBillable());
$sut->setExported(false);
self::assertFalse($sut->isExported());
$sut->setTags(['foo', '0815']);
self::assertEquals(['foo', '0815'], $sut->getTags()); self::assertEquals(['foo', '0815'], $sut->getTags());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setReplaceTags(true)); $sut->setReplaceTags(true);
self::assertTrue($sut->isReplaceTags()); self::assertTrue($sut->isReplaceTags());
$user = (new User())->setUsername('sdfsdfsd'); $user = (new User())->setUsername('sdfsdfsd');
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setUser($user)); $sut->setUser($user);
self::assertSame($user, $sut->getUser()); self::assertSame($user, $sut->getUser());
$activity = (new Activity())->setName('sdfsdfsd'); $activity = (new Activity())->setName('sdfsdfsd');
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setActivity($activity)); $sut->setActivity($activity);
self::assertSame($activity, $sut->getActivity()); self::assertSame($activity, $sut->getActivity());
$project = (new Project())->setName('sdfsdfsd'); $project = (new Project())->setName('sdfsdfsd');
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setProject($project)); $sut->setProject($project);
self::assertSame($project, $sut->getProject()); self::assertSame($project, $sut->getProject());
$customer = (new Customer())->setName('sdfsdfsd'); $customer = (new Customer())->setName('sdfsdfsd');
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setCustomer($customer)); $sut->setCustomer($customer);
self::assertSame($customer, $sut->getCustomer()); self::assertSame($customer, $sut->getCustomer());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setFixedRate(12.78)); $sut->setFixedRate(12.78);
self::assertEquals(12.78, $sut->getFixedRate()); self::assertEquals(12.78, $sut->getFixedRate());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setHourlyRate(123.45)); $sut->setHourlyRate(123.45);
self::assertEquals(123.45, $sut->getHourlyRate()); self::assertEquals(123.45, $sut->getHourlyRate());
self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setUpdateMeta(['foo', 'bar'])); self::assertInstanceOf(TimesheetMultiUpdateDTO::class, $sut->setUpdateMeta(['foo', 'bar']));

View File

@@ -44,10 +44,8 @@ class TimesheetMultiUpdateValidatorTest extends ConstraintValidatorTestCase
$activity->setProject($project1); $activity->setProject($project1);
$timesheet = new TimesheetMultiUpdateDTO(); $timesheet = new TimesheetMultiUpdateDTO();
$timesheet $timesheet->setActivity($activity);
->setActivity($activity) $timesheet->setProject($project2);
->setProject($project2)
;
$this->validator->validate($timesheet, new TimesheetMultiUpdateConstraint(['message' => 'myMessage'])); $this->validator->validate($timesheet, new TimesheetMultiUpdateConstraint(['message' => 'myMessage']));
@@ -90,10 +88,8 @@ class TimesheetMultiUpdateValidatorTest extends ConstraintValidatorTestCase
public function testHourlyRateAndFixedRateInParallelAreNotAllowed() public function testHourlyRateAndFixedRateInParallelAreNotAllowed()
{ {
$timesheet = new TimesheetMultiUpdateDTO(); $timesheet = new TimesheetMultiUpdateDTO();
$timesheet $timesheet->setHourlyRate(10.12);
->setHourlyRate(10.12) $timesheet->setFixedRate(123.45);
->setFixedRate(123.45)
;
$this->validator->validate($timesheet, new TimesheetMultiUpdateConstraint(['message' => 'myMessage'])); $this->validator->validate($timesheet, new TimesheetMultiUpdateConstraint(['message' => 'myMessage']));
@@ -118,10 +114,8 @@ class TimesheetMultiUpdateValidatorTest extends ConstraintValidatorTestCase
$activity->setProject($project); $activity->setProject($project);
$timesheet = new TimesheetMultiUpdateDTO(); $timesheet = new TimesheetMultiUpdateDTO();
$timesheet $timesheet->setActivity($activity);
->setActivity($activity) $timesheet->setProject($project);
->setProject($project)
;
$this->validator->validate($timesheet, new TimesheetMultiUpdateConstraint(['message' => 'myMessage'])); $this->validator->validate($timesheet, new TimesheetMultiUpdateConstraint(['message' => 'myMessage']));