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

+27
View File
@@ -228,6 +228,32 @@ texts:
# Descriptions shown next to the form fields. They describe the placeholder itself rather
# than any one mail, so they are shared: a name means the same thing wherever it appears.
# Daily, for as long as a work month has no signed Arbeitszeitnachweis on it. The month
# is named rather than a deadline: there is none, the office simply wants it back.
reminder_work_month_upload:
label: 'Erinnerung: Arbeitszeitnachweis hochladen'
placeholders: ['workMonth']
subject: 'Reminder: Fehlender Arbeitszeitnachweis'
headline: 'Hallo aus Köln,'
body: |-
bitte denke daran, deinen unterschriebenen Arbeitszeitnachweis hochzuladen. Ohne ihn können wir den Monat nicht abrechnen.
**{workMonth}**
Du findest ihn im Portal unter "Mein Profil - Arbeitszeiten": dort herunterladen, unterschreiben und wieder hochladen.
work_month_rejected:
label: 'Arbeitszeitnachweis abgelehnt'
placeholders: ['workMonth', 'comment']
subject: 'Dein Arbeitszeitnachweis wurde abgelehnt'
headline: 'Hallo aus Köln,'
body: |-
leider können wir deinen Arbeitszeitnachweis für **{workMonth}** so nicht annehmen.
Begründung: {comment}
Bitte lade ihn im Portal unter "Mein Profil - Arbeitszeiten" noch einmal hoch.
placeholders:
bookingUrl: 'Buchungslink für den Team-Infocall, kommt aus der Konfiguration (APPLICATION_FORM_BOOKING_URL)'
season: 'Kommende Saison in Kurzform, z. B. "26/27" - wird automatisch eingesetzt'
@@ -247,3 +273,4 @@ placeholders:
dateTo: 'Ende des Einsatzes'
diffInDays: 'Tage bis zum Beginn des Einsatzes'
tasks: 'Noch offene Punkte der Saison-Checkliste, kommagetrennt'
workMonth: 'Monat der Arbeitszeiten, z. B. "März 2026"'
+17 -4
View File
@@ -2,6 +2,7 @@ monolog:
channels:
- deprecation
- bpn
- saas
- audit
- api
@@ -12,7 +13,7 @@ monolog:
handler: exceptions
excluded_http_codes: [404, 405]
buffer_size: 50
channels: ["!bpn", "!audit", "!api"]
channels: ["!bpn", "!saas", "!audit", "!api"]
exceptions:
type: rotating_file
path: "%kernel.logs_dir%/framework.%kernel.environment%.log"
@@ -24,6 +25,12 @@ monolog:
path: '%kernel.logs_dir%/bpn.%kernel.environment%.log'
max_files: 5
level: info
saas:
channels: ["saas"]
type: rotating_file
path: '%kernel.logs_dir%/saas.%kernel.environment%.log'
max_files: 5
level: info
audit:
channels: [ "audit" ]
type: rotating_file
@@ -45,7 +52,7 @@ monolog:
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!bpn", "!api", "!doctrine", "!console"]
channels: ["!event", "!bpn", "!saas", "!api", "!doctrine", "!console"]
when@dev:
monolog:
@@ -82,7 +89,7 @@ when@prod:
handler: exceptions
excluded_http_codes: [404, 405]
buffer_size: 50
channels: ["!bpn", "!audit", "!api"]
channels: ["!bpn", "!saas", "!audit", "!api"]
exceptions:
type: rotating_file
path: "%kernel.logs_dir%/framework.%kernel.environment%.log"
@@ -94,6 +101,12 @@ when@prod:
path: '%kernel.logs_dir%/bpn.%kernel.environment%.log'
max_files: 5
level: info
saas:
channels: ["saas"]
type: rotating_file
path: '%kernel.logs_dir%/saas.%kernel.environment%.log'
max_files: 5
level: info
audit:
channels: [ "audit" ]
type: rotating_file
@@ -113,4 +126,4 @@ when@prod:
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!bpn", "!audit", "!api"]
channels: ["!event", "!doctrine", "!bpn", "!saas", "!audit", "!api"]
+8
View File
@@ -60,6 +60,14 @@ oneup_uploader:
namer: app.upload_namer
storage:
directory: '%kernel.project_dir%/uploads/season_document_in'
# Signed Arbeitszeitnachweise coming back from a teamer. Same rule again: the key and
# the directory are exactly Upload::TYPE_WORK_MONTH.
work_month:
frontend: dropzone
use_orphanage: true
namer: app.upload_namer
storage:
directory: '%kernel.project_dir%/uploads/work_month'
chunks:
maxage: 86400
storage:
+8
View File
@@ -27,6 +27,10 @@ when@prod:
frequency: '30 1 * * *'
description: "Updates status of teamers depending on their disposition"
- task: app:saas:work-hours
frequency: '0 4 10 * *'
description: "Imports last month's working hours of season teamers from SaaS"
- task: oneup:uploader:clear-orphans
frequency: '30 * * * *'
description: "Removes orphaned uploads"
@@ -53,6 +57,10 @@ when@staging:
frequency: '30 1 * * *'
description: "Updates status of teamers depending on their disposition"
- task: app:saas:work-hours
frequency: '0 4 10 * *'
description: "Imports last month's working hours of season teamers from SaaS"
- task: oneup:uploader:clear-orphans
frequency: '30 * * * *'
description: "Removes orphaned uploads"
+28
View File
@@ -203,6 +203,20 @@ services:
bpn_crm_house_manager_ids: '%bpn_crm_house_manager_ids%'
bpn_default_hotel_code: '%bpn_default_hotel_code%'
App\Saas\ApiClient:
arguments:
$logger: '@monolog.logger.saas'
$options:
base_url: '%env(APP_SAAS_BASE_URL)%'
api_key: '%env(APP_SAAS_API_KEY)%'
App\Saas\DataProvider\EmployeeDataProvider:
arguments:
$logger: '@monolog.logger.saas'
# The roster is slow and barely changes; six hours means one fetch per run and a
# new starter is picked up the same day.
$ttl: 21600
App\Twig\AppRuntime:
arguments:
$intlExtension: '@twig.extension.intl'
@@ -214,6 +228,15 @@ services:
tags:
- name: monolog.processor
# Every SaaS url carries the api key as a path segment, so the request lines Symfony's
# http client writes - and the exception messages that repeat those urls - are redacted
# on the way into the log rather than suppressed.
App\Logging\RedactSaasCredentialsProcessor:
arguments:
$apiKey: '%env(APP_SAAS_API_KEY)%'
tags:
- name: monolog.processor
App\Logging\RequestInfoProcessor:
arguments:
- '@request_stack'
@@ -346,6 +369,11 @@ services:
$options:
project_dir: '%kernel.project_dir%'
App\Service\Pdf\WorkMonthRenderer:
arguments:
$options:
project_dir: '%kernel.project_dir%'
App\Email\Mailer:
arguments:
$defaults: