Skip to content

Adds support for managing and handling public holidays#47

Merged
subsub97 merged 4 commits intomainfrom
public-holiday
Apr 8, 2026
Merged

Adds support for managing and handling public holidays#47
subsub97 merged 4 commits intomainfrom
public-holiday

Conversation

@subsub97
Copy link
Copy Markdown
Collaborator

@subsub97 subsub97 commented Apr 8, 2026

This pull request adds support for managing and handling public holidays throughout the system. It introduces a new public holiday entity, controller, and service, integrates public holiday logic into workday and earnings calculations, and updates event and notification types to reflect public holidays. The main changes are grouped below.

Public Holiday Management:

  • Introduced the PublicHoliday entity and corresponding JPA repository for storing public holidays. [1] [2]
  • Added PublicHolidayService for CRUD operations and holiday date queries, and PublicHolidayController for admin API endpoints to manage public holidays. [1] [2]

Integration with Workday and Earnings Logic:

  • Updated WorkdayService and CompensationCalculator to fetch public holidays and exclude them from work schedules and standard workday/minute calculations. Public holidays are now considered when resolving schedules, calculating daily earnings, and generating workday responses. [1] [2] [3] [4] [5] [6] [7] [8] [9]

Event and Notification Enhancements:

  • Added PUBLIC_HOLIDAY to both DailyEventType and NotificationType, allowing the system to display and notify users about public holidays. [1] [2]

These changes ensure that public holidays are centrally managed and consistently respected across scheduling, payroll, and user notifications.

Copilot AI review requested due to automatic review settings April 8, 2026 11:17
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

Test Results

53 tests   53 ✅  0s ⏱️
 6 suites   0 💤
 6 files     0 ❌

Results for commit 0359e8a.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class “public holiday” support across scheduling, earnings calculation, daily events, and notifications, including an admin API to manage holiday dates.

Changes:

  • Introduces PublicHoliday domain (entity/repository/service) and admin controller endpoints to create/list/delete holidays.
  • Integrates public holidays into workday resolution and compensation calculations (standard minutes, workday counts, daily rate).
  • Adds PUBLIC_HOLIDAY to daily events and notification flows, including batch generation and dispatch-time earnings message calculation.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/main/kotlin/com/moa/entity/PublicHoliday.kt New entity for persisted public holiday dates/names.
