feat: import work hours from SaaS and exchange signed time sheet

This commit is contained in:
fromme committed 2026-09-26 12:13:59 +02:00
1 parent 7cf39850e6
commit 1a7c272414
73 files changed
+10488 -72

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App\Enum;
enum WorkHoursSyncOutcome: string
{
/** Hours were fetched and written. */
case IMPORTED = 'imported';
/** The month is signed, so the hours were compared but left alone. */
case LOCKED = 'locked';
/** Nothing to do: no address on file, or nothing to import. */
case SKIPPED = 'skipped';
/** The request or the payload failed. Worth waking someone for. */
case FAILED = 'failed';
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace App\Enum;
/**
* Where one work month stands in the Arbeitszeitnachweis exchange.
*
* Deliberately not a Symfony workflow: the existing document state machine is keyed to
* Disposition's own marking store and cannot carry a month. Four states and two
* transitions do not need one.
*/
enum WorkMonthStatus: string
{
/** Imported, waiting for the teamer to download, sign and upload it. */
case OPEN = 'open';
/** The teamer uploaded a signed copy; the office has not looked at it yet. */
case SUBMITTED = 'submitted';
/** The office accepted the signed copy. */
case APPROVED = 'approved';
/** The office rejected it. The teamer uploads again, which returns it to SUBMITTED. */
case REJECTED = 'rejected';
public function label(): string
{
return match ($this) {
self::OPEN => 'Offen',
self::SUBMITTED => 'Eingereicht',
self::APPROVED => 'Geprüft',
self::REJECTED => 'Abgelehnt',
};
}
public function badgeClass(): string
{
return match ($this) {
self::OPEN => 'upload-status-badge--new',
self::SUBMITTED => 'upload-status-badge--default',
self::APPROVED => 'upload-status-badge--checked',
self::REJECTED => 'upload-status-badge--error',
};
}
/**
* Whether the imported hours are frozen.
*
* Once a teamer has signed a month, the stored hours have to keep matching the paper
* they signed, so a later import may compare but not overwrite. A rejected month is
* not locked: a rejection usually means the hours themselves were wrong.
*/
public function locksHours(): bool
{
return match ($this) {
self::SUBMITTED, self::APPROVED => true,
self::OPEN, self::REJECTED => false,
};
}
}