李林
2023-10-07 658d4927d468c47208fd012d9128b09249c07eff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package com.chinaztt.mes.production.service.impl;
 
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaztt.mes.production.dto.ComputationDTO;
import com.chinaztt.mes.production.dto.PersonBoardDTO;
import com.chinaztt.mes.production.entity.Computation;
import com.chinaztt.mes.production.entity.DutyRecord;
import com.chinaztt.mes.production.mapper.ComputationMapper;
import com.chinaztt.mes.production.mapper.DutyRecordMapper;
import com.chinaztt.mes.production.mapper.PersonBoardMapper;
import com.chinaztt.mes.production.service.ComputationService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
 
/**
 * 工时计算
 *
 * @author cxf
 * @date 2021-03-10 15:00:08
 */
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class ComputationServiceImpl extends ServiceImpl<ComputationMapper, Computation> implements ComputationService {
    public static final BigDecimal TWELVE = new BigDecimal(12);
    public static final BigDecimal SIX = new BigDecimal(6);
    public static final BigDecimal ONE_HUNDRED_AND_SEVENTY_FOUR = new BigDecimal(174);
    public static final BigDecimal ONE_THOUSAND_EIGHT_HUNDRED_AND_NINETY = new BigDecimal(1890);
    public static final String PRODUCT_DEPARTMENT = "生产部";
    public static final String EQUIPMENT_DEPARTMENT = "设备部";
    public static final String QUALITY_DEPARTMENT = "质量部";
    private DutyRecordMapper dutyRecordMapper;
    private PersonBoardMapper personBoardMapper;
 
    @Override
    public synchronized boolean computeByDutyRecordId(Long dutyRecordId) {
        // 1.产品工资
        List<ComputationDTO> computationDTOList = computeProductWages(dutyRecordId);
        // 2.加班费和津贴
        computeOvertimePayAndSubsidy(dutyRecordId, computationDTOList);
        // 3.刷新主表的待处理字段
        dutyRecordMapper.refreshPendingById(dutyRecordId);
        return true;
    }
 
    @Override
    public void computeComingByDutyRecordId(Long dutyRecordId) {
        // 把当前班次之后确认计算的数据再重新算一下,防止加班费计算误差
        List<Long> comingDutyRecordIds = dutyRecordMapper.selectComingByDutyRecordId(dutyRecordId);
        if (CollectionUtil.isNotEmpty(comingDutyRecordIds)) {
            for (Long id : comingDutyRecordIds) {
                computeByDutyRecordId(id);
            }
        }
    }
 
    private List<ComputationDTO> computeProductWages(Long dutyRecordId) {
        // 1.先查询班次对应的人工记录
        List<ComputationDTO> computationDTOList = baseMapper.getComputationByDutyRecordId(dutyRecordId);
        // 2.删除原来的
        baseMapper.delete(Wrappers.<Computation>lambdaQuery().eq(Computation::getDutyRecordId, dutyRecordId));
        // 3.根据不同类型计算人工工资(系数*定额工时*工作量)
        for (ComputationDTO computationDTO : computationDTOList) {
            baseMapper.delete(Wrappers.<Computation>lambdaQuery().eq(Computation::getArtificialInformationId, computationDTO.getArtificialInformationId()));
            String loadType = computationDTO.getLoadType();
            BigDecimal conversionCoefficient = computationDTO.getConversionCoefficient();
            BigDecimal standardHourWage = computationDTO.getStandardHourWage();
            BigDecimal workLoad = BigDecimal.ZERO;
            BigDecimal laborCost = BigDecimal.ZERO;
            BigDecimal productiveSalary = BigDecimal.ZERO;
            BigDecimal productiveHandymanSalary = BigDecimal.ZERO;
            BigDecimal unproductiveHandymanSalary = BigDecimal.ZERO;
            if ("1".equals(loadType)) {
                // 生产杂工
                workLoad = computationDTO.getWorkingHours();
                if (workLoad != null) {
                    laborCost = conversionCoefficient.multiply(standardHourWage).multiply(workLoad).setScale(2, BigDecimal.ROUND_DOWN);
                    productiveHandymanSalary = laborCost;
                }
            } else if ("2".equals(loadType)) {
                // 非生产杂工
                workLoad = computationDTO.getWorkingHours();
                if (workLoad != null) {
                    laborCost = conversionCoefficient.multiply(standardHourWage).multiply(workLoad).setScale(2, BigDecimal.ROUND_DOWN);
                    unproductiveHandymanSalary = laborCost;
                }
            } else {
                // 产量
                workLoad = computationDTO.getPersonOutput();
                if (workLoad != null) {
                    laborCost = conversionCoefficient.multiply(standardHourWage).multiply(workLoad).setScale(2, BigDecimal.ROUND_DOWN);
                    productiveSalary = laborCost;
                }
            }
            computationDTO.setWorkLoad(workLoad);
            computationDTO.setLaborCost(laborCost);
            computationDTO.setProductiveSalary(productiveSalary);
            computationDTO.setProductiveHandymanSalary(productiveHandymanSalary);
            computationDTO.setUnproductiveHandymanSalary(unproductiveHandymanSalary);
            baseMapper.insert(computationDTO);
        }
        return computationDTOList;
    }
 
    private void computeOvertimePayAndSubsidy(Long dutyRecordId, List<ComputationDTO> computationDTOList) {
        List<PersonBoardDTO> personBoardDTOList = personBoardMapper.getPersonByDutyRecordId(dutyRecordId);
        DutyRecord dutyRecord = dutyRecordMapper.selectById(dutyRecordId);
        // 1.夜班费(8小时夜班8元/天、12小时夜班12元/天),夜班津贴(12小时5.5)
        BigDecimal nightDifferential = BigDecimal.ZERO;
        BigDecimal mealSubsidy = BigDecimal.ZERO;
        if (dutyRecord.getIsNight()) {
            nightDifferential = dutyRecord.getDutyTime();
            if (dutyRecord.getDutyTime().compareTo(TWELVE) != -1) {
                mealSubsidy = new BigDecimal(5.5);
            }
        }
        for (PersonBoardDTO personBoardDTO : personBoardDTOList) {
            personBoardDTO.setNightDifferential(nightDifferential);
            personBoardDTO.setMealSubsidy(mealSubsidy);
            personBoardDTO.setOvertimePay(BigDecimal.ZERO);
        }
        // 2.加班费(超过174小时,按当天产品工资1.5倍计算)
        Map<Long, List<ComputationDTO>> mapList = computationDTOList.stream().collect(Collectors.groupingBy(ComputationDTO::getStaffId));
        personBoardDTOList.stream().forEach(personBoardDTO -> {
            BigDecimal salary = BigDecimal.ZERO;
            BigDecimal overtimePay = BigDecimal.ZERO;
            BigDecimal productiveSalary = BigDecimal.ZERO;
            BigDecimal productiveHandymanSalary = BigDecimal.ZERO;
            BigDecimal unproductiveHandymanSalary = BigDecimal.ZERO;
            // 人工正常计算出来的工资
            List<ComputationDTO> computationList = mapList.get(personBoardDTO.getStaffId());
            if (CollectionUtil.isNotEmpty(computationList)) {
                salary = computationList.stream().map(ComputationDTO::getLaborCost).reduce(BigDecimal.ZERO, BigDecimal::add);
                productiveSalary = computationList.stream().map(ComputationDTO::getProductiveSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
                productiveHandymanSalary = computationList.stream().map(ComputationDTO::getProductiveHandymanSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
                unproductiveHandymanSalary = computationList.stream().map(ComputationDTO::getUnproductiveHandymanSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            if (personBoardDTO.getBeforeTotalDutyTime().add(personBoardDTO.getDutyTime()).compareTo(ONE_HUNDRED_AND_SEVENTY_FOUR) == 1) {
                // 计算加班时长
                BigDecimal overtimeHours = BigDecimal.ZERO;
                if (personBoardDTO.getBeforeTotalDutyTime().compareTo(ONE_HUNDRED_AND_SEVENTY_FOUR) == -1) {
                    overtimeHours = personBoardDTO.getBeforeTotalDutyTime().add(personBoardDTO.getDutyTime()).subtract(ONE_HUNDRED_AND_SEVENTY_FOUR);
                } else {
                    overtimeHours = personBoardDTO.getDutyTime();
                }
                // 根据加班时长计算加班费
                String divisionName = personBoardDTO.getDivisionName();
                if (StringUtils.isNotBlank(divisionName)) {
                    if (divisionName.contains(PRODUCT_DEPARTMENT)) {
                        // 2.1 生产部(生产人员):超过174小时的按当天工资1.5倍计算,其中1.0算基本工资,0.5算加班费
                        if (CollectionUtil.isNotEmpty(computationList)) {
                            overtimePay = salary.multiply(new BigDecimal(0.5)).multiply(overtimeHours)
                                    .divide(personBoardDTO.getDutyTime(), 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                        }
                    } else if (divisionName.contains(EQUIPMENT_DEPARTMENT)) {
                        // 2.2 设备部(维修):超过174小时的,每小时*1890/174*1.1*1.5
                        overtimePay = ONE_THOUSAND_EIGHT_HUNDRED_AND_NINETY.multiply(new BigDecimal(1.1)).multiply(new BigDecimal(1.5))
                                .multiply(overtimeHours).divide(ONE_HUNDRED_AND_SEVENTY_FOUR, 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                    } else if (divisionName.contains(QUALITY_DEPARTMENT)) {
                        // 2.3 质量部(检测):超过174小时的,每小时额外加6块钱
                        overtimePay = overtimeHours.multiply(SIX);
                    }
                    // 计算未超出加班时间的正常工资(暂时按当天工作总量百分比算)
                    BigDecimal withinTime = personBoardDTO.getDutyTime().subtract(overtimeHours);
                    if (personBoardDTO.getDutyTime().compareTo(BigDecimal.ZERO) == 1) {
                        // 如果是生产部门工资,不需要去处理了
                        if (divisionName.contains(PRODUCT_DEPARTMENT)) {
                        } else {
                            salary = salary.multiply(withinTime).divide(personBoardDTO.getDutyTime(), 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                            productiveSalary = productiveSalary.multiply(withinTime).divide(personBoardDTO.getDutyTime(), 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                            productiveHandymanSalary = productiveHandymanSalary.multiply(withinTime).divide(personBoardDTO.getDutyTime(), 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                            unproductiveHandymanSalary = unproductiveHandymanSalary.multiply(withinTime).divide(personBoardDTO.getDutyTime(), 4, BigDecimal.ROUND_DOWN).setScale(2, BigDecimal.ROUND_DOWN);
                        }
                    }
                }
            }
            // 工资或者加班费都需要乘上人员系数(如果系数是0,就是试用期/实习生/合同工,处理为1)
            BigDecimal personnelFactor = personBoardDTO.getPersonnelFactor();
            if (personnelFactor.compareTo(BigDecimal.ZERO) == 0) {
                personnelFactor = BigDecimal.ONE;
            }
            salary = salary.multiply(personnelFactor).setScale(2, BigDecimal.ROUND_DOWN);
            productiveSalary = productiveSalary.multiply(personnelFactor).setScale(2, BigDecimal.ROUND_DOWN);
            productiveHandymanSalary = productiveHandymanSalary.multiply(personnelFactor).setScale(2, BigDecimal.ROUND_DOWN);
            unproductiveHandymanSalary = unproductiveHandymanSalary.multiply(personnelFactor).setScale(2, BigDecimal.ROUND_DOWN);
            overtimePay = overtimePay.multiply(personnelFactor).setScale(2, BigDecimal.ROUND_DOWN);
            personBoardDTO.setSalary(salary);
            personBoardDTO.setProductiveSalary(productiveSalary);
            personBoardDTO.setProductiveHandymanSalary(productiveHandymanSalary);
            personBoardDTO.setUnproductiveHandymanSalary(unproductiveHandymanSalary);
            personBoardDTO.setOvertimePay(overtimePay);
        });
        personBoardDTOList.stream().forEach(personBoardDTO -> personBoardMapper.updateById(personBoardDTO));
    }
 
 
    @Override
    public void checkDutyRecordSubmit(List<Long> dutyRecordIdList) {
        List<DutyRecord> dutyRecordList = dutyRecordMapper.selectBatchIds(dutyRecordIdList);
        List<Boolean> BooleanList = dutyRecordList.stream().map(DutyRecord::getIsConfirm).collect(Collectors.toList());
        if(BooleanList.contains(false)){
            throw new RuntimeException("存在未确认的班次,无法计算");
        }
    }
}