Files
myep-team/config/services.yaml
T

521 lines
21 KiB
YAML

# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json
parameters:
bpn_crm_id_team_admin: '%env(int:APP_BPN_CRM_ID_TEAM_ADMIN)%'
bpn_crm_id_manager: '%env(int:APP_BPN_CRM_ID_MANAGER)%'
bpn_crm_id_teamer: '%env(int:APP_BPN_CRM_ID_TEAMER)%'
bpn_default_hotel_code: '%env(default::APP_BPN_DEFAULT_HOTEL_CODE)%'
# BusPro CRM selection id => hotel code. A person holding a selected attribute from this
# list is a house manager for that house. Matched by id and not by the "Hausleitung XXX"
# label, so renaming a selection in BusPro cannot silently grant or revoke a house.
# The commented entries are BusPro selections whose code is not a house in the "houses"
# parameter below: granting them would let someone in who then sees no assignments and
# no dispositions at all. Uncomment one once its house exists. The reverse gap is fine -
# not every house has a selection.
bpn_crm_house_manager_ids:
1299: 'SSL'
1300: 'SST'
1301: 'MVK'
# 1302: 'ASB'
1303: 'LPJ'
1304: 'DKS'
1305: 'DGS'
1306: 'DPW'
1307: 'DKI'
1308: 'DWW'
1309: 'PMV'
# 1352: 'ASG'
1371: 'ASC'
1373: 'PCJ'
1374: 'SBW'
# 1375: 'UCH'
# 1376: 'SZO'
1377: 'KHH'
1459: 'SVS'
1461: 'SHM'
1462: 'AGR'
# Shared key the sibling websites authenticate their form submissions with. Empty here on
# purpose - the real value belongs in the environment of each stage, and an empty key
# rejects every request instead of letting them through.
application_form_api_key: '%env(string:APPLICATION_FORM_API_KEY)%'
# Shared key n8n reads the assignments feed with (GET /api/assignments). Same rules as above.
n8n_api_key: '%env(string:N8N_API_KEY)%'
# What the n8n assignments feed covers: assignments in these houses (matched against the first
# three characters of destination.hotelCode) for these job profiles - guide, course and
# service-team work. Job profile ids are database ids, so check them against production.
assignment_export.house_codes: [ DPW, DGS, DWW, DKI, DKS ]
assignment_export.job_profile_ids:
- 5 # Reiseleitung
- 9 # Reiseleitung Ski
- 10 # Reiseleitung Snowboard
- 3 # Kursleitung Ski Anfänger:innen
- 14 # Kursleitung Ski Fortgeschrittene
- 18 # Kursleitung Ski Anfänger:innen + Fortgeschrittene
- 36 # Kursleitung Ski Kinder Anfänger:innen & Kinderbetreuung
- 37 # Kursleitung Ski Kinder Fortgeschrittene
- 38 # Kursleitung Ski Kinder
- 4 # Kursleitung Snowboard Anfänger:innen
- 15 # Kursleitung Snowboard Fortgeschrittene
- 19 # Kursleitung Snowboard Anfänger:innen + Fortgeschrittene
- 47 # Kursleitung Snowboard Kinder/Jugendliche Anfänger:innen
- 20 # Reise- und Kursleitung Ski Anfänger:innen
- 21 # Reise- und Kursleitung Ski Fortgeschrittene
- 22 # Reise- und Kursleitung Ski Anfänger:innen + Fortgeschrittene
- 23 # Reise- und Kursleitung Snowboard Anfänger:innen
- 24 # Reise- und Kursleitung Snowboard Fortgeschrittene
- 25 # Reise- und Kursleitung Snowboard Anfänger:innen + Fortgeschrittene
- 6 # Serviceteam 40h (ST40)
- 13 # Serviceteam 25h (ST25)
- 16 # Serviceteam 10h (ST10)
# Booking link the application form receipt mail invites the applicant to. A deployment
# value rather than part of the wording, so a new booking system does not need an edit in
# every stage's mail text.
application_form_booking_url: '%env(string:APPLICATION_FORM_BOOKING_URL)%'
default_email_from: '%env(APP_DEFAULT_EMAIL_FROM)%'
default_email_to: '%env(APP_DEFAULT_EMAIL_TO)%'
# Per deployment and not per environment: staging and production run the same compiled
# environment and differ only in their .env.local. The csv processor turns an unset
# variable into an empty list, which switches the filtering off - so production is
# unaffected without anything having to be remembered at deploy time.
mail_recipient_whitelist: '%env(csv:APP_MAIL_RECIPIENT_WHITELIST)%'
teamer_inactive_period: '-2 years'
# The stand-in PDF served instead of real uploads while the upload_replacement feature is
# on. Relative to the project dir. Which file is a fixed fact of the repo; whether it is
# used at all is the feature flag's job alone.
upload_replacement_file: 'assets/pdf/chicken.pdf'
# The contract upload period, counted from the creation of the disposition. This is the
# single definition: the reminder mail, the "Honorarvertrag fällig" flag on the teamer
# dashboard, the admin list of overdue contracts and the wording of the mails all read it,
# so they cannot mark the same disposition due on three different days.
contract_upload_deadline_days: 5
# The invoice upload period, counted from the end of the assignment. The teamer is
# reminded the day after the assignment ends and again on the last day of this period.
invoice_upload_deadline_days: 14
# Messenger transport the mails of a teamer mailing are queued on. An X-Bus-Transport
# header wins over the routing in messenger.yaml (see SendersLocator::getSenders),
# so this is what keeps a mailing off the shared queue - and what has to be switched
# back to "sync" further down, or a mailing would need a running worker to arrive
# in dev and test while every other mail is sent right away.
mailing_bus_transport: 'mailing'
# Dates (MM-DD) on which teamers must re-confirm their personal data.
personal_data_check_deadlines:
- '04-01'
- '10-01'
# The season window as MM-DD. It wraps the turn of the year and is named after the
# calendar year it starts in ("Saison 2024/25"). The teamer info modal buckets a
# teamer's dispositions by the season their assignment starts in; one whose
# assignment starts outside this window is counted as "außerhalb der Saison".
season_start: '11-01'
season_end: '04-01'
# The teamer questionnaire after a placement (config/questionnaires/teamer_assignment.yaml) is
# offered for placements ending on or after this day (Y-m-d). Set it to the deployment day:
# without a cutoff every past placement would show up as pending on the teamers' dashboards.
# null offers it to nobody.
questionnaire_go_live: ~
# How far ahead of the destination's first travel day a season assignment's checklist
# entries fall due. A relative date expression, applied to destination.dateFrom, and the
# single definition of both deadlines: the first is this offset, the second defaults to
# the first. Per-entry overrides are stored on the task row; this is what they override.
season_task_deadline_offset: '-1 month'
# What the staffing matrix generates (App\Service\Assignment\AssignmentPlanner), keyed by
# App\Enum\StaffingRole / StaffingFee value. Ids and not names, so renaming a job profile
# or fee in the admin cannot silently change what the drafts get. A missing or deleted id
# makes App\Service\Assignment\StaffingResolver refuse rather than skip.
auto_assignment_job_profiles:
guide: 5 # Reiseleitung
ski_beginner: 3 # Kursleitung Ski Anfänger:innen
ski_advanced: 14 # Kursleitung Ski Fortgeschrittene
guide_ski_beginner: 20 # Reise- und Kursleitung Ski Anfänger:innen
guide_ski_advanced: 21 # Reise- und Kursleitung Ski Fortgeschrittene
auto_assignment_fees:
guiding: 3 # Skigebietsguiding (pro Einheit)
course: 4 # Ski-/Snowboardkurs (pro Stunde)
# Houses, hotel code: name. The name is used both as a label and to match
# destination.hotel, the code to match destination.hotelCode.
houses:
AGR: 'Rotbach'
ASC: 'Christiler'
DGS: 'Spinabad'
DKI: 'Schwendi'
DKS: 'Schweizerhaus'
DPW: 'Waldschlössli'
DWW: 'Weißfluh'
KHH: 'Heuberghaus'
L2A: 'Les Deux Alpes'
LPJ: 'Jenatsch'
MVK: 'Klein Tirol'
PCJ: 'Jolimont'
PMV: 'Victoria'
SBW: 'Val Thorens'
SHM: 'Mitterlengau'
SRR: 'Ranalt'
SSL: 'Lederer'
SST: 'Steinachhof'
SVS: 'Silvana'
services:
_defaults:
autowire: true
autoconfigure: true
bind:
$tempDir: '%kernel.project_dir%/temp'
# Application audit channel, not a MyE&P one: this is what feeds the
# log table via App\Logging\DatabaseHandler. Unbinding a service from
# it drops its records from that table.
$logger: '@monolog.logger.audit'
$houses: '%houses%'
$xmlDump: '@xml_dump.storage'
$teamerInactivePeriod: '%teamer_inactive_period%'
$contractUploadDeadlineDays: '%contract_upload_deadline_days%'
$invoiceUploadDeadlineDays: '%invoice_upload_deadline_days%'
# Only the path enters the container - the file itself is parsed on demand by
# App\Config\EmailTextCatalog, since its contents matter on few requests.
$emailTextsFile: '%kernel.project_dir%/config/email_texts.yaml'
# Likewise parsed on demand, by App\Config\QuestionnaireCatalog.
$questionnaireDir: '%kernel.project_dir%/config/questionnaires'
$questionnaireGoLive: '%questionnaire_go_live%'
$mailingBusTransport: '%mailing_bus_transport%'
$seasonStart: '%season_start%'
$seasonEnd: '%season_end%'
$seasonTaskDeadlineOffset: '%season_task_deadline_offset%'
$autoAssignmentJobProfiles: '%auto_assignment_job_profiles%'
$autoAssignmentFees: '%auto_assignment_fees%'
# Tagged by interface rather than listed by hand: order between collectors is
# irrelevant, so the only thing an explicit list could add here is the chance to
# forget one, which would silently stop a metric being recorded.
_instanceof:
App\Service\Statistics\Collector\StatisticsCollectorInterface:
tags: ['app.statistics_collector']
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
Liip\ImagineBundle\Imagine\Cache\Resolver\FormatExtensionResolver:
arguments:
- '@liip_imagine.cache.resolver.default'
- '@liip_imagine.filter.configuration'
tags:
- name: liip_imagine.cache.resolver
resolver: format_extension
App\Command\MyEpImportCommand:
arguments:
$connection: '@doctrine.dbal.default_connection'
App\Command\DbAnonymizeCommand:
arguments:
$environment: '%kernel.environment%'
App\Service\Common\DatabaseAnonymizer:
arguments:
$connection: '@doctrine.dbal.default_connection'
App\BusProNet\ApiClient:
arguments:
$logger: '@monolog.logger.bpn'
$options:
bpn_username: '%env(APP_BPN_USER)%'
bpn_password: '%env(APP_BPN_PASSWORD)%'
bpn_api_ip: '%env(APP_BPN_IP)%'
bpn_api_port: '%env(APP_BPN_PORT)%'
debug: '%env(bool:APP_BPN_DEBUG)%'
App\Service\Teamer\TeamerProvisioner:
arguments:
$logger: '@monolog.logger.bpn'
# The same selection the login reads a teamer's role from - this is the one place that
# writes it, so it must be the same id.
$teamerSelectionId: '%bpn_crm_id_teamer%'
App\BusProNet\ResponseParser:
arguments:
$options:
bpn_crm_id_team_admin: '%bpn_crm_id_team_admin%'
bpn_crm_id_manager: '%bpn_crm_id_manager%'
bpn_crm_id_teamer: '%bpn_crm_id_teamer%'
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\MyEp\Api\AccessTokenProvider:
arguments:
$logger: '@monolog.logger.myep'
$cache: '@cache.app'
$options:
client_id: '%env(MYEP_API_CLIENT_ID)%'
client_secret: '%env(MYEP_API_CLIENT_SECRET)%'
App\MyEp\Api\ApiClient:
arguments:
$logger: '@monolog.logger.myep'
App\MyEp\Api\ResponseMapper:
arguments:
$logger: '@monolog.logger.myep'
App\MyEp\Cache\StaleWhileErrorCache:
arguments:
$logger: '@monolog.logger.myep'
$cache: '@cache.app'
App\MyEp\Import\TravelExportClient:
arguments:
$logger: '@monolog.logger.myep'
App\Twig\AppRuntime:
arguments:
$intlExtension: '@twig.extension.intl'
$environment: '%kernel.environment%'
App\Logging\InjectUserProcessor:
arguments:
- '@security.helper'
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'
tags:
- name: monolog.processor
App\Menu\MenuBuilder:
arguments:
$factory: '@knp_menu.factory'
tags:
- name: knp_menu.menu_builder
method: createMainMenu
alias: default_main
- name: knp_menu.menu_builder
method: createTeamerMenu
alias: default_teamer
App\Menu\AdminMenuBuilder:
arguments:
$factory: '@knp_menu.factory'
tags:
- name: knp_menu.menu_builder
method: createMainMenu
alias: admin_main
- name: knp_menu.menu_builder
method: createTeamerMenu
alias: admin_teamer
App\Menu\ManagerMenuBuilder:
arguments:
$factory: '@knp_menu.factory'
tags:
- name: knp_menu.menu_builder
method: createMainMenu
alias: manager_main
- name: knp_menu.menu_builder
method: createTeamerMenu
alias: manager_teamer
App\Menu\TeamerMenuBuilder:
arguments:
$factory: '@knp_menu.factory'
tags:
- name: knp_menu.menu_builder
method: createMainMenu
alias: teamer_main
App\Menu\HouseManagerMenuBuilder:
arguments:
$factory: '@knp_menu.factory'
tags:
- name: knp_menu.menu_builder
method: createMainMenu
alias: house_manager_main
App\Service\Upload\UploadHandler:
arguments:
$orphanageManager: '@oneup_uploader.orphanage_manager'
$projectDir: '%kernel.project_dir%'
$uploadReplacementFile: '%upload_replacement_file%'
App\Service\Pdf\Sanitizer:
arguments:
$binGs: '%env(BIN_GS)%'
$binConvert: '%env(BIN_CONVERT)%'
App\EventListener\MaintenanceModeListener:
arguments:
$options:
enabled: '%env(bool:MAINTENANCE_MODE_ENABLED)%'
whitelisted_ips: '%env(csv:MAINTENANCE_MODE_IP_WHITELIST)%'
whitelisted_routes: '%env(csv:MAINTENANCE_MODE_ROUTE_WHITELIST)%'
whitelisted_paths: '%env(csv:MAINTENANCE_MODE_PATH_WHITELIST)%'
App\EventListener\UploadSessionListener:
tags:
- name: kernel.event_listener
event: oneup_uploader.post_upload
method: onUpload
App\EventListener\UploadSessionValidationListener:
tags:
- name: kernel.event_listener
event: oneup_uploader.validation
method: onValidate
App\EventListener\DatevEmailSubscriber:
arguments:
$datevEmailRecipient: '%env(DATEV_EMAIL_RECIPIENT)%'
$datevEmailSender: '%env(DATEV_EMAIL_SENDER)%'
App\EventListener\StatisticsChangeSetListener:
arguments:
$collectors: !tagged_iterator app.statistics_collector
App\RequiredTeamerCheck\RequiredTeamerCheckRegistry:
arguments:
# Presentation order only. Each check must point at a page that clears
# that check alone, so a teamer can always work through them whatever
# the order — never make correctness depend on this list.
$checks:
- '@App\RequiredTeamerCheck\DriverLicenseRequiredCheck'
- '@App\RequiredTeamerCheck\FinancialDataRequiredCheck'
- '@App\RequiredTeamerCheck\PersonalDataVerificationRequiredCheck'
- '@App\RequiredTeamerCheck\MealPreferenceRequiredCheck'
App\RequiredTeamerCheck\PersonalDataVerificationRequiredCheck:
arguments:
$deadlines: !service
class: App\RequiredTeamerCheck\RecurringDeadlines
arguments:
$deadlines: '%personal_data_check_deadlines%'
app.upload_namer:
class: App\Service\Upload\UploadNamer
public: true
App\Service\Pdf\ContractRenderer:
arguments:
$options:
project_dir: '%kernel.project_dir%'
App\Service\Pdf\InvoiceApprover:
arguments:
$options:
project_dir: '%kernel.project_dir%'
App\Service\Pdf\InvoiceRenderer:
arguments:
$options:
project_dir: '%kernel.project_dir%'
App\Service\Pdf\WorkMonthRenderer:
arguments:
$options:
project_dir: '%kernel.project_dir%'
App\Email\Mailer:
arguments:
$defaults:
from: '%default_email_from%'
to: '%default_email_to%'
# Its own log channel, not the audit one bound by default: the audit channel writes a row
# to the log table through App\Logging\DatabaseHandler, which records what staff did here.
App\Email\EmailTextRenderer:
arguments:
$bookingUrl: '%application_form_booking_url%'
App\Security\AccessToken\ApiKeyTokenHandler:
arguments:
# user identifier in security.yaml's api_user_provider => key
$apiKeys:
api: '%application_form_api_key%'
n8n: '%n8n_api_key%'
$logger: '@monolog.logger.api'
App\Controller\Api\ApplicationFormController:
arguments:
$logger: '@monolog.logger.api'
tags: [ 'controller.service_arguments' ]
App\Controller\Api\Assignment\ListController:
arguments:
$houseCodes: '%assignment_export.house_codes%'
$jobProfileIds: '%assignment_export.job_profile_ids%'
$logger: '@monolog.logger.api'
tags: [ 'controller.service_arguments' ]
App\EventListener\EmailRecipientWhitelistSubscriber:
arguments:
$allowedRecipients: '%mail_recipient_whitelist%'
# Not the audit channel this argument is bound to by default: a mail dropped by a
# staging guard is a technical event and has no place in the application log table.
$logger: '@logger'
App\Security\OAuth2\MyEpClient:
arguments:
$options:
myep_oauth2_client_id: '%env(MYEP_OAUTH2_CLIENT_ID)%'
myep_oauth2_client_secret: '%env(MYEP_OAUTH2_CLIENT_SECRET)%'
myep_oauth2_url_authorize: '%env(MYEP_OAUTH2_URL_AUTHORIZE)%'
myep_oauth2_url_access_token: '%env(MYEP_OAUTH2_URL_ACCESS_TOKEN)%'
myep_oauth2_url_resource_owner_details: '%env(MYEP_OAUTH2_URL_RESOURCE_OWNER_DETAILS)%'
myep_oauth2_scopes: '%env(csv:MYEP_OAUTH2_SCOPES)%'
# The mails of a mailing are sent right away here, like every other mail, instead of
# waiting for a worker on the dedicated queue. The mailer transport is deliberately not
# switched: a mailing still goes out over MAILING_MAILER_DSN, so the path being exercised
# locally is the one that runs in production.
when@dev:
parameters:
mailing_bus_transport: 'sync'
when@test:
parameters:
# a job profile id ListControllerTest inserts itself - db_test has no job profiles
assignment_export.job_profile_ids: [ 900001 ]
mailing_bus_transport: 'sync'
# the questionnaire tests need it offered; any day before the placements they create
questionnaire_go_live: '2026-01-01'