145 lines
5.4 KiB
PHP
145 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Model;
|
|
|
|
use App\Model\WorkMonthDays;
|
|
use App\Saas\Model\TimeAccount;
|
|
use App\Saas\Model\TimeAccountDay;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class WorkMonthDaysTest extends TestCase
|
|
{
|
|
public function testItRoundTripsThroughTheStoredShape(): void
|
|
{
|
|
$stored = [
|
|
'2026-03-01' => ['minutes' => 285],
|
|
'2026-03-05' => ['minutes' => 0, 'absence' => 'Ruhetag 24/25'],
|
|
];
|
|
|
|
$this->assertSame($stored, WorkMonthDays::fromArray($stored)->toArray());
|
|
}
|
|
|
|
public function testItSortsAndDropsEntriesItCannotRead(): void
|
|
{
|
|
$days = WorkMonthDays::fromArray([
|
|
'2026-03-05' => ['minutes' => 60],
|
|
'not-a-date' => ['minutes' => 120],
|
|
'2026-03-01' => ['minutes' => 30, 'absence' => ''],
|
|
'2026-03-09' => 'not an entry',
|
|
]);
|
|
|
|
$this->assertSame(['2026-03-01', '2026-03-05'], array_keys($days->toArray()));
|
|
$this->assertArrayNotHasKey('absence', $days->toArray()['2026-03-01'], 'An empty label is no label.');
|
|
}
|
|
|
|
public function testAMissingMinutesKeyCountsAsZero(): void
|
|
{
|
|
$days = WorkMonthDays::fromArray(['2026-03-01' => ['absence' => 'Ferien']]);
|
|
|
|
$this->assertSame(0, $days->getTotalMinutes());
|
|
$this->assertSame(1, $days->countAbsenceDays());
|
|
$this->assertSame(0, $days->countWorkedDays());
|
|
}
|
|
|
|
public function testItSummarisesTheMonth(): void
|
|
{
|
|
$days = WorkMonthDays::fromArray([
|
|
'2026-03-01' => ['minutes' => 285],
|
|
'2026-03-05' => ['minutes' => 0, 'absence' => 'Ruhetag 24/25'],
|
|
'2026-03-06' => ['minutes' => 600],
|
|
]);
|
|
|
|
$this->assertSame(885, $days->getTotalMinutes());
|
|
$this->assertSame(2, $days->countWorkedDays());
|
|
$this->assertSame(1, $days->countAbsenceDays());
|
|
}
|
|
|
|
public function testItBuildsFromATimeAccount(): void
|
|
{
|
|
$account = new TimeAccount('e-1', 2026, 3, 345, 9900, 'Operations', [
|
|
'2026-03-01' => new TimeAccountDay(new \DateTimeImmutable('2026-03-01'), 285, null, '09:00', '14:45', 60),
|
|
'2026-03-05' => new TimeAccountDay(new \DateTimeImmutable('2026-03-05'), 60, 'Ferien'),
|
|
], []);
|
|
|
|
$this->assertSame([
|
|
'2026-03-01' => ['minutes' => 285, 'break' => 60, 'start' => '09:00', 'end' => '14:45'],
|
|
'2026-03-05' => ['minutes' => 60, 'absence' => 'Ferien'],
|
|
], WorkMonthDays::fromTimeAccount($account)->toArray());
|
|
}
|
|
|
|
public function testASpanNeedsBothEnds(): void
|
|
{
|
|
$days = WorkMonthDays::fromArray([
|
|
'2026-03-01' => ['minutes' => 60, 'start' => '09:00'],
|
|
'2026-03-02' => ['minutes' => 60, 'start' => '09:00', 'end' => 'noon'],
|
|
'2026-03-03' => ['minutes' => 60, 'start' => '09:00', 'end' => '10:00'],
|
|
])->forCalendarMonth(2026, 3);
|
|
|
|
$this->assertFalse($days[0]->hasSpan());
|
|
$this->assertFalse($days[1]->hasSpan());
|
|
$this->assertTrue($days[2]->hasSpan());
|
|
}
|
|
|
|
public function testItTotalsTheBreaks(): void
|
|
{
|
|
$days = WorkMonthDays::fromArray([
|
|
'2026-03-01' => ['minutes' => 285, 'break' => 60],
|
|
'2026-03-02' => ['minutes' => 405, 'break' => 150],
|
|
'2026-03-03' => ['minutes' => 180],
|
|
]);
|
|
|
|
$this->assertSame(210, $days->getTotalBreakMinutes());
|
|
$this->assertSame(0, $days->forCalendarMonth(2026, 3)[2]->getBreakMinutes());
|
|
}
|
|
|
|
/**
|
|
* The calendar, not the payload, decides how many rows a month has: days the SaaS never
|
|
* reported still need a line in the table and in the PDF.
|
|
*/
|
|
public function testItFillsEveryCalendarDayOfALongMonth(): void
|
|
{
|
|
$days = WorkMonthDays::fromArray(['2026-03-05' => ['minutes' => 0, 'absence' => 'Ruhetag 24/25']]);
|
|
$calendar = $days->forCalendarMonth(2026, 3);
|
|
|
|
$this->assertCount(31, $calendar);
|
|
$this->assertSame('2026-03-01', $calendar[0]->getDate()->format('Y-m-d'));
|
|
$this->assertSame('2026-03-31', $calendar[30]->getDate()->format('Y-m-d'));
|
|
}
|
|
|
|
public function testItFillsEveryCalendarDayOfAShortMonth(): void
|
|
{
|
|
$this->assertCount(28, WorkMonthDays::fromArray([])->forCalendarMonth(2026, 2));
|
|
$this->assertCount(29, WorkMonthDays::fromArray([])->forCalendarMonth(2024, 2));
|
|
}
|
|
|
|
/**
|
|
* "Not reported" and "reported as zero" are different things: a rest day is a real
|
|
* booking of nothing, an unreported day is a gap.
|
|
*/
|
|
public function testItSeparatesUnreportedDaysFromZeroDays(): void
|
|
{
|
|
$calendar = WorkMonthDays::fromArray([
|
|
'2026-03-05' => ['minutes' => 0, 'absence' => 'Ruhetag 24/25'],
|
|
])->forCalendarMonth(2026, 3);
|
|
|
|
$restDay = $calendar[4];
|
|
$this->assertSame('2026-03-05', $restDay->getDate()->format('Y-m-d'));
|
|
$this->assertTrue($restDay->isReported());
|
|
$this->assertSame(0, $restDay->getMinutes());
|
|
$this->assertSame('Ruhetag 24/25', $restDay->getAbsence());
|
|
|
|
$gap = $calendar[0];
|
|
$this->assertFalse($gap->isReported());
|
|
$this->assertNull($gap->getMinutes());
|
|
$this->assertFalse($gap->isAbsence());
|
|
}
|
|
|
|
public function testItKnowsWeekends(): void
|
|
{
|
|
$calendar = WorkMonthDays::fromArray([])->forCalendarMonth(2026, 3);
|
|
|
|
$this->assertTrue($calendar[0]->isWeekend(), '1 March 2026 is a Sunday.');
|
|
$this->assertFalse($calendar[1]->isWeekend());
|
|
}
|
|
}
|