Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/API_SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@
"contractId": 201,
"workerId": 2001,
"workerName": "박민준",
"profileImageUrl": "https://example.com/profile/2001.jpg",
"workerCode": "WORKER001",
"workplaceId": 101,
"workplaceName": "홍대점",
Expand Down Expand Up @@ -590,6 +591,7 @@
"contractId": 201,
"workerId": 2001,
"workerName": "박민준",
"profileImageUrl": "https://example.com/profile/2001.jpg",
"workerCode": "WORKER001",
"hourlyWage": 10030,
"paymentDay": 25,
Expand All @@ -603,6 +605,7 @@
"contractId": 202,
"workerId": 2002,
"workerName": "최수진",
"profileImageUrl": "https://example.com/profile/2002.jpg",
"workerCode": "WORKER002",
"hourlyWage": 11000,
"paymentDay": 25,
Expand Down Expand Up @@ -654,6 +657,7 @@
"contractId": 201,
"workerId": 2001,
"workerName": "박민준",
"profileImageUrl": "https://example.com/profile/2001.jpg",
"workerCode": "WORKER001",
"workplaceId": 101,
"workplaceName": "홍대점",
Expand Down Expand Up @@ -721,6 +725,7 @@
"contractId": 201,
"workerId": 2001,
"workerName": "박민준",
"profileImageUrl": "https://example.com/profile/2001.jpg",
"workerCode": "WORKER001",
"workplaceId": 101,
"workplaceName": "홍대점",
Expand Down Expand Up @@ -1453,6 +1458,7 @@
"id": 308,
"contractId": 201,
"workerName": "박민준",
"profileImageUrl": "https://example.com/profile/2001.jpg",
"workerCode": "WORKER001",
"workplaceName": "홍대점",
"workDate": "2025-12-10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,21 @@ public void calculateOvertime(boolean isSmallWorkplace) {