src/main/kotlin/com/moa/repository/PublicHolidayRepository.kt Repository queries for holiday existence and date ranges.
src/main/kotlin/com/moa/service/PublicHolidayService.kt CRUD + month/year holiday date retrieval helpers.
src/main/kotlin/com/moa/controller/PublicHolidayController.kt Admin API for managing public holidays.
src/main/kotlin/com/moa/service/WorkdayService.kt Excludes holidays from inferred schedules and includes holiday events/earnings adjustments.
src/main/kotlin/com/moa/service/calculator/CompensationCalculator.kt Adds publicHolidays parameter to exclude holidays from workday counts and derived rates/minutes.
src/main/kotlin/com/moa/entity/DailyEventType.kt Adds PUBLIC_HOLIDAY event and resolves it from provided holiday set.
src/main/kotlin/com/moa/entity/notification/NotificationType.kt Adds PUBLIC_HOLIDAY notification type.
src/main/kotlin/com/moa/service/notification/NotificationBatchService.kt Generates PUBLIC_HOLIDAY notifications on holiday dates; avoids work notifications.
src/main/kotlin/com/moa/service/notification/NotificationDispatchService.kt Fetches per-month holiday sets and passes them into message building.
src/main/kotlin/com/moa/service/notification/NotificationMessageBuilder.kt Requires holiday set to compute CLOCK_OUT earnings correctly with holiday-adjusted rates.
src/main/kotlin/com/moa/service/notification/NotificationEarningsService.kt Threads holiday set into earnings calculation used by notifications.
src/main/kotlin/com/moa/service/notification/NotificationSyncService.kt Excludes PUBLIC_HOLIDAY from “work notification” sync logic.
src/main/kotlin/com/moa/service/MemberEarningsService.kt Adds optional publicHolidays parameter to keep calculations consistent where used.
src/main/kotlin/com/moa/service/dto/PublicHolidayCreateRequest.kt Request DTO for creating holidays.
src/main/kotlin/com/moa/service/dto/PublicHolidayResponse.kt Response DTO for returning holiday records.
src/test/kotlin/com/moa/service/calculator/CompensationCalculatorTest.kt Adds tests for holiday-adjusted workday counts and daily rate changes.
src/test/kotlin/com/moa/entity/DailyEventTypeTest.kt Updates resolve signature and adds tests for PUBLIC_HOLIDAY event behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 31 to 98
@@ -45,6 +91,8 @@ class NotificationBatchService(
private fun isAlreadyGenerated(date: LocalDate): Boolean {
val exists = notificationLogRepository
.existsByScheduledDateAndNotificationType(date, NotificationType.CLOCK_IN)
|| notificationLogRepository
.existsByScheduledDateAndNotificationType(date, NotificationType.PUBLIC_HOLIDAY)
if (exists) log.info("Notifications already generated for date: {}", date)
return exists
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isAlreadyGenerated returns true if either CLOCK_IN or PUBLIC_HOLIDAY logs exist for the date, which can incorrectly block generating PUBLIC_HOLIDAY logs on a holiday if CLOCK_IN logs already exist (e.g., created before the holiday was registered), and vice versa. Consider branching the existence check based on publicHolidayService.isHoliday(date) (or checking only the target types you’re about to generate) so holiday/work generation don’t block each other unintentionally.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공휴일 정보는 미리 넣어둘 것이고
임의로 삭제하는 일을 없을 것 같아서 넘어 감

Comment on lines +38 to +39
fun delete(@PathVariable id: Long) =
ApiResponse.success(publicHolidayService.delete(id))
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete currently wraps a Unit return value in ApiResponse.success(publicHolidayService.delete(id)), which will set content to a Unit instance rather than returning an empty success response. Prefer calling publicHolidayService.delete(id) and then returning ApiResponse.success() so the response body is consistent with other void endpoints.

Suggested change
fun delete(@PathVariable id: Long) =
ApiResponse.success(publicHolidayService.delete(id))
fun delete(@PathVariable id: Long) {
publicHolidayService.delete(id)
return ApiResponse.success()
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅜ 어드민이라 ㄱㅊ을 거 같은데 파일럿 너가 싫다면 고민좀 해볼게

Comment on lines +6 to +14
@Entity
@Table(name = "public_holiday")
class PublicHoliday(
@Column(nullable = false)
val date: LocalDate,

@Column(nullable = false, length = 50)
val name: String,
) : BaseEntity() {
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PublicHoliday appears to represent a single holiday per calendar date, but the table definition doesn’t enforce uniqueness on date. Without a unique constraint (and/or a pre-save check), duplicate rows for the same date can be created, making management and deletions ambiguous (one delete won’t necessarily remove the holiday). Consider adding a unique constraint on date (and handling duplicate creates gracefully).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의도적으로 제거한거임

개천절 + 추석 등 하루에 겹칠 수 있어서
그래서 무급 계산할 때 중복 계산 방지하기 위해서 Set<LocalDate> 중복 제거 필수

@subsub97
Copy link
Copy Markdown
Collaborator Author

subsub97 commented Apr 8, 2026

공휴일 무급 처리 기능

공휴일을 무급(NONE) 기반으로 추가합니다.

무급 처리란?

공휴일 당일은 비근무일(NONE)로 처리되어 dailyPay = 0원이지만,
해당 월의 근무일수가 줄어 일급 단가가 상승하여 월 합계는 동일하게 유지됩니다.

예시: 월급 300만원, 5월

변경 전 (공휴일 없음): 근무일 22일, 일급 = 300만 ÷ 22 = 136,364원
변경 후 (어린이날 1일): 근무일 21일, 일급 = 300만 ÷ 21 = 142,857원

어린이날 dailyPay = 0원
나머지 21일 × 142,857원 = 2,999,997원 → 반올림 = 300만원

공휴일에 출근하면? — 추가 소득 발생

공휴일은 기본적으로 NONE(비근무)이지만, 사용자가 직접 WORK로 변경할 수 있습니다.

이 경우 근무일수 계산에서 이미 공휴일이 차감되어 일급이 높아진 상태에서 해당 날에 추가로 소득이 발생하므로, 월 합계가 기준 월급을 초과합니다.

예시: 월급 300만원, 5월 어린이날을 WORK로 변경

근무일수 계산: 22일 - 1일(공휴일) = 21일
일급 단가: 300만 ÷ 21 = 142,857원

일반 근무 21일 × 142,857원 = 2,999,997원

  • 어린이날 출근 1일 × 142,857원 = 142,857원
    = 월 합계: 3,142,854원 (기준 월급보다 약 14만원 추가)

공휴일 출근은 추가 근무에 대한 보상이라는 개념이며, 이것은 의도된 정책. (나중에 변경될 수 있음)
이때도 events=[PUBLIC_HOLIDAY]는 유지되어 앱에서 공휴일 표시가 사라지지 않습니다.

스케줄 결정 우선순위

  1. 사용자 저장값 (saved) → 사용자 의지 우선
  2. 공휴일 && 근무요일 → NONE (무급)
  3. 정책 근무요일 → WORK
  4. 그 외 → NONE
  • 사용자가 공휴일을 WORK로 변경하면 1번에 의해 WORK 유지
  • 사용자가 다시 삭제하면 2번으로 fallback → NONE 복원
  • 공휴일 이벤트는 DB에서 조회하므로 스케줄 변경과 무관하게 항상 표시

Admin API — 공휴일 CRUD

@AdminAuth 기반으로 공휴일을 등록/조회/삭제할 수 있는 관리자 API를 제공합니다.

Method Endpoint 설명
GET /api/v1/admin/public-holidays?year=2026 연도별 공휴일 목록 조회
POST /api/v1/admin/public-holidays 공휴일 등록 ({ date, name })
DELETE /api/v1/admin/public-holidays/{id} 공휴일 삭제 (ID 기반)
  • 같은 날짜에 여러 공휴일 등록 가능 (개천절+추석 등)
  • DELETE는 ID 기반이므로 같은 날의 특정 공휴일만 삭제 가능

알림 처리

  • 공휴일이면 출퇴근 알림 대신 공휴일 알림 1건 생성 (오전 9시, 플레이스홀더 문구)
    • 클라이언트 FCM 대응 이전 까지는 공휴일 알림 전송 안함

@jeyongsong jeyongsong changed the title Public holiday Adds support for managing and handling public holidays Apr 8, 2026
@subsub97 subsub97 merged commit e6d586c into main Apr 8, 2026
7 checks passed
@subsub97 subsub97 deleted the public-holiday branch April 8, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants