zhanghl
2025-05-09 2da734889ad140079e542f08d69a234315f1c5a1
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
package com.aps.core.service.impl;
 
import java.math.BigDecimal;
import java.util.*;
 
import cn.hutool.core.util.IdUtil;
import com.aps.common.core.utils.DateUtils;
import com.aps.common.security.utils.DictUtils;
import com.aps.common.security.utils.SecurityUtils;
import com.aps.core.domain.*;
import com.aps.core.mapper.*;
import com.aps.core.service.*;
import com.aps.system.api.domain.SysDictData;
import jakarta.annotation.Resource;
import com.aps.core.domain.ApsPlateStandardRequire;
import com.aps.core.mapper.ApsPlateStandardRequireMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.aps.core.service.IApsPlateStandardRequireService;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.List;
 
/**
 * 钣金工单标准需求Service业务层处理
 * 
 * @author zhl
 * @date 2025-05-06
 */
@Service
public class ApsPlateStandardRequireServiceImpl implements IApsPlateStandardRequireService 
{
    @Autowired
    private ApsPlateStandardRequireMapper apsPlateStandardRequireMapper;
 
    @Resource
    private ApsPlateStandardRequireBatchMapper requireBatchMapper;
 
    @Resource
    ApsPlatePlanMapper platePlanMapper;
 
    @Resource
    ApsBomHeaderMapper bomHeaderMapper;
    @Resource
    IApsBomService bomLineService;
 
    @Resource
    ApsMaterialStorageManagementMapper itemStorageMapper;
 
    @Resource
    ApsPlateStandardRequireBomStockDetailMapper plateBomStockDetailMapper;
 
    @Resource
    IApsPlateStandardRequireBomStockDetailService bomStockDetailService;
    @Resource
    ApsStandardProcessRouteHeaderMapper standardProcessRouteHeaderMapper;
 
    @Resource
    ApsStandardProcessRouteLineMapper ApsStandardProcessRouteLineMapper;
 
    @Resource
    ApsPlateStandardRequireBomOrderDetailMapper plateBomOrderDetailMapper;
 
    @Autowired
    private ApsPlatePlanMapper apsPlatePlanMapper;
 
    @Resource
    IApsPlateStandardRequireBomOrderDetailService bomOrderDetailService;
 
    @Resource
    IApsStandardProcessRouteLineService routeLineService;
 
    @Resource
    IApsPlateStandardRequireBatchService requireBatchService;
 
    /**
     * 查询钣金工单标准需求
     * 
     * @param id 钣金工单标准需求主键
     * @return 钣金工单标准需求
     */
    @Override
    public ApsPlateStandardRequire selectApsPlateStandardRequireById(Long id)
    {
        return apsPlateStandardRequireMapper.selectApsPlateStandardRequireById(id);
    }
 
    /**
     * 查询钣金工单标准需求列表
     * 
     * @param apsPlateStandardRequire 钣金工单标准需求
     * @return 钣金工单标准需求
     */
    @Override
    public List<ApsPlateStandardRequire> selectApsPlateStandardRequireList(ApsPlateStandardRequire apsPlateStandardRequire)
    {
        return apsPlateStandardRequireMapper.selectApsPlateStandardRequireList(apsPlateStandardRequire);
    }
 
    /**
     * 新增钣金工单标准需求
     * 
     * @param apsPlateStandardRequire 钣金工单标准需求
     * @return 结果
     */
    @Override
    public int insertApsPlateStandardRequire(ApsPlateStandardRequire apsPlateStandardRequire)
    {
        apsPlateStandardRequire.setCreateTime(DateUtils.getNowDate());
        return apsPlateStandardRequireMapper.insertApsPlateStandardRequire(apsPlateStandardRequire);
    }
 
    /**
     * 修改钣金工单标准需求
     * 
     * @param apsPlateStandardRequire 钣金工单标准需求
     * @return 结果
     */
    @Override
    public int updateApsPlateStandardRequire(ApsPlateStandardRequire apsPlateStandardRequire)
    {
        apsPlateStandardRequire.setUpdateTime(DateUtils.getNowDate());
        return apsPlateStandardRequireMapper.updateApsPlateStandardRequire(apsPlateStandardRequire);
    }
 
    /**
     * 批量删除钣金工单标准需求
     * 
     * @param ids 需要删除的钣金工单标准需求主键
     * @return 结果
     */
    @Override
    public int deleteApsPlateStandardRequireByIds(Long[] ids)
    {
        return apsPlateStandardRequireMapper.deleteApsPlateStandardRequireByIds(ids);
    }
 
    /**
     * 删除钣金工单标准需求信息
     * 
     * @param id 钣金工单标准需求主键
     * @return 结果
     */
    @Override
    public int deleteApsPlateStandardRequireById(Long id)
    {
        return apsPlateStandardRequireMapper.deleteApsPlateStandardRequireById(id);
    }
 
    /**
     * 钣金供应缺口报表
     * @param apsPlateStandardRequire
     * @return
     */
    @Override
    public List<ApsPlateStandardRequire> selectPlateSupplyGapList(ApsPlateStandardRequire apsPlateStandardRequire) {
        return apsPlateStandardRequireMapper.selectPlateSupplyGapList(apsPlateStandardRequire);
    }
 
    @Transactional
    @Override
    public void generatorPlan(){
 
        /*初始化数据*/
        requireBatchService.initRequireBatch();
        /*定义工厂为南通 */
        String plantCode="FORTUNA";
        /*定义主单类型为钣金主单*/
        String mainOrderType = "0";
        /*生成新批次号*/
        String batchNum= requireBatchService.getNewBatchNumber();
        /*获取钣金主单信息*/
        List<ApsPlatePlan> mainPlans = platePlanMapper.selectPlatePlanByPlantMajor(plantCode,mainOrderType);
        for (ApsPlatePlan mainPlan : mainPlans) {
            String itemNumber = mainPlan.getItemNumber();
            /*根据料号 获取BOM Header */
            /*当前Bom节点处理完成后,处理下级BOM*/
 
            List<ApsPlateStandardRequire> requiresList=new ArrayList<>();
 
            getBomRequires(plantCode, "0","0",itemNumber,BigDecimal.ONE, batchNum, null, mainPlan, requiresList, 0L);
 
            // 批量插入以提高性能
            if (!requiresList.isEmpty()) {
                int batchSize = 1000;
                for (int i = 0; i < requiresList.size(); i += batchSize) {
                    int end = Math.min(i + batchSize, requiresList.size());
                    List<ApsPlateStandardRequire> batch = requiresList.subList(i, end);
                    apsPlateStandardRequireMapper.batchInsert(batch);
                }
            }
        }
    }
 
