This commit is contained in:
Dennis Eichhorn 2024-04-07 17:31:42 +00:00
parent 4e81fb41cf
commit a8d5939460
14 changed files with 62 additions and 63 deletions

View File

@ -5,6 +5,7 @@ return [
0 => [ 0 => [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard',
'verb' => 1, 'verb' => 1,
'active' => true,
'permission' => [ 'permission' => [
'module' => 'HumanResourceTimeRecording', 'module' => 'HumanResourceTimeRecording',
'type' => 2, 'type' => 2,
@ -16,6 +17,7 @@ return [
0 => [ 0 => [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\TimerecordingController:viewDashboard',
'verb' => 1, 'verb' => 1,
'active' => true,
'permission' => [ 'permission' => [
'module' => 'HumanResourceTimeRecording', 'module' => 'HumanResourceTimeRecording',
'type' => 2, 'type' => 2,

View File

@ -18,7 +18,7 @@
"pid": "/humanresource/timerecording", "pid": "/humanresource/timerecording",
"type": 3, "type": 3,
"subtype": 1, "subtype": 1,
"name": "Today", "name": "Dashboard",
"uri": "{/base}/humanresource/timerecording/dashboard?{?}", "uri": "{/base}/humanresource/timerecording/dashboard?{?}",
"target": "self", "target": "self",
"icon": null, "icon": null,

View File

@ -23,6 +23,7 @@ return [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\ApiController:apiSessionCreate', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\ApiController:apiSessionCreate',
'verb' => RouteVerb::PUT, 'verb' => RouteVerb::PUT,
'csrf' => true, 'csrf' => true,
'active' => true,
'permission' => [ 'permission' => [
'module' => ApiController::NAME, 'module' => ApiController::NAME,
'type' => PermissionType::CREATE, 'type' => PermissionType::CREATE,
@ -35,6 +36,7 @@ return [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\ApiController:apiSessionElementCreate', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\ApiController:apiSessionElementCreate',
'verb' => RouteVerb::PUT, 'verb' => RouteVerb::PUT,
'csrf' => true, 'csrf' => true,
'active' => true,
'permission' => [ 'permission' => [
'module' => ApiController::NAME, 'module' => ApiController::NAME,
'type' => PermissionType::CREATE, 'type' => PermissionType::CREATE,

View File

@ -22,6 +22,7 @@ return [
[ [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\BackendController:viewDashboard', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\BackendController:viewDashboard',
'verb' => RouteVerb::GET, 'verb' => RouteVerb::GET,
'active' => true,
'permission' => [ 'permission' => [
'module' => BackendController::NAME, 'module' => BackendController::NAME,
'type' => PermissionType::READ, 'type' => PermissionType::READ,
@ -33,6 +34,7 @@ return [
[ [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\BackendController:viewPrivateDashboard', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\BackendController:viewPrivateDashboard',
'verb' => RouteVerb::GET, 'verb' => RouteVerb::GET,
'active' => true,
'permission' => [ 'permission' => [
'module' => BackendController::NAME, 'module' => BackendController::NAME,
'type' => PermissionType::READ, 'type' => PermissionType::READ,
@ -44,6 +46,7 @@ return [
[ [
'dest' => '\Modules\HumanResourceTimeRecording\Controller\BackendController:viewPrivateSession', 'dest' => '\Modules\HumanResourceTimeRecording\Controller\BackendController:viewPrivateSession',
'verb' => RouteVerb::GET, 'verb' => RouteVerb::GET,
'active' => true,
'permission' => [ 'permission' => [
'module' => BackendController::NAME, 'module' => BackendController::NAME,
'type' => PermissionType::READ, 'type' => PermissionType::READ,

View File

@ -91,7 +91,8 @@ final class ApiController extends Controller
$element = new SessionElement($session, $dt); $element = new SessionElement($session, $dt);
$element->status = ClockingStatus::tryFromValue($request->getDataInt('status')) ?? ClockingStatus::START; $element->status = ClockingStatus::tryFromValue($request->getDataInt('status')) ?? ClockingStatus::START;
$session->addSessionElement($element); $session->sessionElements[] = $element;
$session->recalculate();
return $session; return $session;
} }
@ -111,7 +112,9 @@ final class ApiController extends Controller
*/ */
public function apiSessionElementCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void public function apiSessionElementCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void
{ {
if (($request->getDataInt('status') ?? -1) === ClockingStatus::START) { if (!$request->hasData('session') &&
($request->getDataInt('status') ?? -1) === ClockingStatus::START
) {
$this->apiSessionCreate($request, $response); $this->apiSessionCreate($request, $response);
return; return;
@ -146,9 +149,12 @@ final class ApiController extends Controller
if ($element->status === ClockingStatus::END) { if ($element->status === ClockingStatus::END) {
/** @var \Modules\HumanResourceTimeRecording\Models\Session $session */ /** @var \Modules\HumanResourceTimeRecording\Models\Session $session */
$session = SessionMapper::get()->where('id', (int) $request->getData('session'))->execute(); $session = SessionMapper::get()
->with('sessionElements')
->where('id', (int) $request->getData('session'))
->execute();
$session->addSessionElement($element); $session->recalculate();
SessionMapper::update()->execute($session); SessionMapper::update()->execute($session);
} }

View File

@ -105,56 +105,35 @@ class Session implements \JsonSerializable
$this->createdAt = new \DateTimeImmutable('now'); $this->createdAt = new \DateTimeImmutable('now');
} }
/** public function recalculate() : void
* Get busy time.
*
* @return int
*
* @since 1.0.0
*/
public function getBusy() : int
{ {
return $this->busy;
}
/**
* Add a session element to the session
*
* @param SessionElement $element Session element
*
* @return void
*
* @since 1.0.0
*/
public function addSessionElement(SessionElement $element) : void
{
if ($element->status === ClockingStatus::START) {
foreach ($this->sessionElements as $e) {
if ($e->status === ClockingStatus::START) {
return;
}
}
$this->start = $element->datetime;
}
if ($element->status === ClockingStatus::END) {
if ($this->end !== null) {
return;
}
$this->end = $element->datetime;
}
$this->sessionElements[] = $element;
\usort($this->sessionElements, [$this, 'compareSessionElementTimestamps']); \usort($this->sessionElements, [$this, 'compareSessionElementTimestamps']);
$start = null;
$end = null;
foreach ($this->sessionElements as $e) {
if ($e->status === ClockingStatus::START
&& ($start === null || $start->getTimestamp() > $e->datetime->getTimestamp())
) {
$start = $e->datetime;
} elseif ($e->status === ClockingStatus::END
&& ($end === null || $end->getTimestamp() < $e->datetime->getTimestamp())
) {
$end = $e->datetime;
}
}
$this->start = $start;
$this->end = $end;
$busyTime = 0; $busyTime = 0;
$lastStart = $this->start; $lastStart = $this->start;
foreach ($this->sessionElements as $e) { foreach ($this->sessionElements as $e) {
if ($e->status === ClockingStatus::START) { if ($e->status === ClockingStatus::START) {
$lastStart = $e->datetime;
continue; continue;
} }

View File

@ -17,7 +17,7 @@ namespace Modules\HumanResourceTimeRecording\Models;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
/** /**
* Mapper class. * SessionElement mapper class.
* *
* @package Modules\HumanResourceTimeRecording\Models * @package Modules\HumanResourceTimeRecording\Models
* @license OMS License 2.0 * @license OMS License 2.0

View File

@ -20,7 +20,7 @@ use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\Stdlib\Base\SmartDateTime; use phpOMS\Stdlib\Base\SmartDateTime;
/** /**
* Mapper class. * Session mapper class.
* *
* @package Modules\HumanResourceTimeRecording\Models * @package Modules\HumanResourceTimeRecording\Models
* @license OMS License 2.0 * @license OMS License 2.0
@ -105,6 +105,8 @@ final class SessionMapper extends DataMapperFactory
* *
* @return Session[] * @return Session[]
* *
* @performance This is way too slow
*
* @since 1.0.0 * @since 1.0.0
*/ */
public static function getLastSessionsFromAllEmployees() : array public static function getLastSessionsFromAllEmployees() : array
@ -121,7 +123,7 @@ final class SessionMapper extends DataMapperFactory
->andOn(self::TABLE . '_d1.hr_timerecording_session_start', '=', 'tm.maxDate'); ->andOn(self::TABLE . '_d1.hr_timerecording_session_start', '=', 'tm.maxDate');
return self::getAll() return self::getAll()
->execute($query); ->executeGetArray($query);
} }
/** /**

View File

@ -17,5 +17,5 @@ return ['Navigation' => [
'List' => 'Liste', 'List' => 'Liste',
'Stats' => 'Statistiken', 'Stats' => 'Statistiken',
'TimeRecording' => 'Zeitaufnahme', 'TimeRecording' => 'Zeitaufnahme',
'Today' => 'Heute', 'Dashboard' => 'Dashboard',
]]; ]];

View File

@ -17,5 +17,5 @@ return ['Navigation' => [
'List' => 'List', 'List' => 'List',
'Stats' => 'Stats', 'Stats' => 'Stats',
'TimeRecording' => 'Time Recording', 'TimeRecording' => 'Time Recording',
'Today' => 'Today', 'Dashboard' => 'Dashboard',
]]; ]];

View File

@ -13,6 +13,7 @@
declare(strict_types=1); declare(strict_types=1);
use Modules\HumanResourceTimeRecording\Models\ClockingType; use Modules\HumanResourceTimeRecording\Models\ClockingType;
use phpOMS\Uri\UriFactory;
$date = new \DateTime('now'); $date = new \DateTime('now');
@ -36,17 +37,21 @@ echo $this->data['nav']->render(); ?>
<tbody> <tbody>
<?php foreach ($this->data['employees'] as $employee) : <?php foreach ($this->data['employees'] as $employee) :
$session = $this->data['sessions'][$employee->id] ?? null; $session = $this->data['sessions'][$employee->id] ?? null;
// @todo Implement clocking view per employee for HR (also to edit clocking)
$employeeUrl = UriFactory::build('{/base}/humanresource/staff/view?id=' . $employee->id);
?> ?>
<tr> <tr>
<td><?= $session?->getStart()->format('Y-m-d') ?? $date->format('Y-m-d H:i:s'); ?> <td><?= $session?->start->format('Y-m-d H:i:s') ?? $date->format('Y-m-d H:i:s'); ?>
<td><span class="tag"><?= $this->getHtml('CT' . ($session?->type ?? ClockingType::NO_DATA)); ?></span> <td><span class="tag"><?= $this->getHtml('CT' . ($session?->type ?? ClockingType::NO_DATA)); ?></span>
<td> <td><a class="content" href="<?= $employeeUrl; ?>">
<?= $this->printHtml($employee->profile->account->name1); ?>, <?= $this->printHtml($employee->profile->account->name1); ?>,
<?= $this->printHtml($employee->profile->account->name2); ?> <?= $this->printHtml($employee->profile->account->name2); ?>
<td><?= $session?->getStart()->format('H:i:s'); ?> </a>
<td><?= $session?->start->format('H:i:s'); ?>
<td><?= $session !== null ? ((int) ($session->getBreak() / 3600)) . 'h' : ''; ?> <?= $session !== null ? ((int) ($session->getBreak() / 60) % 60) . 'm' : ''; ?> <td><?= $session !== null ? ((int) ($session->getBreak() / 3600)) . 'h' : ''; ?> <?= $session !== null ? ((int) ($session->getBreak() / 60) % 60) . 'm' : ''; ?>
<td><?= $session?->getEnd() !== null ? $session->getEnd()->format('H:i') : ''; ?> <td><?= $session?->end?->format('H:i') ?? ''; ?>
<td><?= $session !== null ? ((int) ($session->getBusy() / 3600)) . 'h' : ''; ?> <?= $session !== null ? ((int) ($session->getBusy() / 60) % 60) . 'm' : ''; ?> <td><?= $session !== null ? ((int) ($session->busy / 3600)) . 'h' : ''; ?> <?= $session !== null ? ((int) ($session->busy / 60) % 60) . 'm' : ''; ?>
<?php endforeach; ?> <?php endforeach; ?>
</table> </table>
</div> </div>

View File

@ -222,9 +222,9 @@ echo $this->data['nav']->render(); ?>
<td><a href="<?= $url; ?>"><?= $session->start->format('H:i'); ?></a> <td><a href="<?= $url; ?>"><?= $session->start->format('H:i'); ?></a>
<td><a href="<?= $url; ?>"><?= (int) ($session->getBreak() / 3600); ?>h <?= ((int) ($session->getBreak() / 60) % 60); ?>m</a> <td><a href="<?= $url; ?>"><?= (int) ($session->getBreak() / 3600); ?>h <?= ((int) ($session->getBreak() / 60) % 60); ?>m</a>
<td><a href="<?= $url; ?>"><?= $session->end?->format('H:i'); ?></a> <td><a href="<?= $url; ?>"><?= $session->end?->format('H:i'); ?></a>
<td><a href="<?= $url; ?>"><?= (int) ($session->getBusy() / 3600); ?>h <?= ((int) ($session->getBusy() / 60) % 60); ?>m</a> <td><a href="<?= $url; ?>"><?= (int) ($session->busy / 3600); ?>h <?= ((int) ($session->busy / 60) % 60); ?>m</a>
<?php <?php
$busy['week'] += $session->getBusy(); $busy['week'] += $session->busy;
if ($session->start->getTimestamp() < $startWeek->getTimestamp() if ($session->start->getTimestamp() < $startWeek->getTimestamp()
|| $count === $sessionCount || $count === $sessionCount
) : ?> ) : ?>
@ -238,7 +238,7 @@ echo $this->data['nav']->render(); ?>
endif; endif;
?> ?>
<?php <?php
$busy['month'] += $session->getBusy(); $busy['month'] += $session->busy;
if ($session->start->getTimestamp() < $startMonth->getTimestamp() if ($session->start->getTimestamp() < $startMonth->getTimestamp()
|| $count === $sessionCount || $count === $sessionCount
) : ?> ) : ?>

View File

@ -99,7 +99,7 @@ echo $this->data['nav']->render(); ?>
<td><?= $session->getStart()->format('H:i'); ?> <td><?= $session->getStart()->format('H:i'); ?>
<td><?= (int) ($session->getBreak() / 3600); ?>h <?= ((int) ($session->getBreak() / 60) % 60); ?>m <td><?= (int) ($session->getBreak() / 3600); ?>h <?= ((int) ($session->getBreak() / 60) % 60); ?>m
<td><?= $session->getEnd() !== null ? $session->getEnd()->format('H:i') : ''; ?> <td><?= $session->getEnd() !== null ? $session->getEnd()->format('H:i') : ''; ?>
<td><?= (int) ($session->getBusy() / 3600); ?>h <?= ((int) ($session->getBusy() / 60) % 60); ?>m <td><?= (int) ($session->busy / 3600); ?>h <?= ((int) ($session->busy / 60) % 60); ?>m
<?php endforeach; ?> <?php endforeach; ?>
</table> </table>
</div> </div>

View File

@ -39,7 +39,7 @@ final class SessionTest extends \PHPUnit\Framework\TestCase
public function testDefault() : void public function testDefault() : void
{ {
self::assertEquals(0, $this->session->id); self::assertEquals(0, $this->session->id);
self::assertEquals(0, $this->session->getBusy()); self::assertEquals(0, $this->session->busy);
self::assertEquals(0, $this->session->getBreak()); self::assertEquals(0, $this->session->getBreak());
self::assertEquals([], $this->session->getSessionElements()); self::assertEquals([], $this->session->getSessionElements());
self::assertEquals(ClockingType::OFFICE, $this->session->type); self::assertEquals(ClockingType::OFFICE, $this->session->type);
@ -104,7 +104,7 @@ final class SessionTest extends \PHPUnit\Framework\TestCase
$this->session->addSessionElement($element); $this->session->addSessionElement($element);
self::assertEquals(2 * 60 * 60, $this->session->getBreak()); self::assertEquals(2 * 60 * 60, $this->session->getBreak());
self::assertEquals(7 * 60 * 60, $this->session->getBusy()); self::assertEquals(7 * 60 * 60, $this->session->busy);
} }
#[\PHPUnit\Framework\Attributes\Group('module')] #[\PHPUnit\Framework\Attributes\Group('module')]