Compare commits
126
Commits
0.5.1
..
2dcbb6fdbd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dcbb6fdbd | ||
|
|
9f53b627c0 | ||
|
|
532c0d60e8 | ||
|
|
cf6eff6ab0 | ||
|
|
a964663f51 | ||
|
|
a9c455381d | ||
|
|
7b5ce23342 | ||
|
|
a3b123f466 | ||
|
|
d52a9d77ab | ||
|
|
a09edcae25 | ||
|
|
91849375c7 | ||
|
|
63ded6da57 | ||
|
|
9a65c7f78b | ||
|
|
b89a3c744f | ||
|
|
85d22d5c06 | ||
|
|
6fa6b28af6 | ||
|
|
41a3c79c91 | ||
|
|
82aad9766e | ||
|
|
b094cf8180 | ||
|
|
dff3c7f34d | ||
|
|
aa66d1f53d | ||
|
|
af61eb2334 | ||
|
|
616ed6383e | ||
|
|
e187bdeab7 | ||
|
|
c35b3a86f3 | ||
|
|
4b12eb3897 | ||
|
|
3452aa9999 | ||
|
|
072e99c6fb | ||
|
|
608d96ac4e | ||
|
|
51b5eb423c | ||
|
|
6e9ec73db4 | ||
|
|
1e48a5b608 | ||
|
|
dc7d84e35f | ||
|
|
5bbcc961e6 | ||
|
|
94b22c1d26 | ||
|
|
99cee223de | ||
|
|
fbbb563aad | ||
|
|
d7e99cbf30 | ||
|
|
3e291171a8 | ||
|
|
7ef5c43901 | ||
|
|
9932f33c34 | ||
|
|
06128f72fc | ||
|
|
18c5c45c26 | ||
|
|
0a21c6ed35 | ||
|
|
47ed408185 | ||
|
|
0813fd637b | ||
|
|
094b50d33d | ||
|
|
3bd3e6837a | ||
|
|
4da284a0a6 | ||
|
|
084abbf42c | ||
|
|
bcc6448fa1 | ||
|
|
1fc54f4b89 | ||
|
|
d4ddd4f32c | ||
|
|
90c6ef0c53 | ||
|
|
1ebf57ac3b | ||
|
|
13554d09e0 | ||
|
|
b46f5634c8 | ||
|
|
77cfcc72b4 | ||
|
|
ea93400135 | ||
|
|
646537e19c | ||
|
|
efadeb501b | ||
|
|
cf50607f43 | ||
|
|
d133cb017b | ||
|
|
ad253a82f5 | ||
|
|
fb91d1121e | ||
|
|
e10b72e086 | ||
|
|
aae4764f5e | ||
|
|
371425f3c5 | ||
|
|
c3a1953cc9 | ||
|
|
66022e42df | ||
|
|
907ce88ed1 | ||
|
|
45650a0f9a | ||
|
|
d13fce569b | ||
|
|
fafc2a0ad5 | ||
|
|
f3ff303b93 | ||
|
|
3dd0f451fc | ||
|
|
46313cc290 | ||
|
|
65195e702e | ||
|
|
ec22f42623 | ||
|
|
b43b6382d7 | ||
|
|
12ed91a6da | ||
|
|
cc9941d1d3 | ||
|
|
2b2753e009 | ||
|
|
735b195c06 | ||
|
|
0bd8932fa1 | ||
|
|
114e31029c | ||
|
|
de0535be32 | ||
|
|
b74e9bbd69 | ||
|
|
338c3ce5e4 | ||
|
|
1b4983a06f | ||
|
|
74193f3b46 | ||
|
|
2e0e6db55a | ||
|
|
c528944746 | ||
|
|
05d46ec407 | ||
|
|
7b48e7e9e2 | ||
|
|
343f12b605 | ||
|
|
6203f98e93 | ||
|
|
87f3bb3f0e | ||
|
|
8d00e0c104 | ||
|
|
3fe73f3b10 | ||
|
|
b5af0c5a54 | ||
|
|
e29a6dcab9 | ||
|
|
8ee9666250 | ||
|
|
edcb80421b | ||
|
|
16e05ce819 | ||
|
|
5c15580738 | ||
|
|
0a5e40d7cb | ||
|
|
ae7491f002 | ||
|
|
8296a2a281 | ||
|
|
c711e0ae24 | ||
|
|
74fd96cf25 | ||
|
|
9686a8505e | ||
|
|
bc83fc8dd1 | ||
|
|
9d5af9a0fb | ||
|
|
7f8a50799f | ||
|
|
365c2f041c | ||
|
|
89ad394773 | ||
|
|
881e7362f8 | ||
|
|
c8d9bc040e | ||
|
|
e3ef53b119 | ||
|
|
f78fdfbbd5 | ||
|
|
02771828c9 | ||
|
|
5a88ff3c6f | ||
|
|
f36324bfbd | ||
|
|
9b99c6a4dc | ||
|
|
fe3a4f69bf |
@@ -0,0 +1,10 @@
|
|||||||
|
# http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = false
|
||||||
@@ -2,10 +2,13 @@
|
|||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
/.idea/*
|
||||||
|
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
.idea/**/usage.statistics.xml
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/markdown.xml
|
||||||
.idea/**/dictionaries
|
.idea/**/dictionaries
|
||||||
.idea/**/shelf
|
.idea/**/shelf
|
||||||
|
|
||||||
@@ -106,3 +109,7 @@ Temporary Items
|
|||||||
/api/tests/_*
|
/api/tests/_*
|
||||||
/api/tests/*.suite.yml
|
/api/tests/*.suite.yml
|
||||||
/api/.env
|
/api/.env
|
||||||
|
/ui/static/print.css*
|
||||||
|
/ui/static/global.css*
|
||||||
|
/ui/static/bundle.css*
|
||||||
|
/ui/.svelte-kit/
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ DATABASES_HOST=psql.domain.tld
|
|||||||
DATABASES_NAME=db
|
DATABASES_NAME=db
|
||||||
DATABASES_USER=user
|
DATABASES_USER=user
|
||||||
DATABASES_PASS=1234
|
DATABASES_PASS=1234
|
||||||
|
|
||||||
|
## CORS Einstellungen
|
||||||
|
CORS_ALLOW_ORIGIN_REGEX=/https?:\/\/localhost:\d+/
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM php:8.0-apache
|
FROM php:8.4-apache-bookworm
|
||||||
|
|
||||||
RUN apt update && apt install -y libpq-dev
|
RUN apt update && apt install -y libpq-dev
|
||||||
RUN docker-php-ext-install -j$(nproc) pdo pdo_pgsql
|
RUN docker-php-ext-install -j$(nproc) pdo pdo_pgsql
|
||||||
|
|||||||
+11
-9
@@ -16,20 +16,22 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
"php": "8.4.*",
|
||||||
"slim/slim": "^4.7.1",
|
"slim/slim": "^4.7.1",
|
||||||
"vlucas/phpdotenv": "^4.2",
|
"vlucas/phpdotenv": "^v5.6.1",
|
||||||
"slim/psr7": "^1.3",
|
"slim/psr7": "^1.7.0",
|
||||||
"php-di/slim-bridge": "^3.1.0",
|
"php-di/slim-bridge": "^3.1.0",
|
||||||
"jetbrains/phpstorm-attributes": "^1.0.0",
|
"jetbrains/phpstorm-attributes": "^1.0.0",
|
||||||
"myclabs/php-enum": "^1.8.0"
|
"myclabs/php-enum": "^1.8.0",
|
||||||
|
"ext-pdo": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^0.12.80",
|
"phpstan/phpstan": "^2.1.12",
|
||||||
"codeception/codeception": "^4.1.18",
|
"codeception/codeception": "^5.2.1",
|
||||||
"codeception/module-phpbrowser": "^1.0.0",
|
"codeception/module-phpbrowser": "^3.0.1",
|
||||||
"codeception/module-asserts": "^1.0.0",
|
"codeception/module-asserts": "^3.1.0",
|
||||||
"codeception/module-db": "^1.1.0",
|
"codeception/module-db": "^3.2.2",
|
||||||
"codeception/module-rest": "^1.2.8"
|
"codeception/module-rest": "^3.4.1"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
+11
-1
@@ -7,32 +7,42 @@ $dotenv->load();
|
|||||||
use DI\Container;
|
use DI\Container;
|
||||||
use Slim\Factory\AppFactory;
|
use Slim\Factory\AppFactory;
|
||||||
use Slim\Routing\RouteCollectorProxy;
|
use Slim\Routing\RouteCollectorProxy;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Controller\PreflightController;
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursController;
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursController;
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursViewController;
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursViewController;
|
||||||
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
|
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
|
||||||
use TorstenHettstedt\TimekeepingApi\Middleware\JsonBodyParserMiddleware;
|
use TorstenHettstedt\TimekeepingApi\Middleware\JsonBodyParserMiddleware;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\OriginAccessControlHandler;
|
||||||
|
|
||||||
$container = new Container();
|
$container = new Container();
|
||||||
|
|
||||||
$container->set('databases', function () {
|
$container->set('databases', function () {
|
||||||
$dsn = "pgsql:host=${_ENV['DATABASES_HOST']};port=5432;dbname=${_ENV['DATABASES_NAME']}";
|
$dsn = "pgsql:host={$_ENV['DATABASES_HOST']};port=5432;dbname={$_ENV['DATABASES_NAME']}";
|
||||||
return new PDO($dsn, $_ENV['DATABASES_USER'], $_ENV['DATABASES_PASS']);
|
return new PDO($dsn, $_ENV['DATABASES_USER'], $_ENV['DATABASES_PASS']);
|
||||||
});
|
});
|
||||||
|
|
||||||
AppFactory::setContainer($container);
|
AppFactory::setContainer($container);
|
||||||
$app = AppFactory::create();
|
$app = AppFactory::create();
|
||||||
$app->add(new JsonBodyParserMiddleware());
|
$app->add(new JsonBodyParserMiddleware());
|
||||||
|
$app->addBodyParsingMiddleware();
|
||||||
|
$app->add(new OriginAccessControlHandler());
|
||||||
|
$app->addRoutingMiddleware();
|
||||||
|
|
||||||
$app->group('/views/working-hours', function (RouteCollectorProxy $group) {
|
$app->group('/views/working-hours', function (RouteCollectorProxy $group) {
|
||||||
|
$group->options('/weekly', PreflightController::class . ':preflight');
|
||||||
$group->get('/weekly', WorkingHoursViewController::class . ':browseWeekly');
|
$group->get('/weekly', WorkingHoursViewController::class . ':browseWeekly');
|
||||||
|
$group->options('/monthly', PreflightController::class . ':preflight');
|
||||||
$group->get('/monthly', WorkingHoursViewController::class . ':browseMonthly');
|
$group->get('/monthly', WorkingHoursViewController::class . ':browseMonthly');
|
||||||
|
$group->options('/yearly', PreflightController::class . ':preflight');
|
||||||
$group->get('/yearly', WorkingHoursViewController::class . ':browseYearly');
|
$group->get('/yearly', WorkingHoursViewController::class . ':browseYearly');
|
||||||
});
|
});
|
||||||
|
|
||||||
$app->group('/working-hours', function (RouteCollectorProxy $group) {
|
$app->group('/working-hours', function (RouteCollectorProxy $group) {
|
||||||
|
$group->options('', PreflightController::class . ':preflight');
|
||||||
$group->get('', WorkingHoursController::class . ':browse');
|
$group->get('', WorkingHoursController::class . ':browse');
|
||||||
$group->post('', WorkingHoursController::class . ':creat');
|
$group->post('', WorkingHoursController::class . ':creat');
|
||||||
$group->group('/{id:\d\d\d\d-\d\d-\d\d}', function (RouteCollectorProxy $group) {
|
$group->group('/{id:\d\d\d\d-\d\d-\d\d}', function (RouteCollectorProxy $group) {
|
||||||
|
$group->options('', PreflightController::class . ':preflight');
|
||||||
$group->get('', WorkingHoursController::class . ':read');
|
$group->get('', WorkingHoursController::class . ':read');
|
||||||
$group->put('', WorkingHoursController::class . ':update');
|
$group->put('', WorkingHoursController::class . ':update');
|
||||||
});
|
});
|
||||||
|
|||||||
+38
-1
@@ -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": {
|
||||||
@@ -18,6 +18,15 @@
|
|||||||
"tags": [
|
"tags": [
|
||||||
"Eintragungen Arbeitszeiten"
|
"Eintragungen Arbeitszeiten"
|
||||||
],
|
],
|
||||||
|
"description": "Liefert eine Liste der Einträge. Kann gefiltert werden und dadurch eine leere Liste zurückgeben",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/start-date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/parameters/end-date"
|
||||||
|
}
|
||||||
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/components/responses/ListEntities"
|
"$ref": "#/components/responses/ListEntities"
|
||||||
@@ -179,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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -208,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",
|
||||||
@@ -259,6 +276,26 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date"
|
"format": "date"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"start-date" : {
|
||||||
|
"description": "Filtert Liste ab Datum",
|
||||||
|
"name": "start-date",
|
||||||
|
"in" : "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"end-date" : {
|
||||||
|
"description": "Filtert Liste bis Datum",
|
||||||
|
"name": "end-date",
|
||||||
|
"in" : "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ namespace TorstenHettstedt\TimekeepingApi\Controller;
|
|||||||
|
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
use Slim\Psr7\Response;
|
use Slim\Psr7\Response;
|
||||||
|
|
||||||
abstract class AbstractController
|
abstract class AbstractController
|
||||||
@@ -18,6 +20,8 @@ abstract class AbstractController
|
|||||||
* @param ContainerInterface $container
|
* @param ContainerInterface $container
|
||||||
*
|
*
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
*/
|
*/
|
||||||
public function __construct(ContainerInterface $container)
|
public function __construct(ContainerInterface $container)
|
||||||
{
|
{
|
||||||
@@ -34,7 +38,8 @@ abstract class AbstractController
|
|||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
protected function printResponse(Response $response, mixed $data, int $status_code): Response {
|
protected function printResponse(Response $response, mixed $data, int $status_code): Response
|
||||||
|
{
|
||||||
$payload = json_encode($data);
|
$payload = json_encode($data);
|
||||||
|
|
||||||
$response->getBody()->write($payload);
|
$response->getBody()->write($payload);
|
||||||
|
|||||||
@@ -8,13 +8,8 @@ class HttpConflictRequestException extends HttpSpecializedException
|
|||||||
{
|
{
|
||||||
/** @var int */
|
/** @var int */
|
||||||
protected $code = 409;
|
protected $code = 409;
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $message = 'Conflict.';
|
protected $message = 'Conflict.';
|
||||||
|
protected string $title = '409 Conflict';
|
||||||
/** @var string */
|
protected string $description = 'The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.';
|
||||||
protected $title = '409 Conflict';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
protected $description = 'The 409 (Conflict) status code indicates that the request could not be completed due to a conflict with the current state of the target resource.';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Controller;
|
||||||
|
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Middleware\OriginAccessControlHandler;
|
||||||
|
|
||||||
|
class PreflightController
|
||||||
|
{
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
|
public function preflight(Request $request, Response $response): ResponseInterface
|
||||||
|
{
|
||||||
|
return (new OriginAccessControlHandler())->originAccessControl($request, $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ use Slim\Exception\HttpNotFoundException;
|
|||||||
use Slim\Psr7\Request;
|
use Slim\Psr7\Request;
|
||||||
use Slim\Psr7\Response;
|
use Slim\Psr7\Response;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryBadWhereDataException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordAlreadyExistException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordAlreadyExistException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
||||||
@@ -20,9 +21,9 @@ class WorkingHoursController extends AbstractController
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
@@ -33,7 +34,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['isHomeOfficeDay'] ?? false);
|
||||||
try {
|
try {
|
||||||
$repository->update($model);
|
$repository->update($model);
|
||||||
} catch (RepositoryRecordNotFoundException $exception) {
|
} catch (RepositoryRecordNotFoundException $exception) {
|
||||||
@@ -43,29 +44,44 @@ class WorkingHoursController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws HttpInternalServerErrorException
|
||||||
|
* @throws HttpBadRequestException
|
||||||
* @noinspection PhpUnusedParameterInspection
|
* @noinspection PhpUnusedParameterInspection
|
||||||
*/
|
*/
|
||||||
public function browse(Request $request, Response $response, array $args): Response
|
public function browse(Request $request, Response $response, array $args): Response
|
||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->databases);
|
$repository = new WorkingHoursRepository($this->databases);
|
||||||
|
$queryParams = $request->getQueryParams();
|
||||||
try {
|
try {
|
||||||
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
return $this->printResponse(
|
||||||
|
$response,
|
||||||
|
$repository->findFiltered(
|
||||||
|
$queryParams['start-date'] ?? null,
|
||||||
|
$queryParams['end-date'] ?? null
|
||||||
|
),
|
||||||
|
StatusCodeInterface::STATUS_OK
|
||||||
|
);
|
||||||
|
} catch (RepositoryBadWhereDataException $exception) {
|
||||||
|
throw new HttpBadRequestException($request, 'Ein Wert für das Datum im Query ist ungültig', $exception);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpInternalServerErrorException($request, 'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception);
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank',
|
||||||
|
$exception
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
@@ -80,14 +96,18 @@ class WorkingHoursController extends AbstractController
|
|||||||
} catch (RepositoryRecordNotFoundException $exception) {
|
} catch (RepositoryRecordNotFoundException $exception) {
|
||||||
throw new HttpNotFoundException($request, 'Der Eintrag ist nicht vorhanden.', $exception);
|
throw new HttpNotFoundException($request, 'Der Eintrag ist nicht vorhanden.', $exception);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpInternalServerErrorException($request, 'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception);
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank',
|
||||||
|
$exception
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
@@ -100,13 +120,22 @@ 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['isHomeOfficeDay'] ?? false
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
$repository->insert($model);
|
$repository->insert($model);
|
||||||
} catch (RepositoryRecordAlreadyExistException $exception) {
|
} catch (RepositoryRecordAlreadyExistException $exception) {
|
||||||
throw new HttpConflictRequestException($request, 'Der Eintrag ist schon vorhanden.', $exception);
|
throw new HttpConflictRequestException($request, 'Der Eintrag ist schon vorhanden.', $exception);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpInternalServerErrorException($request, 'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception);
|
throw new HttpInternalServerErrorException(
|
||||||
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank',
|
||||||
|
$exception
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return $this->printResponse($response, $model, StatusCodeInterface::STATUS_CREATED);
|
return $this->printResponse($response, $model, StatusCodeInterface::STATUS_CREATED);
|
||||||
}
|
}
|
||||||
@@ -119,7 +148,7 @@ class WorkingHoursController extends AbstractController
|
|||||||
* @return WorkingHours
|
* @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);
|
||||||
@@ -131,7 +160,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,9 @@ use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursYearlyViewRepositor
|
|||||||
class WorkingHoursViewController extends AbstractController
|
class WorkingHoursViewController extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
@@ -30,15 +30,17 @@ class WorkingHoursViewController extends AbstractController
|
|||||||
try {
|
try {
|
||||||
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpInternalServerErrorException($request,
|
throw new HttpInternalServerErrorException(
|
||||||
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception);
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
@@ -51,15 +53,17 @@ class WorkingHoursViewController extends AbstractController
|
|||||||
try {
|
try {
|
||||||
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpInternalServerErrorException($request,
|
throw new HttpInternalServerErrorException(
|
||||||
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception);
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Response $response
|
* @param Response $response
|
||||||
* @param mixed[] $args
|
* @param array<string, mixed> $args
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*
|
*
|
||||||
@@ -72,8 +76,10 @@ class WorkingHoursViewController extends AbstractController
|
|||||||
try {
|
try {
|
||||||
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
return $this->printResponse($response, $repository->findAll(), StatusCodeInterface::STATUS_OK);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
throw new HttpInternalServerErrorException($request,
|
throw new HttpInternalServerErrorException(
|
||||||
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception);
|
$request,
|
||||||
|
'Der Wert für das Datum oder die Zeit ist falsch in der Datenbank', $exception
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<?php /** @noinspection PhpUndefinedClassInspection */
|
<?php
|
||||||
|
/** @noinspection PhpUndefinedClassInspection */
|
||||||
|
|
||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Middleware;
|
namespace TorstenHettstedt\TimekeepingApi\Middleware;
|
||||||
|
|
||||||
|
|
||||||
use JetBrains\PhpStorm\ArrayShape;
|
use JetBrains\PhpStorm\ArrayShape;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -14,12 +16,15 @@ use Throwable;
|
|||||||
|
|
||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var App<ContainerInterface>
|
||||||
|
*/
|
||||||
protected App $app;
|
protected App $app;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ErrorHandler constructor.
|
* ErrorHandler constructor.
|
||||||
*
|
*
|
||||||
* @param App $app
|
* @param App<ContainerInterface> $app
|
||||||
*/
|
*/
|
||||||
public function __construct(App $app)
|
public function __construct(App $app)
|
||||||
{
|
{
|
||||||
@@ -29,7 +34,7 @@ class ErrorHandler
|
|||||||
/**
|
/**
|
||||||
* @param Throwable $exception
|
* @param Throwable $exception
|
||||||
*
|
*
|
||||||
* @return mixed[]
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
#[ArrayShape([
|
#[ArrayShape([
|
||||||
'timestamp' => "false|string",
|
'timestamp' => "false|string",
|
||||||
@@ -71,8 +76,7 @@ class ErrorHandler
|
|||||||
bool $logErrors,
|
bool $logErrors,
|
||||||
bool $logErrorDetails,
|
bool $logErrorDetails,
|
||||||
?LoggerInterface $logger = null
|
?LoggerInterface $logger = null
|
||||||
): ResponseInterface
|
): ResponseInterface {
|
||||||
{
|
|
||||||
if ($logger !== null && $logErrors === true) {
|
if ($logger !== null && $logErrors === true) {
|
||||||
$logger->error($exception->getMessage());
|
$logger->error($exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -84,7 +88,9 @@ class ErrorHandler
|
|||||||
json_encode($payload, JSON_UNESCAPED_UNICODE)
|
json_encode($payload, JSON_UNESCAPED_UNICODE)
|
||||||
);
|
);
|
||||||
|
|
||||||
return $response;
|
$originAccessControlHandler = new OriginAccessControlHandler();
|
||||||
|
|
||||||
|
return $originAccessControlHandler->originAccessControl($request, $response);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ class JsonBodyParserMiddleware implements MiddlewareInterface
|
|||||||
{
|
{
|
||||||
$contentType = $request->getHeaderLine('Content-Type');
|
$contentType = $request->getHeaderLine('Content-Type');
|
||||||
|
|
||||||
if (strstr($contentType, 'application/json')) {
|
if (str_contains($contentType, 'application/json')) {
|
||||||
$contents = json_decode(file_get_contents('php://input'), true);
|
$contents = json_decode(file_get_contents('php://input'), true);
|
||||||
if (json_last_error() === JSON_ERROR_NONE) {
|
if (json_last_error() === JSON_ERROR_NONE) {
|
||||||
$request = $request->withParsedBody($contents);
|
$request = $request->withParsedBody($contents);
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Middleware;
|
||||||
|
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use Psr\Http\Server\MiddlewareInterface;
|
||||||
|
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
|
||||||
|
use Slim\Routing\RouteContext;
|
||||||
|
|
||||||
|
class OriginAccessControlHandler implements MiddlewareInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process an incoming server request.
|
||||||
|
*
|
||||||
|
* Processes an incoming server request in order to produce a response.
|
||||||
|
* If unable to produce the response itself, it may delegate to the provided
|
||||||
|
* request handler to do so.
|
||||||
|
*/
|
||||||
|
public function process(Request $request, RequestHandler $handler): Response
|
||||||
|
{
|
||||||
|
$response = $handler->handle($request);
|
||||||
|
return $this->originAccessControl($request, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function originAccessControl(Request $request, Response $response): Response
|
||||||
|
{
|
||||||
|
$routeContext = RouteContext::fromRequest($request);
|
||||||
|
$routingResults = $routeContext->getRoutingResults();
|
||||||
|
$methods = $routingResults->getAllowedMethods();
|
||||||
|
$requestHeaders = $request->getHeaderLine('Access-Control-Request-Headers');
|
||||||
|
|
||||||
|
if ($this->testRoute($request)) {
|
||||||
|
// Stunden Minuten Sekunden
|
||||||
|
$accessControlMaxAge = 24 * 60 * 60;
|
||||||
|
$response = $response->withHeader('Access-Control-Allow-Origin', $this->buildOrigin($request));
|
||||||
|
$response = $response->withHeader('Access-Control-Allow-Methods', implode(',', $methods));
|
||||||
|
$response = $response->withHeader('Access-Control-Allow-Headers', $requestHeaders);
|
||||||
|
$response = $response->withHeader('Access-Control-Max-Age', (string)$accessControlMaxAge);
|
||||||
|
}
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function testRoute(Request $request): bool
|
||||||
|
{
|
||||||
|
$corsAllowOriginRegex = $_ENV['CORS_ALLOW_ORIGIN_REGEX'] ?? '/https?:\/\/.*:.*/';
|
||||||
|
return preg_match($corsAllowOriginRegex, $this->buildOrigin($request));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildOrigin(Request $request): string
|
||||||
|
{
|
||||||
|
return $request->getHeaderLine('Origin');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,16 @@
|
|||||||
<?php /** @noinspection PhpUnusedPrivateFieldInspection */
|
<?php
|
||||||
|
/** @noinspection PhpUnusedPrivateFieldInspection */
|
||||||
|
|
||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Models;
|
namespace TorstenHettstedt\TimekeepingApi\Models;
|
||||||
|
|
||||||
use MyCLabs\Enum\Enum;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum-Klasse für die unterstützten Periode-Definitionen.
|
* Enum-Klasse für die unterstützten Periode-Definitionen.
|
||||||
* Der Wert der Periode ist ein Format-String für {@link https://secure.php.net/manual/en/datetime.format.php}.
|
* Der Wert der Periode ist ein Format-String für {@link https://secure.php.net/manual/en/datetime.format.php}.
|
||||||
*
|
|
||||||
* @extends Enum<string>
|
|
||||||
*
|
|
||||||
* @method static PeriodDesignationEnum WEEKLY()
|
|
||||||
* @method static PeriodDesignationEnum MONTHLY()
|
|
||||||
* @method static PeriodDesignationEnum YEARLY()
|
|
||||||
*/
|
*/
|
||||||
class PeriodDesignationEnum extends Enum
|
enum PeriodDesignationEnum: string
|
||||||
{
|
{
|
||||||
private const WEEKLY = 'Y#W';
|
case WEEKLY = 'Y#W';
|
||||||
private const MONTHLY = 'Y-m';
|
case MONTHLY = 'Y-m';
|
||||||
private const YEARLY = 'Y';
|
case YEARLY = 'Y';
|
||||||
}
|
}
|
||||||
@@ -12,54 +12,51 @@ class WorkingHours implements ModelInterface
|
|||||||
{
|
{
|
||||||
protected ?DateTime $workingDay = null;
|
protected ?DateTime $workingDay = null;
|
||||||
protected ?DateInterval $workingTime = null;
|
protected ?DateInterval $workingTime = null;
|
||||||
|
protected bool $isHomeOffice = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateTime|null
|
|
||||||
*/
|
|
||||||
public function getWorkingDay(): ?DateTime
|
public function getWorkingDay(): ?DateTime
|
||||||
{
|
{
|
||||||
return $this->workingDay;
|
return $this->workingDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setWorkingDay(DateTime $workingDay): static
|
||||||
* @param DateTime $workingDay
|
|
||||||
*
|
|
||||||
* @return WorkingHours
|
|
||||||
*/
|
|
||||||
public function setWorkingDay(DateTime $workingDay): WorkingHours
|
|
||||||
{
|
{
|
||||||
$this->workingDay = $workingDay;
|
$this->workingDay = $workingDay;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateInterval|null
|
|
||||||
*/
|
|
||||||
public function getWorkingTime(): ?DateInterval
|
public function getWorkingTime(): ?DateInterval
|
||||||
{
|
{
|
||||||
return $this->workingTime;
|
return $this->workingTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function setWorkingTime(DateInterval $workingTime): static
|
||||||
* @param DateInterval $workingTime
|
|
||||||
*
|
|
||||||
* @return WorkingHours
|
|
||||||
*/
|
|
||||||
public function setWorkingTime(DateInterval $workingTime): WorkingHours
|
|
||||||
{
|
{
|
||||||
$this->workingTime = $workingTime;
|
$this->workingTime = $workingTime;
|
||||||
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,93 +10,63 @@ 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WorkingHoursView constructor.
|
|
||||||
*
|
|
||||||
* @param DateTimeInterface $period
|
|
||||||
* @param PeriodDesignationEnum $periodDesignation
|
|
||||||
* @param int $workingDays
|
|
||||||
* @param DateInterval $totalHours
|
|
||||||
* @param 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateTimeInterface
|
|
||||||
*/
|
|
||||||
public function getPeriod(): DateTimeInterface
|
public function getPeriod(): DateTimeInterface
|
||||||
{
|
{
|
||||||
return $this->period;
|
return $this->period;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return PeriodDesignationEnum
|
|
||||||
*/
|
|
||||||
public function getPeriodDesignation(): PeriodDesignationEnum
|
public function getPeriodDesignation(): PeriodDesignationEnum
|
||||||
{
|
{
|
||||||
return $this->periodDesignation;
|
return $this->periodDesignation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getWorkingDays(): int
|
public function getWorkingDays(): int
|
||||||
{
|
{
|
||||||
return $this->workingDays;
|
return $this->workingDays;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getHomeOfficeDays(): int
|
||||||
* @return DateInterval
|
{
|
||||||
*/
|
return $this->homeOfficeDays;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTotalHours(): DateInterval
|
public function getTotalHours(): DateInterval
|
||||||
{
|
{
|
||||||
return $this->totalHours;
|
return $this->totalHours;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DateInterval
|
|
||||||
*/
|
|
||||||
public function getOvertime(): DateInterval
|
public function getOvertime(): DateInterval
|
||||||
{
|
{
|
||||||
return $this->overtime;
|
return $this->overtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
#[ArrayShape(['period' => "string",
|
#[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
|
||||||
{
|
{
|
||||||
|
$formatOvertime = (($this->getOvertime()->invert === 1) ? '-' : '') . '%H:%I:%S';
|
||||||
return [
|
return [
|
||||||
'period' => $this->getPeriod()->format($this->getPeriodDesignation()->getValue()),
|
'period' => $this->getPeriod()->format($this->getPeriodDesignation()->value),
|
||||||
'periodDesignation' => strtolower($this->getPeriodDesignation()->getKey()),
|
'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'),
|
||||||
'overtime' => $this->getOvertime()->format('%H:%I:%S')
|
'homeOfficeDays' => $this->getHomeOfficeDays(),
|
||||||
|
'overtime' => $this->getOvertime()->format($formatOvertime)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,8 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
{
|
{
|
||||||
|
|
||||||
protected const SQL_SELECT = '';
|
protected const SQL_SELECT = '';
|
||||||
protected const SQL_WHERE = '';
|
protected const SQL_WHERE = '';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
protected PeriodDesignationEnum $periodDesignation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,9 +27,8 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
*
|
*
|
||||||
* @param PDO $database
|
* @param PDO $database
|
||||||
*/
|
*/
|
||||||
public function __construct(PDO $database)
|
public function __construct(protected PDO $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
|
||||||
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,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,8 +81,9 @@ abstract class AbstractWorkingHoursViewRepository implements RepositoryReaderInt
|
|||||||
return new WorkingHoursView(
|
return new WorkingHoursView(
|
||||||
$this->buildDateFromPeriod($row['period']),
|
$this->buildDateFromPeriod($row['period']),
|
||||||
$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'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryBadWhereDataException extends RepositoryException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T of \TorstenHettstedt\TimekeepingApi\Models\ModelInterface
|
* @template T of ModelInterface
|
||||||
*/
|
*/
|
||||||
interface RepositoryReaderInterface
|
interface RepositoryReaderInterface
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Repositories;
|
||||||
|
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T of \TorstenHettstedt\TimekeepingApi\Models\ModelInterface
|
* @template T of ModelInterface
|
||||||
*/
|
*/
|
||||||
interface RepositoryWriterInterface
|
interface RepositoryWriterInterface
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,18 +17,16 @@ class WorkingHoursMonthlyViewRepository extends AbstractWorkingHoursViewReposito
|
|||||||
"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" = ?';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
parent::__construct($database);
|
parent::__construct($database);
|
||||||
$this->periodDesignation = PeriodDesignationEnum::MONTHLY();
|
$this->periodDesignation = PeriodDesignationEnum::MONTHLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Exception;
|
|||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
use PDOStatement;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
||||||
|
|
||||||
@@ -19,17 +20,17 @@ 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;
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
|
|
||||||
/**
|
public function __construct(protected PDO $database)
|
||||||
* WorkingHoursRepository constructor.
|
|
||||||
*
|
|
||||||
* @param PDO $database
|
|
||||||
*/
|
|
||||||
public function __construct(PDO $database)
|
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
|
||||||
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$this->database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,20 +43,73 @@ class WorkingHoursRepository implements RepositoryReaderInterface, RepositoryWri
|
|||||||
* @throws PDOException
|
* @throws PDOException
|
||||||
*/
|
*/
|
||||||
public function findAll(): array
|
public function findAll(): array
|
||||||
|
{
|
||||||
|
return $this->findFiltered(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Gibt alle vorhandenen Einträge zurück
|
||||||
|
*
|
||||||
|
* @param string|null $start
|
||||||
|
* @param string|null $end
|
||||||
|
*
|
||||||
|
* @return WorkingHours[]
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function findFiltered(?string $start, ?string $end): array
|
||||||
{
|
{
|
||||||
$models = [];
|
$models = [];
|
||||||
$stmt = $this->database->prepare('select "Datum" as "workingDay", "Arbeitszeit" as "workingTime" from public."Arbeitszeiten"');
|
$stmt = $this->buildFindFilteredStatement($start, $end);
|
||||||
$stmt->execute();
|
try {
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (PDOException $exception) {
|
||||||
|
if ($exception->getCode() === '22007' || $exception->getCode() === '22008') {
|
||||||
|
throw new RepositoryBadWhereDataException($exception);
|
||||||
|
}
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
|
||||||
while ($row = $stmt->fetch()) {
|
while ($row = $stmt->fetch()) {
|
||||||
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildFilterString(?string $start, ?string $end): string
|
||||||
|
{
|
||||||
|
if (is_string($start) && is_string($end)) {
|
||||||
|
return 'where "Datum" >= :start AND "Datum" <= :end ';
|
||||||
|
}
|
||||||
|
if (is_string($start)) {
|
||||||
|
return 'where "Datum" >= :start ';
|
||||||
|
}
|
||||||
|
if (is_string($end)) {
|
||||||
|
return 'where "Datum" <= :end ';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildFindFilteredStatement(?string $start, ?string $end): ?PDOStatement
|
||||||
|
{
|
||||||
|
$query = static::SQL_SELECT . ' ';
|
||||||
|
$query .= $this->buildFilterString($start, $end);
|
||||||
|
$query .= 'order by "Datum"';
|
||||||
|
$stmt = $this->database->prepare($query);
|
||||||
|
if (is_string($start)) {
|
||||||
|
$stmt->bindParam(':start', $start);
|
||||||
|
}
|
||||||
|
if (is_string($end)) {
|
||||||
|
$stmt->bindParam(':end', $end);
|
||||||
|
}
|
||||||
|
return $stmt ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt den Eintrag zurück, der durch die ID
|
* Gibt den Eintrag zurück, der durch die ID
|
||||||
*
|
*
|
||||||
@@ -68,7 +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('select "Datum" as "workingDay", "Arbeitszeit" as "workingTime" from public."Arbeitszeiten" where "Datum" = ?');
|
$stmt = $this->database->prepare(static::SQL_SELECT . ' where "Datum" = ?');
|
||||||
$stmt->bindParam(1, $primary_key);
|
$stmt->bindParam(1, $primary_key);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$row = $stmt->fetch();
|
$row = $stmt->fetch();
|
||||||
@@ -78,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,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.');
|
||||||
}
|
}
|
||||||
@@ -102,9 +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('insert into public."Arbeitszeiten" ("Datum", "Arbeitszeit") values (?, ?) on conflict do nothing');
|
$stmt = $this->database->prepare(<<<'SQL'
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,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,23 +11,21 @@ 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" = ?';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
parent::__construct($database);
|
parent::__construct($database);
|
||||||
$this->periodDesignation = PeriodDesignationEnum::WEEKLY();
|
$this->periodDesignation = PeriodDesignationEnum::WEEKLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
protected function buildDateFromPeriod(string $period): DateTimeInterface
|
||||||
|
|||||||
@@ -11,23 +11,21 @@ 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" = ?';
|
||||||
|
|
||||||
protected PDO $database;
|
|
||||||
protected PeriodDesignationEnum $periodDesignation;
|
|
||||||
|
|
||||||
public function __construct(PDO $database)
|
public function __construct(PDO $database)
|
||||||
{
|
{
|
||||||
parent::__construct($database);
|
parent::__construct($database);
|
||||||
$this->periodDesignation = PeriodDesignationEnum::YEARLY();
|
$this->periodDesignation = PeriodDesignationEnum::YEARLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+109
-92
@@ -13,136 +13,153 @@ SET xmloption = content;
|
|||||||
SET client_min_messages = warning;
|
SET client_min_messages = warning;
|
||||||
SET row_security = off;
|
SET row_security = off;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS public."Arbeitszeiten";
|
|
||||||
DROP VIEW IF EXISTS public."Arbeitszeiten - Woche";
|
DROP VIEW IF EXISTS public."Arbeitszeiten - Woche";
|
||||||
DROP VIEW IF EXISTS public."Arbeitszeiten - Monat";
|
DROP VIEW IF EXISTS public."Arbeitszeiten - Monat";
|
||||||
DROP VIEW IF EXISTS public."Arbeitszeiten - Jahr";
|
DROP VIEW IF EXISTS public."Arbeitszeiten - Jahr";
|
||||||
|
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,
|
(
|
||||||
"Arbeitszeit" interval(6) null,
|
"Datum" date not 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
|
||||||
--
|
--
|
||||||
|
|
||||||
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") -
|
||||||
FROM public."Arbeitszeiten"
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
GROUP BY (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
ORDER BY (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
FROM public."Arbeitszeiten"
|
||||||
|
GROUP 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",
|
||||||
FROM public."Arbeitszeiten"
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
GROUP BY (date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone))
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
ORDER BY (date_trunc('month'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone));
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
|
FROM public."Arbeitszeiten"
|
||||||
|
GROUP 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",
|
||||||
FROM public."Arbeitszeiten"
|
(sum("Arbeitszeiten"."Arbeitszeit") -
|
||||||
GROUP BY (date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone))
|
((count("Arbeitszeiten"."Datum"))::double precision * '08:00:00'::interval)) AS "Überstunden",
|
||||||
ORDER BY (date_trunc('week'::text, ("Arbeitszeiten"."Datum")::timestamp with time zone));
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
|
FROM public."Arbeitszeiten"
|
||||||
|
GROUP 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
|
||||||
--
|
--
|
||||||
|
|
||||||
COPY public."Arbeitszeiten" ("Datum", "Arbeitszeit") FROM stdin;
|
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'),
|
||||||
2020-01-13 08:06:00
|
('2020-01-13', '08:06:00'),
|
||||||
2020-01-14 08:04:00
|
('2020-01-14', '08:04:00'),
|
||||||
2020-01-15 08:20:00
|
('2020-01-15', '08:20:00'),
|
||||||
2020-01-16 07:13:00
|
('2020-01-16', '07:13:00'),
|
||||||
2020-01-17 07:57:00
|
('2020-01-17', '07:57:00'),
|
||||||
2020-01-20 07:53:00
|
('2020-01-20', '07:53:00'),
|
||||||
2020-01-21 07:49:00
|
('2020-01-21', '07:49:00'),
|
||||||
2020-01-22 07:56:00
|
('2020-01-22', '07:56:00'),
|
||||||
2020-01-23 07:55:00
|
('2020-01-23', '07:55:00'),
|
||||||
2020-01-24 07:08:00
|
('2020-01-24', '07:08:00'),
|
||||||
2020-01-27 08:23:00
|
('2020-01-27', '08:23:00'),
|
||||||
2020-01-28 07:20:00
|
('2020-01-28', '07:20:00'),
|
||||||
2020-01-29 08:13:00
|
('2020-01-29', '08:13:00'),
|
||||||
2020-01-30 08:43:00
|
('2020-01-30', '08:43:00'),
|
||||||
2020-01-31 07:21:00
|
('2020-01-31', '07:21:00'),
|
||||||
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
|
||||||
@@ -13,8 +14,9 @@ class Api extends Module
|
|||||||
const FORMAT_DATE = 'string:regex(/\d{4}-\d{2}-\d{2}/)';
|
const FORMAT_DATE = 'string:regex(/\d{4}-\d{2}-\d{2}/)';
|
||||||
|
|
||||||
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,
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -32,4 +35,6 @@ class Api extends Module
|
|||||||
"message" => "string",
|
"message" => "string",
|
||||||
"path" => "string",
|
"path" => "string",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const TEST_ORIGIN = 'http://localhost:1234';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -8,13 +8,27 @@ use Helper\Api;
|
|||||||
|
|
||||||
class BrowseWorkingHoursMonthlyCest
|
class BrowseWorkingHoursMonthlyCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/views/working-hours/monthly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiTester $I
|
* @param ApiTester $I
|
||||||
*/
|
*/
|
||||||
public function browseWorkingHoursView(ApiTester $I): void
|
public function browseWorkingHoursView(ApiTester $I): void
|
||||||
{
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$I->sendGet('/views/working-hours/monthly');
|
$I->sendGet('/views/working-hours/monthly');
|
||||||
$I->seeResponseCodeIs(HttpCode::OK);
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,27 @@ use Helper\Api;
|
|||||||
|
|
||||||
class BrowseWorkingHoursWeeklyCest
|
class BrowseWorkingHoursWeeklyCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/views/working-hours/weekly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiTester $I
|
* @param ApiTester $I
|
||||||
*/
|
*/
|
||||||
public function browseWorkingHoursView(ApiTester $I): void
|
public function browseWorkingHoursView(ApiTester $I): void
|
||||||
{
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$I->sendGet('/views/working-hours/weekly');
|
$I->sendGet('/views/working-hours/weekly');
|
||||||
$I->seeResponseCodeIs(HttpCode::OK);
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,27 @@ use Helper\Api;
|
|||||||
|
|
||||||
class BrowseWorkingHoursYearlyCest
|
class BrowseWorkingHoursYearlyCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/views/working-hours/yearly');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiTester $I
|
* @param ApiTester $I
|
||||||
*/
|
*/
|
||||||
public function browseWorkingHoursView(ApiTester $I): void
|
public function browseWorkingHoursView(ApiTester $I): void
|
||||||
{
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$I->sendGet('/views/working-hours/yearly');
|
$I->sendGet('/views/working-hours/yearly');
|
||||||
$I->seeResponseCodeIs(HttpCode::OK);
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_VIEW_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,87 @@
|
|||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
use ApiTester;
|
use ApiTester;
|
||||||
|
use Codeception\Example;
|
||||||
use Codeception\Util\HttpCode;
|
use Codeception\Util\HttpCode;
|
||||||
use Helper\Api;
|
use Helper\Api;
|
||||||
|
|
||||||
class BrowseWorkingHoursCest
|
class BrowseWorkingHoursCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/working-hours');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ApiTester $I
|
* @param ApiTester $I
|
||||||
|
*
|
||||||
|
* @dataProvider validWorkingHoursFilterProvider
|
||||||
*/
|
*/
|
||||||
public function browseWorkingHours(ApiTester $I): void
|
public function browseEmptyWorkingHours(ApiTester $I): void
|
||||||
{
|
{
|
||||||
$I->sendGet('/working-hours');
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours', ['start-date' => '2020-04-01', 'end-date' => '2020-04-30']);
|
||||||
$I->seeResponseCodeIs(HttpCode::OK);
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseEquals('[]');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validWorkingHoursFilterProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[null]],
|
||||||
|
[['start-date' => '2020-01-01', 'end-date' => '2020-01-08']],
|
||||||
|
[['start-date' => '2020-01-01', 'end-date' => '2020-04-30']],
|
||||||
|
[['start-date' => '2020-03-01', 'end-date' => '2020-03-07']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
* @param Example $example
|
||||||
|
*
|
||||||
|
* @dataProvider validWorkingHoursFilterProvider
|
||||||
|
*/
|
||||||
|
public function browseWorkingHours(ApiTester $I, Example $example): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours', $example[0]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function invalidWorkingHoursFilterProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['2020-11-31'],
|
||||||
|
['2020-11'],
|
||||||
|
['2020'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ApiTester $I
|
||||||
|
* @param Example $example
|
||||||
|
*
|
||||||
|
* @dataProvider invalidWorkingHoursFilterProvider
|
||||||
|
*/
|
||||||
|
public function browseWorkingHoursWithWrongFilterDate(ApiTester $I, Example $example): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->sendGet('/working-hours', ['start-date' => $example[0]]);
|
||||||
|
$I->seeResponseCodeIs(HttpCode::BAD_REQUEST);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeResponseIsJson();
|
||||||
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,45 @@ use Helper\Api;
|
|||||||
|
|
||||||
class CreateWorkingHoursCest
|
class CreateWorkingHoursCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'POST');
|
||||||
|
$I->sendOptions('/working-hours');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
public function createWorkingHoursWithNewValidRecord(ApiTester $I): void
|
public function createWorkingHoursWithNewValidRecord(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');
|
||||||
|
$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('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$I->sendPost('/working-hours', [
|
$I->sendPost('/working-hours', [
|
||||||
'workingDay' => '2020-04-01',
|
'workingDay' => '2020-04-01',
|
||||||
'workingTime' => '08:00:00',
|
'workingTime' => '08:00:00',
|
||||||
]);
|
]);
|
||||||
$I->seeResponseCodeIs(HttpCode::CREATED);
|
$I->seeResponseCodeIs(HttpCode::CREATED);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
@@ -26,11 +56,14 @@ class CreateWorkingHoursCest
|
|||||||
{
|
{
|
||||||
$I->haveHttpHeader('accept', 'application/json');
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
$I->haveHttpHeader('content-type', 'application/json');
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$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->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
@@ -40,11 +73,14 @@ class CreateWorkingHoursCest
|
|||||||
{
|
{
|
||||||
$I->haveHttpHeader('accept', 'application/json');
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
$I->haveHttpHeader('content-type', 'application/json');
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$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->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,34 @@ use Helper\Api;
|
|||||||
|
|
||||||
class ReadWorkingHoursCest
|
class ReadWorkingHoursCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'GET');
|
||||||
|
$I->sendOptions('/working-hours/2020-04-15');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
public function readExistingWorkingHours(ApiTester $I): void
|
public function readExistingWorkingHours(ApiTester $I): void
|
||||||
{
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$I->sendGet('/working-hours/2020-01-15');
|
$I->sendGet('/working-hours/2020-01-15');
|
||||||
$I->seeResponseCodeIs(HttpCode::OK);
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function readNotExistingWorkingHours(ApiTester $I): void
|
public function readNotExistingWorkingHours(ApiTester $I): void
|
||||||
{
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$I->sendGet('/working-hours/2020-04-15');
|
$I->sendGet('/working-hours/2020-04-15');
|
||||||
$I->seeResponseCodeIs(HttpCode::NOT_FOUND);
|
$I->seeResponseCodeIs(HttpCode::NOT_FOUND);
|
||||||
|
$I->canSeeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
$I->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php /** @noinspection PhpUnused */
|
<?php
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Api\WorkingHours;
|
||||||
|
|
||||||
@@ -8,15 +9,30 @@ use Helper\Api;
|
|||||||
|
|
||||||
class UpdateWorkingHoursCest
|
class UpdateWorkingHoursCest
|
||||||
{
|
{
|
||||||
|
public static function preflight(ApiTester $I): void
|
||||||
|
{
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->haveHttpHeader('Access-Control-Request-Method', 'PUT');
|
||||||
|
$I->sendOptions('/working-hours/2020-01-15');
|
||||||
|
$I->seeResponseCodeIs(HttpCode::OK);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Origin', Api::TEST_ORIGIN);
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Methods');
|
||||||
|
$I->seeHttpHeader('Access-Control-Allow-Headers');
|
||||||
|
$I->seeHttpHeader('Access-Control-Max-Age');
|
||||||
|
}
|
||||||
|
|
||||||
public function updateWorkingHoursWithExistingValidRecord(ApiTester $I): void
|
public function updateWorkingHoursWithExistingValidRecord(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');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$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->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::WORKING_HOURS_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
@@ -25,11 +41,14 @@ class UpdateWorkingHoursCest
|
|||||||
{
|
{
|
||||||
$I->haveHttpHeader('accept', 'application/json');
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
$I->haveHttpHeader('content-type', 'application/json');
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$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->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
@@ -38,11 +57,14 @@ class UpdateWorkingHoursCest
|
|||||||
{
|
{
|
||||||
$I->haveHttpHeader('accept', 'application/json');
|
$I->haveHttpHeader('accept', 'application/json');
|
||||||
$I->haveHttpHeader('content-type', 'application/json');
|
$I->haveHttpHeader('content-type', 'application/json');
|
||||||
|
$I->haveHttpHeader('Origin', Api::TEST_ORIGIN);
|
||||||
$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->seeResponseIsJson();
|
$I->seeResponseIsJson();
|
||||||
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
$I->seeResponseMatchesJsonType(Api::ERROR_JSON_FORMAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
||||||
|
|
||||||
|
use Codeception\Test\Unit;
|
||||||
|
use Exception;
|
||||||
|
use PDO;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Slim\Psr7\Request;
|
||||||
|
use Slim\Psr7\Response;
|
||||||
|
|
||||||
|
class AbstractControllerTest extends Unit
|
||||||
|
{
|
||||||
|
|
||||||
|
protected const FICTIONAL_STATUS_CODE = 666;
|
||||||
|
|
||||||
|
protected ContainerInterface $container;
|
||||||
|
protected Request $request;
|
||||||
|
protected Response|MockObject $response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
* @throws \PHPUnit\Framework\MockObject\Exception
|
||||||
|
*/
|
||||||
|
protected function _before(): void
|
||||||
|
{
|
||||||
|
parent::_before();
|
||||||
|
/** @noinspection SpellCheckingInspection */
|
||||||
|
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
||||||
|
'has' => true,
|
||||||
|
'get' => new PDO(
|
||||||
|
'pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass'
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
$this->response = $this->createMock(Response::class);
|
||||||
|
$this->response->expects($this->any())->method('getStatusCode')->willReturn(self::FICTIONAL_STATUS_CODE);
|
||||||
|
$this->response->expects($this->any())->method('getBody')->willReturn(
|
||||||
|
$this->makeEmpty(StreamInterface::class, [
|
||||||
|
'write' => function (mixed $data) {
|
||||||
|
$this->assertIsString($data);
|
||||||
|
$this->assertJson($data);
|
||||||
|
return strlen($data);
|
||||||
|
},
|
||||||
|
'getContents' => 'abc',
|
||||||
|
])
|
||||||
|
);
|
||||||
|
$this->response->expects($this->any())->method('withHeader')->willReturn($this->response);
|
||||||
|
$this->response->expects($this->any())->method('withStatus')->willReturn($this->response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,127 +2,315 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
||||||
|
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Attribute\DataProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
use PDO;
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
use Slim\Exception\HttpBadRequestException;
|
use Slim\Exception\HttpBadRequestException;
|
||||||
use Slim\Exception\HttpInternalServerErrorException;
|
|
||||||
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;
|
||||||
|
|
||||||
class WorkingHoursControllerTest extends Unit
|
class WorkingHoursControllerTest extends AbstractControllerTest
|
||||||
{
|
{
|
||||||
|
|
||||||
const EXISTING_DATE = '2020-01-28';
|
protected const int FICTIONAL_STATUS_CODE = 666;
|
||||||
const EXISTING_INTERVAL = '07:20:00';
|
public const string EXISTING_DATE = '2020-01-28';
|
||||||
const NEW_DATE = '2020-04-01';
|
public const string EXISTING_INTERVAL = '07:20:00';
|
||||||
const NEW_INTERVAL = '07:59:00';
|
public const string NEW_DATE = '2020-04-01';
|
||||||
|
public const string NEW_INTERVAL = '07:59:00';
|
||||||
protected ContainerInterface $container;
|
|
||||||
protected Request $request;
|
|
||||||
protected Response $response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
* @throws \PHPUnit\Framework\MockObject\Exception
|
||||||
*/
|
*/
|
||||||
protected function _before(): void
|
protected function _before(): void
|
||||||
{
|
{
|
||||||
parent::_before();
|
parent::_before();
|
||||||
/** @noinspection SpellCheckingInspection */
|
|
||||||
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
|
||||||
'has' => true,
|
|
||||||
'get' => new PDO('pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass')
|
|
||||||
]);
|
|
||||||
$this->request = $this->makeEmpty(Request::class, [
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::EXISTING_DATE,
|
'workingDay' => self::EXISTING_DATE,
|
||||||
'workingTime' => self::EXISTING_INTERVAL,
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
]
|
'isHomeOfficeDay' => true,
|
||||||
]);
|
],
|
||||||
$this->response = $this->makeEmpty(Response::class, [
|
|
||||||
'getBody' => $this->makeEmpty(StreamInterface::class, [
|
|
||||||
'write' => function (mixed $data) {
|
|
||||||
$this->assertIsString($data);
|
|
||||||
$this->assertJson($data);
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
'withHeader' => $this->makeEmpty(Response::class, [
|
|
||||||
'withStatus' => $this->makeEmpty(Response::class),
|
|
||||||
]),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testConstructWithNonDatabase(): void
|
public function testConstructWithNonDatabase(): void
|
||||||
{
|
{
|
||||||
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
||||||
'has' => false
|
'has' => false,
|
||||||
]);
|
]);
|
||||||
$this->expectException(NotDatabasesException::class);
|
$this->expectException(NotDatabasesException::class);
|
||||||
new WorkingHoursController($this->container);
|
new WorkingHoursController($this->container);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws NotDatabasesException
|
* @return array<int, array<int, array<string, string>>>
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public function testBrowse(): void
|
public function valideFilterDataProvider(): array
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursController($this->container);
|
return [
|
||||||
$response = $controller->browse($this->request, $this->response, []);
|
[
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
[],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param array<string, string> $queryParams
|
||||||
|
*
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
* @throws HttpBadRequestException
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws HttpNotFoundException
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('valideFilterDataProvider')]
|
||||||
|
public function testBrowseWithValideParameter(array $queryParams): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getQueryParams' => $queryParams,
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$response = $controller->browse($this->request, $this->response, []);
|
||||||
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int, array<string, string|null>>>
|
||||||
|
*/
|
||||||
|
public function invalideFilterDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '',
|
||||||
|
'end-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
//
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-00',
|
||||||
|
'end-date' => '2020-04-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-00',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020-04-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020-04-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-00',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
//
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03',
|
||||||
|
'end-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020-03',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
//
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020',
|
||||||
|
'end-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'end-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020-03-01',
|
||||||
|
'end-date' => '2020',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'start-date' => '2020',
|
||||||
|
'end-date' => '2020-03-31',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $queryParams
|
||||||
|
*
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('invalideFilterDataProvider')]
|
||||||
|
public function testBrowseWithInvalideParameter(array $queryParams): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getQueryParams' => $queryParams,
|
||||||
|
]);
|
||||||
|
$controller = new WorkingHoursController($this->container);
|
||||||
|
$this->expectException(HttpBadRequestException::class);
|
||||||
|
$controller->browse($this->request, $this->response, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testUpdateExistRecord(): void
|
public function testUpdateExistRecord(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$response = $controller->update($this->request, $this->response, [
|
$response = $controller->update($this->request, $this->response, [
|
||||||
'id' => self::EXISTING_DATE
|
'id' => self::EXISTING_DATE,
|
||||||
]);
|
]);
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
* @throws HttpBadRequestException
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws HttpNotFoundException
|
* @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 NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testUpdateNotExistRecord(): void
|
public function testUpdateNotExistRecord(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$this->expectException(HttpNotFoundException::class);
|
$this->expectException(HttpNotFoundException::class);
|
||||||
$controller->update($this->request, $this->response, [
|
$controller->update($this->request, $this->response, [
|
||||||
'id' => self::NEW_DATE
|
'id' => self::NEW_DATE,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[][]
|
* @return string[][]
|
||||||
*/
|
*/
|
||||||
public function invalidDataProvider(): array
|
public function invalidCreatDataProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
self::NEW_DATE,
|
self::NEW_DATE,
|
||||||
'07:59'
|
'07:59',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'2020-13-33',
|
'2020-13-33',
|
||||||
self::NEW_INTERVAL
|
self::NEW_INTERVAL,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -131,21 +319,20 @@ class WorkingHoursControllerTest extends Unit
|
|||||||
* @param string $date
|
* @param string $date
|
||||||
* @param string $time
|
* @param string $time
|
||||||
*
|
*
|
||||||
* @dataProvider invalidDataProvider
|
* @throws ContainerExceptionInterface
|
||||||
*
|
|
||||||
* @throws HttpBadRequestException
|
|
||||||
* @throws HttpConflictRequestException
|
|
||||||
* @throws HttpInternalServerErrorException
|
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testCreatInvalidData(string $date, string $time): void
|
#[DataProvider('invalidCreatDataProvider')]
|
||||||
|
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,
|
||||||
]
|
'isHomeOfficeDay' => $isHomeOffice,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$this->expectException(HttpBadRequestException::class);
|
$this->expectException(HttpBadRequestException::class);
|
||||||
@@ -153,39 +340,60 @@ class WorkingHoursControllerTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws HttpBadRequestException
|
* @throws ContainerExceptionInterface
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws HttpConflictRequestException
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testCreatNewRecord(): void
|
public function testCreatNewRecord(): void
|
||||||
{
|
{
|
||||||
$this->request = $this->makeEmpty(Request::class, [
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::NEW_DATE,
|
'workingDay' => self::NEW_DATE,
|
||||||
'workingTime' => self::NEW_INTERVAL,
|
'workingTime' => self::NEW_INTERVAL,
|
||||||
]
|
'isHomeOfficeDay' => true,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$response = $controller->creat($this->request, $this->response, []);
|
$response = $controller->creat($this->request, $this->response, []);
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws HttpBadRequestException
|
* @throws ContainerExceptionInterface
|
||||||
* @throws HttpConflictRequestException
|
|
||||||
* @throws HttpInternalServerErrorException
|
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function testCreatNewRecordWithoutHomeOffice(): void
|
||||||
|
{
|
||||||
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
|
'getParsedBody' => [
|
||||||
|
'workingDay' => self::NEW_DATE,
|
||||||
|
'workingTime' => self::NEW_INTERVAL,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$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
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testCreatExistRecord(): void
|
public function testCreatExistRecord(): void
|
||||||
{
|
{
|
||||||
$this->request = $this->makeEmpty(Request::class, [
|
$this->request = $this->makeEmpty(Request::class, [
|
||||||
'getParsedBody' => [
|
'getParsedBody' => [
|
||||||
'workingDay' => self::EXISTING_DATE,
|
'workingDay' => self::EXISTING_DATE,
|
||||||
'workingTime' => self::EXISTING_INTERVAL,
|
'workingTime' => self::EXISTING_INTERVAL,
|
||||||
]
|
'isHomeOfficeDay' => true,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$this->expectException(HttpConflictRequestException::class);
|
$this->expectException(HttpConflictRequestException::class);
|
||||||
@@ -193,30 +401,33 @@ class WorkingHoursControllerTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws ContainerExceptionInterface
|
||||||
* @throws HttpNotFoundException
|
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testReadExistRecord(): void
|
public function testReadExistRecord(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$response = $controller->read($this->request, $this->response, [
|
$response = $controller->read($this->request, $this->response, [
|
||||||
'id' => self::EXISTING_DATE
|
'id' => self::EXISTING_DATE,
|
||||||
]);
|
]);
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws HttpInternalServerErrorException
|
* @throws ContainerExceptionInterface
|
||||||
* @throws HttpNotFoundException
|
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testReadNotExistRecord(): void
|
public function testReadNotExistRecord(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursController($this->container);
|
$controller = new WorkingHoursController($this->container);
|
||||||
$this->expectException(HttpNotFoundException::class);
|
$this->expectException(HttpNotFoundException::class);
|
||||||
$controller->read($this->request, $this->response, [
|
$controller->read($this->request, $this->response, [
|
||||||
'id' => self::NEW_DATE
|
'id' => self::NEW_DATE,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,49 +2,30 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Controller;
|
||||||
|
|
||||||
use Codeception\Test\Unit;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use PDO;
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Http\Message\StreamInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
use Slim\Psr7\Request;
|
use Slim\Psr7\Request;
|
||||||
use Slim\Psr7\Response;
|
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\NotDatabasesException;
|
use TorstenHettstedt\TimekeepingApi\Controller\NotDatabasesException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursViewController;
|
use TorstenHettstedt\TimekeepingApi\Controller\WorkingHoursViewController;
|
||||||
|
|
||||||
class WorkingHoursViewControllerTest extends Unit
|
class WorkingHoursViewControllerTest extends AbstractControllerTest
|
||||||
{
|
{
|
||||||
protected ContainerInterface $container;
|
|
||||||
protected Request $request;
|
|
||||||
protected Response $response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
* @throws \PHPUnit\Framework\MockObject\Exception
|
||||||
*/
|
*/
|
||||||
protected function _before(): void
|
protected function _before(): void
|
||||||
{
|
{
|
||||||
parent::_before();
|
parent::_before();
|
||||||
/** @noinspection SpellCheckingInspection */
|
$this->request = $this->makeEmpty(Request::class);
|
||||||
$this->container = $this->makeEmpty(ContainerInterface::class, [
|
|
||||||
'has' => true,
|
|
||||||
'get' => new PDO('pgsql:host=psql.torsten-hettstedt.net;port=5432;dbname=testdb;user=bruce;password=mypass')
|
|
||||||
]);
|
|
||||||
$this->request = $this->makeEmpty(Request::class, []);
|
|
||||||
$this->response = $this->makeEmpty(Response::class, [
|
|
||||||
'getBody' => $this->makeEmpty(StreamInterface::class, [
|
|
||||||
'write' => function (mixed $data) {
|
|
||||||
$this->assertIsString($data);
|
|
||||||
$this->assertJson($data);
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
'withHeader' => $this->makeEmpty(Response::class, [
|
|
||||||
'withStatus' => $this->makeEmpty(Response::class),
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws NotDatabasesException
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testConstructWithNonDatabase(): void
|
public function testConstructWithNonDatabase(): void
|
||||||
@@ -57,32 +38,44 @@ class WorkingHoursViewControllerTest extends Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testBrowseMonthly(): void
|
public function testBrowseMonthly(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursViewController($this->container);
|
$controller = new WorkingHoursViewController($this->container);
|
||||||
$response = $controller->browseMonthly($this->request, $this->response, []);
|
$response = $controller->browseMonthly($this->request, $this->response, []);
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testBrowseYearly(): void
|
public function testBrowseYearly(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursViewController($this->container);
|
$controller = new WorkingHoursViewController($this->container);
|
||||||
$response = $controller->browseYearly($this->request, $this->response, []);
|
$response = $controller->browseYearly($this->request, $this->response, []);
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @throws NotDatabasesException
|
||||||
|
* @throws ContainerExceptionInterface
|
||||||
|
* @throws NotFoundExceptionInterface
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testBrowseWeekly(): void
|
public function testBrowseWeekly(): void
|
||||||
{
|
{
|
||||||
$controller = new WorkingHoursViewController($this->container);
|
$controller = new WorkingHoursViewController($this->container);
|
||||||
$response = $controller->browseWeekly($this->request, $this->response, []);
|
$response = $controller->browseWeekly($this->request, $this->response, []);
|
||||||
$this->assertInstanceOf(Response::class, $response);
|
$this->assertEquals(self::FICTIONAL_STATUS_CODE, $response->getStatusCode());
|
||||||
|
$this->assertNotEmpty($response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Middleware;
|
|||||||
use Codeception\Stub\Expected;
|
use Codeception\Stub\Expected;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
use Psr\Http\Message\ResponseFactoryInterface;
|
use Psr\Http\Message\ResponseFactoryInterface;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
@@ -12,11 +13,15 @@ use Psr\Http\Message\StreamInterface;
|
|||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Slim\App;
|
use Slim\App;
|
||||||
use Slim\Exception\HttpException;
|
use Slim\Exception\HttpException;
|
||||||
|
use Slim\Interfaces\RouteParserInterface;
|
||||||
|
use Slim\Routing\RouteContext;
|
||||||
|
use Slim\Routing\RoutingResults;
|
||||||
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
|
use TorstenHettstedt\TimekeepingApi\Middleware\ErrorHandler;
|
||||||
|
|
||||||
class ErrorHandlerTest extends Unit
|
class ErrorHandlerTest extends Unit
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** @var App<ContainerInterface> */
|
||||||
protected App $app;
|
protected App $app;
|
||||||
protected ServerRequestInterface $request;
|
protected ServerRequestInterface $request;
|
||||||
protected Exception $exception;
|
protected Exception $exception;
|
||||||
@@ -35,12 +40,25 @@ class ErrorHandlerTest extends Unit
|
|||||||
'write' => function (mixed $data) {
|
'write' => function (mixed $data) {
|
||||||
$this->assertIsString($data);
|
$this->assertIsString($data);
|
||||||
$this->assertJson($data);
|
$this->assertJson($data);
|
||||||
|
return strlen($data);
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
$this->request = $this->makeEmpty(ServerRequestInterface::class);
|
$this->request = $this->makeEmpty(ServerRequestInterface::class, [
|
||||||
|
'getAttribute' => function ($name) {
|
||||||
|
/** @noinspection PhpSwitchCanBeReplacedWithMatchExpressionInspection */
|
||||||
|
switch ($name) {
|
||||||
|
case RouteContext::ROUTE_PARSER:
|
||||||
|
return $this->makeEmpty(RouteParserInterface::class);
|
||||||
|
case RouteContext::ROUTING_RESULTS:
|
||||||
|
return $this->makeEmpty(RoutingResults::class);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,16 +85,15 @@ class ErrorHandlerTest extends Unit
|
|||||||
public function testUseWithHttpException(): void
|
public function testUseWithHttpException(): void
|
||||||
{
|
{
|
||||||
$this->exception = $this->make(HttpException::class, [
|
$this->exception = $this->make(HttpException::class, [
|
||||||
'message' => '',
|
'message' => '',
|
||||||
'code' => 400,
|
'code' => 400,
|
||||||
'file' => '/path(to/file',
|
'file' => '/path(to/file',
|
||||||
'getTitle' => Expected::once('The Title'),
|
'getTitle' => Expected::once('The Title'),
|
||||||
]);
|
]);
|
||||||
$this->logger = $this->makeEmpty(LoggerInterface::class, []);
|
$this->logger = $this->makeEmpty(LoggerInterface::class);
|
||||||
|
|
||||||
$middleWare = new ErrorHandler($this->app);
|
$middleWare = new ErrorHandler($this->app);
|
||||||
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,7 +106,7 @@ class ErrorHandlerTest extends Unit
|
|||||||
'code' => 400,
|
'code' => 400,
|
||||||
'file' => '/path(to/file',
|
'file' => '/path(to/file',
|
||||||
]);
|
]);
|
||||||
$this->logger = $this->makeEmpty(LoggerInterface::class, []);
|
$this->logger = $this->makeEmpty(LoggerInterface::class);
|
||||||
|
|
||||||
$middleWare = new ErrorHandler($this->app);
|
$middleWare = new ErrorHandler($this->app);
|
||||||
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
$middleWare($this->request, $this->exception, true, true, true, $this->logger);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@@ -16,13 +17,14 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array<DateTime|DateInterval|array<string,string>>>
|
||||||
*/
|
*/
|
||||||
public function workingHoursProvider(): array
|
public function workingHoursProvider(): array
|
||||||
{
|
{
|
||||||
@@ -31,17 +33,21 @@ class WorkingHoursTest extends Unit
|
|||||||
new DateTime('2020-11-30'),
|
new DateTime('2020-11-30'),
|
||||||
new DateInterval("PT8H"),
|
new DateInterval("PT8H"),
|
||||||
[
|
[
|
||||||
'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'),
|
||||||
new DateInterval("PT7H45M"),
|
new DateInterval("PT7H45M"),
|
||||||
[
|
[
|
||||||
'workingDay' => '2021-05-03',
|
'workingDay' => '2021-05-03',
|
||||||
'workingTime' => '07:45:00',
|
'workingTime' => '07:45:00',
|
||||||
|
'isHomeOffice' => false,
|
||||||
],
|
],
|
||||||
|
false,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -49,17 +55,14 @@ class WorkingHoursTest extends Unit
|
|||||||
/**
|
/**
|
||||||
* @param DateTime $date
|
* @param DateTime $date
|
||||||
* @param DateInterval $interval
|
* @param DateInterval $interval
|
||||||
* @param array<string, string> $should_json
|
* @param array<string, string> $shouldJson
|
||||||
*
|
* @param bool $isHomeOffice
|
||||||
* @dataProvider workingHoursProvider
|
|
||||||
*/
|
*/
|
||||||
public function testWorkingHoursWithContent(DateTime $date, DateInterval $interval, array $should_json): void
|
#[DataProvider('workingHoursProvider')]
|
||||||
|
public function testWorkingHoursWithContent(DateTime $date, DateInterval $interval, array $shouldJson, bool $isHomeOffice): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$obj = new WorkingHours();
|
$obj = new WorkingHours();
|
||||||
$obj
|
$obj->setWorkingDay($date)->setWorkingTime($interval)->setIsHomeOffice($isHomeOffice);
|
||||||
->setWorkingDay($date)
|
|
||||||
->setWorkingTime($interval);
|
|
||||||
|
|
||||||
$this->assertNotNull($obj->getWorkingDay());
|
$this->assertNotNull($obj->getWorkingDay());
|
||||||
$this->assertInstanceOf(DateTime::class, $obj->getWorkingDay());
|
$this->assertInstanceOf(DateTime::class, $obj->getWorkingDay());
|
||||||
@@ -69,8 +72,37 @@ 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->assertEquals($isHomeOffice, $obj->isHomeOffice());
|
||||||
|
|
||||||
$json = $obj->jsonSerialize();
|
$json = $obj->jsonSerialize();
|
||||||
$this->assertIsArray($json);
|
$this->assertEquals($shouldJson, $json);
|
||||||
$this->assertEquals($should_json, $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->setWorkingDay($date)->setWorkingTime($interval);
|
||||||
|
|
||||||
|
$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->assertFalse($obj->isHomeOffice());
|
||||||
|
|
||||||
|
$json = $obj->jsonSerialize();
|
||||||
|
$this->assertEquals($shouldJson, $json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Models;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@@ -13,101 +14,157 @@ class WorkingHoursViewTest extends Unit
|
|||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{DateTime,PeriodDesignationEnum,int,DateInterval,DateInterval,array<string,mixed>}>
|
||||||
*/
|
*/
|
||||||
public function workingHoursProvider(): array
|
public function workingHoursProvider(): array
|
||||||
{
|
{
|
||||||
|
$date = new DateTime('2020-11-30');
|
||||||
|
$work_days = 15;
|
||||||
|
$home_work_days = 12;
|
||||||
|
$total_hours = new DateInterval("PT32H22M");
|
||||||
|
$overtime = new DateInterval("PT22M");
|
||||||
|
$absence_time = clone $overtime;
|
||||||
|
$absence_time->invert = 1;
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
new DateTime('2020-11-30'),
|
$date,
|
||||||
PeriodDesignationEnum::WEEKLY(),
|
PeriodDesignationEnum::WEEKLY,
|
||||||
15,
|
$work_days,
|
||||||
new DateInterval("PT32H22M"),
|
$total_hours,
|
||||||
new DateInterval("PT22M"),
|
$home_work_days,
|
||||||
|
$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'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
new DateTime('2020-11-30'),
|
$date,
|
||||||
PeriodDesignationEnum::MONTHLY(),
|
PeriodDesignationEnum::WEEKLY,
|
||||||
15,
|
$work_days,
|
||||||
new DateInterval("PT32H22M"),
|
$total_hours,
|
||||||
new DateInterval("PT22M"),
|
$home_work_days,
|
||||||
|
$absence_time,
|
||||||
|
[
|
||||||
|
'period' => '2020#49',
|
||||||
|
'periodDesignation' => 'weekly',
|
||||||
|
'workingDays' => 15,
|
||||||
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
|
'overtime' => '-00:22:00'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$date,
|
||||||
|
PeriodDesignationEnum::MONTHLY,
|
||||||
|
$work_days,
|
||||||
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
|
$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'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
new DateTime('2020-11-30'),
|
$date,
|
||||||
PeriodDesignationEnum::YEARLY(),
|
PeriodDesignationEnum::MONTHLY,
|
||||||
15,
|
$work_days,
|
||||||
new DateInterval("PT32H22M"),
|
$total_hours,
|
||||||
new DateInterval("PT22M"),
|
$home_work_days,
|
||||||
|
$absence_time,
|
||||||
|
[
|
||||||
|
'period' => '2020-11',
|
||||||
|
'periodDesignation' => 'monthly',
|
||||||
|
'workingDays' => 15,
|
||||||
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
|
'overtime' => '-00:22:00'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
$date,
|
||||||
|
PeriodDesignationEnum::YEARLY,
|
||||||
|
$work_days,
|
||||||
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
|
$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'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
$date,
|
||||||
|
PeriodDesignationEnum::YEARLY,
|
||||||
|
$work_days,
|
||||||
|
$total_hours,
|
||||||
|
$home_work_days,
|
||||||
|
$absence_time,
|
||||||
|
[
|
||||||
|
'period' => '2020',
|
||||||
|
'periodDesignation' => 'yearly',
|
||||||
|
'workingDays' => 15,
|
||||||
|
'totalHours' => '32:22:00',
|
||||||
|
'homeOfficeDays' => $home_work_days,
|
||||||
|
'overtime' => '-00:22:00'
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DateTimeInterface $period
|
* @param DateTimeInterface $period
|
||||||
* @param PeriodDesignationEnum $periodDesignation
|
* @param PeriodDesignationEnum $periodDesignation
|
||||||
* @param int $workingDays
|
* @param int $workingDays
|
||||||
* @param DateInterval $totalHours
|
* @param DateInterval $totalHours
|
||||||
* @param DateInterval $overtime
|
* @param int $homeWorkDays ,
|
||||||
* @param array<string, string> $should_json
|
* @param DateInterval $overtime
|
||||||
*
|
* @param array<string, string> $shouldJson
|
||||||
* @dataProvider workingHoursProvider
|
|
||||||
*/
|
*/
|
||||||
|
#[DataProvider('workingHoursProvider')]
|
||||||
public function testWorkingHoursViewWithContent(
|
public function testWorkingHoursViewWithContent(
|
||||||
DateTimeInterface $period,
|
DateTimeInterface $period,
|
||||||
PeriodDesignationEnum $periodDesignation,
|
PeriodDesignationEnum $periodDesignation,
|
||||||
int $workingDays,
|
int $workingDays,
|
||||||
DateInterval $totalHours,
|
DateInterval $totalHours,
|
||||||
|
int $homeWorkDays,
|
||||||
DateInterval $overtime,
|
DateInterval $overtime,
|
||||||
array $should_json
|
array $shouldJson
|
||||||
): void
|
): void {
|
||||||
{
|
$obj = new WorkingHoursView($period, $periodDesignation, $workingDays, $totalHours, $homeWorkDays, $overtime);
|
||||||
|
|
||||||
$obj = new WorkingHoursView($period, $periodDesignation, $workingDays, $totalHours, $overtime);
|
|
||||||
|
|
||||||
$this->assertNotNull($obj->getPeriod());
|
|
||||||
$this->assertInstanceOf(DateTimeInterface::class, $obj->getPeriod());
|
$this->assertInstanceOf(DateTimeInterface::class, $obj->getPeriod());
|
||||||
$this->assertEquals($period, $obj->getPeriod());
|
$this->assertEquals($period, $obj->getPeriod());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getPeriodDesignation());
|
|
||||||
$this->assertInstanceOf(PeriodDesignationEnum::class, $obj->getPeriodDesignation());
|
$this->assertInstanceOf(PeriodDesignationEnum::class, $obj->getPeriodDesignation());
|
||||||
$this->assertEquals($periodDesignation, $obj->getPeriodDesignation());
|
$this->assertEquals($periodDesignation, $obj->getPeriodDesignation());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getWorkingDays());
|
|
||||||
$this->assertIsInt($obj->getWorkingDays());
|
|
||||||
$this->assertEquals($workingDays, $obj->getWorkingDays());
|
$this->assertEquals($workingDays, $obj->getWorkingDays());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getTotalHours());
|
|
||||||
$this->assertInstanceOf(DateInterval::class, $obj->getTotalHours());
|
$this->assertInstanceOf(DateInterval::class, $obj->getTotalHours());
|
||||||
$this->assertEquals($totalHours, $obj->getTotalHours());
|
$this->assertEquals($totalHours, $obj->getTotalHours());
|
||||||
|
|
||||||
$this->assertNotNull($obj->getOvertime());
|
$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());
|
||||||
|
|
||||||
|
|
||||||
$json = $obj->jsonSerialize();
|
$json = $obj->jsonSerialize();
|
||||||
$this->assertIsArray($json);
|
$this->assertEquals($shouldJson, $json);
|
||||||
$this->assertEquals($should_json, $json);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateInterval;
|
use DateInterval;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
@@ -10,39 +11,115 @@ use InvalidArgumentException;
|
|||||||
use PDO;
|
use PDO;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
use TorstenHettstedt\TimekeepingApi\Models\ModelInterface;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHours;
|
||||||
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryBadWhereDataException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordAlreadyExistException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordAlreadyExistException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursRepository;
|
||||||
|
|
||||||
class WorkingHoursRepositoryTest extends Unit
|
class WorkingHoursRepositoryTest extends Unit
|
||||||
{
|
{
|
||||||
const EXISTING_DATE = '2020-01-28';
|
public const string EXISTING_DATE = '2020-01-28';
|
||||||
const EXISTING_INTERVAL = 'PT7H20M';
|
public const string EXISTING_INTERVAL = 'PT7H20M';
|
||||||
const NEW_DATE = '2020-04-01';
|
public const string NEW_DATE = '2020-04-01';
|
||||||
const NEW_INTERVAL = 'PT7H59M';
|
public const string NEW_INTERVAL = 'PT7H59M';
|
||||||
const RECORDS_COUNT = 61;
|
public const int RECORDS_COUNT = 61;
|
||||||
|
|
||||||
protected PDO $pdoObject;
|
protected PDO $pdoObject;
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function testFindAll(): void
|
public function testFindAllWithBlankParameter(): void
|
||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$models = $repository->findAll();
|
$models = $repository->findAll();
|
||||||
$this->assertIsArray($models);
|
|
||||||
$this->assertContainsOnly(WorkingHours::class, $models);
|
$this->assertContainsOnlyInstancesOf(WorkingHours::class, $models);
|
||||||
$this->assertCount(self::RECORDS_COUNT, $models);
|
$this->assertCount(self::RECORDS_COUNT, $models);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int|string|null>>
|
||||||
|
*/
|
||||||
|
public function valideFilterParameter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[null, null, 61,],
|
||||||
|
['2020-04-01', null, 0,],
|
||||||
|
['2020-04-01', '2020-04-30', 0,],
|
||||||
|
[null, '2020-04-30', 61,],
|
||||||
|
['2020-03-01', null, 22,],
|
||||||
|
['2020-03-01', '2020-03-31', 22,],
|
||||||
|
['2020-03-01', '2020-03-07', 5,],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $start
|
||||||
|
* @param string|null $ende
|
||||||
|
* @param int $count
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('valideFilterParameter')]
|
||||||
|
public function testFindAllWithValideParameter(?string $start, ?string $ende, int $count): void
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
|
$models = $repository->findFiltered($start, $ende);
|
||||||
|
|
||||||
|
$this->assertContainsOnlyInstancesOf(WorkingHours::class, $models);
|
||||||
|
$this->assertCount($count, $models);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, array<int|string|null>>
|
||||||
|
*/
|
||||||
|
public function invalideFilterParameter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['2020-04-31', null],
|
||||||
|
[null, '2020-04-31'],
|
||||||
|
['2020-04-00', '2020-04-31'],
|
||||||
|
['2020-04-01', '2020-04-31'],
|
||||||
|
['2020-04-00', '2020-04-30'],
|
||||||
|
//
|
||||||
|
['2020-04', null],
|
||||||
|
[null, '2020-04'],
|
||||||
|
['2020-04', '2020-04'],
|
||||||
|
['2020-04-01', '2020-04'],
|
||||||
|
['2020-04', '2020-04-30'],
|
||||||
|
//
|
||||||
|
['2020', null],
|
||||||
|
[null, '2020'],
|
||||||
|
['2020', '2020'],
|
||||||
|
['2020-04-01', '2020'],
|
||||||
|
['2020', '2020-04-30'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $start
|
||||||
|
* @param string|null $ende
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
#[DataProvider('invalideFilterParameter')]
|
||||||
|
public function testFindAllWithInvalideParameter(?string $start, ?string $ende): void
|
||||||
|
{
|
||||||
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
|
$this->expectException(RepositoryBadWhereDataException::class);
|
||||||
|
$repository->findFiltered($start, $ende);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RepositoryRecordNotFoundException
|
* @throws RepositoryRecordNotFoundException
|
||||||
*/
|
*/
|
||||||
@@ -50,7 +127,6 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$model = $repository->findByKey(self::EXISTING_DATE);
|
$model = $repository->findByKey(self::EXISTING_DATE);
|
||||||
$this->assertInstanceOf(WorkingHours::class, $model);
|
|
||||||
$this->assertEquals(new DateTime(self::EXISTING_DATE), $model->getWorkingDay());
|
$this->assertEquals(new DateTime(self::EXISTING_DATE), $model->getWorkingDay());
|
||||||
$this->assertEquals(new DateInterval(self::EXISTING_INTERVAL), $model->getWorkingTime());
|
$this->assertEquals(new DateInterval(self::EXISTING_INTERVAL), $model->getWorkingTime());
|
||||||
}
|
}
|
||||||
@@ -81,12 +157,12 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$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);
|
||||||
$this->assertInstanceOf(WorkingHours::class, $model);
|
|
||||||
$this->assertEquals(new DateTime(self::NEW_DATE), $model->getWorkingDay());
|
$this->assertEquals(new DateTime(self::NEW_DATE), $model->getWorkingDay());
|
||||||
$this->assertEquals(new DateInterval(self::NEW_INTERVAL), $model->getWorkingTime());
|
$this->assertEquals(new DateInterval(self::NEW_INTERVAL), $model->getWorkingTime());
|
||||||
$this->assertCount(self::RECORDS_COUNT + 1, $repository->findAll());
|
$this->assertCount(self::RECORDS_COUNT + 1, $repository->findAll());
|
||||||
@@ -99,8 +175,9 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$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);
|
||||||
@@ -126,8 +203,9 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$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);
|
||||||
@@ -142,8 +220,9 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$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);
|
||||||
@@ -169,8 +248,9 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$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);
|
||||||
@@ -184,8 +264,9 @@ class WorkingHoursRepositoryTest extends Unit
|
|||||||
{
|
{
|
||||||
$repository = new WorkingHoursRepository($this->pdoObject);
|
$repository = new WorkingHoursRepository($this->pdoObject);
|
||||||
$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);
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
namespace TorstenHettstedt\TimekeepingApi\Tests\Unit\Repositories;
|
||||||
|
|
||||||
|
use Codeception\Attribute\DataProvider;
|
||||||
use Codeception\Test\Unit;
|
use Codeception\Test\Unit;
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use Exception;
|
|
||||||
use PDO;
|
use PDO;
|
||||||
use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
use TorstenHettstedt\TimekeepingApi\Models\WorkingHoursView;
|
||||||
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
use TorstenHettstedt\TimekeepingApi\Repositories\RepositoryRecordNotFoundException;
|
||||||
@@ -15,95 +15,101 @@ use TorstenHettstedt\TimekeepingApi\Repositories\WorkingHoursYearlyViewRepositor
|
|||||||
|
|
||||||
class WorkingHoursViewRepositoryTest extends Unit
|
class WorkingHoursViewRepositoryTest extends Unit
|
||||||
{
|
{
|
||||||
protected PDO $pdoObject;
|
protected PDO $pdoObject;
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{string, int}>
|
||||||
*/
|
*/
|
||||||
public function listObjectProvider(): array
|
public function listObjectProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[WorkingHoursYearlyViewRepository::class, 1],
|
[WorkingHoursYearlyViewRepository::class, 1],
|
||||||
[WorkingHoursMonthlyViewRepository::class, 3],
|
[WorkingHoursMonthlyViewRepository::class, 3],
|
||||||
[WorkingHoursWeeklyViewRepository::class, 13],
|
[WorkingHoursWeeklyViewRepository::class, 13],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{string, string, int, DateTime}>
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public function existingObjectProvider(): array
|
public function existingObjectProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[WorkingHoursYearlyViewRepository::class, '2020', 61, new DateTime('2020-01-01', new DateTimeZone('UCT'))],
|
[
|
||||||
[WorkingHoursMonthlyViewRepository::class, '2020 February', 20, new DateTime('2020-02-01', new DateTimeZone('UCT'))],
|
WorkingHoursYearlyViewRepository::class,
|
||||||
[WorkingHoursWeeklyViewRepository::class, '2020#03', 5, new DateTime('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,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array[]
|
* @return array<array{string, string}>
|
||||||
*/
|
*/
|
||||||
public function notExistingObjectProvider(): array
|
public function notExistingObjectProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[WorkingHoursYearlyViewRepository::class, '2022',],
|
[WorkingHoursYearlyViewRepository::class, '2022',],
|
||||||
[WorkingHoursMonthlyViewRepository::class, '2020 May',],
|
[WorkingHoursMonthlyViewRepository::class, '2020 May',],
|
||||||
[WorkingHoursWeeklyViewRepository::class, '2020#30',],
|
[WorkingHoursWeeklyViewRepository::class, '2020#30',],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[DataProvider(('listObjectProvider'))]
|
||||||
* @param string $period_class
|
public function testFindAll(string $periodClass, int $countRecords): void
|
||||||
* @param int $count_records
|
|
||||||
*
|
|
||||||
* @dataProvider listObjectProvider
|
|
||||||
*/
|
|
||||||
public function testFindAll(string $period_class, int $count_records): void
|
|
||||||
{
|
{
|
||||||
$repository = new $period_class($this->pdoObject);
|
$repository = new $periodClass($this->pdoObject);
|
||||||
$models = $repository->findAll();
|
$models = $repository->findAll();
|
||||||
$this->assertIsArray($models);
|
$this->assertIsArray($models);
|
||||||
$this->assertContainsOnly(WorkingHoursView::class, $models);
|
$this->assertContainsOnlyInstancesOf(WorkingHoursView::class, $models);
|
||||||
$this->assertCount($count_records, $models);
|
$this->assertCount($countRecords, $models);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[DataProvider(('existingObjectProvider'))]
|
||||||
*
|
public function testExistingFindByKey(
|
||||||
* @dataProvider existingObjectProvider
|
string $periodClass,
|
||||||
*
|
string $search,
|
||||||
* @param string $period_class
|
int $workingDays,
|
||||||
* @param string $search
|
DateTime $period,
|
||||||
* @param int $workingDays
|
int $homeOfficeDays
|
||||||
* @param DateTime $period
|
): void {
|
||||||
*/
|
$repository = new $periodClass($this->pdoObject);
|
||||||
public function testExistingFindByKey(string $period_class, string $search, int $workingDays, DateTime $period): void
|
|
||||||
{
|
|
||||||
$repository = new $period_class($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'))]
|
||||||
* @param string $period_class
|
public function testNotExistingFindByKey(string $periodClass, string $search): void
|
||||||
* @param string $search
|
|
||||||
*
|
|
||||||
* @dataProvider notExistingObjectProvider
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function testNotExistingFindByKey(string $period_class, string $search): void
|
|
||||||
{
|
{
|
||||||
$repository = new $period_class($this->pdoObject);
|
$repository = new $periodClass($this->pdoObject);
|
||||||
$this->expectException(RepositoryRecordNotFoundException::class);
|
$this->expectException(RepositoryRecordNotFoundException::class);
|
||||||
$repository->findByKey($search);
|
$repository->findByKey($search);
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-24
@@ -1,26 +1,35 @@
|
|||||||
volumes:
|
volumes:
|
||||||
logs:
|
logs:
|
||||||
driver: local
|
driver: local
|
||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
build: ./api/
|
build: ./api/
|
||||||
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
|
||||||
test-api:
|
test-api:
|
||||||
build: ./api/
|
build: ./api/
|
||||||
environment:
|
environment:
|
||||||
docker: "true"
|
docker: "true"
|
||||||
DATABASES_HOST: psql.torsten-hettstedt.net
|
DATABASES_HOST: psql.torsten-hettstedt.net
|
||||||
DATABASES_NAME: testdb
|
DATABASES_NAME: testdb
|
||||||
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:
|
||||||
|
build: ./ui/
|
||||||
|
env_file:
|
||||||
|
- ui/.env
|
||||||
|
ports:
|
||||||
|
- "8080:3000"
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
- test-api
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
create_css_files() {
|
||||||
|
cd ui || exit 2
|
||||||
|
lessc --source-map less/print.less static/print.css
|
||||||
|
lessc --source-map less/global.less static/global.css
|
||||||
|
cd ../..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_ui_dependence() {
|
||||||
|
cd ui/ || exit 2
|
||||||
|
npm install
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_api_dependence() {
|
||||||
|
cd api/ || exit 2
|
||||||
|
composer install
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
build_ui() {
|
||||||
|
cd ui/ || exit 2
|
||||||
|
npm run build
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
codeception_install() {
|
||||||
|
pwd
|
||||||
|
cd api/ || exit 2
|
||||||
|
test -e ./tests/unit.suite.yml || ./vendor/bin/codecept build
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
test_unit() {
|
||||||
|
pwd
|
||||||
|
cd api/ || exit 2
|
||||||
|
./vendor/bin/codecept run unit
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
test_api() {
|
||||||
|
cd api/ || exit 2
|
||||||
|
./vendor/bin/codecept run api
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
composer_run() {
|
||||||
|
docker compose up -d
|
||||||
|
}
|
||||||
|
|
||||||
|
create_css_files && install_ui_dependence && install_api_dependence && build_ui && codeception_install && test_unit && composer_run && test_api
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@startuml
|
||||||
|
'https://plantuml.com/salt
|
||||||
|
|
||||||
|
salt
|
||||||
|
{
|
||||||
|
{/ <b>Täglich | Wöchentlich }
|
||||||
|
Datum |"0000-00-00 <&calendar>"
|
||||||
|
Uhrzeit | "00:00:00 <&clock>"
|
||||||
|
[Übernehmen und weiter <&plus>]
|
||||||
|
[Abbrechen <&action-undo>]
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
@startuml
|
||||||
|
'https://plantuml.com/salt
|
||||||
|
|
||||||
|
salt
|
||||||
|
{
|
||||||
|
{/ Täglich | <b>Wöchentlich }
|
||||||
|
Woche vom
|
||||||
|
"0000-00-00 <&calendar>"
|
||||||
|
[<&chevron-left> Woche zurück] | [Woche vor <&chevron-right>]
|
||||||
|
{!
|
||||||
|
Montag | "00:00:00 <&clock>"
|
||||||
|
Dienstag | "00:00:00 <&clock>"
|
||||||
|
Mittwoch | "00:00:00 <&clock>"
|
||||||
|
Donnerstag | "00:00:00 <&clock>"
|
||||||
|
Freitag | "00:00:00 <&clock>"
|
||||||
|
<color:red>Samstag | "<color:red>00:00:00 <&clock>"
|
||||||
|
<color:red>Sontag | "<color:red>00:00:00 <&clock>"
|
||||||
|
}
|
||||||
|
[Übernehmen und weiter <&plus>]
|
||||||
|
[Abbrechen <&action-undo>]
|
||||||
|
}
|
||||||
|
@enduml
|
||||||
@@ -2,44 +2,48 @@
|
|||||||
-- DROP TABLE public."Arbeitszeiten";
|
-- DROP TABLE public."Arbeitszeiten";
|
||||||
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")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
-- # 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")::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",
|
||||||
from "Arbeitszeiten"
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
group by (date_trunc('week'::text, "Arbeitszeiten"."Datum"::timestamp with time zone))
|
from "Arbeitszeiten"
|
||||||
order 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));
|
||||||
|
|
||||||
-- ## 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")::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",
|
||||||
from "Arbeitszeiten"
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
group by (date_trunc('month'::text, "Arbeitszeiten"."Datum"::timestamp with time zone))
|
from "Arbeitszeiten"
|
||||||
order 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));
|
||||||
|
|
||||||
-- ## 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",
|
||||||
from "Arbeitszeiten"
|
(count(*) filter (where "Arbeitszeiten"."HomeOffice"))::int as "Home-Office-Tage"
|
||||||
group by (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
from "Arbeitszeiten"
|
||||||
order by (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
group by (date_part('year'::text, "Arbeitszeiten"."Datum"))
|
||||||
|
order by (date_part('year'::text, "Arbeitszeiten"."Datum"));
|
||||||
@@ -0,0 +1,442 @@
|
|||||||
|
truncate table public."Arbeitszeiten";
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-09', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-10', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-11', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-14', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-15', '0 years 0 mons 0 days 9 hours 11 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-16', '0 years 0 mons 0 days 8 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-17', '0 years 0 mons 0 days 9 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-18', '0 years 0 mons 0 days 7 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-21', '0 years 0 mons 0 days 8 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-22', '0 years 0 mons 0 days 8 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-23', '0 years 0 mons 0 days 9 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-24', '0 years 0 mons 0 days 7 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-25', '0 years 0 mons 0 days 7 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-04', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-05', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-06', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-07', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-08', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-11', '0 years 0 mons 0 days 6 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-12', '0 years 0 mons 0 days 8 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-13', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-14', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-15', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-18', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-19', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-21', '0 years 0 mons 0 days 15 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-22', '0 years 0 mons 0 days 4 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-25', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-26', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-02-27', '0 years 0 mons 0 days 7 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-01-28', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-01', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-04', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-05', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-06', '0 years 0 mons 0 days 8 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-07', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-08', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-11', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-12', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-13', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-14', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-15', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-18', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-19', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-20', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-21', '0 years 0 mons 0 days 8 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-22', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-25', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-26', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-27', '0 years 0 mons 0 days 8 hours 28 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-28', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-03-29', '0 years 0 mons 0 days 6 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-01', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-02', '0 years 0 mons 0 days 8 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-03', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-04', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-05', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-08', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-09', '0 years 0 mons 0 days 8 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-10', '0 years 0 mons 0 days 8 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-11', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-12', '0 years 0 mons 0 days 7 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-15', '0 years 0 mons 0 days 10 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-16', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-17', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-18', '0 years 0 mons 0 days 5 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-23', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-24', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-25', '0 years 0 mons 0 days 8 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-04-26', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-06', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-07', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-08', '0 years 0 mons 0 days 10 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-09', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-10', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-13', '0 years 0 mons 0 days 8 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-14', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-15', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-16', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-17', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-20', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-21', '0 years 0 mons 0 days 9 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-22', '0 years 0 mons 0 days 8 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-23', '0 years 0 mons 0 days 7 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-24', '0 years 0 mons 0 days 7 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-27', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-28', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-05-29', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-03', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-04', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-05', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-06', '0 years 0 mons 0 days 7 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-07', '0 years 0 mons 0 days 7 hours 35 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-11', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-12', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-13', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-14', '0 years 0 mons 0 days 4 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-17', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-18', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-19', '0 years 0 mons 0 days 8 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-20', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-21', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-24', '0 years 0 mons 0 days 8 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-25', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-26', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-27', '0 years 0 mons 0 days 6 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-06-28', '0 years 0 mons 0 days 7 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-01', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-02', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-03', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-04', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-05', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-08', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-09', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-10', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-11', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-12', '0 years 0 mons 0 days 7 hours 15 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-15', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-16', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-17', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-18', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-19', '0 years 0 mons 0 days 7 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-22', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-23', '0 years 0 mons 0 days 8 hours 11 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-24', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-25', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-26', '0 years 0 mons 0 days 8 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-29', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-30', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-07-31', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-01', '0 years 0 mons 0 days 9 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-02', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-19', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-20', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-21', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-22', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-23', '0 years 0 mons 0 days 7 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-26', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-27', '0 years 0 mons 0 days 8 hours 15 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-28', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-29', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-08-30', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-02', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-03', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-04', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-05', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-06', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-09', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-10', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-11', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-12', '0 years 0 mons 0 days 8 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-13', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-16', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-17', '0 years 0 mons 0 days 7 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-18', '0 years 0 mons 0 days 6 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-19', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-20', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-23', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-24', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-25', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-26', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-27', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-09-30', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-01', '0 years 0 mons 0 days 8 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-02', '0 years 0 mons 0 days 7 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-07', '0 years 0 mons 0 days 7 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-08', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-09', '0 years 0 mons 0 days 8 hours 40 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-10', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-14', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-15', '0 years 0 mons 0 days 7 hours 45 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-21', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-22', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-23', '0 years 0 mons 0 days 8 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-24', '0 years 0 mons 0 days 7 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-25', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-28', '0 years 0 mons 0 days 7 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-29', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-10-30', '0 years 0 mons 0 days 7 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-01', '0 years 0 mons 0 days 8 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-04', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-05', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-06', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-07', '0 years 0 mons 0 days 7 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-08', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-11', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-12', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-13', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-14', '0 years 0 mons 0 days 7 hours 40 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-15', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-18', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-19', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-20', '0 years 0 mons 0 days 7 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-21', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-22', '0 years 0 mons 0 days 7 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-25', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-26', '0 years 0 mons 0 days 7 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-27', '0 years 0 mons 0 days 8 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-28', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-11-29', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-02', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-03', '0 years 0 mons 0 days 7 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-04', '0 years 0 mons 0 days 8 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-05', '0 years 0 mons 0 days 5 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-06', '0 years 0 mons 0 days 7 hours 35 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-09', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-10', '0 years 0 mons 0 days 8 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-11', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-12', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-13', '0 years 0 mons 0 days 6 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-16', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-17', '0 years 0 mons 0 days 8 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-18', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-19', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2019-12-20', '0 years 0 mons 0 days 6 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-07', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-08', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-09', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-10', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-13', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-14', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-15', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-16', '0 years 0 mons 0 days 7 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-17', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-20', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-21', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-22', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-23', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-24', '0 years 0 mons 0 days 7 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-27', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-28', '0 years 0 mons 0 days 7 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-29', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-30', '0 years 0 mons 0 days 8 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-01-31', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-03', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-04', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-05', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-06', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-07', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-10', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-11', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-12', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-13', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-14', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-17', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-18', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-19', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-20', '0 years 0 mons 0 days 7 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-21', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-24', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-25', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-26', '0 years 0 mons 0 days 8 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-27', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-02-28', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-02', '0 years 0 mons 0 days 8 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-03', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-04', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-05', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-06', '0 years 0 mons 0 days 4 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-09', '0 years 0 mons 0 days 7 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-10', '0 years 0 mons 0 days 7 hours 34 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-11', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-12', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-13', '0 years 0 mons 0 days 8 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-16', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-17', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-18', '0 years 0 mons 0 days 7 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-19', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-20', '0 years 0 mons 0 days 5 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-23', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-24', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-25', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-26', '0 years 0 mons 0 days 10 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-27', '0 years 0 mons 0 days 4 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-30', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-03-31', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-01', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-02', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-03', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-06', '0 years 0 mons 0 days 7 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-07', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-08', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-14', '0 years 0 mons 0 days 7 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-15', '0 years 0 mons 0 days 7 hours 56 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-16', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-17', '0 years 0 mons 0 days 7 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-20', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-21', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-22', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-23', '0 years 0 mons 0 days 8 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-24', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-27', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-28', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-29', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-04-30', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-04', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-05', '0 years 0 mons 0 days 6 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-06', '0 years 0 mons 0 days 9 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-07', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-08', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-11', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-12', '0 years 0 mons 0 days 8 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-13', '0 years 0 mons 0 days 8 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-14', '0 years 0 mons 0 days 8 hours 39 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-15', '0 years 0 mons 0 days 6 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-18', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-19', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-20', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-25', '0 years 0 mons 0 days 7 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-26', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-27', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-28', '0 years 0 mons 0 days 8 hours 24 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-26', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-05-29', '0 years 0 mons 0 days 8 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-02', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-03', '0 years 0 mons 0 days 6 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-04', '0 years 0 mons 0 days 8 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-05', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-08', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-09', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-10', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-11', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-15', '0 years 0 mons 0 days 7 hours 45 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-16', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-17', '0 years 0 mons 0 days 8 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-18', '0 years 0 mons 0 days 8 hours 23 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-19', '0 years 0 mons 0 days 8 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-22', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-23', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-24', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-25', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-26', '0 years 0 mons 0 days 9 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-29', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-06-30', '0 years 0 mons 0 days 8 hours 18 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-01', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-02', '0 years 0 mons 0 days 7 hours 41 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-07', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-08', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-09', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-10', '0 years 0 mons 0 days 6 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-03', '0 years 0 mons 0 days 7 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-06', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-13', '0 years 0 mons 0 days 7 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-14', '0 years 0 mons 0 days 7 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-15', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-16', '0 years 0 mons 0 days 7 hours 57 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-07-17', '0 years 0 mons 0 days 8 hours 10 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-10', '0 years 0 mons 0 days 7 hours 50 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-11', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-12', '0 years 0 mons 0 days 7 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-13', '0 years 0 mons 0 days 7 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-14', '0 years 0 mons 0 days 8 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-17', '0 years 0 mons 0 days 8 hours 46 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-18', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-19', '0 years 0 mons 0 days 8 hours 9 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-20', '0 years 0 mons 0 days 8 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-21', '0 years 0 mons 0 days 7 hours 26 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-24', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-25', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-26', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-27', '0 years 0 mons 0 days 7 hours 31 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-28', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-08-31', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-01', '0 years 0 mons 0 days 8 hours 12 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-02', '0 years 0 mons 0 days 7 hours 45 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-03', '0 years 0 mons 0 days 8 hours 37 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-04', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-07', '0 years 0 mons 0 days 7 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-08', '0 years 0 mons 0 days 8 hours 1 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-09', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-10', '0 years 0 mons 0 days 6 hours 42 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-11', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-14', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-15', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-16', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-17', '0 years 0 mons 0 days 7 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-18', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-21', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-22', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-23', '0 years 0 mons 0 days 8 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-24', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-25', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-28', '0 years 0 mons 0 days 7 hours 49 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-29', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-09-30', '0 years 0 mons 0 days 8 hours 15 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-01', '0 years 0 mons 0 days 7 hours 27 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-02', '0 years 0 mons 0 days 7 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-05', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-06', '0 years 0 mons 0 days 8 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-07', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-08', '0 years 0 mons 0 days 7 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-09', '0 years 0 mons 0 days 9 hours 0 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-12', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-13', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-14', '0 years 0 mons 0 days 8 hours 13 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-15', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-16', '0 years 0 mons 0 days 8 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-19', '0 years 0 mons 0 days 8 hours 16 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-20', '0 years 0 mons 0 days 7 hours 58 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-21', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-22', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-23', '0 years 0 mons 0 days 7 hours 38 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-27', '0 years 0 mons 0 days 9 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-28', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-29', '0 years 0 mons 0 days 7 hours 51 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-10-30', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-02', '0 years 0 mons 0 days 8 hours 11 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-04', '0 years 0 mons 0 days 8 hours 5 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-05', '0 years 0 mons 0 days 7 hours 44 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-06', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-09', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-11', '0 years 0 mons 0 days 8 hours 28 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-12', '0 years 0 mons 0 days 7 hours 21 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-13', '0 years 0 mons 0 days 9 hours 2 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-16', '0 years 0 mons 0 days 7 hours 59 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-17', '0 years 0 mons 0 days 8 hours 32 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-18', '0 years 0 mons 0 days 8 hours 8 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-19', '0 years 0 mons 0 days 8 hours 25 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-20', '0 years 0 mons 0 days 8 hours 6 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-03', '0 years 0 mons 0 days 8 hours 14 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-10', '0 years 0 mons 0 days 8 hours 3 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-23', '0 years 0 mons 0 days 8 hours 17 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-24', '0 years 0 mons 0 days 7 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-25', '0 years 0 mons 0 days 7 hours 52 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-26', '0 years 0 mons 0 days 7 hours 53 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-27', '0 years 0 mons 0 days 8 hours 20 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-11-30', '0 years 0 mons 0 days 8 hours 43 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-01', '0 years 0 mons 0 days 8 hours 33 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-02', '0 years 0 mons 0 days 6 hours 19 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-03', '0 years 0 mons 0 days 7 hours 48 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-04', '0 years 0 mons 0 days 8 hours 29 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-07', '0 years 0 mons 0 days 8 hours 7 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-09', '0 years 0 mons 0 days 7 hours 55 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-10', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-11', '0 years 0 mons 0 days 7 hours 30 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-08', '0 years 0 mons 0 days 8 hours 22 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-14', '0 years 0 mons 0 days 7 hours 54 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-15', '0 years 0 mons 0 days 8 hours 4 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-16', '0 years 0 mons 0 days 7 hours 36 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-17', '0 years 0 mons 0 days 7 hours 47 mins 0.00 secs');
|
||||||
|
INSERT INTO public."Arbeitszeiten" ("Datum", "Arbeitszeit") VALUES ('2020-12-18', '0 years 0 mons 0 days 7 hours 33 mins 0.00 secs');
|
||||||
Executable
+62
@@ -0,0 +1,62 @@
|
|||||||
|
Aktivität;Dauer;Von;Bis;Kommentar
|
||||||
|
Arbeit;04:19;2021-07-30 13:01;2021-07-30 17:20;
|
||||||
|
Mittagspause ;00:45;2021-07-30 12:16;2021-07-30 13:01;
|
||||||
|
Arbeit;04:22;2021-07-30 07:54;2021-07-30 12:16;
|
||||||
|
Arbeit;03:25;2021-07-29 13:25;2021-07-29 16:50;
|
||||||
|
Mittagspause ;01:01;2021-07-29 12:23;2021-07-29 13:25;
|
||||||
|
Arbeit;04:44;2021-07-29 07:39;2021-07-29 12:23;
|
||||||
|
Arbeit;04:47;2021-07-28 12:49;2021-07-28 17:37;
|
||||||
|
Mittagspause ;00:58;2021-07-28 11:51;2021-07-28 12:49;
|
||||||
|
Arbeit;03:48;2021-07-28 08:02;2021-07-28 11:51;
|
||||||
|
Arbeit;03:49;2021-07-27 12:34;2021-07-27 16:24;
|
||||||
|
Mittagspause ;00:47;2021-07-27 11:47;2021-07-27 12:34;
|
||||||
|
Arbeit;03:20;2021-07-27 08:27;2021-07-27 11:47;
|
||||||
|
Arbeit;04:47;2021-07-26 12:59;2021-07-26 17:47;
|
||||||
|
Mittagspause ;00:52;2021-07-26 12:07;2021-07-26 12:59;
|
||||||
|
Arbeit;03:59;2021-07-26 08:08;2021-07-26 12:07;
|
||||||
|
Arbeit;05:53;2021-07-23 12:43;2021-07-23 18:37;
|
||||||
|
Mittagspause ;00:36;2021-07-23 12:07;2021-07-23 12:43;
|
||||||
|
Arbeit;04:10;2021-07-23 07:57;2021-07-23 12:07;
|
||||||
|
Arbeit;04:09;2021-07-22 12:54;2021-07-22 17:04;
|
||||||
|
Mittagspause ;00:42;2021-07-22 12:11;2021-07-22 12:54;
|
||||||
|
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;
|
||||||
|
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;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;
|
||||||
|
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;03:19;2021-07-19 13:12;2021-07-19 16:31;
|
||||||
|
Mittagspause ;00:52;2021-07-19 12:20;2021-07-19 13:12;
|
||||||
|
Arbeit;04:28;2021-07-19 07:51;2021-07-19 12:20;
|
||||||
|
Arbeit;02:48;2021-07-16 12:55;2021-07-16 15:43;
|
||||||
|
Mittagspause ;00:47;2021-07-16 12:07;2021-07-16 12:55;
|
||||||
|
Arbeit;04:01;2021-07-16 08:06;2021-07-16 12:07;
|
||||||
|
Arbeit;03:38;2021-07-15 13:05;2021-07-15 16:43;
|
||||||
|
Mittagspause ;01:05;2021-07-15 11:59;2021-07-15 13:05;
|
||||||
|
Arbeit;03:56;2021-07-15 08:02;2021-07-15 11:59;
|
||||||
|
Arbeit;03:38;2021-07-14 13:07;2021-07-14 16:45;
|
||||||
|
Mittagspause ;01:00;2021-07-14 12:07;2021-07-14 13:07;
|
||||||
|
Arbeit;04:00;2021-07-14 08:06;2021-07-14 12:07;
|
||||||
|
Arbeit;04:22;2021-07-13 12:53;2021-07-13 17:15;
|
||||||
|
Mittagspause ;01:03;2021-07-13 11:50;2021-07-13 12:53;
|
||||||
|
Arbeit;03:47;2021-07-13 08:02;2021-07-13 11:50;
|
||||||
|
Arbeit;03:26;2021-07-12 13:02;2021-07-12 16:28;
|
||||||
|
Mittagspause ;01:03;2021-07-12 11:58;2021-07-12 13:02;
|
||||||
|
Arbeit;04:14;2021-07-12 07:44;2021-07-12 11:58;
|
||||||
|
Arbeit;03:57;2021-07-09 13:09;2021-07-09 17:06;
|
||||||
|
Mittagspause ;01:06;2021-07-09 12:02;2021-07-09 13:09;
|
||||||
|
Arbeit;03:57;2021-07-09 08:04;2021-07-09 12:02;
|
||||||
|
Arbeit;03:17;2021-07-08 13:12;2021-07-08 16:29;
|
||||||
|
Mittagspause ;01:07;2021-07-08 12:04;2021-07-08 13:12;
|
||||||
|
Arbeit;04:00;2021-07-08 08:03;2021-07-08 12:04;
|
||||||
|
Arbeit;03:41;2021-07-07 13:08;2021-07-07 16:49;
|
||||||
|
Mittagspause ;01:00;2021-07-07 12:07;2021-07-07 13:08;
|
||||||
|
Arbeit;04:05;2021-07-07 08:02;2021-07-07 12:07;
|
||||||
|
Arbeit;02:50;2021-07-06 13:05;2021-07-06 15:55;
|
||||||
|
Mittagspause ;01:05;2021-07-06 11:59;2021-07-06 13:05;
|
||||||
|
Arbeit;03:49;2021-07-06 08:10;2021-07-06 11:59;
|
||||||
|
Arbeit;03:32;2021-07-05 13:00;2021-07-05 16:33;
|
||||||
|
Mittagspause ;00:59;2021-07-05 12:00;2021-07-05 13:00;
|
||||||
|
Arbeit;03:48;2021-07-05 08:12;2021-07-05 12:00;
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
@startuml
|
||||||
|
'https://plantuml.com/activity-diagram-beta
|
||||||
|
|UI|
|
||||||
|
:Bestimme //Startdatum//;
|
||||||
|
:Bestimme //Enddatum//;
|
||||||
|
:Erstelle //Liste der Wochentage//;
|
||||||
|
|REST|
|
||||||
|
start
|
||||||
|
:Rufe API auf;
|
||||||
|
|UI|
|
||||||
|
:Zeige //Liste der Wochentage// an;
|
||||||
|
:Verarbeite Informationen der Rest-API;
|
||||||
|
stop
|
||||||
|
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Url ohne abschlißenden Slash
|
||||||
|
API_URL= http://localhost:8080
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
public
|
||||||
|
/.env
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FROM node:22-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
RUN npm prune --production
|
||||||
|
|
||||||
|
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" ]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// Einstellungen auf die Grundlagen zurücksetzen,
|
||||||
|
// beinhaltet auch Einstellungen für die neuen HTML5-Tags
|
||||||
|
//noinspection CssUnknownTarget
|
||||||
|
@import "meyer_resets.css";
|
||||||
|
// Import und Einbindung der Zeichensätze
|
||||||
|
@import "lesslib/fonts.less";
|
||||||
|
// Bestimmung der Farben
|
||||||
|
@import "lesslib/colors.less";
|
||||||
|
// Mixins des Layouts
|
||||||
|
@import "lesslib/mixins.less";
|
||||||
|
// Layout-Unabhängige Mixins
|
||||||
|
@import "lesslib/mixins.framework.less";
|
||||||
|
// Bestimmung des grundsätzlichen Erscheinungsbildes der einzelnen Elemente,
|
||||||
|
// einschließlich des Hauptbereiches
|
||||||
|
@import "lesslib/basics.less";
|
||||||
|
// Darstellung unabhängig der Darstellungsgrößen
|
||||||
|
@import "lesslib/layout.less";
|
||||||
|
// Darstellung in den verschiedenen Ansichtsgrößen
|
||||||
|
@import "lesslib/media_queries.less";
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
html {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #000;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: OpenSans, "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong, b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em, i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section article {
|
||||||
|
font-size: 95%;
|
||||||
|
p, ul, ol, dl {
|
||||||
|
line-height: 1.5em;
|
||||||
|
.hyphens();
|
||||||
|
font-size: 1.00em;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
li {
|
||||||
|
list-style: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
dt {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.50em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.30em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.20em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid thin;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
fieldset {
|
||||||
|
legend {
|
||||||
|
font-size: 1.30em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Einstellungen für die Pagination-Navigation
|
||||||
|
.pagination-nav {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 1rem;
|
||||||
|
.option {
|
||||||
|
&.number, &.ellipsis {
|
||||||
|
line-height: 1.25rem;
|
||||||
|
span {
|
||||||
|
margin: .25rem .5rem;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
text-decoration: underline solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
svg {
|
||||||
|
path {
|
||||||
|
fill: darken(@mainColorLight,40%,relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
Definition der Farben
|
||||||
|
*/
|
||||||
|
|
||||||
|
@hueMain: 35;
|
||||||
|
@hueSub: 215;
|
||||||
|
|
||||||
|
@mainColor: hsl(@hueMain, 90%, 29%);
|
||||||
|
@redColor: hsl(0, 90%, 29%);
|
||||||
|
@mainColorDark: darken(@mainColor,66%,relativ);
|
||||||
|
@mainColorLight: lighten(@mainColor,66%,relativ);
|
||||||
|
@subColor: hsl(@hueSub, 90%, 29%);
|
||||||
|
@subColorDark: darken(@subColor,66%,relativ);
|
||||||
|
@subColorLight: lighten(@subColor,66%,relativ);
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-regular.eot);
|
||||||
|
src: url(../fonts/opensans-regular.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-regular.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-regular.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-regular.svg#open_sansregular) format("svg");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-italic.eot);
|
||||||
|
src: url(../fonts/opensans-italic.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-italic.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-italic.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-italic.svg#open_sansitalic) format("svg");
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-bold.eot);
|
||||||
|
src: url(../fonts/opensans-bold.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-bold.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-bold.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-bold.svg#open_sansbold) format("svg");
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: OpenSans;
|
||||||
|
src: url(../fonts/opensans-bolditalic.eot);
|
||||||
|
src: url(../fonts/opensans-bolditalic.eot?#iefix) format("embedded-opentype"),
|
||||||
|
url(../fonts/opensans-bolditalic.woff) format("woff"),
|
||||||
|
url(../fonts/opensans-bolditalic.ttf) format("truetype"),
|
||||||
|
url(../fonts/opensans-bolditalic.svg#open_sansbold_italic) format("svg");
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
@breiteMainBereich: 70rem;
|
||||||
|
@breiteTextBereich: 60rem;
|
||||||
|
@breiteNavBereich: @breiteMainBereich - @breiteTextBereich;
|
||||||
|
|
||||||
|
html {
|
||||||
|
body {
|
||||||
|
background: @mainColorLight;
|
||||||
|
div {
|
||||||
|
> header, > footer {
|
||||||
|
color: @mainColorDark;
|
||||||
|
background: @mainColor;
|
||||||
|
font-family: serif;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-indent: inherit;
|
||||||
|
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> header h1 {
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
> footer {
|
||||||
|
color: contrast(@mainColorDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > nav {
|
||||||
|
ul {
|
||||||
|
background: darken(@mainColorLight, 33%, relativ);
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-left: @mainColorDark solid thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: @mainColorDark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten(@mainColor, 20%, relativ);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: lighten(@mainColor, 66%, relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected, &.selected:hover {
|
||||||
|
background: lighten(@mainColor, 10%, relativ);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: lighten(@mainColor, 66%, relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
* {
|
||||||
|
border-style: none;
|
||||||
|
border-color: @mainColorDark;
|
||||||
|
border-width: medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
@sectionMargin: 1rem;
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ul, ol, dl {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
text-indent: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr:last-child {
|
||||||
|
td, th {
|
||||||
|
border-bottom: @mainColor solid thin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
td.absence-time {
|
||||||
|
color: @redColor;
|
||||||
|
text-decoration-line: underline;
|
||||||
|
text-decoration-style: dashed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tfoot {
|
||||||
|
* {
|
||||||
|
border: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
@formLabelWidth: 10rem;
|
||||||
|
@formItemWidth: 40rem;
|
||||||
|
|
||||||
|
nav.menu {
|
||||||
|
width: @formItemWidth + @formLabelWidth + 4rem;
|
||||||
|
border-color: @mainColor;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
margin: 1rem auto;
|
||||||
|
|
||||||
|
span {
|
||||||
|
border-color: @mainColor;
|
||||||
|
border-style: solid solid none;
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bolder;
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken(@mainColorLight, 10%, relativ);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: darken(@mainColorLight, 20%, relativ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
width: @formItemWidth + @formLabelWidth + 2rem;
|
||||||
|
margin: 1rem auto;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: @mainColor;
|
||||||
|
padding: @sectionMargin;
|
||||||
|
|
||||||
|
&.tasten {
|
||||||
|
border: none;
|
||||||
|
background-color: @mainColorLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
margin-left: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
& > label {
|
||||||
|
width: @formLabelWidth;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: inline-block;
|
||||||
|
width: @formItemWidth;
|
||||||
|
padding: 0.25rem;
|
||||||
|
margin-bottom: 1.0rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
.button(@mainColor, spin(lighten(@mainColor, 66%, relativ), @subColor));
|
||||||
|
|
||||||
|
&.sub-button {
|
||||||
|
.button(lighten(@mainColor, 20%, relativ), spin(@mainColor, @subColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
margin: inherit;
|
||||||
|
text-indent: inherit;
|
||||||
|
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
background: @mainColorLight;
|
||||||
|
color: @mainColor;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
//Media Queries
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
header, footer, main, nav, aside {
|
||||||
|
body div > & {
|
||||||
|
width: 19cm;
|
||||||
|
margin: auto;
|
||||||
|
.border-box();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header, footer{
|
||||||
|
padding: 0.5rem;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
body div {
|
||||||
|
& > header {
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
& > nav, > header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 1.5rem 0.5rem 3.25rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteTextBereich;
|
||||||
|
section {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
h1, h2, p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
padding: 0;
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 85%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
article.liste {
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin: auto;
|
||||||
|
td, th {
|
||||||
|
padding: 0.5rem;
|
||||||
|
collapse: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
padding-top: 2rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteNavBereich;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 831px) {
|
||||||
|
header, footer, main, nav, aside {
|
||||||
|
body div > & {
|
||||||
|
width: @breiteMainBereich;
|
||||||
|
margin: auto;
|
||||||
|
.border-box();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header, footer{
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
body div {
|
||||||
|
& > header {
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
& > nav {
|
||||||
|
ul {
|
||||||
|
.flexEltern();
|
||||||
|
margin: 0;
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
height: 1.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 1.5rem 0.5rem 3.25rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteTextBereich;
|
||||||
|
section {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
h1, h2, p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
padding: 0;
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 85%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
margin: auto;
|
||||||
|
td, th {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
padding-top: 2rem;
|
||||||
|
.flexKinder(flow,'row');
|
||||||
|
width: @breiteNavBereich;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 830px) {
|
||||||
|
html body div {
|
||||||
|
@boxHeight: 5rem;
|
||||||
|
& > header {
|
||||||
|
height: @boxHeight;
|
||||||
|
font-size: @boxHeight;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
& > footer {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
& > nav {
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
border: none;
|
||||||
|
width: auto;
|
||||||
|
height: 1.5em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
a {
|
||||||
|
padding: 0.75rem;
|
||||||
|
}
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main section {
|
||||||
|
header {
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
h1, h2, p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin-left: 5em;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.75em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
@formLabelWidth: 10rem;
|
||||||
|
@formItemWidth: 40rem;
|
||||||
|
@sectionMargin: 1rem;
|
||||||
|
margin: @sectionMargin;
|
||||||
|
fieldset {
|
||||||
|
width: auto;
|
||||||
|
margin: 1rem auto;
|
||||||
|
padding: @sectionMargin;
|
||||||
|
legend {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
margin-left: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
& > label {
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
input, textarea {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
padding: 0.25rem;
|
||||||
|
margin: 0.5rem;
|
||||||
|
margin-bottom: 1.0rem;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: thin;
|
||||||
|
}
|
||||||
|
div.radio-button {
|
||||||
|
padding-left: 20%;
|
||||||
|
label, input {
|
||||||
|
display: inline;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin-top: inherit;
|
||||||
|
margin-right: 1rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-bottom: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
ul.suche {
|
||||||
|
width: 30rem;
|
||||||
|
margin-left: @formLabelWidth + 1.5rem;
|
||||||
|
margin-top: -1rem;
|
||||||
|
background: white;
|
||||||
|
position: absolute;
|
||||||
|
border: solid thin;
|
||||||
|
li {list-style: none;
|
||||||
|
text-indent: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: @mainColorLight;
|
||||||
|
color: @mainColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 90%;
|
||||||
|
margin: 5%;
|
||||||
|
td, th {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid thin;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 720px) {
|
||||||
|
html body div {
|
||||||
|
@boxHeight: 2.5rem;
|
||||||
|
& > header {
|
||||||
|
height: @boxHeight;
|
||||||
|
font-size: @boxHeight;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > nav {
|
||||||
|
.hamburgerMenu();
|
||||||
|
}
|
||||||
|
main section header {
|
||||||
|
h1, h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5rem auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 1rem 0;
|
||||||
|
td, th {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid thin;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
.hyphens();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
.hyphens() {
|
||||||
|
-webkit-hyphens: auto;
|
||||||
|
-moz-hyphens: auto;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-box() {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-gradient(@stop1, @stop2) {
|
||||||
|
.gradient(0deg, @stop1, @stop2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-gradient(@stop1, @stop2) {
|
||||||
|
.gradient(-90deg, @stop1, @stop2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient(@winkel, @stop1, @stop2) {
|
||||||
|
@winkelOld: @winkel + 90deg;
|
||||||
|
@groundColor: @stop2;
|
||||||
|
background: @groundColor -moz-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor -webkit-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor -o-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor -ms-linear-gradient(@winkelOld, @stop2, @stop1);
|
||||||
|
background: @groundColor linear-gradient(@winkel, @stop2, @stop1);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-shadow(...) {
|
||||||
|
-webkit-box-shadow: @arguments;
|
||||||
|
-moz-box-shadow: @arguments;
|
||||||
|
box-shadow: @arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-radius(...) {
|
||||||
|
-webkit-border-radius: @arguments;
|
||||||
|
-moz-border-radius: @arguments;
|
||||||
|
border-radius: @arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nowrap() {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hamburgerMenu() {
|
||||||
|
color: white;
|
||||||
|
width: 3rem;
|
||||||
|
height: 2rem;
|
||||||
|
top: 0.25rem;
|
||||||
|
left: 0.25rem;
|
||||||
|
.border-radius(4px);
|
||||||
|
border: none;
|
||||||
|
.vertical-gradient(@mainColorDark; @mainColor);
|
||||||
|
> ul {
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
background: fade(@mainColorDark,80%);
|
||||||
|
.border-radius(5px);
|
||||||
|
display: none;
|
||||||
|
> li {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
line-height: normal;
|
||||||
|
border: none;
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-top: 1px #888 solid;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
.nowrap();
|
||||||
|
display: block;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
color: white;
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: white;
|
||||||
|
.vertical-gradient(@mainColorLight; @mainColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:first-child {
|
||||||
|
a {
|
||||||
|
.border-radius( 5px 5px 0 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
a {
|
||||||
|
.border-radius( 0 0 5px 5px );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:hover, &:focus, &:active, &:checked {
|
||||||
|
ul {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
content: "\2261";
|
||||||
|
font-size: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 0.8em;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexEltern() {
|
||||||
|
display:-ms-flexbox;
|
||||||
|
display: flexbox;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexEltern(column) {
|
||||||
|
display:-ms-flexbox;
|
||||||
|
display: flexbox;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexKinder(flow, @rest...) {
|
||||||
|
-webkit-flex-flow: @rest;
|
||||||
|
-ms-flex-flow: @rest;
|
||||||
|
flex-flow: @rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flexKinder(glow, @num) {
|
||||||
|
-webkit-flex-grow: @num;
|
||||||
|
-ms-flex: @num;
|
||||||
|
flex: @num;
|
||||||
|
flex-grow: @num;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
.button(@color, @colorFont) {
|
||||||
|
@colorBackground: @color;
|
||||||
|
@colorBackgroundLight: lighten(@colorBackground,50%,relativ);
|
||||||
|
@colorShadow: fade(@colorBackground,50%);
|
||||||
|
height: 2.5em;
|
||||||
|
.border-radius(1.25em);
|
||||||
|
padding: 0.5em 1.25em;
|
||||||
|
.vertical-gradient(@colorBackgroundLight, @colorBackground);
|
||||||
|
.box-shadow(0.1em 0.2em 0.5em @colorShadow);
|
||||||
|
color: @colorFont;
|
||||||
|
font-weight: bold;
|
||||||
|
&:hover {
|
||||||
|
.vertical-gradient(@colorBackground, @colorBackgroundLight);
|
||||||
|
}
|
||||||
|
margin: 0.25rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
html {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body div {
|
||||||
|
color: #000;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: OpenSans, "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong, b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em, i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section article {
|
||||||
|
font-size: 95%;
|
||||||
|
p, ul, ol, dl {
|
||||||
|
line-height: 1.5em;
|
||||||
|
//noinspection TaskProblemsInspection,LessResolvedByNameOnly
|
||||||
|
.hyphens();
|
||||||
|
font-size: 1.00em;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
li {
|
||||||
|
list-style: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
dt {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.50em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.30em;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.20em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
th {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
fieldset {
|
||||||
|
legend {
|
||||||
|
font-size: 1.30em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
div.zelle > label {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.http-error {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
Definition der Farben
|
||||||
|
*/
|
||||||
|
|
||||||
|
@hueMain: 180;
|
||||||
|
@hueSub: 60;
|
||||||
|
|
||||||
|
@mainColor: hsl(@hueMain, 90%, 29%);
|
||||||
|
@mainColorDark: darken(@mainColor,66%,relativ);
|
||||||
|
@mainColorLight: lighten(@mainColor,66%,relativ);
|
||||||
|
@subColor: hsl(@hueSub, 90%, 29%);
|
||||||
|
@subColorDark: darken(@subColor,66%,relativ);
|
||||||
|
@subColorLight: lighten(@subColor,66%,relativ);
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
html {
|
||||||
|
body div {
|
||||||
|
> header, > footer {
|
||||||
|
background: none;
|
||||||
|
font-family: serif;
|
||||||
|
h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
text-indent: inherit;
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> header {
|
||||||
|
border-bottom: black solid thick;
|
||||||
|
h1 {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
> footer {
|
||||||
|
color: contrast(@mainColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
* {
|
||||||
|
border-style: none;
|
||||||
|
border-color: @mainColorDark;
|
||||||
|
border-width: medium;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
@sectionMargin: 1rem;
|
||||||
|
header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1, h2 {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: @sectionMargin;
|
||||||
|
text-indent: @sectionMargin * 2;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
colgroup:not(:first-child) col:first-child{
|
||||||
|
border-left: solid medium;
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
tr:last-child {
|
||||||
|
td, th {
|
||||||
|
border-bottom: @mainColorDark solid thick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody div {
|
||||||
|
th {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: solid thin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0;
|
||||||
|
p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
margin: inherit;
|
||||||
|
text-indent: inherit;
|
||||||
|
&:first-letter {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
background: @mainColorLight ;
|
||||||
|
color: @mainColor ;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Einstellungen auf die Grundlagen zurücksetzen,
|
||||||
|
// beinhaltet auch Einstellungen für die neuen HTML5-Tags
|
||||||
|
//noinspection CssUnknownTarget
|
||||||
|
@import "meyer_resets.css";
|
||||||
|
// Bestimmung der Farben
|
||||||
|
@import "lesslib/colors.less";
|
||||||
|
// Mixins des Layouts
|
||||||
|
@import "lesslib/mixins.less";
|
||||||
|
// Layout-Unabhängige Mixins
|
||||||
|
@import "lesslib/mixins.framework.less";
|
||||||
|
// Bestimmung des grundsätzlichen Erscheinungsbildes der einzelnen Elemente,
|
||||||
|
// einschließlich des Hauptbereiches
|
||||||
|
@import "lesslib/print/basics.less";
|
||||||
|
// Darstellung unabhängig der Darstellungsgrößen
|
||||||
|
@import "lesslib/print/layout.less";
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "svelte-demo",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ganalytics": "^3.1.2",
|
||||||
|
"navaid": "^1.0.2",
|
||||||
|
"open-iconic": "^1.1.1",
|
||||||
|
"papaparse": "^5.5.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
|
"@sveltejs/kit": "^2.16.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"@sveltejs/adapter-node": "^5.2.12",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"vite": "^6.2.6",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"wx-svelte-grid": "^2.1.5",
|
||||||
|
"@iconify-icons/oi": "^1.1.0",
|
||||||
|
"@iconify/svelte": "^5.0.0"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"svelte": "./puplic/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<base href="/"/>
|
||||||
|
<meta charset="utf8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
|
||||||
|
<title>Svelte app</title>
|
||||||
|
|
||||||
|
<link rel='icon' type='image/png' href='%sveltekit.assets%/favicon.png'>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<meta name="color-scheme" content="dark light"/>
|
||||||
|
%sveltekit.head%
|
||||||
|
<link rel="stylesheet" href="%sveltekit.assets%/global.css" media="screen">
|
||||||
|
<link rel="stylesheet" href="%sveltekit.assets%/print.css" media="print">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div style="display: contents">
|
||||||
|
%sveltekit.body%
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export class PeriodRecord {
|
||||||
|
/**
|
||||||
|
* @param {{ workingDays: Number; totalHours: any; overtime: string; period: string; }} record
|
||||||
|
*/
|
||||||
|
constructor(record) {
|
||||||
|
this.workingDays = Number(record.workingDays)
|
||||||
|
this.totalHours = String(record.totalHours)
|
||||||
|
this.period = String(record.period)
|
||||||
|
this.overtime = String('##:##:##')
|
||||||
|
this.isMinus = false
|
||||||
|
const interval = record.overtime.match(/^(-?)(\d\d:\d\d:\d\d)$/)
|
||||||
|
if (interval) {
|
||||||
|
this.overtime = String(interval[2])
|
||||||
|
this.isMinus = String(interval[1]) === '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
<script module>
|
||||||
|
|
||||||
|
const MyFetch = {
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {String} request_body
|
||||||
|
*/
|
||||||
|
'post': async (rest_url, request_body) => {
|
||||||
|
return MyFetch._fetch(rest_url, MyFetch._buildRequestOptions(request_body, false))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {String} http_body
|
||||||
|
*/
|
||||||
|
'put': async (rest_url, http_body) => {
|
||||||
|
return MyFetch._fetch(rest_url, MyFetch._buildRequestOptions(http_body, true))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {Object|null} query_parameters
|
||||||
|
*/
|
||||||
|
'get': async (rest_url, query_parameters = null) => {
|
||||||
|
if (query_parameters !== null) {
|
||||||
|
let filter = []
|
||||||
|
for (let parameter in query_parameters) {
|
||||||
|
filter.push(parameter + '=' + query_parameters[parameter])
|
||||||
|
}
|
||||||
|
if (filter.length > 0) {
|
||||||
|
rest_url += '?' + filter.join('&')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MyFetch._fetch(rest_url, {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {Object} request_options
|
||||||
|
*/
|
||||||
|
'_fetch': async (rest_url, request_options) => {
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
let response = await MyFetch._pureFetch(rest_url, request_options).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
if (response === null || !response.ok) {
|
||||||
|
throw new Error('Fail!')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} request_body
|
||||||
|
* @param {Boolean} needPut
|
||||||
|
*/
|
||||||
|
'_buildRequestOptions': (request_body, needPut) => {
|
||||||
|
return {
|
||||||
|
method: (needPut === true) ? 'PUT' : 'POST',
|
||||||
|
mode: 'cors',
|
||||||
|
cache: 'no-cache',
|
||||||
|
credentials: 'omit',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: request_body,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} rest_url
|
||||||
|
* @param {Object} request_options
|
||||||
|
*/
|
||||||
|
'_pureFetch': async (rest_url, request_options = {}) => {
|
||||||
|
// noinspection JSUnresolvedVariable
|
||||||
|
return await fetch(rest_url, request_options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WorkingHoursRepository = {
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} startDate
|
||||||
|
* @param {String} endDate
|
||||||
|
*/
|
||||||
|
'browse': (api_url, startDate = '', endDate = '') => {
|
||||||
|
let filter = {}
|
||||||
|
if (startDate !== '') {
|
||||||
|
filter['start-date'] = startDate
|
||||||
|
}
|
||||||
|
if (endDate !== '') {
|
||||||
|
filter['end-date'] = endDate
|
||||||
|
}
|
||||||
|
return MyFetch.get(api_url + '/working-hours', filter)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
*/
|
||||||
|
'read': (api_url, date) => {
|
||||||
|
return MyFetch.get(api_url + '/working-hours/' + date)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
*/
|
||||||
|
'readOrNew': async (api_url, date) => {
|
||||||
|
let response = await MyFetch._pureFetch(api_url + '/working-hours/' + date)
|
||||||
|
if (response.status === 404) {
|
||||||
|
return {workingTime: '00:00:00', workingDay: date}
|
||||||
|
} else if (!response.ok) {
|
||||||
|
throw new Error('Fail!')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
'add': async (api_url, record) => {
|
||||||
|
return MyFetch.post(api_url + '/working-hours', JSON.stringify(record))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
'update': async (api_url, date, record) => {
|
||||||
|
return MyFetch.put(api_url + '/working-hours/' + date, JSON.stringify(record))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
* @param {String} date
|
||||||
|
* @param {Object} record
|
||||||
|
*/
|
||||||
|
'addOrUpdate': async (api_url, date, record) => {
|
||||||
|
let request_options = MyFetch._buildRequestOptions(JSON.stringify(record), false)
|
||||||
|
let response = await MyFetch._pureFetch(api_url + '/working-hours', request_options)
|
||||||
|
if (response.status === 409) {
|
||||||
|
return WorkingHoursRepository.update(api_url, date, record)
|
||||||
|
} else if (!response.ok) {
|
||||||
|
throw new Error('Fail!')
|
||||||
|
}
|
||||||
|
return response.json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WorkingHoursViewsRepository = {
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
*/
|
||||||
|
'weekly': async api_url => {
|
||||||
|
return MyFetch.get(api_url + '/views/working-hours/weekly', {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
*/
|
||||||
|
'monthly': async api_url => {
|
||||||
|
return MyFetch.get(api_url + '/views/working-hours/monthly', {})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {String} api_url
|
||||||
|
*/
|
||||||
|
'yearly': async api_url => {
|
||||||
|
return MyFetch.get(api_url + '/views/working-hours/yearly', {})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script>
|
||||||
|
import {PeriodRecord} from './Models.svelte.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {{ promise: Promise<object>; title: string;}}
|
||||||
|
*/
|
||||||
|
let {params} = $props();
|
||||||
|
/** @type PeriodRecord[] **/
|
||||||
|
let records = $state([])
|
||||||
|
let isLoading = $state(true)
|
||||||
|
const init = async () => await params.promise.then(
|
||||||
|
/**
|
||||||
|
* @param {Array<object>} data
|
||||||
|
*/
|
||||||
|
data => {
|
||||||
|
records = data.map(row => new PeriodRecord(row))
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{params.title}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<section id="list-records">
|
||||||
|
<header>
|
||||||
|
<h1>{params.title}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
{#if isLoading === true}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:else}
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Zeitraum</th>
|
||||||
|
<th>Arbeitstage</th>
|
||||||
|
<th>Arbeitsstunden</th>
|
||||||
|
<th>Über- / Unterstunden</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each records as record}
|
||||||
|
<tr>
|
||||||
|
<td>{record.period}</td>
|
||||||
|
<td>{record.workingDays}</td>
|
||||||
|
<td>{record.totalHours}</td>
|
||||||
|
<td class="{record.isMinus ? 'absence-time' : ''}">{record.overtime}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/if}
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
import { page } from '$app/state';
|
||||||
|
let { children } = $props();
|
||||||
|
/** @param {String} str **/
|
||||||
|
const isActive = str => page.url.pathname === str;
|
||||||
|
/** @param {String} str **/
|
||||||
|
const isSelected = str => isActive(str) ? 'selected' : '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li class="{isSelected('/')}"><a href="/" aria-current={isActive('/')}>Home</a></li>
|
||||||
|
<li class="{isSelected('/views/weekly')}"><a href="/views/weekly" aria-current={isActive('/views/weekly')}>Wochenansicht</a></li>
|
||||||
|
<li class="{isSelected('/views/monthly')}"><a href="/views/monthly" aria-current={isActive('/views/monthly')}>Monatsansicht</a></li>
|
||||||
|
<li class="{isSelected('/views/yearly')}"><a href="/views/yearly" aria-current={isActive('/views/yearly')}>Jahresansicht</a></li>
|
||||||
|
<li class="{isSelected('/working-hours')}"><a href="/working-hours" aria-current={isActive('/working-hours')}>Einträge</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer><p>© Torsten Lücke 2021</p></footer>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "../lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Wochenansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.weekly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Monatsansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.monthly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Wochenansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.weekly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursViewsRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
title: 'Jahresansicht',
|
||||||
|
promise: WorkingHoursViewsRepository.yearly(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script>
|
||||||
|
import Views from "$lib/Views.svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Views params={data}/>
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load() {
|
||||||
|
return {
|
||||||
|
promise: WorkingHoursRepository.browse(env.API_URL)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Date} actualDayObject **/
|
||||||
|
const getMonday = actualDayObject => {
|
||||||
|
if (actualDayObject.getDate() === 0) {
|
||||||
|
actualDayObject.setDate(actualDayObject.getDate() - 7)
|
||||||
|
}
|
||||||
|
if (actualDayObject.getDate() !== 1) {
|
||||||
|
actualDayObject.setDate(actualDayObject.getDate() - (actualDayObject.getDay() - 1))
|
||||||
|
}
|
||||||
|
return actualDayObject
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {String[]} **/
|
||||||
|
const weekday_list = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sontag']
|
||||||
|
|
||||||
|
/** @param {Date} actualDayObject **/
|
||||||
|
const getDateString = actualDayObject => {
|
||||||
|
return actualDayObject.getFullYear() + "-"
|
||||||
|
+ (actualDayObject.getMonth() + 1).toString().padStart(2, '0') + "-"
|
||||||
|
+ actualDayObject.getDate().toString().padStart(2, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
update: async ({ request }) => {
|
||||||
|
const data = await request.formData();
|
||||||
|
const body_data = {
|
||||||
|
workingDay: data.get('workingDay'),
|
||||||
|
workingTime: data.get('workingTime'),
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.update(env.API_URL, data.get('workingDay'), body_data);
|
||||||
|
},
|
||||||
|
create: async ({ request }) => {
|
||||||
|
const form_data = await request.formData();
|
||||||
|
const body_data = {
|
||||||
|
workingDay: form_data.get('workingDay'),
|
||||||
|
workingTime: form_data.get('workingTime'),
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.add(env.API_URL, body_data);
|
||||||
|
},
|
||||||
|
"create-by-week": async ({ request }) => {
|
||||||
|
const form_data = await request.formData();
|
||||||
|
// @ts-ignore
|
||||||
|
let actualDayObject = getMonday(new Date(form_data.get('week')))
|
||||||
|
/** @type {Object<String,String>} **/
|
||||||
|
let day_list = {}
|
||||||
|
weekday_list.forEach(day => {
|
||||||
|
day_list[day] = getDateString(actualDayObject)
|
||||||
|
actualDayObject.setDate(actualDayObject.getDate() + 1)
|
||||||
|
})
|
||||||
|
for (const pair of form_data.entries()) {
|
||||||
|
const working_day = day_list[pair[0]] ?? null
|
||||||
|
if (working_day === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const body_data = {
|
||||||
|
workingDay: working_day,
|
||||||
|
workingTime: pair[1],
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'import-csv': async ({ request }) => {
|
||||||
|
const form_data = await request.formData();
|
||||||
|
for (const pair of form_data.entries()) {
|
||||||
|
const working_day = pair[0]
|
||||||
|
const body_data = {
|
||||||
|
workingDay: working_day,
|
||||||
|
workingTime: pair[1],
|
||||||
|
}
|
||||||
|
await WorkingHoursRepository.addOrUpdate(env.API_URL, working_day, body_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<script>
|
||||||
|
import documentIcon from "@iconify-icons/oi/document.js";
|
||||||
|
import wrenchIcon from "@iconify-icons/oi/wrench.js";
|
||||||
|
import IconifyIcon from "@iconify/svelte";
|
||||||
|
let { data } = $props();
|
||||||
|
const TITLE = "Bearbeitung Einträge"
|
||||||
|
/** @type {{workingDay: String, workingTime: String, }[]|null} **/
|
||||||
|
let records = $state([])
|
||||||
|
const init = async () => await data.promise.then(
|
||||||
|
/**
|
||||||
|
* @param {{workingDay: String, workingTime: String, }[]} data
|
||||||
|
*/
|
||||||
|
data => {
|
||||||
|
records = data
|
||||||
|
}
|
||||||
|
)
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<section id="list-records">
|
||||||
|
<header>
|
||||||
|
<h1>{TITLE}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<!--suppress HtmlUnknownTarget -->
|
||||||
|
<form action="/working-hours">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Neuer Eintrag</legend>
|
||||||
|
<button formaction="/working-hours/create-by-day" type="submit">per Tag
|
||||||
|
<IconifyIcon icon={documentIcon}/>
|
||||||
|
</button>
|
||||||
|
<button formaction="/working-hours/create-by-week" type="submit">per Woche
|
||||||
|
<IconifyIcon icon={documentIcon}/>
|
||||||
|
</button>
|
||||||
|
<button formaction="/working-hours/import-csv" type="submit">Import per CSV
|
||||||
|
<IconifyIcon icon={documentIcon}/>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
{#if records === null}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:else}
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
<col/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Datum</th>
|
||||||
|
<th>Arbeitsstunden</th>
|
||||||
|
<th>Aktionen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each records as record}
|
||||||
|
<tr>
|
||||||
|
<td>{record.workingDay}</td>
|
||||||
|
<td>{record.workingTime}</td>
|
||||||
|
<td>
|
||||||
|
<button formaction={'/working-hours/' + record.workingDay} type="submit">Bearbeiten
|
||||||
|
<IconifyIcon icon={wrenchIcon}/>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/if}
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {env} from '$env/dynamic/private';
|
||||||
|
import {WorkingHoursRepository} from "$lib/Repositories.svelte";
|
||||||
|
|
||||||
|
export function load({ params }) {
|
||||||
|
return {
|
||||||
|
working_day: params.working_day,
|
||||||
|
promise: WorkingHoursRepository.read(env.API_URL, params.working_day)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<script>
|
||||||
|
import IconifyIcon from '@iconify/svelte'
|
||||||
|
import checkIcon from "@iconify-icons/oi/check";
|
||||||
|
import xIcon from "@iconify-icons/oi/x";
|
||||||
|
let { data } = $props();
|
||||||
|
const TITLE = "Bearbeitung eines Eintrags"
|
||||||
|
/** @type {{workingDay: String, workingTime: String, }|null} **/
|
||||||
|
let activeDate = $state(null)
|
||||||
|
const init = async () => await data.promise.then(
|
||||||
|
/**
|
||||||
|
* @param {{workingDay: String, workingTime: String, }} data
|
||||||
|
*/
|
||||||
|
data => {
|
||||||
|
activeDate = data
|
||||||
|
}
|
||||||
|
)
|
||||||
|
init()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
|
||||||
|
<section id="edit-record">
|
||||||
|
<header>
|
||||||
|
<h1>{TITLE}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<form action="/working-hours?/update" name="editRecord" method="POST">
|
||||||
|
{#if activeDate === null}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:else}
|
||||||
|
<fieldset name="record-data-by-day">
|
||||||
|
<div>
|
||||||
|
<label for="workingDay">Arbeitstag</label>
|
||||||
|
<input name="workingDay" type="date" id="workingDay" value="{activeDate.workingDay}" required pattern="\d\{4}-[0-1]\d-[0-3]\d">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="workingTime">Arbeitszeit</label>
|
||||||
|
<input name="workingTime" placeholder="Arbeitszeit" type="time" step="1"
|
||||||
|
value="{activeDate.workingTime}"
|
||||||
|
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{/if}
|
||||||
|
<fieldset name="buttons">
|
||||||
|
<button type="submit">Übernehmen und Weiter
|
||||||
|
<IconifyIcon icon={checkIcon} color="green"/>
|
||||||
|
</button>
|
||||||
|
<button formmethod="GET" type="submit" class="sub-button">Abbrechen
|
||||||
|
<IconifyIcon icon={xIcon}/>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script>
|
||||||
|
import IconifyIcon from '@iconify/svelte'
|
||||||
|
import checkIcon from "@iconify-icons/oi/check";
|
||||||
|
import xIcon from "@iconify-icons/oi/x";
|
||||||
|
import {goto} from "$app/navigation";
|
||||||
|
|
||||||
|
const TITLE = "Erstellung eines neuen Eintrags"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
|
||||||
|
<section id="edit-record">
|
||||||
|
<header>
|
||||||
|
<h1>{TITLE}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<form action="/working-hours?/create" name="newRecord" method="POST">
|
||||||
|
<fieldset name="record-data-by-day">
|
||||||
|
<div>
|
||||||
|
<label for="workingDay">Arbeitstag</label>
|
||||||
|
<input name="workingDay" type="date" id="workingDay" value="" required
|
||||||
|
pattern="\d\{4}-[0-1]\d-[0-3]\d">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="workingTime">Arbeitszeit</label>
|
||||||
|
<input name="workingTime" placeholder="Arbeitszeit" type="time" step="1" value=""
|
||||||
|
id="workingTime" required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset name="buttons">
|
||||||
|
<button type="submit">Übernehmen und Weiter
|
||||||
|
<IconifyIcon icon={checkIcon} color="green"/>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="sub-button" onclick={() => goto('/working-hours')}>Abbrechen
|
||||||
|
<IconifyIcon icon={xIcon}/>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<script>
|
||||||
|
import IconifyIcon from '@iconify/svelte'
|
||||||
|
import checkIcon from "@iconify-icons/oi/check";
|
||||||
|
import xIcon from "@iconify-icons/oi/x";
|
||||||
|
import {goto} from "$app/navigation";
|
||||||
|
const TITLE = "Erstellung neuer Einträge für eine Woche"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
tbody.weekend td input, tbody.weekend td {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<section id="edit-record">
|
||||||
|
<header>
|
||||||
|
<h1>{TITLE}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<form action="/working-hours?/create-by-week" name="newRecord" method="POST">
|
||||||
|
<fieldset name="how-week-is-use">
|
||||||
|
<div>
|
||||||
|
<label for="monday">Woche vom</label>
|
||||||
|
<input name="week" type="date" id="week" required value="">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset name="record-data-by-week">
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{#each ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag'] as day}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{day}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input name="{day}" value="08:00:00" type="time" step="1"
|
||||||
|
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
<tbody class="weekend">
|
||||||
|
{#each ['Samstag', 'Sontag'] as day}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{day}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input name="{day}" value="00:00:00" type="time" step="1"
|
||||||
|
required pattern="[0-5]\d:[0-5]\d:[0-5]\d">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset name="buttons">
|
||||||
|
<button type="submit">Übernehmen und Weiter
|
||||||
|
<IconifyIcon icon={checkIcon} color="green"/>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="sub-button" onclick={() => goto('/working-hours')}>Abbrechen
|
||||||
|
<IconifyIcon icon={xIcon}/>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<script>
|
||||||
|
import IconifyIcon from '@iconify/svelte'
|
||||||
|
import checkIcon from "@iconify-icons/oi/check";
|
||||||
|
import xIcon from "@iconify-icons/oi/x";
|
||||||
|
import {goto} from "$app/navigation";
|
||||||
|
// @ts-ignore
|
||||||
|
import Papa from 'papaparse';
|
||||||
|
|
||||||
|
const TITLE = "Erstellung neuer Einträge für eine Woche"
|
||||||
|
|
||||||
|
/** @type {{ workingTime: String, workingDay: String, }[]} **/
|
||||||
|
let imported_data = $state([])
|
||||||
|
|
||||||
|
|
||||||
|
let files = $state(null)
|
||||||
|
|
||||||
|
const VerarbeiteDateien = () => {
|
||||||
|
for (const file of files ?? []) {
|
||||||
|
VerarbeiteCsv(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @param {File} file **/
|
||||||
|
const VerarbeiteCsv = file => {
|
||||||
|
Papa.parse(file, {
|
||||||
|
header: true,
|
||||||
|
skipEmptyLines: true,
|
||||||
|
// @ts-ignore
|
||||||
|
complete: results => {
|
||||||
|
/** @type {Object<String, Number>} **/
|
||||||
|
let cash_work_days = {}
|
||||||
|
results.data.forEach(
|
||||||
|
/** @param {Object<String, any>} item **/
|
||||||
|
item => {
|
||||||
|
// noinspection JSNonASCIINames
|
||||||
|
let activity = item['Aktivität']
|
||||||
|
if (activity.trim() === 'Mittagspause') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let work_from = new Date(item['Von'])
|
||||||
|
let work_to = new Date(item['Bis'])
|
||||||
|
let actual_day = work_from.toISOString().substring(0, 10)
|
||||||
|
let actual_work_hours = (work_to.getTime() - work_from.getTime()) / (1000 * 60 * 60)
|
||||||
|
cash_work_days[actual_day] ??= 0
|
||||||
|
cash_work_days[actual_day] += actual_work_hours
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for (let day in cash_work_days) {
|
||||||
|
let hours = cash_work_days[day]
|
||||||
|
let hours_digits = Math.trunc(hours)
|
||||||
|
let minutes_digits = Math.round((hours - hours_digits) * 60)
|
||||||
|
let working_time = String(hours_digits).padStart(2, '0') + ':'
|
||||||
|
+ String(minutes_digits).padStart(2, '0') + ':00'
|
||||||
|
imported_data.push({workingTime: working_time, workingDay: day})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{TITLE}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
|
||||||
|
<section id="edit-record">
|
||||||
|
<header>
|
||||||
|
<h1>{TITLE}</h1>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<form action="/working-hours?/import-csv" name="newRecord" method="POST">
|
||||||
|
{#if imported_data.length < 1}
|
||||||
|
<fieldset>
|
||||||
|
<legend>Download-Liste</legend>
|
||||||
|
<div class="zelle">
|
||||||
|
<label for="csv-file">Download der Zeiten von <i>aTimeLogger2</i></label>
|
||||||
|
<input type="file" accept="text/csv" id="csv-file" bind:files/>
|
||||||
|
<button type="button" onclick={VerarbeiteDateien}>Upload</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{:else}
|
||||||
|
<fieldset name="record-data-for-import">
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{#each imported_data as item}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{item.workingDay}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="time" readonly name="{item.workingDay}" value="{item.workingTime}">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset name="buttons">
|
||||||
|
<button type="submit">Übernehmen und Weiter
|
||||||
|
<IconifyIcon icon={checkIcon} color="green"/>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="sub-button" onclick={() => goto('/working-hours')}>Abbrechen
|
||||||
|
<IconifyIcon icon={xIcon}/>
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
{/if}
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user