Compare commits
20
Commits
63ded6da57
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcce40113d | ||
|
|
0e1244c02f | ||
|
|
b4d30066b9 | ||
|
|
ab9bccf532 | ||
|
|
4acf1275a0 | ||
|
|
6fb8f87c1c | ||
|
|
1d53db71e7 | ||
|
|
dbca8ad570 | ||
|
|
4d91a04430 | ||
|
|
2dcbb6fdbd | ||
|
|
9f53b627c0 | ||
|
|
532c0d60e8 | ||
|
|
cf6eff6ab0 | ||
|
|
a964663f51 | ||
|
|
a9c455381d | ||
|
|
7b5ce23342 | ||
|
|
a3b123f466 | ||
|
|
d52a9d77ab | ||
|
|
a09edcae25 | ||
|
|
91849375c7 |
@@ -112,3 +112,4 @@ Temporary Items
|
|||||||
/ui/static/print.css*
|
/ui/static/print.css*
|
||||||
/ui/static/global.css*
|
/ui/static/global.css*
|
||||||
/ui/static/bundle.css*
|
/ui/static/bundle.css*
|
||||||
|
/ui/.svelte-kit/
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"title": "Zeiterfassung App",
|
"title": "Zeiterfassung App",
|
||||||
"contact": {
|
"contact": {
|
||||||
"name": "Torsten Lücke",
|
"name": "Torsten Lücke",
|
||||||
"url": "http://torsten-hettstedt.de",
|
"url": "https://torsten-hettstedt.de",
|
||||||
"email": "arbeit@torsten-hettstedt.de"
|
"email": "arbeit@torsten-hettstedt.de"
|
||||||
},
|
},
|
||||||
"license": {
|
"license": {
|
||||||
@@ -188,6 +188,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "[0-5]\\d:[0-5]\\d:[0-5]\\d",
|
"pattern": "[0-5]\\d:[0-5]\\d:[0-5]\\d",
|
||||||
"example": "08:01:00"
|
"example": "08:01:00"
|
||||||
|
},
|
||||||
|
"isHomeOffice": {
|
||||||
|
"description": "Wurde an diesem Tag zuhause gearbeitet?",
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -217,6 +221,10 @@
|
|||||||
"description": "Arbeitstage im Zeitraum",
|
"description": "Arbeitstage im Zeitraum",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"homeOfficeDays": {
|
||||||
|
"description": "Anzahl der Tage, an denen zuhause gearbeitet wurde.",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"overtime": {
|
"overtime": {
|
||||||
"description": "Überstunden im Zeitraum",
|
"description": "Überstunden im Zeitraum",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -21,12 +21,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
* @throws HttpNotFoundException
|
* @throws HttpNotFoundException
|
||||||
*/
|
*/
|
||||||
@@ -34,7 +28,7 @@ class WorkingHoursController extends AbstractController
|
|||||||
{
|
{
|
||||||
$body = $request->getParsedBody();
|
$body = $request->getParsedBody();
|
||||||
$repository = new WorkingHoursRepository($this->databases);
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
$model = $this->buildModel($request, $args['id'], $body['workingTime']);
|
$model = $this->buildModel($request, $args['id'], $body['workingTime'], $body['isHomeOffice'] ?? false);
|
||||||
try {
|
try {
|
||||||
$repository->update($model);
|
$repository->update($model);
|
||||||
} catch (RepositoryRecordNotFoundException $exception) {
|
} catch (RepositoryRecordNotFoundException $exception) {
|
||||||
@@ -44,12 +38,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
* @noinspection PhpUnusedParameterInspection
|
* @noinspection PhpUnusedParameterInspection
|
||||||
@@ -79,12 +67,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpNotFoundException
|
* @throws HttpNotFoundException
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
*/
|
*/
|
||||||
@@ -105,12 +87,6 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param Response $response
|
|
||||||
* @param array<string, mixed> $args
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
*
|
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
* @throws HttpConflictRequestException
|
* @throws HttpConflictRequestException
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
@@ -120,7 +96,12 @@ class WorkingHoursController extends AbstractController
|
|||||||
{
|
{
|
||||||
$body = $request->getParsedBody();
|
$body = $request->getParsedBody();
|
||||||
$repository = new WorkingHoursRepository($this->databases);
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
$model = $this->buildModel($request, $body['workingDay'], $body['workingTime']);
|
$model = $this->buildModel(
|
||||||
|
$request,
|
||||||
|
$body['workingDay'],
|
||||||
|
$body['workingTime'],
|
||||||
|
$body['isHomeOffice'] ?? false
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
} catch (RepositoryRecordAlreadyExistException $exception) {
|
} catch (RepositoryRecordAlreadyExistException $exception) {
|
||||||
@@ -136,14 +117,9 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
|
||||||
* @param string $date
|
|
||||||
* @param string $time
|
|
||||||
*
|
|
||||||
* @return WorkingHours
|
|
||||||
* @throws HttpBadRequestException
|
* @throws HttpBadRequestException
|
||||||
*/
|
*/
|
||||||
protected function buildModel(Request $request, string $date, string $time): WorkingHours
|
protected function buildModel(Request $request, string $date, string $time, bool $isHomeOffice): WorkingHours
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$workingDay = new DateTime($date);
|
$workingDay = new DateTime($date);
|
||||||
@@ -155,7 +131,10 @@ class WorkingHoursController extends AbstractController
|
|||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpBadRequestException($request, 'Der Wert für die Zeit ist falsch', $exception);
|
throw new HttpBadRequestException($request, 'Der Wert für die Zeit ist falsch', $exception);
|
||||||
}
|
}
|
||||||
return (new WorkingHours())->setWorkingDay($workingDay)->setWorkingTime($workingTime);
|
return (new WorkingHours())
|
||||||
|
->setWorkingDay($workingDay)
|
||||||
|
->setWorkingTime($workingTime)
|
||||||
|
->setIsHomeOffice($isHomeOffice);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ class WorkingHours implements ModelInterface
|
|||||||
{
|
{
|
||||||
protected ?DateTime $workingDay = null;
|
protected ?DateTime $workingDay = null;
|
||||||
protected ?DateInterval $workingTime = null;
|
protected ?DateInterval $workingTime = null;
|
||||||
|
protected bool $isHomeOffice = false;
|
||||||
|
|
||||||
public function getWorkingDay(): ?DateTime
|
public function getWorkingDay(): ?DateTime
|
||||||
{
|
{
|
||||||
@@ -35,15 +36,27 @@ class WorkingHours implements ModelInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isHomeOffice(): bool
|
||||||
|
{
|
||||||
|
return $this->isHomeOffice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsHomeOffice(bool $isHomeOffice): WorkingHours
|
||||||
|
{
|
||||||
|
$this->isHomeOffice = $isHomeOffice;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, string>
|
* @return array<string, string>
|
||||||
*/
|
*/
|
||||||
#[ArrayShape(['workingDay' => "string", 'workingTime' => "string"])]
|
#[ArrayShape(['workingDay' => "string", 'workingTime' => "string", 'isHomeOffice' => "boolean"])]
|
||||||
public function jsonSerialize(): array
|
public function jsonSerialize(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'workingDay' => $this->getWorkingDay()->format('Y-m-d'),
|
'workingDay' => $this->getWorkingDay()->format('Y-m-d'),
|
||||||
'workingTime' => $this->getWorkingTime()->format('%H:%I:%S')
|
'workingTime' => $this->getWorkingTime()->format('%H:%I:%S'),
|
||||||
|
'isHomeOffice' => $this->isHomeOffice(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,24 +10,15 @@ use JetBrains\PhpStorm\ArrayShape;
|
|||||||
|
|
||||||
class WorkingHoursView implements ModelInterface
|
class WorkingHoursView implements ModelInterface
|
||||||
{
|
{
|
||||||
protected DateTimeInterface $period;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
protected int $workingDays;
|
|
||||||
protected DateInterval $totalHours;
|
|
||||||
protected DateInterval $overtime;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
DateTimeInterface $period,
|
protected DateTimeInterface $period,
|
||||||
PeriodDesignationEnum $periodDesignation,
|
protected PeriodDesignationEnum $periodDesignation,
|
||||||
int $workingDays,
|
protected int $workingDays,
|
||||||
DateInterval $totalHours,
|
protected DateInterval $totalHours,
|
||||||
DateInterval $overtime
|
private int $homeOfficeDays,
|
||||||
|
protected DateInterval $overtime
|
||||||
) {
|
) {
|
||||||
$this->period = $period;
|
|
||||||
$this->periodDesignation = $periodDesignation;
|
|
||||||
$this->workingDays = $workingDays;
|
|
||||||
$this->totalHours = $totalHours;
|
|
||||||
$this->overtime = $overtime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPeriod(): DateTimeInterface
|
public function getPeriod(): DateTimeInterface
|
||||||
@@ -45,6 +36,11 @@ class WorkingHoursView implements ModelInterface
|
|||||||
return $this->workingDays;
|
return $this->workingDays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHomeOfficeDays(): int
|
||||||
|
{
|
||||||
|
return $this->homeOfficeDays;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTotalHours(): DateInterval
|
public function getTotalHours(): DateInterval
|
||||||
{
|
{
|
||||||
return $this->totalHours;
|
return $this->totalHours;
|
||||||
@@ -55,16 +51,11 @@ class WorkingHoursView implements ModelInterface
|
|||||||
return $this->overtime;
|
return $this->overtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[ArrayShape(['period' => "string",
|
||||||
* @inheritDoc
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
#[ArrayShape([
|
|
||||||
'period' => "string",
|
|
||||||
'periodDesignation' => "string",
|
'periodDesignation' => "string",
|
||||||
'totalHours' => "string",
|
'totalHours' => "string",
|
||||||
'workingDays' => "int",
|
'workingDays' => "int",
|
||||||
|
'homeOfficeDays' => "int",
|
||||||
'overtime' => "string"
|
'overtime' => "string"
|
||||||
])] public function jsonSerialize(): array
|
])] public function jsonSerialize(): array
|
||||||
{
|
{
|
||||||
@@ -74,6 +65,7 @@ class WorkingHoursView implements ModelInterface
|
|||||||
'periodDesignation' => strtolower($this->getPeriodDesignation()->name),
|
'periodDesignation' => strtolower($this->getPeriodDesignation()->name),
|
||||||
'workingDays' => $this->getWorkingDays(),
|
'workingDays' => $this->getWorkingDays(),
|
||||||
'totalHours' => $this->getTotalHours()->format('%H:%I:%S'),
|
'totalHours' => $this->getTotalHours()->format('%H:%I:%S'),
|
||||||
|
'homeOfficeDays' => $this->getHomeOfficeDays(),
|
||||||
'overtime' => $this->getOvertime()->format($formatOvertime)
|
'overtime' => $this->getOvertime()->format($formatOvertime)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
|||||||
abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInterface
|
abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = '';
|
protected const string SQL_SELECT = '';
|
||||||
protected const SQL_WHERE = '';
|
protected const string SQL_WHERE = '';
|
||||||
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
protected PeriodDesignationEnum $periodDesignation;
|
||||||
|
|
||||||
@@ -51,6 +51,7 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
$this->periodDesignation,
|
$this->periodDesignation,
|
||||||
(int) $row['workingDays'],
|
(int) $row['workingDays'],
|
||||||
$this->buildDateInterval($row['totalHours']),
|
$this->buildDateInterval($row['totalHours']),
|
||||||
|
(int) $row['homeOfficeDays'],
|
||||||
$this->buildDateInterval($row['overtime'])
|
$this->buildDateInterval($row['overtime'])
|
||||||
);
|
);
|
||||||
$models[] = $model;
|
$models[] = $model;
|
||||||
@@ -82,6 +83,7 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
$this->periodDesignation,
|
$this->periodDesignation,
|
||||||
(int)$row['workingDays'],
|
(int)$row['workingDays'],
|
||||||
$this->buildDateInterval($row['totalHours']),
|
$this->buildDateInterval($row['totalHours']),
|
||||||
|
(int)$row['homeOfficeDays'],
|
||||||
$this->buildDateInterval($row['overtime'])
|
$this->buildDateInterval($row['overtime'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,16 @@ use TorstenHettstedt\TimekeepingApi\Models\PeriodDesignationEnum;
|
|||||||
class WorkingHoursMonthlyViewRepository extends AbstractWorkingHoursViewRepository
|
class WorkingHoursMonthlyViewRepository extends AbstractWorkingHoursViewRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = <<<SQL
|
protected const string SQL_SELECT = <<<SQL
|
||||||
select
|
select
|
||||||
"Monat" as "period",
|
"Monat" as "period",
|
||||||
"Gesamtarbeitszeit" as "totalHours",
|
"Gesamtarbeitszeit" as "totalHours",
|
||||||
"Arbeitstage" as "workingDays",
|
"Arbeitstage" as "workingDays",
|
||||||
|
"Home-Office-Tage" as "homeOfficeDays",
|
||||||
"Überstunden" as "overtime"
|
"Überstunden" as "overtime"
|
||||||
from "Arbeitszeiten - Monat"
|
from "Arbeitszeiten - Monat"
|
||||||
SQL;
|
SQL;
|
||||||
protected const SQL_WHERE = ' where "Monat" = ?';
|
protected const string SQL_WHERE = ' where "Monat" = ?';
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
|||||||
*/
|
*/
|
||||||
class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWriterInterface
|
class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWriterInterface
|
||||||
{
|
{
|
||||||
|
protected const string SQL_SELECT = <<<'SQL'
|
||||||
|
select
|
||||||
|
"Datum" as "workingDay",
|
||||||
|
"Arbeitszeit" as "workingTime",
|
||||||
|
"HomeOffice" as "isHomeOfficeDay"
|
||||||
|
from public."Arbeitszeiten"
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(protected PDO $database)
|
public function __construct(protected PDO $database)
|
||||||
{
|
{
|
||||||
@@ -66,7 +74,8 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
$model = new WorkingHours();
|
$model = new WorkingHours();
|
||||||
$model
|
$model
|
||||||
->setWorkingDay(new DateTime($row['workingDay']))
|
->setWorkingDay(new DateTime($row['workingDay']))
|
||||||
->setWorkingTime(new DateInterval('P0000-00-00T' . $row['workingTime']));
|
->setWorkingTime(new DateInterval('P0000-00-00T' . $row['workingTime']))
|
||||||
|
->setIsHomeOffice($row['isHomeOfficeDay']);
|
||||||
$models[] = $model;
|
$models[] = $model;
|
||||||
}
|
}
|
||||||
return $models;
|
return $models;
|
||||||
@@ -88,7 +97,7 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
|
|
||||||
protected function buildFindFilteredStatement(?string $start, ?string $end): ?PDOStatement
|
protected function buildFindFilteredStatement(?string $start, ?string $end): ?PDOStatement
|
||||||
{
|
{
|
||||||
$query = 'select "Datum" as "workingDay", "Arbeitszeit" as "workingTime" from public."Arbeitszeiten" ';
|
$query = static::SQL_SELECT . ' ';
|
||||||
$query .= $this->buildFilterString($start, $end);
|
$query .= $this->buildFilterString($start, $end);
|
||||||
$query .= 'order by "Datum"';
|
$query .= 'order by "Datum"';
|
||||||
$stmt = $this->database->prepare($query);
|
$stmt = $this->database->prepare($query);
|
||||||
@@ -113,9 +122,7 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
*/
|
*/
|
||||||
public function findByKey(mixed $primary_key): WorkingHours
|
public function findByKey(mixed $primary_key): WorkingHours
|
||||||
{
|
{
|
||||||
$stmt = $this->database->prepare(
|
$stmt = $this->database->prepare(static::SQL_SELECT . ' where "Datum" = ?');
|
||||||
'select "Datum" as "workingDay", "Arbeitszeit" as "workingTime" from public."Arbeitszeiten" where "Datum" = ?'
|
|
||||||
);
|
|
||||||
$stmt->bindParam(1, $primary_key);
|
$stmt->bindParam(1, $primary_key);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$row = $stmt->fetch();
|
$row = $stmt->fetch();
|
||||||
@@ -125,7 +132,8 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
$model = new WorkingHours();
|
$model = new WorkingHours();
|
||||||
$model
|
$model
|
||||||
->setWorkingDay(new DateTime($row['workingDay']))
|
->setWorkingDay(new DateTime($row['workingDay']))
|
||||||
->setWorkingTime(new DateInterval('P0000-00-00T' . $row['workingTime']));
|
->setWorkingTime(new DateInterval('P0000-00-00T' . $row['workingTime']))
|
||||||
|
->setIsHomeOffice($row['isHomeOfficeDay']);
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +148,7 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
if ($model instanceof WorkingHours) {
|
if ($model instanceof WorkingHours) {
|
||||||
$workingDay = $model->getWorkingDay()->format('Y-m-d');
|
$workingDay = $model->getWorkingDay()->format('Y-m-d');
|
||||||
$workingTime = $model->getWorkingTime()->format('%H:%I:%S');
|
$workingTime = $model->getWorkingTime()->format('%H:%I:%S');
|
||||||
|
$isHomeOffice = $model->isHomeOffice();
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidArgumentException('Es wird ein Modell der Klasse "' . WorkingHours::class . '" verlangt.');
|
throw new InvalidArgumentException('Es wird ein Modell der Klasse "' . WorkingHours::class . '" verlangt.');
|
||||||
}
|
}
|
||||||
@@ -149,11 +158,15 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
throw new RepositoryRecordAlreadyExistException();
|
throw new RepositoryRecordAlreadyExistException();
|
||||||
} /** @noinspection PhpUnusedLocalVariableInspection */
|
} /** @noinspection PhpUnusedLocalVariableInspection */
|
||||||
catch (RepositoryRecordNotFoundException $exception) {
|
catch (RepositoryRecordNotFoundException $exception) {
|
||||||
$stmt = $this->database->prepare(
|
$stmt = $this->database->prepare(<<<'SQL'
|
||||||
'insert into public."Arbeitszeiten" ("Datum", "Arbeitszeit") values (?, ?) on conflict do nothing'
|
insert into public."Arbeitszeiten" ("Datum", "Arbeitszeit", "HomeOffice")
|
||||||
|
values (?, ?, ?)
|
||||||
|
on conflict do nothing
|
||||||
|
SQL
|
||||||
);
|
);
|
||||||
$stmt->bindParam(1, $workingDay);
|
$stmt->bindParam(1, $workingDay);
|
||||||
$stmt->bindParam(2, $workingTime);
|
$stmt->bindParam(2, $workingTime);
|
||||||
|
$stmt->bindParam(3, $isHomeOffice, PDO::PARAM_BOOL);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,12 +181,19 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
if ($model instanceof WorkingHours) {
|
if ($model instanceof WorkingHours) {
|
||||||
$workingDay = $model->getWorkingDay()->format('Y-m-d');
|
$workingDay = $model->getWorkingDay()->format('Y-m-d');
|
||||||
$workingTime = $model->getWorkingTime()->format('%H:%I:%S');
|
$workingTime = $model->getWorkingTime()->format('%H:%I:%S');
|
||||||
|
$isHomeOffice = $model->isHomeOffice();
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidArgumentException('Es wird ein Modell der Klasse "' . WorkingHours::class . '" verlangt.');
|
throw new InvalidArgumentException('Es wird ein Modell der Klasse "' . WorkingHours::class . '" verlangt.');
|
||||||
}
|
}
|
||||||
$stmt = $this->database->prepare('update public."Arbeitszeiten" set "Arbeitszeit" = ? where "Datum" = ?');
|
$stmt = $this->database->prepare(<<<'SQL'
|
||||||
|
update public."Arbeitszeiten"
|
||||||
|
set "Arbeitszeit" = ?, "HomeOffice" = ?
|
||||||
|
where "Datum" = ?
|
||||||
|
SQL
|
||||||
|
);
|
||||||
$stmt->bindParam(1, $workingTime);
|
$stmt->bindParam(1, $workingTime);
|
||||||
$stmt->bindParam(2, $workingDay);
|
$stmt->bindParam(2, $isHomeOffice, PDO::PARAM_BOOL);
|
||||||
|
$stmt->bindParam(3, $workingDay);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
if ($stmt->rowCount() < 1) {
|
if ($stmt->rowCount() < 1) {
|
||||||
throw new RepositoryRecordNotFoundException();
|
throw new RepositoryRecordNotFoundException();
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ use TorstenHettstedt\TimekeepingApi\Models\PeriodDesignationEnum;
|
|||||||
class WorkingHoursWeeklyViewRepository extends AbstractWorkingHoursViewRepository
|
class WorkingHoursWeeklyViewRepository extends AbstractWorkingHoursViewRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = <<<SQL
|
protected const string SQL_SELECT = <<<SQL
|
||||||
select
|
select
|
||||||
"Woche" as "period",
|
"Woche" as "period",
|
||||||
"Gesamtarbeitszeit" as "totalHours",
|
"Gesamtarbeitszeit" as "totalHours",
|
||||||
"Arbeitstage" as "workingDays",
|
"Arbeitstage" as "workingDays",
|
||||||
|
"Home-Office-Tage" as "homeOfficeDays",
|
||||||
"Überstunden" as "overtime"
|
"Überstunden" as "overtime"
|
||||||
from "Arbeitszeiten - Woche"
|
from "Arbeitszeiten - Woche"
|
||||||
SQL;
|
SQL;
|
||||||
protected const SQL_WHERE = ' where "Woche" = ?';
|
protected const string SQL_WHERE = ' where "Woche" = ?';
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ use TorstenHettstedt\TimekeepingApi\Models\PeriodDesignationEnum;
|
|||||||
class WorkingHoursYearlyViewRepository extends AbstractWorkingHoursViewRepository
|
class WorkingHoursYearlyViewRepository extends AbstractWorkingHoursViewRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = <<<SQL
|
protected const string SQL_SELECT = <<<SQL
|
||||||
select
|
select
|
||||||
"Jahr" as "period",
|
"Jahr" as "period",
|
||||||
"Gesamtarbeitszeit" as "totalHours",
|
"Gesamtarbeitszeit" as "totalHours",
|
||||||
"Arbeitstage" as "workingDays",
|
"Arbeitstage" as "workingDays",
|
||||||
|
"Home-Office-Tage" as "homeOfficeDays",
|
||||||
"Überstunden" as "overtime"
|
"Überstunden" as "overtime"
|
||||||
from "Arbeitszeiten - Jahr"
|
from "Arbeitszeiten - Jahr"
|
||||||
SQL;
|
SQL;
|
||||||
protected const SQL_WHERE = ' where "Jahr" = ?';
|
protected const string SQL_WHERE = ' where "Jahr" = ?';
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
|
|||||||
+74
-57
@@ -22,14 +22,17 @@ DROP TABLE IF EXISTS public."Arbeitszeiten";
|
|||||||
-- Name: Arbeitszeiten; Type: TABLE; Schema: public; Owner: torsten
|
-- Name: Arbeitszeiten; Type: TABLE; Schema: public; Owner: torsten
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE TABLE public."Arbeitszeiten" (
|
CREATE TABLE public."Arbeitszeiten"
|
||||||
|
(
|
||||||
"Datum" date not null,
|
"Datum" date not null,
|
||||||
"Arbeitszeit" interval(6) null,
|
"Arbeitszeit" interval(6) null,
|
||||||
|
"HomeOffice" boolean default FALSE not null,
|
||||||
constraint "Arbeitszeiten_pkey" primary key ("Datum")
|
constraint "Arbeitszeiten_pkey" primary key ("Datum")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public."Arbeitszeiten" OWNER TO bruce;
|
ALTER TABLE public."Arbeitszeiten"
|
||||||
|
OWNER TO bruce;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: Arbeitszeiten - Jahr; Type: VIEW; Schema: public; Owner: torsten
|
-- Name: Arbeitszeiten - Jahr; Type: VIEW; Schema: public; Owner: torsten
|
||||||
@@ -37,54 +40,65 @@ ALTER TABLE public."Arbeitszeiten" OWNER TO bruce;
|
|||||||
|
|
||||||
CREATE OR REPLACE VIEW public."Arbeitszeiten - Jahr" AS
|
CREATE OR REPLACE VIEW public."Arbeitszeiten - Jahr" AS
|
||||||
SELECT (date_part('year'::text, "Arbeitszeiten"."Datum"))::text AS "Jahr",
|
SELECT (date_part('year'::text, "Arbeitszeiten"."Datum"))::text AS "Jahr",
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") AS "Gesamtarbeitszeit",
|
sum("Arbeitszeiten"."Arbeitszeit")::interval AS "Gesamtarbeitszeit",
|
||||||
count("Arbeitszeiten"."Datum") AS "Arbeitstage",
|
count("Arbeitszeiten"."Datum")::bigint AS "Arbeitstage",
|
||||||
(sum("Arbeitszeiten"."Arbeitszeit") - ((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden"
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
FROM public."Arbeitszeiten"
|
FROM public."Arbeitszeiten"
|
||||||
GROUP BY (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
GROUP BY (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
||||||
ORDER BY (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
ORDER BY (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public."Arbeitszeiten - Jahr" OWNER TO bruce;
|
ALTER TABLE public."Arbeitszeiten - Jahr"
|
||||||
|
OWNER TO bruce;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: Arbeitszeiten - Monat; Type: VIEW; Schema: public; Owner: torsten
|
-- Name: Arbeitszeiten - Monat; Type: VIEW; Schema: public; Owner: torsten
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW public."Arbeitszeiten - Monat" AS
|
CREATE OR REPLACE VIEW public."Arbeitszeiten - Monat" AS
|
||||||
SELECT to_char(date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone), 'TMYYYY TMMonth'::text) AS "Monat",
|
SELECT to_char(date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone),
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") AS "Gesamtarbeitszeit",
|
'TMYYYY TMMonth'::text) AS "Monat",
|
||||||
count("Arbeitszeiten"."Datum") AS "Arbeitstage",
|
sum("Arbeitszeiten"."Arbeitszeit")::interval AS "Gesamtarbeitszeit",
|
||||||
(sum("Arbeitszeiten"."Arbeitszeit") - ((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden"
|
count("Arbeitszeiten"."Datum")::bigint AS "Arbeitstage",
|
||||||
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
FROM public."Arbeitszeiten"
|
FROM public."Arbeitszeiten"
|
||||||
GROUP BY (date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone))
|
GROUP BY (date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone))
|
||||||
ORDER BY (date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone));
|
ORDER BY (date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone));
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public."Arbeitszeiten - Monat" OWNER TO bruce;
|
ALTER TABLE public."Arbeitszeiten - Monat"
|
||||||
|
OWNER TO bruce;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: Arbeitszeiten - Woche; Type: VIEW; Schema: public; Owner: torsten
|
-- Name: Arbeitszeiten - Woche; Type: VIEW; Schema: public; Owner: torsten
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE OR REPLACE VIEW public."Arbeitszeiten - Woche" AS
|
CREATE OR REPLACE VIEW public."Arbeitszeiten - Woche" AS
|
||||||
SELECT to_char(date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone), 'TMYYYY#WW'::text) AS "Woche",
|
SELECT to_char(date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone),
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") AS "Gesamtarbeitszeit",
|
'TMYYYY#WW'::text) AS "Woche",
|
||||||
count("Arbeitszeiten"."Datum") AS "Arbeitstage",
|
sum("Arbeitszeiten"."Arbeitszeit")::interval AS "Gesamtarbeitszeit",
|
||||||
(sum("Arbeitszeiten"."Arbeitszeit") - ((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden"
|
count("Arbeitszeiten"."Datum")::bigint AS "Arbeitstage",
|
||||||
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
FROM public."Arbeitszeiten"
|
FROM public."Arbeitszeiten"
|
||||||
GROUP BY (date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone))
|
GROUP BY (date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone))
|
||||||
ORDER BY (date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone));
|
ORDER BY (date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone));
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public."Arbeitszeiten - Woche" OWNER TO bruce;
|
ALTER TABLE public."Arbeitszeiten - Woche"
|
||||||
|
OWNER TO bruce;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: Arbeitszeiten; Type: TABLE DATA; Schema: public; Owner: torsten
|
-- Data for Name: Arbeitszeiten; Type: TABLE DATA; Schema: public; Owner: torsten
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit")
|
||||||
('2020-01-07','08:06:00'),
|
VALUES ('2020-01-07', '08:06:00'),
|
||||||
('2020-01-08', '08:16:00'),
|
('2020-01-08', '08:16:00'),
|
||||||
('2020-01-09', '07:49:00'),
|
('2020-01-09', '07:49:00'),
|
||||||
('2020-01-10', '07:30:00'),
|
('2020-01-10', '07:30:00'),
|
||||||
@@ -106,43 +120,46 @@ INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES
|
|||||||
('2020-02-03', '07:56:00'),
|
('2020-02-03', '07:56:00'),
|
||||||
('2020-02-04', '08:05:00'),
|
('2020-02-04', '08:05:00'),
|
||||||
('2020-02-05', '07:58:00'),
|
('2020-02-05', '07:58:00'),
|
||||||
('2020-02-06','08:01:00'),
|
('2020-02-06', '08:01:00')
|
||||||
('2020-02-07','07:59:00'),
|
;
|
||||||
('2020-02-10','07:50:00'),
|
|
||||||
('2020-02-11','08:01:00'),
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit", "HomeOffice")
|
||||||
('2020-02-12','08:14:00'),
|
VALUES ('2020-02-07', '07:59:00', FALSE),
|
||||||
('2020-02-13','08:12:00'),
|
('2020-02-10', '07:50:00', FALSE),
|
||||||
('2020-02-14','07:51:00'),
|
('2020-02-11', '08:01:00', FALSE),
|
||||||
('2020-02-17','07:59:00'),
|
('2020-02-12', '08:14:00', FALSE),
|
||||||
('2020-02-18','08:05:00'),
|
('2020-02-13', '08:12:00', FALSE),
|
||||||
('2020-02-19','07:34:00'),
|
('2020-02-14', '07:51:00', FALSE),
|
||||||
('2020-02-20','07:33:00'),
|
('2020-02-17', '07:59:00', FALSE),
|
||||||
('2020-02-21','07:56:00'),
|
('2020-02-18', '08:05:00', FALSE),
|
||||||
('2020-02-24','08:02:00'),
|
('2020-02-19', '07:34:00', FALSE),
|
||||||
('2020-02-25','08:13:00'),
|
('2020-02-20', '07:33:00', FALSE),
|
||||||
('2020-02-26','08:42:00'),
|
('2020-02-21', '07:56:00', FALSE),
|
||||||
('2020-02-27','07:49:00'),
|
('2020-02-24', '08:02:00', FALSE),
|
||||||
('2020-02-28','08:16:00'),
|
('2020-02-25', '08:13:00', FALSE),
|
||||||
('2020-03-02','08:22:00'),
|
('2020-02-26', '08:42:00', FALSE),
|
||||||
('2020-03-03','08:20:00'),
|
('2020-02-27', '07:49:00', TRUE),
|
||||||
('2020-03-04','08:12:00'),
|
('2020-02-28', '08:16:00', FALSE),
|
||||||
('2020-03-05','08:32:00'),
|
('2020-03-02', '08:22:00', FALSE),
|
||||||
('2020-03-06','04:41:00'),
|
('2020-03-03', '08:20:00', FALSE),
|
||||||
('2020-03-09','07:05:00'),
|
('2020-03-04', '08:12:00', FALSE),
|
||||||
('2020-03-10','07:34:00'),
|
('2020-03-05', '08:32:00', FALSE),
|
||||||
('2020-03-11','07:39:00'),
|
('2020-03-06', '04:41:00', FALSE),
|
||||||
('2020-03-12','07:50:00'),
|
('2020-03-09', '07:05:00', FALSE),
|
||||||
('2020-03-13','08:26:00'),
|
('2020-03-10', '07:34:00', FALSE),
|
||||||
('2020-03-16','07:51:00'),
|
('2020-03-11', '07:39:00', TRUE),
|
||||||
('2020-03-17','07:50:00'),
|
('2020-03-12', '07:50:00', FALSE),
|
||||||
('2020-03-18','07:19:00'),
|
('2020-03-13', '08:26:00', FALSE),
|
||||||
('2020-03-19','07:55:00'),
|
('2020-03-16', '07:51:00', FALSE),
|
||||||
('2020-03-20','05:43:00'),
|
('2020-03-17', '07:50:00', FALSE),
|
||||||
('2020-03-23','08:05:00'),
|
('2020-03-18', '07:19:00', FALSE),
|
||||||
('2020-03-24','08:21:00'),
|
('2020-03-19', '07:55:00', FALSE),
|
||||||
('2020-03-25','08:10:00'),
|
('2020-03-20', '05:43:00', TRUE),
|
||||||
('2020-03-26','10:31:00'),
|
('2020-03-23', '08:05:00', FALSE),
|
||||||
('2020-03-27','04:55:00'),
|
('2020-03-24', '08:21:00', FALSE),
|
||||||
('2020-03-30','08:21:00'),
|
('2020-03-25', '08:10:00', FALSE),
|
||||||
('2020-03-31','08:01:00')
|
('2020-03-26', '10:31:00', FALSE),
|
||||||
|
('2020-03-27', '04:55:00', FALSE),
|
||||||
|
('2020-03-30', '08:21:00', FALSE),
|
||||||
|
('2020-03-31', '08:01:00', FALSE)
|
||||||
;
|
;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Helper;
|
namespace Helper;
|
||||||
|
|
||||||
// here you can define custom actions
|
// here you can define custom actions
|
||||||
@@ -15,6 +16,7 @@ class Api extends Module
|
|||||||
const WORKING_HOURS_JSON_FORMAT = [
|
const WORKING_HOURS_JSON_FORMAT = [
|
||||||
'workingDay' => self::FORMAT_DATE,
|
'workingDay' => self::FORMAT_DATE,
|
||||||
'workingTime' => self::FORMAT_TIME,
|
'workingTime' => self::FORMAT_TIME,
|
||||||
|
'isHomeOffice' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
const WORKING_HOURS_VIEW_JSON_FORMAT = [
|
const WORKING_HOURS_VIEW_JSON_FORMAT = [
|
||||||
@@ -22,6 +24,7 @@ class Api extends Module
|
|||||||
"periodDesignation" => "string",
|
"periodDesignation" => "string",
|
||||||
"totalHours" => self::FORMAT_TIME,
|
"totalHours" => self::FORMAT_TIME,
|
||||||
"workingDays" => 'integer:>0',
|
"workingDays" => 'integer:>0',
|
||||||
|
'homeOfficeDays' => 'integer:>=0',
|
||||||
"overtime" => self::FORMAT_TIME,
|
"overtime" => self::FORMAT_TIME,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ modules:
|
|||||||
enabled:
|
enabled:
|
||||||
- \Helper\Api
|
- \Helper\Api
|
||||||
- REST:
|
- REST:
|
||||||
url: http://localhost:8091/
|
url: http://api:80/
|
||||||
depends: PhpBrowser
|
depends: PhpBrowser
|
||||||
part: Json
|
part: Json
|
||||||
@@ -21,6 +21,22 @@ class CreateWorkingHoursCest
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function createWorkingHoursWithNewValidRecord(ApiTester $I): void
|
public function createWorkingHoursWithNewValidRecord(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendPost('/working-hours', [
|
||||||
|
'workingDay' => '2020-04-01',
|
||||||
|
'workingTime' => '08:00:00',
|
||||||
|
'isHomeOffice' => false,
|
||||||
|
]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::CREATED);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createWorkingHoursWithNewValidRecordWithoutHomeOffice(ApiTester $I): void
|
||||||
{
|
{
|
||||||
$I->haveHttpHeader('accept', 'application/json');
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
$I->haveHttpHeader('content-type', 'application/json');
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
@@ -44,6 +60,7 @@ class CreateWorkingHoursCest
|
|||||||
$I->sendPost('/working-hours', [
|
$I->sendPost('/working-hours', [
|
||||||
'workingDay' => '2020-04-01',
|
'workingDay' => '2020-04-01',
|
||||||
'workingTime' => 8.0,
|
'workingTime' => 8.0,
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
||||||
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
@@ -60,6 +77,7 @@ class CreateWorkingHoursCest
|
|||||||
$I->sendPost('/working-hours', [
|
$I->sendPost('/working-hours', [
|
||||||
'workingDay' => '2020-01-15',
|
'workingDay' => '2020-01-15',
|
||||||
'workingTime' => '08:00:00',
|
'workingTime' => '08:00:00',
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$I->seeResponseCodeIs(HttpCode::CONFLICT);
|
$I->seeResponseCodeIs(HttpCode::CONFLICT);
|
||||||
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php /** @noinspection PhpUnused */
|
<?php
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ class UpdateWorkingHoursCest
|
|||||||
$I->sendPut('/working-hours/2020-01-15', [
|
$I->sendPut('/working-hours/2020-01-15', [
|
||||||
'workingDay' => '2020-01-15',
|
'workingDay' => '2020-01-15',
|
||||||
'workingTime' => '10:00:00',
|
'workingTime' => '10:00:00',
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$I->seeResponseCodeIs(HttpCode::OK);
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
@@ -43,6 +45,7 @@ class UpdateWorkingHoursCest
|
|||||||
$I->sendPut('/working-hours/2020-04-15', [
|
$I->sendPut('/working-hours/2020-04-15', [
|
||||||
'workingDay' => '2020-04-15',
|
'workingDay' => '2020-04-15',
|
||||||
'workingTime' => '10:00:00',
|
'workingTime' => '10:00:00',
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$I->seeResponseCodeIs(HttpCode::NOT_FOUND);
|
$I->seeResponseCodeIs(HttpCode::NOT_FOUND);
|
||||||
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
@@ -58,6 +61,7 @@ class UpdateWorkingHoursCest
|
|||||||
$I->sendPut('/working-hours/2020-01-15', [
|
$I->sendPut('/working-hours/2020-01-15', [
|
||||||
'workingDay' => '2020-01-15',
|
'workingDay' => '2020-01-15',
|
||||||
'workingTime' => 8.0,
|
'workingTime' => 8.0,
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
||||||
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
|||||||
|
|
||||||
use Codeception\Attribute\DataProvider;
|
use Codeception\Attribute\DataProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\Exception as MockException;
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
use Slim\Exception\HttpBadRequestException;
|
use Slim\Exception\HttpBadRequestException;
|
||||||
use Slim\Exception\HttpNotFoundException;
|
use Slim\Exception\HttpNotFoundException;
|
||||||
use Slim\Psr7\Request;
|
use Slim\Psr7\Request;
|
||||||
use Slim\Psr7\Response;
|
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\HttpConflictRequestException;
|
use TorstenHettstedt\TimekeepingApi\Controller\HttpConflictRequestException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\NotDatabasesException;
|
use TorstenHettstedt\TimekeepingApi\Controller\NotDatabasesException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursController;
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursController;
|
||||||
@@ -25,13 +24,9 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
public const string NEW_DATE = '2020-04-01';
|
public const string NEW_DATE = '2020-04-01';
|
||||||
public const string NEW_INTERVAL = '07:59:00';
|
public const string NEW_INTERVAL = '07:59:00';
|
||||||
|
|
||||||
protected ContainerInterface $container;
|
|
||||||
protected Request $request;
|
|
||||||
protected Response|MockObject $response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @throws \PHPUnit\Framework\MockObject\Exception
|
* @throws MockException
|
||||||
*/
|
*/
|
||||||
protected function _before(): void
|
protected function _before(): void
|
||||||
{
|
{
|
||||||
@@ -40,6 +35,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::EXISTING_DATE,
|
'workingDay' => self::EXISTING_DATE,
|
||||||
'workingTime' => self::EXISTING_INTERVAL,
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
|
'isHomeOffice' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -266,6 +262,28 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
$this->assertNotEmpty($response->getBody()->getContents());
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testUpdateExistRecordWithoutHomeOffice(): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => self::EXISTING_DATE,
|
||||||
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->update($this->request, $this->response, [
|
||||||
|
'id' => self::EXISTING_DATE,
|
||||||
|
]);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ContainerExceptionInterface
|
* @throws ContainerExceptionInterface
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
@@ -299,21 +317,19 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $date
|
|
||||||
* @param string $time
|
|
||||||
*
|
|
||||||
* @throws ContainerExceptionInterface
|
* @throws ContainerExceptionInterface
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
* @throws NotFoundExceptionInterface
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
#[DataProvider('invalidCreatDataProvider')]
|
#[DataProvider('invalidCreatDataProvider')]
|
||||||
public function testCreatInvalidData(string $date, string $time): void
|
public function testCreatInvalidData(string $date, string $time, bool $isHomeOffice = false): void
|
||||||
{
|
{
|
||||||
$this->request = $this->makeEmpty(Request::class, [
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => $date,
|
'workingDay' => $date,
|
||||||
'workingTime' => $time,
|
'workingTime' => $time,
|
||||||
|
'isHomeOffice' => $isHomeOffice,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
@@ -328,6 +344,27 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testCreatNewRecord(): void
|
public function testCreatNewRecord(): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => self::NEW_DATE,
|
||||||
|
'workingTime' => self::NEW_INTERVAL,
|
||||||
|
'isHomeOffice' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->creat($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testCreatNewRecordWithoutHomeOffice(): void
|
||||||
{
|
{
|
||||||
$this->request = $this->makeEmpty(Request::class, [
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
@@ -353,6 +390,7 @@ class WorkingHoursControllerTest extends AbstractControllerTest
|
|||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::EXISTING_DATE,
|
'workingDay' => self::EXISTING_DATE,
|
||||||
'workingTime' => self::EXISTING_INTERVAL,
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
|
'isHomeOffice' => true,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class WorkingHoursTest extends Unit
|
|||||||
$obj = new WorkingHours();
|
$obj = new WorkingHours();
|
||||||
$this->assertNull($obj->getWorkingDay());
|
$this->assertNull($obj->getWorkingDay());
|
||||||
$this->assertNull($obj->getWorkingTime());
|
$this->assertNull($obj->getWorkingTime());
|
||||||
|
$this->assertFalse($obj->isHomeOffice());
|
||||||
|
|
||||||
$this->expectException(Error::class);
|
$this->expectException(Error::class);
|
||||||
$obj->jsonSerialize();
|
$obj->jsonSerialize();
|
||||||
@@ -34,7 +35,9 @@ class WorkingHoursTest extends Unit
|
|||||||
[
|
[
|
||||||
'workingDay' => '2020-11-30',
|
'workingDay' => '2020-11-30',
|
||||||
'workingTime' => '08:00:00',
|
'workingTime' => '08:00:00',
|
||||||
|
'isHomeOffice' => true,
|
||||||
],
|
],
|
||||||
|
true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
new DateTime('2021-05-03'),
|
new DateTime('2021-05-03'),
|
||||||
@@ -42,7 +45,9 @@ class WorkingHoursTest extends Unit
|
|||||||
[
|
[
|
||||||
'workingDay' => '2021-05-03',
|
'workingDay' => '2021-05-03',
|
||||||
'workingTime' => '07:45:00',
|
'workingTime' => '07:45:00',
|
||||||
|
'isHomeOffice' => false,
|
||||||
],
|
],
|
||||||
|
false,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -51,10 +56,38 @@ class WorkingHoursTest extends Unit
|
|||||||
* @param DateTime $date
|
* @param DateTime $date
|
||||||
* @param DateInterval $interval
|
* @param DateInterval $interval
|
||||||
* @param array<string, string> $shouldJson
|
* @param array<string, string> $shouldJson
|
||||||
|
* @param bool $isHomeOffice
|
||||||
*/
|
*/
|
||||||
#[DataProvider('workingHoursProvider')]
|
#[DataProvider('workingHoursProvider')]
|
||||||
public function testWorkingHoursWithContent(DateTime $date, DateInterval $interval, array $shouldJson): void
|
public function testWorkingHoursWithContent(DateTime $date, DateInterval $interval, array $shouldJson, bool $isHomeOffice): void
|
||||||
{
|
{
|
||||||
|
$obj = new WorkingHours();
|
||||||
|
$obj->setWorkingDay($date)->setWorkingTime($interval)->setIsHomeOffice($isHomeOffice);
|
||||||
|
|
||||||
|
$this->assertNotNull($obj->getWorkingDay());
|
||||||
|
$this->assertInstanceOf(DateTime::class, $obj->getWorkingDay());
|
||||||
|
$this->assertEquals($date, $obj->getWorkingDay());
|
||||||
|
|
||||||
|
$this->assertNotNull($obj->getWorkingTime());
|
||||||
|
$this->assertInstanceOf(DateInterval::class, $obj->getWorkingTime());
|
||||||
|
$this->assertEquals($interval, $obj->getWorkingTime());
|
||||||
|
|
||||||
|
$this->assertIsBool($obj->isHomeOffice());
|
||||||
|
$this->assertEquals($isHomeOffice, $obj->isHomeOffice());
|
||||||
|
|
||||||
|
$json = $obj->jsonSerialize();
|
||||||
|
$this->assertEquals($shouldJson, $json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DateTime $date
|
||||||
|
* @param DateInterval $interval
|
||||||
|
* @param array<string, string> $shouldJson
|
||||||
|
*/
|
||||||
|
#[DataProvider('workingHoursProvider')]
|
||||||
|
public function testWorkingHoursWithContentWithoutHomeOfficeInformation(DateTime $date, DateInterval $interval, array $shouldJson): void
|
||||||
|
{
|
||||||
|
$shouldJson['isHomeOffice'] = false;
|
||||||
$obj = new WorkingHours();
|
$obj = new WorkingHours();
|
||||||
$obj->setWorkingDay($date)->setWorkingTime($interval);
|
$obj->setWorkingDay($date)->setWorkingTime($interval);
|
||||||
|
|
||||||
@@ -66,6 +99,9 @@ class WorkingHoursTest extends Unit
|
|||||||
$this->assertInstanceOf(DateInterval::class, $obj->getWorkingTime());
|
$this->assertInstanceOf(DateInterval::class, $obj->getWorkingTime());
|
||||||
$this->assertEquals($interval, $obj->getWorkingTime());
|
$this->assertEquals($interval, $obj->getWorkingTime());
|
||||||
|
|
||||||
|
$this->assertIsBool($obj->isHomeOffice());
|
||||||
|
$this->assertFalse($obj->isHomeOffice());
|
||||||
|
|
||||||
$json = $obj->jsonSerialize();
|
$json = $obj->jsonSerialize();
|
||||||
$this->assertEquals($shouldJson, $json);
|
$this->assertEquals($shouldJson, $json);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class WorkingHoursViewTest extends Unit
|
|||||||
{
|
{
|
||||||
$date = new DateTime('2020-11-30');
|
$date = new DateTime('2020-11-30');
|
||||||
$work_days = 15;
|
$work_days = 15;
|
||||||
|
$home_work_days = 12;
|
||||||
$total_hours = new DateInterval("PT32H22M");
|
$total_hours = new DateInterval("PT32H22M");
|
||||||
$overtime = new DateInterval("PT22M");
|
$overtime = new DateInterval("PT22M");
|
||||||
$absence_time = clone $overtime;
|
$absence_time = clone $overtime;
|
||||||
@@ -30,12 +31,14 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum::WEEKLY,
|
PeriodDesignationEnum::WEEKLY,
|
||||||
$work_days,
|
$work_days,
|
||||||
$total_hours,
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
$overtime,
|
$overtime,
|
||||||
[
|
[
|
||||||
'period' => '2020#49',
|
'period' => '2020#49',
|
||||||
'periodDesignation' => 'weekly',
|
'periodDesignation' => 'weekly',
|
||||||
'workingDays' => 15,
|
'workingDays' => 15,
|
||||||
'totalHours' => '32:22:00',
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
'overtime' => '00:22:00'
|
'overtime' => '00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -44,12 +47,14 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum::WEEKLY,
|
PeriodDesignationEnum::WEEKLY,
|
||||||
$work_days,
|
$work_days,
|
||||||
$total_hours,
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
$absence_time,
|
$absence_time,
|
||||||
[
|
[
|
||||||
'period' => '2020#49',
|
'period' => '2020#49',
|
||||||
'periodDesignation' => 'weekly',
|
'periodDesignation' => 'weekly',
|
||||||
'workingDays' => 15,
|
'workingDays' => 15,
|
||||||
'totalHours' => '32:22:00',
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
'overtime' => '-00:22:00'
|
'overtime' => '-00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -58,12 +63,14 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum::MONTHLY,
|
PeriodDesignationEnum::MONTHLY,
|
||||||
$work_days,
|
$work_days,
|
||||||
$total_hours,
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
$overtime,
|
$overtime,
|
||||||
[
|
[
|
||||||
'period' => '2020-11',
|
'period' => '2020-11',
|
||||||
'periodDesignation' => 'monthly',
|
'periodDesignation' => 'monthly',
|
||||||
'workingDays' => 15,
|
'workingDays' => 15,
|
||||||
'totalHours' => '32:22:00',
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
'overtime' => '00:22:00'
|
'overtime' => '00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -72,12 +79,14 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum::MONTHLY,
|
PeriodDesignationEnum::MONTHLY,
|
||||||
$work_days,
|
$work_days,
|
||||||
$total_hours,
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
$absence_time,
|
$absence_time,
|
||||||
[
|
[
|
||||||
'period' => '2020-11',
|
'period' => '2020-11',
|
||||||
'periodDesignation' => 'monthly',
|
'periodDesignation' => 'monthly',
|
||||||
'workingDays' => 15,
|
'workingDays' => 15,
|
||||||
'totalHours' => '32:22:00',
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
'overtime' => '-00:22:00'
|
'overtime' => '-00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -86,12 +95,14 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum::YEARLY,
|
PeriodDesignationEnum::YEARLY,
|
||||||
$work_days,
|
$work_days,
|
||||||
$total_hours,
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
$overtime,
|
$overtime,
|
||||||
[
|
[
|
||||||
'period' => '2020',
|
'period' => '2020',
|
||||||
'periodDesignation' => 'yearly',
|
'periodDesignation' => 'yearly',
|
||||||
'workingDays' => 15,
|
'workingDays' => 15,
|
||||||
'totalHours' => '32:22:00',
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
'overtime' => '00:22:00'
|
'overtime' => '00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -100,12 +111,14 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum::YEARLY,
|
PeriodDesignationEnum::YEARLY,
|
||||||
$work_days,
|
$work_days,
|
||||||
$total_hours,
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
$absence_time,
|
$absence_time,
|
||||||
[
|
[
|
||||||
'period' => '2020',
|
'period' => '2020',
|
||||||
'periodDesignation' => 'yearly',
|
'periodDesignation' => 'yearly',
|
||||||
'workingDays' => 15,
|
'workingDays' => 15,
|
||||||
'totalHours' => '32:22:00',
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
'overtime' => '-00:22:00'
|
'overtime' => '-00:22:00'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -117,6 +130,7 @@ class WorkingHoursViewTest extends Unit
|
|||||||
* @param PeriodDesignationEnum $periodDesignation
|
* @param PeriodDesignationEnum $periodDesignation
|
||||||
* @param int $workingDays
|
* @param int $workingDays
|
||||||
* @param DateInterval $totalHours
|
* @param DateInterval $totalHours
|
||||||
|
* @param int $homeWorkDays ,
|
||||||
* @param DateInterval $overtime
|
* @param DateInterval $overtime
|
||||||
* @param array<string, string> $shouldJson
|
* @param array<string, string> $shouldJson
|
||||||
*/
|
*/
|
||||||
@@ -126,10 +140,11 @@ class WorkingHoursViewTest extends Unit
|
|||||||
PeriodDesignationEnum $periodDesignation,
|
PeriodDesignationEnum $periodDesignation,
|
||||||
int $workingDays,
|
int $workingDays,
|
||||||
DateInterval $totalHours,
|
DateInterval $totalHours,
|
||||||
|
int $homeWorkDays,
|
||||||
DateInterval $overtime,
|
DateInterval $overtime,
|
||||||
array $shouldJson
|
array $shouldJson
|
||||||
): void {
|
): void {
|
||||||
$obj = new WorkingHoursView($period, $periodDesignation, $workingDays, $totalHours, $overtime);
|
$obj = new WorkingHoursView($period, $periodDesignation, $workingDays, $totalHours, $homeWorkDays, $overtime);
|
||||||
|
|
||||||
$this->assertInstanceOf(DateTimeInterface::class, $obj->getPeriod());
|
$this->assertInstanceOf(DateTimeInterface::class, $obj->getPeriod());
|
||||||
$this->assertEquals($period, $obj->getPeriod());
|
$this->assertEquals($period, $obj->getPeriod());
|
||||||
@@ -142,6 +157,9 @@ class WorkingHoursViewTest extends Unit
|
|||||||
$this->assertInstanceOf(DateInterval::class, $obj->getTotalHours());
|
$this->assertInstanceOf(DateInterval::class, $obj->getTotalHours());
|
||||||
$this->assertEquals($totalHours, $obj->getTotalHours());
|
$this->assertEquals($totalHours, $obj->getTotalHours());
|
||||||
|
|
||||||
|
$this->assertIsInt($obj->getHomeOfficeDays());
|
||||||
|
$this->assertEquals($homeWorkDays, $obj->getHomeOfficeDays());
|
||||||
|
|
||||||
$this->assertInstanceOf(DateInterval::class, $obj->getOvertime());
|
$this->assertInstanceOf(DateInterval::class, $obj->getOvertime());
|
||||||
$this->assertEquals($overtime, $obj->getOvertime());
|
$this->assertEquals($overtime, $obj->getOvertime());
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
$model = $this->make(WorkingHours::class, [
|
$model = $this->make(WorkingHours::class, [
|
||||||
'workingDay' => new DateTime(self::NEW_DATE),
|
'workingDay' => new DateTime(self::NEW_DATE),
|
||||||
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
$model = $repository->findByKey(self::NEW_DATE);
|
$model = $repository->findByKey(self::NEW_DATE);
|
||||||
@@ -176,6 +177,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
$model = $this->make(WorkingHours::class, [
|
$model = $this->make(WorkingHours::class, [
|
||||||
'workingDay' => new DateTime(self::EXISTING_DATE),
|
'workingDay' => new DateTime(self::EXISTING_DATE),
|
||||||
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$this->expectException(RepositoryRecordAlreadyExistException::class);
|
$this->expectException(RepositoryRecordAlreadyExistException::class);
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
@@ -203,6 +205,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
$model = $this->make(WorkingHours::class, [
|
$model = $this->make(WorkingHours::class, [
|
||||||
'workingDay' => new DateTime(self::EXISTING_DATE),
|
'workingDay' => new DateTime(self::EXISTING_DATE),
|
||||||
'workingTime' => new DateInterval(self::EXISTING_INTERVAL),
|
'workingTime' => new DateInterval(self::EXISTING_INTERVAL),
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$repository->delete($model);
|
$repository->delete($model);
|
||||||
$this->expectException(RepositoryRecordNotFoundException::class);
|
$this->expectException(RepositoryRecordNotFoundException::class);
|
||||||
@@ -219,6 +222,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
$model = $this->make(WorkingHours::class, [
|
$model = $this->make(WorkingHours::class, [
|
||||||
'workingDay' => new DateTime(self::NEW_DATE),
|
'workingDay' => new DateTime(self::NEW_DATE),
|
||||||
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$this->expectException(RepositoryRecordNotFoundException::class);
|
$this->expectException(RepositoryRecordNotFoundException::class);
|
||||||
$repository->delete($model);
|
$repository->delete($model);
|
||||||
@@ -246,6 +250,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
$model = $this->make(WorkingHours::class, [
|
$model = $this->make(WorkingHours::class, [
|
||||||
'workingDay' => new DateTime(self::EXISTING_DATE),
|
'workingDay' => new DateTime(self::EXISTING_DATE),
|
||||||
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$repository->update($model);
|
$repository->update($model);
|
||||||
$model_db = $repository->findByKey(self::EXISTING_DATE);
|
$model_db = $repository->findByKey(self::EXISTING_DATE);
|
||||||
@@ -261,6 +266,7 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
$model = $this->make(WorkingHours::class, [
|
$model = $this->make(WorkingHours::class, [
|
||||||
'workingDay' => new DateTime(self::NEW_DATE),
|
'workingDay' => new DateTime(self::NEW_DATE),
|
||||||
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
'workingTime' => new DateInterval(self::NEW_INTERVAL),
|
||||||
|
'isHomeOffice' => false,
|
||||||
]);
|
]);
|
||||||
$this->expectException(RepositoryRecordNotFoundException::class);
|
$this->expectException(RepositoryRecordNotFoundException::class);
|
||||||
$repository->update($model);
|
$repository->update($model);
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use Codeception\Attribute\DataProvider;
|
|||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use PDO;use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
use PDO;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursWeeklyViewRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursWeeklyViewRepository;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursMonthlyViewRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursMonthlyViewRepository;
|
||||||
@@ -19,7 +20,9 @@ class WorkingHoursViewRepositoryTest extends Unit
|
|||||||
protected function _before(): void
|
protected function _before(): void
|
||||||
{
|
{
|
||||||
/** @noinspection SpellCheckingInspection */
|
/** @noinspection SpellCheckingInspection */
|
||||||
$this->pdoObject = new PDO('pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass');
|
$this->pdoObject = new PDO(
|
||||||
|
'pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass'
|
||||||
|
);
|
||||||
parent::_before();
|
parent::_before();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +44,27 @@ class WorkingHoursViewRepositoryTest extends Unit
|
|||||||
public function existingObjectProvider(): array
|
public function existingObjectProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[WorkingHoursYearlyViewRepository::class, '2020', 61, date_create('2020-01-01', new DateTimeZone('UCT'))],
|
[
|
||||||
[WorkingHoursMonthlyViewRepository::class, '2020 February', 20, date_create('2020-02-01', new DateTimeZone('UCT'))],
|
WorkingHoursYearlyViewRepository::class,
|
||||||
[WorkingHoursWeeklyViewRepository::class, '2020#03', 5, date_create('2020-01-13', new DateTimeZone('UCT'))],
|
'2020',
|
||||||
|
61,
|
||||||
|
date_create('2020-01-01', new DateTimeZone('UCT')),
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
WorkingHoursMonthlyViewRepository::class,
|
||||||
|
'2020 February',
|
||||||
|
20,
|
||||||
|
date_create('2020-02-01', new DateTimeZone('UCT')),
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
WorkingHoursWeeklyViewRepository::class,
|
||||||
|
'2020#03',
|
||||||
|
5,
|
||||||
|
date_create('2020-01-13', new DateTimeZone('UCT')),
|
||||||
|
0,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +91,19 @@ class WorkingHoursViewRepositoryTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[DataProvider(('existingObjectProvider'))]
|
#[DataProvider(('existingObjectProvider'))]
|
||||||
public function testExistingFindByKey(string $periodClass, string $search, int $workingDays, DateTime $period): void
|
public function testExistingFindByKey(
|
||||||
{
|
string $periodClass,
|
||||||
|
string $search,
|
||||||
|
int $workingDays,
|
||||||
|
DateTime $period,
|
||||||
|
int $homeOfficeDays
|
||||||
|
): void {
|
||||||
$repository = new $periodClass($this->pdoObject);
|
$repository = new $periodClass($this->pdoObject);
|
||||||
$model = $repository->findByKey($search);
|
$model = $repository->findByKey($search);
|
||||||
$this->assertInstanceOf(WorkingHoursView::class, $model);
|
$this->assertInstanceOf(WorkingHoursView::class, $model);
|
||||||
$this->assertEquals($workingDays, $model->getWorkingDays());
|
$this->assertEquals($workingDays, $model->getWorkingDays());
|
||||||
$this->assertEquals($period->format('Ymd'), $model->getPeriod()->format('Ymd'));
|
$this->assertEquals($period->format('Ymd'), $model->getPeriod()->format('Ymd'));
|
||||||
|
$this->assertEquals($homeOfficeDays, $model->getHomeOfficeDays());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[DataProvider(('notExistingObjectProvider'))]
|
#[DataProvider(('notExistingObjectProvider'))]
|
||||||
|
|||||||
+6
-3
@@ -7,7 +7,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
docker: "true"
|
docker: "true"
|
||||||
ports:
|
ports:
|
||||||
- 8090:80
|
- "8090:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./api:/var/www
|
- ./api:/var/www
|
||||||
- logs:/var/www/logs
|
- logs:/var/www/logs
|
||||||
@@ -20,13 +20,16 @@ services:
|
|||||||
DATABASES_USER: bruce
|
DATABASES_USER: bruce
|
||||||
DATABASES_PASS: mypass
|
DATABASES_PASS: mypass
|
||||||
ports:
|
ports:
|
||||||
- 8091:80
|
- "8091:80"
|
||||||
volumes:
|
volumes:
|
||||||
- ./api:/var/www
|
- ./api:/var/www
|
||||||
- logs:/var/www/logs
|
- logs:/var/www/logs
|
||||||
ui:
|
ui:
|
||||||
build: ./ui/
|
build: ./ui/
|
||||||
|
env_file:
|
||||||
|
- ui/.env
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- "8080:3000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
|
- test-api
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
create_css_files() {
|
create_css_files() {
|
||||||
cd ui/public || exit 2
|
cd ui || exit 2
|
||||||
lessc --source-map less/print.less print.css
|
lessc --source-map less/print.less static/print.css
|
||||||
lessc --source-map less/global.less global.css
|
lessc --source-map less/global.less static/global.css
|
||||||
cd ../..
|
cd ../..
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +27,13 @@ build_ui() {
|
|||||||
cd ..
|
cd ..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codeception_install() {
|
||||||
|
pwd
|
||||||
|
cd api/ || exit 2
|
||||||
|
test -e ./tests/unit.suite.yml || ./vendor/bin/codecept build
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
test_unit() {
|
test_unit() {
|
||||||
pwd
|
pwd
|
||||||
cd api/ || exit 2
|
cd api/ || exit 2
|
||||||
@@ -44,4 +51,4 @@ composer_run() {
|
|||||||
docker compose up -d
|
docker compose up -d
|
||||||
}
|
}
|
||||||
|
|
||||||
create_css_files && install_ui_dependence && install_api_dependence && build_ui && test_unit && composer_run && test_api
|
create_css_files && install_ui_dependence && install_api_dependence && build_ui && codeception_install && test_unit && composer_run && test_api
|
||||||
@@ -4,42 +4,46 @@ create table public."Arbeitszeiten"
|
|||||||
(
|
(
|
||||||
"Datum" date not null,
|
"Datum" date not null,
|
||||||
"Arbeitszeit" interval(6) null,
|
"Arbeitszeit" interval(6) null,
|
||||||
|
"HomeOffice" boolean default FALSE not null,
|
||||||
constraint "Arbeitszeiten_pkey" primary key ("Datum")
|
constraint "Arbeitszeiten_pkey" primary key ("Datum")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
-- # Definition der Ansichten
|
-- # Definition der Ansichten
|
||||||
-- ## Wöchentlich
|
-- ## Wöchentlich
|
||||||
create view "Arbeitszeiten - Woche" ("Woche", "Gesamtarbeitszeit", "Arbeitstage", "Überstunden") as
|
create view "Arbeitszeiten - Woche" ("Woche", "Gesamtarbeitszeit", "Arbeitstage", "Überstunden", "Home-Office-Tage") as
|
||||||
select
|
select to_char(date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone),
|
||||||
to_char(date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone),
|
|
||||||
'TMYYYY#WW'::text) as "Woche",
|
'TMYYYY#WW'::text) as "Woche",
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") as "Gesamtarbeitszeit",
|
sum("Arbeitszeiten"."Arbeitszeit")::interval AS "Gesamtarbeitszeit",
|
||||||
count("Arbeitszeiten"."Datum") as "Arbeitstage",
|
count("Arbeitszeiten"."Datum")::bigint AS "Arbeitstage",
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") - count("Arbeitszeiten"."Datum") ::double precision * '08:00:00'::interval as "Überstunden"
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
from "Arbeitszeiten"
|
from "Arbeitszeiten"
|
||||||
group by (date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone))
|
group by (date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone))
|
||||||
order by (date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone));
|
order by (date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone));
|
||||||
|
|
||||||
-- ## Monatlich
|
-- ## Monatlich
|
||||||
create view "Arbeitszeiten - Monat" ("Monat", "Gesamtarbeitszeit", "Arbeitstage", "Überstunden") as
|
create view "Arbeitszeiten - Monat" ("Monat", "Gesamtarbeitszeit", "Arbeitstage", "Überstunden", "Home-Office-Tage") as
|
||||||
select
|
select to_char(date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone),
|
||||||
to_char(date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone),
|
|
||||||
'TMYYYY TMMonth'::text) as "Monat",
|
'TMYYYY TMMonth'::text) as "Monat",
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") as "Gesamtarbeitszeit",
|
sum("Arbeitszeiten"."Arbeitszeit")::interval AS "Gesamtarbeitszeit",
|
||||||
count("Arbeitszeiten"."Datum") as "Arbeitstage",
|
count("Arbeitszeiten"."Datum")::bigint AS "Arbeitstage",
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") - count("Arbeitszeiten"."Datum") ::double precision * '08:00:00'::interval as "Überstunden"
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
from "Arbeitszeiten"
|
from "Arbeitszeiten"
|
||||||
group by (date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone))
|
group by (date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone))
|
||||||
order by (date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone));
|
order by (date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone));
|
||||||
|
|
||||||
-- ## Jährlich
|
-- ## Jährlich
|
||||||
create view "Arbeitszeiten - Jahr"("Jahr", "Gesamtarbeitszeit", "Arbeitstage", "Überstunden") as
|
create view "Arbeitszeiten - Jahr"("Jahr", "Gesamtarbeitszeit", "Arbeitstage", "Überstunden", "Home-Office-Tage") as
|
||||||
select
|
select date_part('year'::text, "Arbeitszeiten"."Datum")::text as "Jahr",
|
||||||
date_part('year'::text, "Arbeitszeiten"."Datum")::text as "Jahr",
|
sum("Arbeitszeiten"."Arbeitszeit")::interval AS "Gesamtarbeitszeit",
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") as "Gesamtarbeitszeit",
|
count("Arbeitszeiten"."Datum")::bigint AS "Arbeitstage",
|
||||||
count("Arbeitszeiten"."Datum") as "Arbeitstage",
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
sum("Arbeitszeiten"."Arbeitszeit") - count("Arbeitszeiten"."Datum") ::double precision * '08:00:00'::interval as "Überstunden"
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
from "Arbeitszeiten"
|
from "Arbeitszeiten"
|
||||||
group by (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
group by (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
||||||
order by (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
order by (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
||||||
@@ -23,7 +23,7 @@ Arbeit;04:16;2021-07-22 07:55;2021-07-22 12:11;
|
|||||||
Arbeit;03:11;2021-07-21 13:37;2021-07-21 16:48;
|
Arbeit;03:11;2021-07-21 13:37;2021-07-21 16:48;
|
||||||
Mittagspause ;01:07;2021-07-21 12:29;2021-07-21 13:37;
|
Mittagspause ;01:07;2021-07-21 12:29;2021-07-21 13:37;
|
||||||
Arbeit;04:58;2021-07-21 07:31;2021-07-21 12:29;
|
Arbeit;04:58;2021-07-21 07:31;2021-07-21 12:29;
|
||||||
Arbeit;01:17;2021-07-20 18:44;2021-07-20 20:02;
|
Verkehr;01:17;2021-07-20 18:44;2021-07-20 20:02;
|
||||||
Arbeit;03:50;2021-07-20 13:14;2021-07-20 17:04;
|
Arbeit;03:50;2021-07-20 13:14;2021-07-20 17:04;
|
||||||
Mittagspause ;00:41;2021-07-20 12:32;2021-07-20 13:14;
|
Mittagspause ;00:41;2021-07-20 12:32;2021-07-20 13:14;
|
||||||
Arbeit;04:55;2021-07-20 07:37;2021-07-20 12:32;
|
Arbeit;04:55;2021-07-20 07:37;2021-07-20 12:32;
|
||||||
|
|||||||
|
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
+15
-2
@@ -1,3 +1,16 @@
|
|||||||
FROM httpd:2.4
|
FROM node:22-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
RUN npm prune --production
|
||||||
|
|
||||||
COPY ./public/ /usr/local/apache2/htdocs/
|
FROM node:22-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/build build/
|
||||||
|
COPY --from=builder /app/node_modules node_modules/
|
||||||
|
COPY package.json .
|
||||||
|
EXPOSE 3000
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
CMD [ "node", "build" ]
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"@sveltejs/adapter-auto": "^6.0.0",
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
"@sveltejs/kit": "^2.16.0",
|
"@sveltejs/kit": "^2.16.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"@sveltejs/adapter-node": "^5.2.12",
|
||||||
"svelte": "^5.0.0",
|
"svelte": "^5.0.0",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
export class PeriodRecord {
|
export class PeriodRecord {
|
||||||
/**
|
/**
|
||||||
* @param {{ workingDays: Number; totalHours: any; overtime: string; period: string; }} record
|
* @param {{ workingDays: Number; totalHours: any; overtime: string; period: string; homeOfficeDays: Number }} record
|
||||||
*/
|
*/
|
||||||
constructor(record) {
|
constructor(record) {
|
||||||
this.workingDays = Number(record.workingDays)
|
this.workingDays = Number(record.workingDays)
|
||||||
|
this.homeOfficeDays = Number(record.homeOfficeDays)
|
||||||
this.totalHours = String(record.totalHours)
|
this.totalHours = String(record.totalHours)
|
||||||
this.period = String(record.period)
|
this.period = String(record.period)
|
||||||
this.overtime = String('##:##:##')
|
this.overtime = String('##:##:##')
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let {promise_data, actual_records= $bindable(), page_count = 10} = $props();
|
||||||
|
let records: Array<any[]> = $state([])
|
||||||
|
let page_number = $state(0)
|
||||||
|
let max_page_number = $state(0)
|
||||||
|
const setPage = (new_page: number) => {
|
||||||
|
page_number = new_page
|
||||||
|
actual_records = records[page_number - 1]
|
||||||
|
}
|
||||||
|
const init = async () => {
|
||||||
|
let page_records: any[] = []
|
||||||
|
console.log(promise_data, actual_records)
|
||||||
|
for (let index in promise_data) {
|
||||||
|
page_records.push(promise_data[index])
|
||||||
|
if (page_records.length >= page_count) {
|
||||||
|
records.push(page_records.values().toArray())
|
||||||
|
page_records = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page_records.length > 0) {
|
||||||
|
records.push(page_records.values().toArray())
|
||||||
|
}
|
||||||
|
max_page_number = records.length
|
||||||
|
setPage(1)
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
div.table-pagination {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-pagination > button {
|
||||||
|
display: inline-block;
|
||||||
|
width: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-pagination > button[disabled] {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.table-pagination > span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="table-pagination">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === 1}
|
||||||
|
onclick={() => setPage(1)}>≪
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === 1}
|
||||||
|
onclick={() => setPage(page_number - 1)}><
|
||||||
|
</button>
|
||||||
|
<span>{page_number} von {max_page_number}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === max_page_number}
|
||||||
|
onclick={() => setPage(page_number + 1)}>>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={page_number === max_page_number}
|
||||||
|
onclick={() => setPage(max_page_number)}>≫
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
+18
-9
@@ -1,16 +1,16 @@
|
|||||||
<script>
|
<script>
|
||||||
import {PeriodRecord} from './Models.svelte.js'
|
import {PeriodRecord} from './Models.svelte.js'
|
||||||
|
import TablePagination from "$lib/TablePagination.svelte";
|
||||||
|
|
||||||
/**
|
let { promise, title } = $props();
|
||||||
* @type {{ promise: Promise<object>; title: string;}}
|
|
||||||
*/
|
|
||||||
let {params} = $props();
|
|
||||||
/** @type PeriodRecord[] **/
|
/** @type PeriodRecord[] **/
|
||||||
let records = $state([])
|
let records = $state([])
|
||||||
|
/** @type PeriodRecord[] **/
|
||||||
|
let actual_records = $state([])
|
||||||
let isLoading = $state(true)
|
let isLoading = $state(true)
|
||||||
const init = async () => await params.promise.then(
|
const init = async () => await promise.then(
|
||||||
/**
|
/**
|
||||||
* @param {Array<object>} data
|
* @param {Array<{ workingDays: Number; totalHours: any; overtime: string; period: string; homeOfficeDays: Number; }>} data
|
||||||
*/
|
*/
|
||||||
data => {
|
data => {
|
||||||
records = data.map(row => new PeriodRecord(row))
|
records = data.map(row => new PeriodRecord(row))
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{params.title}</title>
|
<title>{title}</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section id="list-records">
|
<section id="list-records">
|
||||||
<header>
|
<header>
|
||||||
<h1>{params.title}</h1>
|
<h1>{title}</h1>
|
||||||
</header>
|
</header>
|
||||||
<article>
|
<article>
|
||||||
{#if isLoading === true}
|
{#if isLoading === true}
|
||||||
@@ -43,20 +43,29 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Zeitraum</th>
|
<th>Zeitraum</th>
|
||||||
<th>Arbeitstage</th>
|
<th>Arbeitstage</th>
|
||||||
|
<th>davon HomeOffice</th>
|
||||||
<th>Arbeitsstunden</th>
|
<th>Arbeitsstunden</th>
|
||||||
<th>Über- / Unterstunden</th>
|
<th>Über- / Unterstunden</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each records as record}
|
{#each actual_records as record}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{record.period}</td>
|
<td>{record.period}</td>
|
||||||
<td>{record.workingDays}</td>
|
<td>{record.workingDays}</td>
|
||||||
|
<td>{record.homeOfficeDays}</td>
|
||||||
<td>{record.totalHours}</td>
|
<td>{record.totalHours}</td>
|
||||||
<td class="{record.isMinus ? 'absence-time' : ''}">{record.overtime}</td>
|
<td class="{record.isMinus ? 'absence-time' : ''}">{record.overtime}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">
|
||||||
|
<TablePagination promise_data={records} bind:actual_records={actual_records}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Views params={data}/>
|
<Views {...data}/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Views params={data}/>
|
<Views {...data}/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Views params={data}/>
|
<Views {...data}/>
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const getMonday = actualDayObject => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @type {String[]} **/
|
/** @type {String[]} **/
|
||||||
const day_list = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sontag']
|
const weekday_list = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sontag']
|
||||||
|
|
||||||
/** @param {Date} actualDayObject **/
|
/** @param {Date} actualDayObject **/
|
||||||
const getDateString = actualDayObject => {
|
const getDateString = actualDayObject => {
|
||||||
@@ -34,6 +34,7 @@ export const actions = {
|
|||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: data.get('workingDay'),
|
workingDay: data.get('workingDay'),
|
||||||
workingTime: data.get('workingTime'),
|
workingTime: data.get('workingTime'),
|
||||||
|
isHomeOffice: data.get('isHomeOffice'),
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.update(env.API_URL, data.get('workingDay'), body_data);
|
await WorkingHoursRepository.update(env.API_URL, data.get('workingDay'), body_data);
|
||||||
},
|
},
|
||||||
@@ -42,6 +43,7 @@ export const actions = {
|
|||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: form_data.get('workingDay'),
|
workingDay: form_data.get('workingDay'),
|
||||||
workingTime: form_data.get('workingTime'),
|
workingTime: form_data.get('workingTime'),
|
||||||
|
isHomeOffice: form_data.get('isHomeOffice'),
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.add(env.API_URL, body_data);
|
await WorkingHoursRepository.add(env.API_URL, body_data);
|
||||||
},
|
},
|
||||||
@@ -49,25 +51,42 @@ export const actions = {
|
|||||||
const form_data = await request.formData();
|
const form_data = await request.formData();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let actualDayObject = getMonday(new Date(form_data.get('week')))
|
let actualDayObject = getMonday(new Date(form_data.get('week')))
|
||||||
day_list.forEach(day => {
|
/** @type {Object<String,String>} **/
|
||||||
if (form_data.get(day) !== '00:00:00' && form_data.get(day) !== '00:00') {
|
let day_list = {}
|
||||||
const working_day = getDateString(actualDayObject)
|
weekday_list.forEach(day => {
|
||||||
const body_data = {
|
day_list[day] = getDateString(actualDayObject)
|
||||||
workingDay: working_day,
|
|
||||||
workingTime: form_data.get(day),
|
|
||||||
}
|
|
||||||
WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
|
||||||
}
|
|
||||||
actualDayObject.setDate(actualDayObject.getDate() + 1)
|
actualDayObject.setDate(actualDayObject.getDate() + 1)
|
||||||
})
|
})
|
||||||
|
for (const pair of form_data.entries()) {
|
||||||
|
let key = /(?<day>\w+)\[time]/.exec(pair[0])
|
||||||
|
if (key === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let day = key?.groups?.day ?? ''
|
||||||
|
const working_day = day_list[day] ?? null
|
||||||
|
if (working_day === null || pair[1] === '00:00:00' || pair[1] === '00:00') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const body_data = {
|
||||||
|
workingDay: working_day,
|
||||||
|
workingTime: pair[1],
|
||||||
|
isHomeOffice: form_data.has(day + '[isHomeOffice]')
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'import-csv': async ({ request }) => {
|
'import-csv': async ({ request }) => {
|
||||||
const form_data = await request.formData();
|
const form_data = await request.formData();
|
||||||
for (const pair of form_data.entries()) {
|
for (const pair of form_data.entries()) {
|
||||||
const working_day = pair[0]
|
let key = /^(?<day>\d{4}-\d{2}-\d{2})\[time]$/.exec(pair[0])
|
||||||
|
if (key === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let working_day = key?.groups?.day ?? ''
|
||||||
const body_data = {
|
const body_data = {
|
||||||
workingDay: working_day,
|
workingDay: working_day,
|
||||||
workingTime: pair[1],
|
workingTime: pair[1],
|
||||||
|
isHomeOffice: form_data.has(working_day + '[isHomeOffice]')
|
||||||
}
|
}
|
||||||
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,16 @@
|
|||||||
import documentIcon from "@iconify-icons/oi/document.js";
|
import documentIcon from "@iconify-icons/oi/document.js";
|
||||||
import wrenchIcon from "@iconify-icons/oi/wrench.js";
|
import wrenchIcon from "@iconify-icons/oi/wrench.js";
|
||||||
import IconifyIcon from "@iconify/svelte";
|
import IconifyIcon from "@iconify/svelte";
|
||||||
|
import TablePagination from "$lib/TablePagination.svelte";
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
const TITLE = "Bearbeitung Einträge"
|
const TITLE = "Bearbeitung Einträge"
|
||||||
/** @type {{workingDay: String, workingTime: String, }[]|null} **/
|
/** @type {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }[]|null} **/
|
||||||
let records = $state([])
|
let records = $state(null)
|
||||||
|
/** @type {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }[]} **/
|
||||||
|
let actual_records = $state([])
|
||||||
const init = async () => await data.promise.then(
|
const init = async () => await data.promise.then(
|
||||||
/**
|
/**
|
||||||
* @param {{workingDay: String, workingTime: String, }[]} data
|
* @param {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }[]} data
|
||||||
*/
|
*/
|
||||||
data => {
|
data => {
|
||||||
records = data
|
records = data
|
||||||
@@ -53,14 +56,16 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Datum</th>
|
<th>Datum</th>
|
||||||
<th>Arbeitsstunden</th>
|
<th>Arbeitsstunden</th>
|
||||||
|
<th>Zu Hause gearbeitet?</th>
|
||||||
<th>Aktionen</th>
|
<th>Aktionen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each records as record}
|
{#each actual_records as record}
|
||||||
<tr>
|
<tr style="{record.isHomeOffice ? 'background: aqua' : ''}">
|
||||||
<td>{record.workingDay}</td>
|
<td>{record.workingDay}</td>
|
||||||
<td>{record.workingTime}</td>
|
<td>{record.workingTime}</td>
|
||||||
|
<td>{record.isHomeOffice ? 'ja' : ''}</td>
|
||||||
<td>
|
<td>
|
||||||
<button formaction={'/working-hours/' + record.workingDay} type="submit">Bearbeiten
|
<button formaction={'/working-hours/' + record.workingDay} type="submit">Bearbeiten
|
||||||
<IconifyIcon icon={wrenchIcon}/>
|
<IconifyIcon icon={wrenchIcon}/>
|
||||||
@@ -69,6 +74,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">
|
||||||
|
<TablePagination promise_data={records} bind:actual_records={actual_records} page_count={15}/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
import xIcon from "@iconify-icons/oi/x";
|
import xIcon from "@iconify-icons/oi/x";
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
const TITLE = "Bearbeitung eines Eintrags"
|
const TITLE = "Bearbeitung eines Eintrags"
|
||||||
/** @type {{workingDay: String, workingTime: String, }|null} **/
|
/** @type {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }|null} **/
|
||||||
let activeDate = $state(null)
|
let activeDate = $state(null)
|
||||||
const init = async () => await data.promise.then(
|
const init = async () => await data.promise.then(
|
||||||
/**
|
/**
|
||||||
* @param {{workingDay: String, workingTime: String, }} data
|
* @param {{workingDay: String, workingTime: String, isHomeOffice: Boolean, }} data
|
||||||
*/
|
*/
|
||||||
data => {
|
data => {
|
||||||
activeDate = data
|
activeDate = data
|
||||||
@@ -42,6 +42,10 @@
|
|||||||
value="{activeDate.workingTime}"
|
value="{activeDate.workingTime}"
|
||||||
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="isHomeOffice">Arbeitszeit</label>
|
||||||
|
<input name="isHomeOffice" type="checkbox" checked={activeDate.isHomeOffice} id="isHomeOffice">
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
<fieldset name="buttons">
|
<fieldset name="buttons">
|
||||||
|
|||||||
@@ -29,6 +29,10 @@
|
|||||||
<input name="workingTime" placeholder="Arbeitszeit" type="time" step="1" value=""
|
<input name="workingTime" placeholder="Arbeitszeit" type="time" step="1" value=""
|
||||||
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="isHomeOffice">Arbeitszeit</label>
|
||||||
|
<input name="isHomeOffice" type="checkbox" id="isHomeOffice">
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset name="buttons">
|
<fieldset name="buttons">
|
||||||
<button type="submit">Übernehmen und Weiter
|
<button type="submit">Übernehmen und Weiter
|
||||||
|
|||||||
@@ -14,6 +14,22 @@
|
|||||||
tbody.weekend td input, tbody.weekend td {
|
tbody.weekend td input, tbody.weekend td {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
tbody.weekend label {
|
||||||
|
color: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
section form fieldset div input {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
section form fieldset div input[type=checkbox] {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
margin: 0 1rem 1.0rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -39,10 +55,19 @@
|
|||||||
{day}
|
{day}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input name="{day}" value="08:00:00" type="time" step="1"
|
<input name="{day}[time]" value="08:00:00" type="time" step="1"
|
||||||
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<label>wurde zu Hause gearbeitet?
|
||||||
|
<input name="{day}[isHomeOffice]" type="checkbox">
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tbody class="weekend">
|
<tbody class="weekend">
|
||||||
@@ -52,10 +77,19 @@
|
|||||||
{day}
|
{day}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input name="{day}" value="00:00:00" type="time" step="1"
|
<input name="{day}[time]" value="00:00:00" type="time" step="1"
|
||||||
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<label>wurde zu Hause gearbeitet?
|
||||||
|
<input name="{day}[isHomeOffice]" type="checkbox">
|
||||||
|
</label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
const TITLE = "Erstellung neuer Einträge für eine Woche"
|
const TITLE = "Erstellung neuer Einträge für eine Woche"
|
||||||
|
|
||||||
/** @type {{ workingTime: String, workingDay: String, }[]} **/
|
/** @type {{ workingTime: String, workingDay: String, isHomeOffice: Boolean, }[]} **/
|
||||||
let imported_data = $state([])
|
let imported_data = $state([])
|
||||||
|
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
complete: results => {
|
complete: results => {
|
||||||
/** @type {Object<String, Number>} **/
|
/** @type {Object<String, Number>} **/
|
||||||
let cash_work_days = {}
|
let cash_work_days = {}
|
||||||
|
/** @type {Object<String, Boolean>} **/
|
||||||
|
let home_office_days = {}
|
||||||
results.data.forEach(
|
results.data.forEach(
|
||||||
/** @param {Object<String, any>} item **/
|
/** @param {Object<String, any>} item **/
|
||||||
item => {
|
item => {
|
||||||
@@ -42,6 +44,9 @@
|
|||||||
let work_to = new Date(item['Bis'])
|
let work_to = new Date(item['Bis'])
|
||||||
let actual_day = work_from.toISOString().substring(0, 10)
|
let actual_day = work_from.toISOString().substring(0, 10)
|
||||||
let actual_work_hours = (work_to.getTime() - work_from.getTime()) / (1000 * 60 * 60)
|
let actual_work_hours = (work_to.getTime() - work_from.getTime()) / (1000 * 60 * 60)
|
||||||
|
if (activity.trim() === 'Verkehr') {
|
||||||
|
home_office_days[actual_day] = true
|
||||||
|
}
|
||||||
cash_work_days[actual_day] ??= 0
|
cash_work_days[actual_day] ??= 0
|
||||||
cash_work_days[actual_day] += actual_work_hours
|
cash_work_days[actual_day] += actual_work_hours
|
||||||
}
|
}
|
||||||
@@ -52,7 +57,11 @@
|
|||||||
let minutes_digits = Math.round((hours - hours_digits) * 60)
|
let minutes_digits = Math.round((hours - hours_digits) * 60)
|
||||||
let working_time = String(hours_digits).padStart(2, '0') + ':'
|
let working_time = String(hours_digits).padStart(2, '0') + ':'
|
||||||
+ String(minutes_digits).padStart(2, '0') + ':00'
|
+ String(minutes_digits).padStart(2, '0') + ':00'
|
||||||
imported_data.push({workingTime: working_time, workingDay: day})
|
imported_data.push({
|
||||||
|
workingTime: working_time,
|
||||||
|
workingDay: day,
|
||||||
|
isHomeOffice: home_office_days[day] ?? false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -90,7 +99,10 @@
|
|||||||
{item.workingDay}
|
{item.workingDay}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="time" readonly name="{item.workingDay}" value="{item.workingTime}">
|
<input type="time" readonly name="{item.workingDay}[time]" value="{item.workingTime}">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" readonly name="{item.workingDay}[isHomeOffice]" checked={item.isHomeOffice}>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
|
import adapter from '@sveltejs/adapter-node';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
kit: {}
|
kit: {
|
||||||
|
adapter: adapter()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
Reference in New Issue
Block a user