0
0
mirror of https://github.com/kevinpapst/kimai2.git synced 2024-12-22 12:18:29 +00:00
kevinpapst_kimai2/tests/Timesheet/RateServiceTest.php
2024-12-22 01:25:30 +01:00

284 lines
12 KiB
PHP

<?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\Tests\Timesheet;
use App\Entity\Activity;
use App\Entity\ActivityRate;
use App\Entity\Customer;
use App\Entity\CustomerRate;
use App\Entity\Project;
use App\Entity\ProjectRate;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Entity\UserPreference;
use App\Repository\TimesheetRepository;
use App\Timesheet\RateService;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Timesheet\RateService
*/
class RateServiceTest extends TestCase
{
protected function getRateRepositoryMock(array $rates = [])
{
$mock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
if (!empty($rates)) {
$mock->expects($this->any())->method('findMatchingRates')->willReturn($rates);
}
return $mock;
}
private static function createDateTime(string $datetime = null): \DateTime
{
return new \DateTime($datetime ?? 'now', new \DateTimeZone('UTC'));
}
public function testCalculateWithTimesheetHourlyRate(): void
{
$record = new Timesheet();
$record->setEnd(self::createDateTime());
$record->setDuration(1800);
$record->setHourlyRate(100);
$record->setActivity(new Activity());
$record->setUser($this->getTestUser());
$sut = new RateService([], $this->getRateRepositoryMock());
$rate = $sut->calculate($record);
self::assertEquals(50, $rate->getRate());
}
public function testCalculateWithTimesheetFixedRate(): void
{
$record = new Timesheet();
$record->setEnd(self::createDateTime());
$record->setDuration(1800);
$record->setFixedRate(10);
// make sure that fixed rate is always applied, even if hourly rate is set
$record->setHourlyRate(99);
$record->setActivity(new Activity());
$record->setUser($this->getTestUser());
$sut = new RateService([], $this->getRateRepositoryMock());
$rate = $sut->calculate($record);
self::assertEquals(10, $rate->getRate());
}
public static function getRateTestData()
{ // expected, expInt, durat, userH, userIn, timeH, timeF, actH, actIn, actF, proH, proIn, proFi, custH, custIn, custF
yield 'a0' => [0.0, 0.0, 0, 0, 0, null, null, null, null, false, null, null, false, null, null, false];
yield 'a2' => [0.0, 0.0, 0, 0, null, null, null, null, null, false, null, null, false, null, null, false];
yield 'a4' => [0.0, 0.0, 1800, 0, 0, null, null, null, null, false, null, null, false, null, null, false];
yield 'a6' => [0.5, 6.72, 1800, 1, 13.44, null, null, null, null, false, null, null, false, null, null, false];
yield 'a8' => [0.0, 1, 0, 0, 0, 0, 0, 0, 1, true, 0, null, true, 0, null, true];
// rate: 1.5 => timesheet hourly rate , internal: 2.5 => activity hourly rate (30 min)
yield 'b1' => [1.5, 0.5, 1800, 1, 1, 3, null, 5, null, false, 7, null, false, 9, null, false];
yield 'b2' => [2.5, 4.5, 1800, 1, 9, null, null, 5, null, false, 7, null, false, 9, null, false];
yield 'b3' => [3.5, 6.5, 1800, 1, 1, null, null, null, null, false, 7, 13, false, 9, 9, false];
yield 'b4' => [4.5, 6.5, 1800, 1, 15, null, null, null, null, false, null, null, false, 9, 13, false];
// rate: 2.0 => timesheet fixed rate , internal: 3.0 => activity fixed rate
yield 'b5' => [2.0, 1.0, 1800, 1, 1, null, 2, 3, null, true, 4, null, true, 5, null, true];
yield 'b6' => [3.0, 3.0, 1800, 1, 3, null, null, 3, null, true, 4, null, true, 5, null, true];
yield 'b7' => [4.0, 7.0, 1800, 1, 7, null, null, null, null, false, 4, null, true, 5, null, true];
yield 'b8' => [3.0, 4.7, 1800, 1, 4.7, null, null, 3, null, true, null, null, false, 5, null, true];
// rate: 2.0 => timesheet fixed rate , internal: 5.0 => customer hourly rate
yield 'b9' => [2.0, 71.0, 1800, 1, 71, null, 2, null, null, false, null, null, false, 5, null, true];
// rate: 5.0 => timesheet hourly rate , internal: 7.5 => user internal rate (30 min)
yield 'c0' => [5.0, 7.5, 1800, 100, 15, 10, null, null, null, false, null, null, false, null, null, false];
// internal: 10 because no rule applies and as fallback the users internal rate is used
yield 'd0' => [10, 100, 1800, 100, 100, null, 10, null, null, false, null, null, false, null, null, false];
yield 'e0' => [10, 50, 1800, 100, 100, null, null, 20, null, false, null, null, false, null, null, false];
yield 'f0' => [20, 78, 1800, 100, 100, null, null, 20, 78, true, null, null, false, null, null, false];
yield 'g0' => [15, 11.5, 1800, 100, 100, null, null, null, null, false, 30, 23, false, null, null, false];
yield 'h0' => [30, 100, 1800, 100, 100, null, null, null, null, false, 30, null, true, null, null, false];
yield 'i0' => [20, 13.5, 1800, 100, 100, null, null, null, null, false, null, null, false, 40, 27, false];
yield 'j0' => [40, 84, 1800, 100, 45, null, null, null, null, false, null, null, false, 40, 84, true];
// make sure the last fallback for the internal rate is the users hourly rate
yield 'k0' => [8.82, 6, 1800, 17.64, 12, null, null, null, null, false, null, null, false, null, null, true];
yield 'k1' => [8.82, 8.82, 1800, 17.64, null, null, null, null, null, false, null, null, false, null, null, true];
}
/**
* @dataProvider getRateTestData
*/
public function testRates(
$expectedRate,
$expectedInternalRate,
$duration,
$userRate,
$userInternalRate,
$timesheetHourly,
$timesheetFixed,
$activityRate,
$activityInternal,
$activityIsFixed,
$projectRate,
$projectInternal,
$projectIsFixed,
$customerRate,
$customerInternal,
$customerIsFixed
) {
$customer = new Customer('foo');
$project = new Project();
$project->setCustomer($customer);
$activity = new Activity();
$activity->setProject($project);
$timesheet = new Timesheet();
$timesheet
->setEnd(self::createDateTime())
->setHourlyRate($timesheetHourly)
->setFixedRate($timesheetFixed)
->setActivity($activity)
->setProject($project)
->setDuration($duration)
->setUser($this->getTestUser($userRate, $userInternalRate))
;
$rates = [];
if (null !== $customerRate) {
$rate = new CustomerRate();
$rate->setRate($customerRate);
$rate->setIsFixed($customerIsFixed);
if (null !== $customerInternal) {
$rate->setInternalRate($customerInternal);
}
$rates[] = $rate;
}
if (null !== $projectRate) {
$rate = new ProjectRate();
$rate->setRate($projectRate);
$rate->setIsFixed($projectIsFixed);
if (null !== $projectInternal) {
$rate->setInternalRate($projectInternal);
}
$rates[] = $rate;
}
if (null !== $activityRate) {
$rate = new ActivityRate();
$rate->setRate($activityRate);
$rate->setIsFixed($activityIsFixed);
if (null !== $activityInternal) {
$rate->setInternalRate($activityInternal);
}
$rates[] = $rate;
}
$sut = new RateService([], $this->getRateRepositoryMock($rates));
$rate = $sut->calculate($timesheet);
self::assertEquals($expectedRate, $rate->getRate());
self::assertEquals($expectedInternalRate, $rate->getInternalRate());
}
protected function getTestUser($rate = 75, $internalRate = 75)
{
$user = new User();
$pref = new UserPreference(UserPreference::HOURLY_RATE, $rate);
$prefInt = new UserPreference(UserPreference::INTERNAL_RATE, $internalRate);
$user->setPreferences([$pref, $prefInt]);
return $user;
}
public function testCalculateWithEmptyEnd(): void
{
$record = new Timesheet();
$record->setBegin(self::createDateTime());
$record->setDuration(1800);
$record->setFixedRate(100);
$record->setHourlyRate(100);
$record->setActivity(new Activity());
self::assertEquals(0, $record->getRate());
$sut = new RateService([], $this->getRateRepositoryMock());
$rate = $sut->calculate($record);
self::assertEquals(0, $rate->getRate());
}
/**
* Uses the hourly rate from user_preferences to calculate the rate.
*
* @dataProvider getRuleDefinitions
*/
public function testCalculateWithRulesByUsersHourlyRate($duration, $rules, $expectedRate): void
{
$end = self::createDateTime('12:00:00');
$start = clone $end;
$start->setTimestamp($end->getTimestamp() - $duration);
$record = new Timesheet();
$record->setUser($this->getTestUser());
$record->setBegin($start);
$record->setDuration($duration);
$record->setActivity(new Activity());
self::assertEquals(0, $record->getRate());
$record->setEnd($end);
$sut = new RateService($rules, $this->getRateRepositoryMock());
$rate = $sut->calculate($record);
self::assertEquals($expectedRate, $rate->getRate());
}
public static function getRuleDefinitions()
{
$start = self::createDateTime('12:00:00');
$day = $start->format('l');
return [
[
31837,
[],
663.2708
],
[
31837,
[
'default' => [
'days' => [$day],
'factor' => 2.0
],
'foo' => [
'days' => ['bar'],
'factor' => 1.5
],
],
1326.5417
],
[
31837,
[
'default' => [
'days' => [$day],
'factor' => 2.0
],
'foo' => [
'days' => ['MonDay', 'tUEsdAy', 'WEdnesday', 'THursday', 'friDay', 'SATURday', 'sunDAY'],
'factor' => 1.5
],
],
2321.4479
],
];
}
}