#1154: VacationSendMailService.kt

projectforge-business/src/main/kotlin/org/projectforge/business/vacation/service/VacationSendMailService.kt Service, projectforge-business/src/main/kotlin/org/projectforge/business/vacation/service/VacationSendMailService.kt 310 lines · 234 code · 50 comments · 26 blank
Sends e-mail notifications to vacationers, managers, replacements, and HR staff when vacation entries are created, modified, or deleted.

Architecture

VacationSendMailService is a @Service that wires together ConfigurationService (mail server config, HR e-mail address), EmployeeDao (employee/user lookups), and SendMail (mail infrastructure). It is invoked exclusively from VacationDao's after* lifecycle hooks.

Notification Flow

  1. checkAndSendMail(obj, operationType, dbObj) — Entry point. Checks mail server is configured, builds a VacationInfo holder.
  2. For special vacation entries with status IN_PROGRESS or APPROVED, sends to HR e-mail address (configured in administration).
  3. Sends to the vacationer (if they didn't make the change themselves — checked via ThreadLocalUserContext.loggedInUserId).
  4. Sends to the manager (if not the actor).
  5. Sends to the primary replacement and all other replacements (deduplicated via a set).

Mail Composition

The prepareMail() method creates a Mail object with HTML content rendered from the mail/vacationMail.html Groovy template. The template receives a map containing:

VacationInfo Inner Class

This data holder resolves all employee references (vacationer, manager, replacement, otherReplacements) to PFUserDO objects and formats dates, working days, half-day flags, and special status. The updateI18n(recipient) method re-resolves user-visible strings for the recipient's locale, enabling localized e-mail delivery to recipients in different language settings.

URL Generation

The companion object lazily builds a link to the vacation edit page via SendMail.buildUrl() combined with MenuItemDefId.VACATION.url, producing deep links like /vacation/edit/123?returnToCaller=account.

Design Rationale

Mail sending is separated from the DAO to keep persistence concerns isolated. The service uses VacationInfo as an intermediate data class rather than passing the entity directly to templates — this ensures templates never access lazy-loaded JPA proxies (which would fail outside a transaction). The updateI18n method supports the multi-locale requirement of a German company with international employees. The HR notification path for special vacations ensures compliance monitoring even when employees and managers are the same person.

Git History

868d6abb7 2025 -> 2026
600951ee0 2025 -> 2026
b131193e7 Member variables refactored by using by lazy {}...
d5cc73eb6 Member variables refactored by using by lazy {}...
63081666f Source file headers: 2024-> 2025.
199c26801 Source file headers: 2024-> 2025.
e1db80611 Hotfix: com.sun.mail added.
0a78bc22c Hotfix: com.sun.mail added.
1b50060c3 BaseDao: renamed get->find, save->insert, getList->select, load->select
514533e99 BaseDao: renamed get->find, save->insert, getList->select, load->select
87aaf6a5a Migration stuff: BaseDao refactored...
41217b065 Migration stuff: BaseDao refactored...
67805f2fc ThreadLocalUserContext.user -> loggedInUser
58ffda82b ThreadLocalUserContext.user -> loggedInUser
4c04cfd65 MAJOR-CHANGE! Migration of integer id's to Long id's.
fd13c259a MAJOR-CHANGE! Migration of integer id's to Long id's.
06828f490 Migration stuff in progress...
4b3872a1b Migration stuff in progress...
b6092df09 Copyright 2023 -> 2024
9a0609dd1 Copyright 2023 -> 2024