    /**
     * 构建需求信息
     *
     * @param plant 工厂代码
     * @param bomHeaderCode BOM上级物料编码
     * @param bomLineId BOM行ID
     * @param itemCode 物料代码
     * @param itemNum 物料数量
     * @param batchNum 批次号
     * @param upLevelStartDate 上级工序的开始日期
     * @param plan 计划对象
     * @param allRequires 所有需求的列表
     * @param level 层级
     */
    private void getBomRequires(String plant, String bomHeaderCode, String bomLineId,String itemCode,BigDecimal itemNum, String batchNum, Date upLevelStartDate, ApsPlatePlan plan, List<ApsPlateStandardRequire> allRequires, Long level) {
        /*构建需求信息*/
        ApsPlateStandardRequire require = new ApsPlateStandardRequire();
        require.setId(IdUtil.getSnowflakeNextId());
        require.setRequireTrackId(plan.getId() + "");
        require.setBatchNumber(batchNum);
        require.setDocNum(plan.getDocumentNumber());
        require.setOrgCode(plant);
        require.setBomHeaderCode(bomHeaderCode);
        require.setBomLineId(bomLineId);
        require.setBomLineCode(itemCode);
        require.setBomLineLevel(level);
        require.setBomUseAmount(itemNum);
        require.setCreateTime(DateUtils.getNowDate());
        require.setCreateBy(SecurityUtils.getUsername());
        require.setDelFlag("0");
        require.setProductionBase(plan.getProductionBase());
        /*计算需求数量*/
        if (level == 0) {
            require.setBomUseAmount(BigDecimal.ONE);
            require.setCompleteDate(plan.getPlanEndDay());
            require.setDemandDate(plan.getPlanEndDay());
        } else {
            require.setBomUseAmount(itemNum.multiply(plan.getProductionQuantity()));
        }
        /*查找库存,计算净需求,保存剩余库存,保存库存扣减明细*/
        BigDecimal remainderStock = BigDecimal.ZERO;
        /*默认净需求为BOM用量*/
        require.setNetRequirement(require.getBomUseAmount());
        Optional<ApsMaterialStorageManagement> itemStorage = getItemStorage(plant, itemCode);
        if (itemStorage.isPresent()) {
            ApsMaterialStorageManagement storage = itemStorage.get();
            /*剩余库存*/
            remainderStock = storage.getRemainderStock();
            /*计算净需求 默认=需求数量*/
            if (remainderStock.compareTo(BigDecimal.ZERO) == 0) {
                require.setNetRequirement(require.getBomUseAmount());
            }
            if (remainderStock.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal subtract = require.getBomUseAmount().subtract(remainderStock);
                BigDecimal deductionAmount = BigDecimal.ZERO;
                BigDecimal afterStockAmount = BigDecimal.ZERO;
                if (subtract.compareTo(BigDecimal.ZERO) >= 0) {
                    deductionAmount = remainderStock;
                    afterStockAmount = BigDecimal.ZERO;
                    require.setNetRequirement(subtract);
                } else {
                    deductionAmount = require.getBomUseAmount();
                    afterStockAmount = remainderStock.subtract(deductionAmount);
                    require.setNetRequirement(BigDecimal.ZERO);
                }
                /*记录库存剩余数量,记录库存使用记录*/
                bomStockDetailService.saveStorageAndDetail(storage, plan, bomLineId,itemCode, batchNum, deductionAmount, afterStockAmount,require.getId());
            }
        }
        /*未匹配数量,默认为净需求*/
        require.setUnmatchedDemandAmount(require.getNetRequirement());
        /*工艺路线总需求*/
        ApsStandardProcessRouteLine routeHeader = routeLineService.getRouteLineTotalTime(require);
        String routeId = routeHeader.getRouteId();
        BigDecimal totalRouteTime = routeHeader.getRouteTime();
        long millisecond = 60 * 60 * 1000L;
        long totalRouteMillisecond = totalRouteTime.multiply(BigDecimal.valueOf(millisecond)).longValue();
        require.setProcessRouteId(routeId);
        require.setProcessRouteHours(String.valueOf(totalRouteTime));
 
        /*完成时间,level=0 时默认为工单的计划完成日期*/
        Date completeDate = plan.getPlanEndDay();
        /*预留天数*/
        Long reservedDay = getReservedDays();
        if (level > 0) {
            /* 当前需求完成日期为上阶层需求的开始时间 - 预留天数(转换为毫秒)*/
            long reservedMillisecond = reservedDay * 24 * millisecond;
            completeDate.setTime(upLevelStartDate.getTime() - reservedMillisecond);
        }
        /*设置完成日期*/
        require.setCompleteDate(completeDate);
        require.setDemandDate(completeDate);
        /*设置开始时间*/
        Date startDay = new Date();
        startDay.setTime(completeDate.getTime() - totalRouteMillisecond);
        require.setStartDate(startDay);
 
        /*计算是否有风险*/
        require.setHasDelayRisk("0");
        Date dateZero = getDateZero(DateUtils.getNowDate());
        if (startDay.before(dateZero)) {
            require.setHasDelayRisk("1");
        }
 
        /*生产基地*/
        require.setProductionBase(plan.getProductionBase());
 
        require.setMatchState("已匹配");
        require.setMatchMode("库存匹配");
        if (require.getNetRequirement().compareTo(BigDecimal.ZERO) > 0) {
            require.setMatchState("待匹配");
            require.setMatchMode("工单匹配");
            /*使用子件工单进行需求匹配*/
            matchRequireAndSubPlan(require);
        }
        allRequires.add(require);
        if (require.getNetRequirement().compareTo(BigDecimal.ZERO) > 0) {
            /*当前Bom节点处理完成后,处理下级BOM*/
            long nextLevel=level+1;
            List<ApsBom> bomLineList = bomLineService.selectApsBomLineList(plant, itemCode);
            if (!bomLineList.isEmpty()) {
                bomLineList.forEach(line -> {
                    getBomRequires(plant, itemCode, line.getBomLineId(),line.getItemCode() ,line.getNum() , batchNum, require.getStartDate(), plan, allRequires, nextLevel);
                });
            }
        }
    }
    /**
     * 获取物料库存信息
     * */
    private Optional<ApsMaterialStorageManagement> getItemStorage(String plant, String itemNumber) {
        ApsMaterialStorageManagement storageParam = new ApsMaterialStorageManagement();
        storageParam.setItemNumber(itemNumber);
        storageParam.setApplicableFactories(plant);
        return itemStorageMapper.selectApsMaterialStorageManagementList(storageParam).stream()
                .findFirst();
    }
 
    /**
     * 获取钣金计划 预留天数
     * */
    private Long getReservedDays(){
        Long days=0L;
        List<SysDictData> dictDataList = DictUtils.getDictCache("aps_is_suspended");
        if (dictDataList != null) {
            Long.getLong(dictDataList.get(0).getDictValue());
        }
        return days;
    }
 
    /**
    * 使用子件工单匹配需求中的净需求
    * */
 
    private void matchRequireAndSubPlan(ApsPlateStandardRequire require) {
        BigDecimal netRequirement = require.getNetRequirement();
        if (netRequirement.compareTo(BigDecimal.ZERO) > 0) {
            ApsPlatePlan platePlan = apsPlatePlanMapper.selectUnMatchPlateSubPlan(require.getOrgCode());
            /*子件工单的未匹配数量 作为当前的库存*/
            while (platePlan != null && netRequirement.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal stock = platePlan.getUnmatchedQuantity();
                if (netRequirement.compareTo(stock) < 0) {
                    /* 库存数量 大于 净需求数量*/
                    /* 净需求数量=0 ,子件工单未匹配数量= 库存-净需求*/
                    require.setNetRequirement(netRequirement);
                    BigDecimal subtract = stock.subtract(netRequirement);
                    bomOrderDetailService.savePlastPlanAndBomOrderDetail(require, platePlan, subtract, stock, netRequirement);
                    netRequirement = BigDecimal.ZERO;
                    /*净需求已经被满足,不需要继续匹配*/
 
                } else if (netRequirement.compareTo(stock) == 0) {
                    /*净需求数量 == 库存数量*/
                    require.setNetRequirement(netRequirement);
                    BigDecimal subtract = BigDecimal.ZERO;
                    bomOrderDetailService.savePlastPlanAndBomOrderDetail(require, platePlan, subtract, stock, netRequirement);
                    netRequirement = BigDecimal.ZERO;
                    /*净需求已经被满足,不需要继续匹配*/
                }
                if (netRequirement.compareTo(stock) > 0) {
                    /*需求大于库存*/
                    /*净需求 被部分满足 */
                    BigDecimal rest = netRequirement.subtract(stock);
                    require.setNetRequirement(rest);
                    require.setMatchState("匹配中");
                    /*工单 未匹配数量为0 全部用于匹配需求*/
                    bomOrderDetailService.savePlastPlanAndBomOrderDetail(require, platePlan, BigDecimal.ZERO, stock, netRequirement);
                    /*净需求未被满足,需要继续匹配*/
                    platePlan = apsPlatePlanMapper.selectUnMatchPlateSubPlan(require.getOrgCode());
                    netRequirement = rest;
                }
            }
        }
        require.setUnmatchedDemandAmount(require.getNetRequirement());
 
    }
 
 
    /**
     * 获取日期零点
     * */
    private Date getDateZero(Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
 
}