yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java
@@ -119,4 +119,14 @@ ErrorCode SALARY_STRUCTURE_NAME_EXISTS = new ErrorCode(1_030_005_025, "èªé ¬ç»æåç§°å·²åå¨"); ErrorCode SALARY_STRUCTURE_IN_USE = new ErrorCode(1_030_005_026, "èªé ¬ç»ææ£å¨è¢«å工使ç¨ï¼æ æ³å é¤"); // ========== çæ¬¡ç®¡ç 1-030-009-000 ========== ErrorCode SHIFT_NOT_EXISTS = new ErrorCode(1_030_009_000, "çæ¬¡ä¸åå¨"); ErrorCode SHIFT_NAME_EXISTS = new ErrorCode(1_030_009_001, "çæ¬¡åç§°å·²åå¨"); ErrorCode SHIFT_IN_USE = new ErrorCode(1_030_009_002, "çæ¬¡å·²è¢«æç使ç¨ï¼æ æ³å é¤"); // ========== æç管ç 1-030-010-000 ========== ErrorCode SCHEDULE_ITEM_LIST_EMPTY = new ErrorCode(1_030_010_000, "æçæ°æ®ä¸è½ä¸ºç©º"); ErrorCode SCHEDULE_SHIFT_NOT_EXISTS = new ErrorCode(1_030_010_001, "æçå¼ç¨ççæ¬¡ä¸åå¨"); ErrorCode SCHEDULE_EMPLOYEE_NOT_EXISTS = new ErrorCode(1_030_010_002, "æçå¼ç¨çåå·¥ä¸åå¨"); } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmEmployeeScheduleController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,46 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleListReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleRespVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleSaveReqVO; import cn.iocoder.yudao.module.hrm.service.attendance.HrmEmployeeScheduleService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管çåå° - HRM åå·¥æç") @RestController @RequestMapping("/hrm/employee-schedule") @Validated public class HrmEmployeeScheduleController { @Resource private HrmEmployeeScheduleService employeeScheduleService; @GetMapping("/list") @Operation(summary = "è·å¾ææåå·¥æçå表") @PreAuthorize("@ss.hasPermission('hrm:employee-schedule:query')") public CommonResult<List<HrmEmployeeScheduleRespVO>> getEmployeeScheduleList( @Valid HrmEmployeeScheduleListReqVO reqVO) { return success(employeeScheduleService.getScheduleList(reqVO)); } @PostMapping("/save") @Operation(summary = "æ¹éä¿ååå·¥æçï¼æ´æè¦çå¼ï¼") @PreAuthorize("@ss.hasPermission('hrm:employee-schedule:save')") public CommonResult<Boolean> saveEmployeeSchedule( @Valid @RequestBody List<@Valid HrmEmployeeScheduleSaveReqVO> saveReqVOList) { employeeScheduleService.saveSchedule(saveReqVOList); return success(true); } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmShiftController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,100 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance; import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftPageReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftRespVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO; import cn.iocoder.yudao.module.hrm.service.attendance.HrmShiftService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管çåå° - HRM çæ¬¡å®ä¹") @RestController @RequestMapping("/hrm/shift") @Validated public class HrmShiftController { @Resource private HrmShiftService shiftService; @PostMapping("/create") @Operation(summary = "æ°å¢ç次") @PreAuthorize("@ss.hasPermission('hrm:shift:create')") public CommonResult<Long> createShift(@Valid @RequestBody HrmShiftSaveReqVO createReqVO) { return success(shiftService.createShift(createReqVO)); } @PutMapping("/update") @Operation(summary = "æ´æ°ç次") @PreAuthorize("@ss.hasPermission('hrm:shift:update')") public CommonResult<Boolean> updateShift(@Valid @RequestBody HrmShiftSaveReqVO updateReqVO) { shiftService.updateShift(updateReqVO); return success(true); } @DeleteMapping("/delete") @Operation(summary = "å é¤ç次") @Parameter(name = "id", description = "çæ¬¡ID", required = true) @PreAuthorize("@ss.hasPermission('hrm:shift:delete')") public CommonResult<Boolean> deleteShift(@RequestParam("id") Long id) { shiftService.deleteShift(id); return success(true); } @GetMapping("/get") @Operation(summary = "è·å¾ç次") @Parameter(name = "id", description = "çæ¬¡ID", required = true) @PreAuthorize("@ss.hasPermission('hrm:shift:query')") public CommonResult<HrmShiftRespVO> getShift(@RequestParam("id") Long id) { HrmShiftDO shift = shiftService.getShift(id); return success(BeanUtils.toBean(shift, HrmShiftRespVO.class)); } @GetMapping("/page") @Operation(summary = "è·å¾ç次å页") @PreAuthorize("@ss.hasPermission('hrm:shift:query')") public CommonResult<PageResult<HrmShiftRespVO>> getShiftPage(@Valid HrmShiftPageReqVO pageReqVO) { PageResult<HrmShiftDO> pageResult = shiftService.getShiftPage(pageReqVO); return success(BeanUtils.toBean(pageResult, HrmShiftRespVO.class)); } @GetMapping("/list") @Operation(summary = "è·å¾ç次å表ï¼å¯ç¨ç¶æï¼") @PreAuthorize("@ss.hasPermission('hrm:shift:query')") public CommonResult<List<HrmShiftRespVO>> getShiftList() { List<HrmShiftDO> list = shiftService.getShiftList(); return success(BeanUtils.toBean(list, HrmShiftRespVO.class)); } @GetMapping("/export-excel") @Operation(summary = "导åºç次 Excel") @PreAuthorize("@ss.hasPermission('hrm:shift:export')") @ApiAccessLog(operateType = EXPORT) public void exportShiftExcel(@Valid HrmShiftPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); PageResult<HrmShiftDO> pageResult = shiftService.getShiftPage(pageReqVO); List<HrmShiftRespVO> data = BeanUtils.toBean(pageResult.getList(), HrmShiftRespVO.class); ExcelUtils.write(response, "çæ¬¡.xls", "æ°æ®", HrmShiftRespVO.class, data); } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmEmployeeScheduleListReqVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,21 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import lombok.Data; @Schema(description = "管çåå° - åå·¥æçæ¥è¯¢ Request VO") @Data public class HrmEmployeeScheduleListReqVO { @Schema(description = "æä»½ï¼æ ¼å¼ï¼yyyy-MM", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08") @NotBlank(message = "æä»½ä¸è½ä¸ºç©º") private String yearMonth; @Schema(description = "é¨é¨ID", example = "1") private Long deptId; @Schema(description = "åå·¥ID", example = "1") private Long employeeId; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmEmployeeScheduleRespVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,39 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDate; @Schema(description = "管çåå° - åå·¥æç Response VO") @Data public class HrmEmployeeScheduleRespVO { @Schema(description = "æçID") private Long id; @Schema(description = "åå·¥ID") private Long employeeId; @Schema(description = "åå·¥å§å") private String employeeName; @Schema(description = "å工工å·") private String employeeNo; @Schema(description = "æçæ¥æ") private LocalDate date; @Schema(description = "çæ¬¡IDï¼ä¸ºç©ºè¡¨ç¤ºä¼æ¯") private Long shiftId; @Schema(description = "çæ¬¡åç§°") private String shiftName; @Schema(description = "çæ¬¡é¢è²") private String shiftColor; @Schema(description = "夿³¨") private String remark; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmEmployeeScheduleSaveReqVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,27 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; import java.time.LocalDate; @Schema(description = "管çåå° - åå·¥æçä¿å项 Request VO") @Data public class HrmEmployeeScheduleSaveReqVO { @Schema(description = "åå·¥ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "åå·¥ä¸è½ä¸ºç©º") private Long employeeId; @Schema(description = "æçæ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-08-01") @NotNull(message = "æçæ¥æä¸è½ä¸ºç©º") private LocalDate date; @Schema(description = "çæ¬¡IDï¼ä¸ºç©ºè¡¨ç¤ºä¼æ¯", example = "1") private Long shiftId; @Schema(description = "夿³¨", example = "è°ä¼") private String remark; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmShiftPageReqVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,21 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @Schema(description = "管çåå° - çæ¬¡å页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class HrmShiftPageReqVO extends PageParam { @Schema(description = "çæ¬¡åç§°", example = "ç½ç") private String name; @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨", example = "0") private Integer status; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmShiftRespVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,56 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo; import cn.idev.excel.annotation.ExcelIgnoreUnannotated; import cn.idev.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.LocalTime; @Schema(description = "管çåå° - çæ¬¡ Response VO") @Data @ExcelIgnoreUnannotated public class HrmShiftRespVO { @Schema(description = "çæ¬¡ID") @ExcelProperty("çæ¬¡ID") private Long id; @Schema(description = "çæ¬¡åç§°") @ExcelProperty("çæ¬¡åç§°") private String name; @Schema(description = "ä¸çæ¶é´") @ExcelProperty("ä¸çæ¶é´") private LocalTime startTime; @Schema(description = "ä¸çæ¶é´") @ExcelProperty("ä¸çæ¶é´") private LocalTime endTime; @Schema(description = "æ åå·¥æ¶ï¼å°æ¶ï¼") @ExcelProperty("æ åå·¥æ¶") private BigDecimal workHours; @Schema(description = "æ¯å¦è·¨å¤©") @ExcelProperty("æ¯å¦è·¨å¤©") private Boolean crossDay; @Schema(description = "æ ç¾é¢è²ï¼åå è¿å¶ï¼") @ExcelProperty("æ ç¾é¢è²") private String color; @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨") private Integer status; @Schema(description = "夿³¨") @ExcelProperty("夿³¨") private String remark; @Schema(description = "å建æ¶é´") @ExcelProperty("å建æ¶é´") private LocalDateTime createTime; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmShiftSaveReqVO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,45 @@ package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import java.math.BigDecimal; import java.time.LocalTime; @Schema(description = "管çåå° - çæ¬¡æ°å¢/ä¿®æ¹ Request VO") @Data public class HrmShiftSaveReqVO { @Schema(description = "çæ¬¡IDï¼ä¿®æ¹æ¶å¿ å¡«ï¼", example = "1") private Long id; @Schema(description = "çæ¬¡åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "ç½ç") @NotBlank(message = "çæ¬¡åç§°ä¸è½ä¸ºç©º") private String name; @Schema(description = "ä¸çæ¶é´", requiredMode = Schema.RequiredMode.REQUIRED, example = "08:00:00") @NotNull(message = "ä¸çæ¶é´ä¸è½ä¸ºç©º") private LocalTime startTime; @Schema(description = "ä¸çæ¶é´", requiredMode = Schema.RequiredMode.REQUIRED, example = "17:00:00") @NotNull(message = "ä¸çæ¶é´ä¸è½ä¸ºç©º") private LocalTime endTime; @Schema(description = "æ åå·¥æ¶ï¼å°æ¶ï¼", example = "8") private BigDecimal workHours; @Schema(description = "æ¯å¦è·¨å¤©", example = "false") private Boolean crossDay; @Schema(description = "æ ç¾é¢è²ï¼åå è¿å¶ï¼", example = "#1677ff") private String color; @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨", example = "0") private Integer status; @Schema(description = "夿³¨", example = "æ åç½ç") private String remark; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/employee/vo/HrmEmployeePageReqVO.java
@@ -24,4 +24,7 @@ @Schema(description = "åå·¥ç¶æ", example = "1") private Integer employeeStatus; @Schema(description = "å ³é®è¯ï¼æ¨¡ç³å¹é åå·¥ç¼å·/å§å/ææºå·ï¼", example = "å¼ ä¸") private String keyword; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSalaryCalculationRespVO.java
@@ -97,6 +97,10 @@ @ExcelProperty("å çæ¶é¿") private BigDecimal overtimeHours; @Schema(description = "æçæ»å·¥æ¶") @ExcelProperty("æçæ»å·¥æ¶") private BigDecimal totalWorkHours; @Schema(description = "ç¶æ") @ExcelProperty(value = "ç¶æ", converter = DictConvert.class) @DictFormat("hrm_salary_calculation_status") yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmEmployeeScheduleDO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,56 @@ package cn.iocoder.yudao.module.hrm.dal.dataobject.attendance; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; import java.time.LocalDate; /** * åå·¥æçæç» DO * * @author è¶ çº§ç®¡çå */ @TableName("hrm_employee_schedule") @KeySequence("hrm_employee_schedule_seq") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor public class HrmEmployeeScheduleDO extends BaseDO { /** * æçID */ @TableId private Long id; /** * åå·¥ID * * å ³è HrmEmployeeDO#getId() */ private Long employeeId; /** * æçæ¥æ */ private LocalDate date; /** * çæ¬¡ID * * å ³è HrmShiftDO#getId()ï¼ä¸ºç©ºè¡¨ç¤ºä¼æ¯ */ private Long shiftId; /** * 夿³¨ */ private String remark; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmShiftDO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,73 @@ package cn.iocoder.yudao.module.hrm.dal.dataobject.attendance; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; import java.math.BigDecimal; import java.time.LocalTime; /** * çæ¬¡å®ä¹ DO * * @author è¶ çº§ç®¡çå */ @TableName("hrm_shift") @KeySequence("hrm_shift_seq") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor public class HrmShiftDO extends BaseDO { /** * çæ¬¡ID */ @TableId private Long id; /** * çæ¬¡åç§° */ private String name; /** * ä¸çæ¶é´ */ private LocalTime startTime; /** * ä¸çæ¶é´ */ private LocalTime endTime; /** * æ åå·¥æ¶ï¼å°æ¶ï¼ */ private BigDecimal workHours; /** * æ¯å¦è·¨å¤© */ private Boolean crossDay; /** * æ ç¾é¢è²ï¼åå è¿å¶ï¼ */ private String color; /** * ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨ */ private Integer status; /** * 夿³¨ */ private String remark; } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSalaryCalculationDO.java
@@ -123,6 +123,11 @@ private BigDecimal overtimeHours; /** * æçæ»å·¥æ¶ï¼å°æ¶ï¼ */ private BigDecimal totalWorkHours; /** * ç¶æï¼0-å¾ ç¡®è®¤ï¼10-已确认ï¼20-已忾 */ private Integer status; yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/attendance/HrmEmployeeScheduleMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,51 @@ package cn.iocoder.yudao.module.hrm.dal.mysql.attendance; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO; import org.apache.ibatis.annotations.Mapper; import java.time.LocalDate; import java.util.Collection; import java.util.List; /** * åå·¥æçæç» Mapper * * @author è¶ çº§ç®¡çå */ @Mapper public interface HrmEmployeeScheduleMapper extends BaseMapperX<HrmEmployeeScheduleDO> { default HrmEmployeeScheduleDO selectByEmployeeIdAndDate(Long employeeId, LocalDate date) { return selectOne(new LambdaQueryWrapperX<HrmEmployeeScheduleDO>() .eq(HrmEmployeeScheduleDO::getEmployeeId, employeeId) .eq(HrmEmployeeScheduleDO::getDate, date)); } default List<HrmEmployeeScheduleDO> selectListByPeriod(LocalDate startDate, LocalDate endDate) { return selectList(new LambdaQueryWrapperX<HrmEmployeeScheduleDO>() .between(HrmEmployeeScheduleDO::getDate, startDate, endDate) .orderByAsc(HrmEmployeeScheduleDO::getDate)); } default List<HrmEmployeeScheduleDO> selectListByEmployeeIdsAndPeriod(Collection<Long> employeeIds, LocalDate startDate, LocalDate endDate) { return selectList(new LambdaQueryWrapperX<HrmEmployeeScheduleDO>() .in(HrmEmployeeScheduleDO::getEmployeeId, employeeIds) .between(HrmEmployeeScheduleDO::getDate, startDate, endDate) .orderByAsc(HrmEmployeeScheduleDO::getDate)); } default List<HrmEmployeeScheduleDO> selectListByEmployeeIdAndPeriod(Long employeeId, LocalDate startDate, LocalDate endDate) { return selectList(new LambdaQueryWrapperX<HrmEmployeeScheduleDO>() .eq(HrmEmployeeScheduleDO::getEmployeeId, employeeId) .between(HrmEmployeeScheduleDO::getDate, startDate, endDate) .orderByAsc(HrmEmployeeScheduleDO::getDate)); } default Long selectCountByShiftId(Long shiftId) { return selectCount(new LambdaQueryWrapperX<HrmEmployeeScheduleDO>() .eq(HrmEmployeeScheduleDO::getShiftId, shiftId)); } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/attendance/HrmShiftMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,33 @@ package cn.iocoder.yudao.module.hrm.dal.mysql.attendance; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftPageReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** * çæ¬¡å®ä¹ Mapper * * @author è¶ çº§ç®¡çå */ @Mapper public interface HrmShiftMapper extends BaseMapperX<HrmShiftDO> { default PageResult<HrmShiftDO> selectPage(HrmShiftPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<HrmShiftDO>() .likeIfPresent(HrmShiftDO::getName, reqVO.getName()) .eqIfPresent(HrmShiftDO::getStatus, reqVO.getStatus()) .orderByAsc(HrmShiftDO::getStartTime)); } default List<HrmShiftDO> selectListByStatus(Integer status) { return selectList(new LambdaQueryWrapperX<HrmShiftDO>() .eqIfPresent(HrmShiftDO::getStatus, status) .orderByAsc(HrmShiftDO::getStartTime)); } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/employee/HrmEmployeeMapper.java
@@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.hrm.dal.mysql.employee; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -7,6 +8,7 @@ import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; import java.util.List; /** @@ -46,12 +48,41 @@ } default PageResult<HrmEmployeeDO> selectPage(HrmEmployeePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<HrmEmployeeDO>() LambdaQueryWrapperX<HrmEmployeeDO> wrapper = new LambdaQueryWrapperX<HrmEmployeeDO>() .likeIfPresent(HrmEmployeeDO::getEmployeeNo, reqVO.getEmployeeNo()) .likeIfPresent(HrmEmployeeDO::getName, reqVO.getName()) .eqIfPresent(HrmEmployeeDO::getDeptId, reqVO.getDeptId()) .eqIfPresent(HrmEmployeeDO::getEmployeeStatus, reqVO.getEmployeeStatus()) .orderByDesc(HrmEmployeeDO::getId)); .eqIfPresent(HrmEmployeeDO::getEmployeeStatus, reqVO.getEmployeeStatus()); appendKeyword(wrapper, reqVO.getKeyword()); wrapper.orderByDesc(HrmEmployeeDO::getId); return selectPage(reqVO, wrapper); } /** * æé¨é¨éåï¼å«åé¨é¨ï¼å页æ¥è¯¢åå·¥ */ default PageResult<HrmEmployeeDO> selectPageByDeptIds(HrmEmployeePageReqVO reqVO, Collection<Long> deptIds) { LambdaQueryWrapperX<HrmEmployeeDO> wrapper = new LambdaQueryWrapperX<HrmEmployeeDO>() .likeIfPresent(HrmEmployeeDO::getEmployeeNo, reqVO.getEmployeeNo()) .likeIfPresent(HrmEmployeeDO::getName, reqVO.getName()) .inIfPresent(HrmEmployeeDO::getDeptId, deptIds) .eqIfPresent(HrmEmployeeDO::getEmployeeStatus, reqVO.getEmployeeStatus()); appendKeyword(wrapper, reqVO.getKeyword()); wrapper.orderByDesc(HrmEmployeeDO::getId); return selectPage(reqVO, wrapper); } /** * å ³é®è¯æ¨¡ç³å¹é åå·¥ç¼å· / å§å / ææºå· */ private void appendKeyword(LambdaQueryWrapperX<HrmEmployeeDO> wrapper, String keyword) { if (StrUtil.isBlank(keyword)) { return; } String kw = keyword.trim(); wrapper.and(w -> w.like(HrmEmployeeDO::getEmployeeNo, kw) .or().like(HrmEmployeeDO::getName, kw) .or().like(HrmEmployeeDO::getPhone, kw)); } default List<HrmEmployeeDO> selectListByStatus(Integer employeeStatus) { yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,26 @@ package cn.iocoder.yudao.module.hrm.service.attendance; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleListReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleRespVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleSaveReqVO; import java.util.List; /** * åå·¥æç Service æ¥å£ * * @author è¶ çº§ç®¡çå */ public interface HrmEmployeeScheduleService { /** * æ¥è¯¢æææçå表 */ List<HrmEmployeeScheduleRespVO> getScheduleList(HrmEmployeeScheduleListReqVO reqVO); /** * æ¹éä¿åæçï¼æ´æè¦çå¼ï¼æç次 upsertï¼æ çæ¬¡å é¤ï¼ */ void saveSchedule(List<HrmEmployeeScheduleSaveReqVO> saveReqVOList); } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,143 @@ package cn.iocoder.yudao.module.hrm.service.attendance; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleListReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleRespVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmEmployeeScheduleMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmShiftMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDate; import java.time.YearMonth; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; /** * åå·¥æç Service å®ç°ç±» * * @author è¶ çº§ç®¡çå */ @Service @Validated public class HrmEmployeeScheduleServiceImpl implements HrmEmployeeScheduleService { @Resource private HrmEmployeeScheduleMapper employeeScheduleMapper; @Resource private HrmEmployeeMapper employeeMapper; @Resource private HrmShiftMapper shiftMapper; @Override public List<HrmEmployeeScheduleRespVO> getScheduleList(HrmEmployeeScheduleListReqVO reqVO) { YearMonth yearMonth = YearMonth.parse(reqVO.getYearMonth()); LocalDate startDate = yearMonth.atDay(1); LocalDate endDate = yearMonth.atEndOfMonth(); // 1. ææ¡ä»¶æ¥è¯¢æçè®°å½ List<HrmEmployeeScheduleDO> list; if (reqVO.getEmployeeId() != null) { list = employeeScheduleMapper.selectListByEmployeeIdAndPeriod(reqVO.getEmployeeId(), startDate, endDate); } else if (reqVO.getDeptId() != null) { List<HrmEmployeeDO> employees = employeeMapper.selectByDeptId(reqVO.getDeptId()); Set<Long> employeeIds = employees.stream().map(HrmEmployeeDO::getId).collect(Collectors.toSet()); list = CollUtil.isEmpty(employeeIds) ? Collections.emptyList() : employeeScheduleMapper.selectListByEmployeeIdsAndPeriod(employeeIds, startDate, endDate); } else { list = employeeScheduleMapper.selectListByPeriod(startDate, endDate); } if (CollUtil.isEmpty(list)) { return Collections.emptyList(); } // 2. æ¼æ¥å工信æ¯ä¸çæ¬¡ä¿¡æ¯ Set<Long> employeeIds = list.stream().map(HrmEmployeeScheduleDO::getEmployeeId) .filter(Objects::nonNull).collect(Collectors.toSet()); Set<Long> shiftIds = list.stream().map(HrmEmployeeScheduleDO::getShiftId) .filter(Objects::nonNull).collect(Collectors.toSet()); Map<Long, HrmEmployeeDO> employeeMap = CollUtil.isEmpty(employeeIds) ? Collections.emptyMap() : employeeMapper.selectBatchIds(employeeIds).stream() .collect(Collectors.toMap(HrmEmployeeDO::getId, Function.identity(), (a, b) -> a)); Map<Long, HrmShiftDO> shiftMap = CollUtil.isEmpty(shiftIds) ? Collections.emptyMap() : shiftMapper.selectBatchIds(shiftIds).stream() .collect(Collectors.toMap(HrmShiftDO::getId, Function.identity(), (a, b) -> a)); return list.stream().map(schedule -> { HrmEmployeeScheduleRespVO respVO = new HrmEmployeeScheduleRespVO(); respVO.setId(schedule.getId()); respVO.setEmployeeId(schedule.getEmployeeId()); HrmEmployeeDO employee = schedule.getEmployeeId() == null ? null : employeeMap.get(schedule.getEmployeeId()); if (employee != null) { respVO.setEmployeeName(employee.getName()); respVO.setEmployeeNo(employee.getEmployeeNo()); } respVO.setDate(schedule.getDate()); respVO.setShiftId(schedule.getShiftId()); HrmShiftDO shift = schedule.getShiftId() == null ? null : shiftMap.get(schedule.getShiftId()); if (shift != null) { respVO.setShiftName(shift.getName()); respVO.setShiftColor(shift.getColor()); } respVO.setRemark(schedule.getRemark()); return respVO; }).collect(Collectors.toList()); } @Override @Transactional(rollbackFor = Exception.class) public void saveSchedule(List<HrmEmployeeScheduleSaveReqVO> saveReqVOList) { if (CollUtil.isEmpty(saveReqVOList)) { throw exception(SCHEDULE_ITEM_LIST_EMPTY); } // 1. æ ¡éªåå·¥åå¨ Set<Long> employeeIds = saveReqVOList.stream().map(HrmEmployeeScheduleSaveReqVO::getEmployeeId) .collect(Collectors.toSet()); if (CollUtil.isNotEmpty(employeeIds)) { List<HrmEmployeeDO> employees = employeeMapper.selectBatchIds(employeeIds); if (employees.size() != employeeIds.size()) { throw exception(SCHEDULE_EMPLOYEE_NOT_EXISTS); } } // 2. æ ¡éªç次åå¨ Set<Long> shiftIds = saveReqVOList.stream().map(HrmEmployeeScheduleSaveReqVO::getShiftId) .filter(Objects::nonNull).collect(Collectors.toSet()); if (CollUtil.isNotEmpty(shiftIds)) { List<HrmShiftDO> shifts = shiftMapper.selectBatchIds(shiftIds); if (shifts.size() != shiftIds.size()) { throw exception(SCHEDULE_SHIFT_NOT_EXISTS); } } // 3. éæ¡ upsertï¼æçæ¬¡åä¿åï¼æ çæ¬¡åå é¤ for (HrmEmployeeScheduleSaveReqVO reqVO : saveReqVOList) { HrmEmployeeScheduleDO existing = employeeScheduleMapper.selectByEmployeeIdAndDate( reqVO.getEmployeeId(), reqVO.getDate()); if (reqVO.getShiftId() == null) { if (existing != null) { employeeScheduleMapper.deleteById(existing.getId()); } } else { if (existing == null) { employeeScheduleMapper.insert(BeanUtils.toBean(reqVO, HrmEmployeeScheduleDO.class)); } else { existing.setShiftId(reqVO.getShiftId()); existing.setRemark(reqVO.getRemark()); employeeScheduleMapper.updateById(existing); } } } } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmShiftService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,47 @@ package cn.iocoder.yudao.module.hrm.service.attendance; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftPageReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO; import java.util.List; /** * çæ¬¡å®ä¹ Service æ¥å£ * * @author è¶ çº§ç®¡çå */ public interface HrmShiftService { /** * æ°å¢ç次 */ Long createShift(HrmShiftSaveReqVO createReqVO); /** * æ´æ°ç次 */ void updateShift(HrmShiftSaveReqVO updateReqVO); /** * å é¤ç次 */ void deleteShift(Long id); /** * è·å¾ç次 */ HrmShiftDO getShift(Long id); /** * è·å¾ç次å页 */ PageResult<HrmShiftDO> getShiftPage(HrmShiftPageReqVO pageReqVO); /** * è·å¾ç次å表ï¼å¯ç¨ç¶æï¼ */ List<HrmShiftDO> getShiftList(); } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmShiftServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,100 @@ package cn.iocoder.yudao.module.hrm.service.attendance; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftPageReqVO; import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmShiftSaveReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmEmployeeScheduleMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmShiftMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; /** * çæ¬¡å®ä¹ Service å®ç°ç±» * * @author è¶ çº§ç®¡çå */ @Service @Validated public class HrmShiftServiceImpl implements HrmShiftService { @Resource private HrmShiftMapper shiftMapper; @Resource private HrmEmployeeScheduleMapper employeeScheduleMapper; @Override public Long createShift(HrmShiftSaveReqVO createReqVO) { validateShiftNameUnique(null, createReqVO.getName()); HrmShiftDO shift = BeanUtils.toBean(createReqVO, HrmShiftDO.class); if (shift.getStatus() == null) { shift.setStatus(0); } if (shift.getCrossDay() == null) { shift.setCrossDay(false); } shiftMapper.insert(shift); return shift.getId(); } @Override public void updateShift(HrmShiftSaveReqVO updateReqVO) { validateShiftExists(updateReqVO.getId()); validateShiftNameUnique(updateReqVO.getId(), updateReqVO.getName()); HrmShiftDO updateObj = BeanUtils.toBean(updateReqVO, HrmShiftDO.class); if (updateObj.getCrossDay() == null) { updateObj.setCrossDay(false); } shiftMapper.updateById(updateObj); } @Override public void deleteShift(Long id) { validateShiftExists(id); // æ ¡éªç次æ¯å¦å·²è¢«æçä½¿ç¨ Long usedCount = employeeScheduleMapper.selectCountByShiftId(id); if (usedCount != null && usedCount > 0) { throw exception(SHIFT_IN_USE); } shiftMapper.deleteById(id); } private void validateShiftExists(Long id) { if (shiftMapper.selectById(id) == null) { throw exception(SHIFT_NOT_EXISTS); } } private void validateShiftNameUnique(Long id, String name) { HrmShiftDO shift = shiftMapper.selectOne(HrmShiftDO::getName, name); if (shift == null) { return; } if (id == null || !shift.getId().equals(id)) { throw exception(SHIFT_NAME_EXISTS); } } @Override public HrmShiftDO getShift(Long id) { return shiftMapper.selectById(id); } @Override public PageResult<HrmShiftDO> getShiftPage(HrmShiftPageReqVO pageReqVO) { return shiftMapper.selectPage(pageReqVO); } @Override public List<HrmShiftDO> getShiftList() { return shiftMapper.selectListByStatus(0); } } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeServiceImpl.java
@@ -325,6 +325,16 @@ @Override public PageResult<HrmEmployeeDO> getEmployeePage(HrmEmployeePageReqVO pageReqVO) { if (pageReqVO.getDeptId() != null) { // éä¸é¨é¨æ¶ï¼è¿å¸¦æ¥è¯¢å ¶ææåé¨é¨çåå·¥ï¼å«èªèº«ï¼ List<DeptRespDTO> childDepts = deptApi.getChildDeptList(pageReqVO.getDeptId()); Set<Long> deptIds = new LinkedHashSet<>(); deptIds.add(pageReqVO.getDeptId()); if (CollUtil.isNotEmpty(childDepts)) { childDepts.forEach(dept -> deptIds.add(dept.getId())); } return employeeMapper.selectPageByDeptIds(pageReqVO, deptIds); } return employeeMapper.selectPage(pageReqVO); } yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
@@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSalaryCalculationPageReqVO; import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmOvertimeApplicationDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmEmployeeSalaryDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmEmployeeSocialSecurityDO; @@ -16,6 +18,8 @@ import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDO; import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryStructureDO; import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmLeaveApplicationMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmEmployeeScheduleMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmShiftMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmOvertimeApplicationMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper; import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmEmployeeSalaryMapper; @@ -39,8 +43,11 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -96,6 +103,10 @@ private HrmSalaryPaymentMapper salaryPaymentMapper; @Resource private HrmSalaryPaymentDetailMapper salaryPaymentDetailMapper; @Resource private HrmEmployeeScheduleMapper employeeScheduleMapper; @Resource private HrmShiftMapper shiftMapper; @Override public PageResult<HrmSalaryCalculationDO> getSalaryCalculationPage(HrmSalaryCalculationPageReqVO pageReqVO) { @@ -225,6 +236,11 @@ OvertimeSalary overtimeSalary = calculateOvertimeSalary(employee, employeeSalary, period, baseSalary); BigDecimal overtimePay = overtimeSalary.pay(); // ææç累计åºå¤å¤©æ°ä¸æçæ»å·¥æ¶ï¼æ æçæ¶åºå¤å¤©æ°åéé»è®¤ 22ï¼ ScheduleStat scheduleStat = calculateScheduleStat(employeeId, period); Integer workDays = scheduleStat.scheduledDays() > 0 ? scheduleStat.scheduledDays() : 22; BigDecimal totalWorkHours = scheduleStat.totalWorkHours(); // åºåå·¥èµæ»é¢ BigDecimal totalIncome = baseSalary.add(performanceSalary).add(mealAllowance) .add(transportAllowance).add(otherAllowance).add(overtimePay); @@ -295,8 +311,9 @@ .otherDeduction(otherDeduction) .leaveDeduction(leaveDeduction) .actualSalary(actualSalary) .workDays(22) // é»è®¤å·¥ä½æ¥ .workDays(workDays) // ææç累计åºåºå¤å¤©æ°ï¼æ æçæ¶åé 22 .overtimeHours(overtimeSalary.hours()) .totalWorkHours(totalWorkHours) .status(0) // å¾ ç¡®è®¤ .build(); } @@ -401,6 +418,37 @@ private record OvertimeSalary(BigDecimal hours, BigDecimal pay) {} private record ScheduleStat(int scheduledDays, BigDecimal totalWorkHours) {} /** * ææç表累计åå·¥çåºå¤å¤©æ°ä¸æçæ»å·¥æ¶ã * ä» ç»è®¡æç次çæçè®°å½ï¼ç次æ åå·¥æ¶ä¹åå³ä¸ºæçå·¥æ¶ã */ private ScheduleStat calculateScheduleStat(Long employeeId, String period) { YearMonth yearMonth = YearMonth.parse(period); List<HrmEmployeeScheduleDO> schedules = employeeScheduleMapper.selectListByEmployeeIdAndPeriod( employeeId, yearMonth.atDay(1), yearMonth.atEndOfMonth()); if (CollUtil.isEmpty(schedules)) { return new ScheduleStat(0, BigDecimal.ZERO); } Set<Long> shiftIds = schedules.stream().map(HrmEmployeeScheduleDO::getShiftId) .filter(Objects::nonNull).collect(Collectors.toSet()); Map<Long, HrmShiftDO> shiftMap = CollUtil.isEmpty(shiftIds) ? Collections.emptyMap() : shiftMapper.selectBatchIds(shiftIds).stream() .collect(Collectors.toMap(HrmShiftDO::getId, shift -> shift, (a, b) -> a)); int scheduledDays = 0; BigDecimal totalWorkHours = BigDecimal.ZERO; for (HrmEmployeeScheduleDO schedule : schedules) { HrmShiftDO shift = schedule.getShiftId() == null ? null : shiftMap.get(schedule.getShiftId()); if (shift == null) { continue; } scheduledDays++; totalWorkHours = totalWorkHours.add(shift.getWorkHours() != null ? shift.getWorkHours() : BigDecimal.ZERO); } return new ScheduleStat(scheduledDays, totalWorkHours.setScale(2, RoundingMode.HALF_UP)); } @Override @Transactional(rollbackFor = Exception.class) public void confirmSalaryCalculation(List<Long> ids) {