// 5인 이상 사업장: 주 40시간 초과 시 연장수당 지급
if (this.totalWorkHours.compareTo(STANDARD_WORK_HOURS_PER_WEEK) > 0) {
BigDecimal overtimeHoursCalculated = this.totalWorkHours.subtract(STANDARD_WORK_HOURS_PER_WEEK);
this.overtimeHours = overtimeHoursCalculated;
BigDecimal weeklyOvertimeHours = this.totalWorkHours.subtract(STANDARD_WORK_HOURS_PER_WEEK);
this.overtimeHours = weeklyOvertimeHours;

// 연장수당 = 초과 시간 × (기본시급 × 1.5)
// 주간 연장 시간 중 일일 연장 시간을 제외한 "순수 주간 연장 가산 대상" 계산
// (일일 연장분은 WorkRecord.overtimeSalary에서 이미 0.5배 가산됨)
BigDecimal totalDailyOvertimeHours = this.workRecords.stream()
.filter(wr -> wr.getStatus() == WorkRecordStatus.COMPLETED)
.map(WorkRecord::getOvertimeHours)
.reduce(BigDecimal.ZERO, BigDecimal::add);

BigDecimal pureWeeklyOvertimeHours = weeklyOvertimeHours.subtract(totalDailyOvertimeHours).max(BigDecimal.ZERO);

// 연장수당 = 순수 주간 연장 시간 × 기본시급 × 0.5배율
BigDecimal hourlyWage = this.contract.getHourlyWage();
this.overtimeAmount = overtimeHoursCalculated.multiply(hourlyWage).multiply(OVERTIME_RATE);
this.overtimeAmount = pureWeeklyOvertimeHours.multiply(hourlyWage).multiply(BigDecimal.valueOf(0.5));
} else {
this.overtimeHours = BigDecimal.ZERO;
this.overtimeAmount = BigDecimal.ZERO;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.paycheck.domain.contract.dto;

import com.example.paycheck.domain.contract.entity.WorkerContract;
import com.example.paycheck.domain.user.entity.User;
import com.example.paycheck.domain.worker.entity.Worker;
import com.example.paycheck.domain.salary.util.DeductionCalculator;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
Expand Down Expand Up @@ -79,6 +81,7 @@ public static class Response {
private String workplaceName;
private Long workerId;
private String workerName;
private String profileImageUrl;
private String workerCode;
private String workerPhone;
private BigDecimal hourlyWage;
Expand All @@ -90,14 +93,17 @@ public static class Response {
private DeductionCalculator.PayrollDeductionType payrollDeductionType;

public static Response from(WorkerContract contract) {
Worker worker = contract.getWorker();
User user = worker != null ? worker.getUser() : null;
return Response.builder()
.id(contract.getId())
.workplaceId(contract.getWorkplace().getId())
.workplaceName(contract.getWorkplace().getName())
.workerId(contract.getWorker().getId())
.workerName(contract.getWorker().getUser().getName())
.workerCode(contract.getWorker().getWorkerCode())
.workerPhone(contract.getWorker().getUser().getPhone())
.workerId(worker != null ? worker.getId() : null)
.workerName(user != null ? user.getName() : null)
.profileImageUrl(user != null ? user.getProfileImageUrl() : null)
.workerCode(worker != null ? worker.getWorkerCode() : null)
.workerPhone(user != null ? user.getPhone() : null)
.hourlyWage(contract.getHourlyWage())
.workSchedules(contract.getWorkSchedules())
.contractStartDate(contract.getContractStartDate())
Expand All @@ -119,6 +125,7 @@ public static class ListResponse {
private Long workplaceId;
private String workplaceName;
private String workerName;
private String profileImageUrl;
private String workerCode;
private String workerPhone;
private BigDecimal hourlyWage;
Expand All @@ -129,13 +136,16 @@ public static class ListResponse {
private Boolean isActive;

public static ListResponse from(WorkerContract contract) {
Worker worker = contract.getWorker();
User user = worker != null ? worker.getUser() : null;
return ListResponse.builder()
.id(contract.getId())
.workplaceId(contract.getWorkplace().getId())
.workplaceName(contract.getWorkplace().getName())
.workerName(contract.getWorker().getUser().getName())
.workerCode(contract.getWorker().getWorkerCode())
.workerPhone(contract.getWorker().getUser().getPhone())
.workerName(user != null ? user.getName() : null)
.profileImageUrl(user != null ? user.getProfileImageUrl() : null)
.workerCode(worker != null ? worker.getWorkerCode() : null)
.workerPhone(user != null ? user.getPhone() : null)
.hourlyWage(contract.getHourlyWage())
.contractStartDate(contract.getContractStartDate())
.contractEndDate(contract.getContractEndDate())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,28 @@
public interface WorkerContractRepository extends JpaRepository<WorkerContract, Long> {
@Query("SELECT c FROM WorkerContract c " +
"JOIN FETCH c.worker w " +
"JOIN FETCH w.user u " +
"JOIN FETCH c.workplace wp " +
"WHERE w.id = :workerId")
List<WorkerContract> findByWorkerId(@Param("workerId") Long workerId);

@Query("SELECT c FROM WorkerContract c " +
"JOIN FETCH c.worker w " +
"JOIN FETCH w.user u " +
"JOIN FETCH c.workplace wp " +
"WHERE wp.id = :workplaceId")
List<WorkerContract> findByWorkplaceId(@Param("workplaceId") Long workplaceId);

@Query("SELECT c FROM WorkerContract c " +
"JOIN FETCH c.worker w " +
"JOIN FETCH w.user u " +
"JOIN FETCH c.workplace wp " +
"WHERE wp.id = :workplaceId AND c.isActive = :isActive")
List<WorkerContract> findByWorkplaceIdAndIsActive(@Param("workplaceId") Long workplaceId, @Param("isActive") Boolean isActive);

@Query("SELECT c FROM WorkerContract c " +
"JOIN FETCH c.worker w " +
"JOIN FETCH w.user u " +
"JOIN FETCH c.workplace wp " +
"WHERE w.id = :workerId AND wp.id = :workplaceId")
Optional<WorkerContract> findByWorkerIdAndWorkplaceId(@Param("workerId") Long workerId, @Param("workplaceId") Long workplaceId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,14 @@ public SalaryDto.Response calculateSalaryByWorkRecords(Long contractId, Integer
BigDecimal totalBasePay = BigDecimal.ZERO;
BigDecimal totalNightPay = BigDecimal.ZERO;
BigDecimal totalHolidayPay = BigDecimal.ZERO;
BigDecimal totalDailyOvertimePay = BigDecimal.ZERO;

for (WorkRecord record : workRecords) {
totalWorkHours = totalWorkHours.add(record.getTotalHours());
totalBasePay = totalBasePay.add(record.getBaseSalary());
totalNightPay = totalNightPay.add(record.getNightSalary());
totalHolidayPay = totalHolidayPay.add(record.getHolidaySalary());
totalDailyOvertimePay = totalDailyOvertimePay.add(record.getOvertimeSalary());
}

// ========================================
Expand All @@ -133,7 +135,7 @@ public SalaryDto.Response calculateSalaryByWorkRecords(Long contractId, Integer
// 당월 WeeklyAllowance 조회
List<WeeklyAllowance> weeklyAllowances = weeklyAllowanceRepository.findByContractIdAndYearMonth(contractId, year, month);
BigDecimal totalWeeklyPaidLeaveAmount = BigDecimal.ZERO;
BigDecimal totalOvertimePay = BigDecimal.ZERO;
BigDecimal totalWeeklyOvertimePay = BigDecimal.ZERO;

// 월급날 계산 (당월 paymentDay)
LocalDate paymentDayDate = adjustDayOfMonth(LocalDate.of(year, month, 1), paymentDay);
Expand All @@ -147,7 +149,7 @@ public SalaryDto.Response calculateSalaryByWorkRecords(Long contractId, Integer
if (!isLastWeek) {
// 마지막 주차가 아니면 현재 월 급여에 포함
totalWeeklyPaidLeaveAmount = totalWeeklyPaidLeaveAmount.add(allowance.getWeeklyPaidLeaveAmount());
totalOvertimePay = totalOvertimePay.add(allowance.getOvertimeAmount());
totalWeeklyOvertimePay = totalWeeklyOvertimePay.add(allowance.getOvertimeAmount());
}
// 마지막 주차면 제외 (다음 달 급여로 이월)
}
Expand All @@ -167,10 +169,13 @@ public SalaryDto.Response calculateSalaryByWorkRecords(Long contractId, Integer
if (isPreviousLastWeek) {
// 전월 마지막 주차의 수당을 현재 월 급여에 추가 (이월분)
totalWeeklyPaidLeaveAmount = totalWeeklyPaidLeaveAmount.add(allowance.getWeeklyPaidLeaveAmount());
totalOvertimePay = totalOvertimePay.add(allowance.getOvertimeAmount());
totalWeeklyOvertimePay = totalWeeklyOvertimePay.add(allowance.getOvertimeAmount());
}
}

// 연장 수당 합계 = 일일 연장 가산분 + 주간 연장 가산분
BigDecimal totalOvertimePay = totalDailyOvertimePay.add(totalWeeklyOvertimePay);

BigDecimal totalGrossPay = totalBasePay.add(totalNightPay).add(totalHolidayPay)
.add(totalWeeklyPaidLeaveAmount).add(totalOvertimePay);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.paycheck.domain.worker.dto;

import com.example.paycheck.domain.user.entity.User;
import com.example.paycheck.domain.worker.entity.Worker;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
Expand All @@ -21,16 +22,19 @@ public static class Response {
private Long userId;
private String name;
private String phone;
private String profileImageUrl;
private String workerCode;
private String accountNumber;
private String bankName;

public static Response from(Worker worker) {
User user = worker.getUser();
return Response.builder()
.id(worker.getId())
.userId(worker.getUser().getId())
.name(worker.getUser().getName())
.phone(worker.getUser().getPhone())
.userId(user != null ? user.getId() : null)
.name(user != null ? user.getName() : null)
.phone(user != null ? user.getPhone() : null)
.profileImageUrl(user != null ? user.getProfileImageUrl() : null)
.workerCode(worker.getWorkerCode())
.accountNumber(worker.getAccountNumber())
.bankName(worker.getBankName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ public static class DetailedResponse {
private BigDecimal nightSalary;
@Schema(description = "휴일 수당")
private BigDecimal holidaySalary;
@Schema(description = "연장 가산 수당")
private BigDecimal overtimeSalary;
@Schema(description = "총 급여")
private BigDecimal totalSalary;

Expand Down Expand Up @@ -121,6 +123,7 @@ public static DetailedResponse from(WorkRecord workRecord, WorkRecordCurrentStat
.baseSalary(workRecord.getBaseSalary())
.nightSalary(workRecord.getNightSalary())
.holidaySalary(workRecord.getHolidaySalary())
.overtimeSalary(workRecord.getOvertimeSalary())
.totalSalary(workRecord.getTotalSalary())
.build();
}
Expand Down
Loading
Loading