| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.ruoyi.approve.bean.dto.ApprovalInstanceDto; |
| | | import com.ruoyi.approve.bean.vo.ApprovalTemplateVo; |
| | | import com.ruoyi.approve.service.ApprovalInstanceService; |
| | | import com.ruoyi.approve.service.ApprovalTemplateService; |
| | | import com.ruoyi.basic.mapper.ProductMapper; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.basic.pojo.Product; |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.common.enums.TypeEnums; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.production.bean.dto.ProductStructureDto; |
| | | import com.ruoyi.production.bean.dto.ProductionProductMainDto; |
| | | import com.ruoyi.production.enums.ProductOrderStatusEnum; |
| | | import com.ruoyi.production.mapper.*; |
| | |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.quality.mapper.*; |
| | | import com.ruoyi.quality.pojo.*; |
| | | import com.ruoyi.stock.pojo.StockInRecord; |
| | | import com.ruoyi.stock.service.StockInRecordService; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.technology.mapper.TechnologyOperationMapper; |
| | |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | |
| | | @AllArgsConstructor |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public class ProductionProductMainServiceImpl extends ServiceImpl<ProductionProductMainMapper, ProductionProductMain> implements ProductionProductMainService { |
| | | |
| | | /** |
| | | * 生产报工大类:只区分小提琴/大提琴/贝斯,不再区分零件。 |
| | | * key为入库规范值,value为产品名称匹配别名(现场数据贝斯写作“贝司”)。 |
| | | */ |
| | | private static final Map<String, List<String>> MAJOR_CATEGORY_PATTERNS = new LinkedHashMap<>() {{ |
| | | put("小提琴", List.of("小提琴")); |
| | | put("大提琴", List.of("大提琴")); |
| | | put("贝斯", List.of("贝斯", "贝司")); |
| | | }}; |
| | | |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | private final SysUserMapper userMapper; |
| | |
| | | private final ProductionAccountMapper productionAccountMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOrderBomMapper productionOrderBomMapper; |
| | | private final ProductionBomStructureMapper productionBomStructureMapper; |
| | | private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper; |
| | | private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper; |
| | | private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final ApprovalTemplateService approvalTemplateService; |
| | | private final ApprovalInstanceService approvalInstanceService; |
| | | private final StockUtils stockUtils; |
| | | private final StockInRecordService stockInRecordService; |
| | | private final StockInventoryService stockInventoryService; |
| | | |
| | | @Override |
| | |
| | | throw new ServiceException("产品规格不存在"); |
| | | } |
| | | |
| | | // 报工完成时对计数的零件做区分:合格/轻微返工/严重返工/报废四类数量。 |
| | | BigDecimal qualifiedQty = defaultDecimal(dto.getQuantity()); |
| | | BigDecimal reworkMinorQty = defaultDecimal(dto.getReworkMinorQty()); |
| | | BigDecimal reworkMajorQty = defaultDecimal(dto.getReworkMajorQty()); |
| | | BigDecimal scrapQtyInput = defaultDecimal(dto.getScrapQty()); |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) < 0 |
| | | || reworkMinorQty.compareTo(BigDecimal.ZERO) < 0 |
| | | || reworkMajorQty.compareTo(BigDecimal.ZERO) < 0 |
| | | || scrapQtyInput.compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("报工数量不能为负数"); |
| | | } |
| | | if (qualifiedQty.add(reworkMinorQty).add(reworkMajorQty).add(scrapQtyInput).compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("请按合格/轻微返工/严重返工/报废分类填写零件数量"); |
| | | } |
| | | |
| | | ProductionProductMain productionProductMain = new ProductionProductMain(); |
| | | productionProductMain.setProductNo(generateProductNo()); |
| | | productionProductMain.setMajorCategory(resolveMajorCategory(productModel)); |
| | | productionProductMain.setUserId(user == null ? dto.getUserId() : user.getUserId()); |
| | | productionProductMain.setUserName(user == null ? dto.getUserName() : user.getNickName()); |
| | | productionProductMain.setProductionOperationTaskId(taskId); |
| | | productionProductMain.setStatus(0); |
| | | productionProductMain.setReportType(dto.getReportType() != null |
| | | ? dto.getReportType() |
| | | : deriveReportType(qualifiedQty, reworkMinorQty, reworkMajorQty, scrapQtyInput)); |
| | | productionProductMain.setAuditStatus(dto.getAuditStatus() == null ? 0 : dto.getAuditStatus()); |
| | | productionProductMain.setAuditRemark(dto.getAuditRemark()); |
| | | productionProductMain.setWorkHour(dto.getWorkHour()); |
| | | productionProductMainMapper.insert(productionProductMain); |
| | | syncOperationParamInputValue(dto, routingOperation.getId(), productionProductMain.getId()); |
| | | createReportApprovalInstance(productionProductMain); |
| | | |
| | | List<ProductStructureDto> productStructureDtos = resolveInputStructures( |
| | | productionOrder.getId(), routingOperation, productModel.getId()); |
| | | // 如果没有bom子节点了,那么投入就是他本身 |
| | | if (productStructureDtos.isEmpty()) { |
| | | ProductStructureDto fallbackInput = new ProductStructureDto(); |
| | | fallbackInput.setProductModelId(productModel.getId()); |
| | | fallbackInput.setUnitQuantity(BigDecimal.ONE); |
| | | productStructureDtos.add(fallbackInput); |
| | | } |
| | | for (ProductStructureDto item : productStructureDtos) { |
| | | // 当前实现按工序成品直接作为投入,后续若接入领料记录可在这里替换来源。 |
| | | ProductionProductInput productionProductInput = new ProductionProductInput(); |
| | | productionProductInput.setProductionProductMainId(productionProductMain.getId()); |
| | | productionProductInput.setProductMainId(productionProductMain.getId()); |
| | | productionProductInput.setProductModelId(item.getProductModelId()); |
| | | productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity()))); |
| | | productionProductInput.setQuantity(productionProductInput.getInputQuantity()); |
| | | productionProductInputMapper.insert(productionProductInput); |
| | | } |
| | | // 需求变更:生产报工不再按零件(BOM结构)拆分投入,投入即本次报工的产品本身,按大类统计。 |
| | | ProductionProductInput productionProductInput = new ProductionProductInput(); |
| | | productionProductInput.setProductionProductMainId(productionProductMain.getId()); |
| | | productionProductInput.setProductModelId(productModel.getId()); |
| | | productionProductInput.setInputQuantity(qualifiedQty); |
| | | productionProductInput.setQuantity(qualifiedQty); |
| | | productionProductInputMapper.insert(productionProductInput); |
| | | |
| | | ProductionProductOutput productionProductOutput = new ProductionProductOutput(); |
| | | productionProductOutput.setProductionProductMainId(productionProductMain.getId()); |
| | | productionProductOutput.setProductMainId(productionProductMain.getId()); |
| | | productionProductOutput.setProductModelId(productModel.getId()); |
| | | productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity())); |
| | | productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty())); |
| | | productionProductOutput.setReworkMinorQty(reworkMinorQty); |
| | | productionProductOutput.setReworkMajorQty(reworkMajorQty); |
| | | productionProductOutputMapper.insert(productionProductOutput); |
| | | BigDecimal reportQty = defaultDecimal(productionProductOutput.getQuantity()); |
| | | BigDecimal scrapQty = defaultDecimal(productionProductOutput.getScrapQty()); |
| | | BigDecimal productQty = reportQty; |
| | | String qualifiedBatchNo = null; |
| | | |
| | | List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperation>lambdaQuery() |
| | |
| | | qualityInspectParamMapper.insert(param); |
| | | }); |
| | | } |
| | | } else { |
| | | } else if(isLastOperation){ |
| | | //(最后一道工序才入库) |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(productionProductMain.getId()); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode())); |
| | | stockInventoryDto.setQualitity(productQty); |
| | | stockInventoryDto.setProductModelId(productModel.getId()); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | qualifiedBatchNo = resolveLatestStockInBatchNo( |
| | | productionProductMain.getId(), |
| | | StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), |
| | | productModel.getId(), |
| | | "0"); |
| | | } |
| | | |
| | | productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty)); |
| | |
| | | if (ObjectUtils.isNull(productionOrder.getStartTime())) { |
| | | productionOrder.setStartTime(LocalDateTime.now()); |
| | | } |
| | | // 订单状态由最后一道工序的合格产出推动,避免中间工序提前完工。 |
| | | productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); |
| | | // 仅待开始状态才推进到进行中,避免非末道工序报工时覆盖已完成状态。 |
| | | if (ProductOrderStatusEnum.WAIT.getCode().equals(productionOrder.getStatus())) { |
| | | productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); |
| | | } |
| | | if (isLastOperation) { |
| | | productionOrder.setCompleteQuantity(defaultDecimal(productionOrder.getCompleteQuantity()).add(productQty)); |
| | | if (productionOrder.getQuantity() != null |
| | |
| | | productionAccount.setSchedulingUserId(user == null ? null : user.getUserId()); |
| | | productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName()); |
| | | productionAccount.setFinishedNum(productQty); |
| | | productionAccount.setWorkHours(workHours); |
| | | productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null); |
| | | productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName()); |
| | | productionAccount.setSchedulingDate(LocalDateTime.now()); |
| | | productionAccountMapper.insert(productionAccount); |
| | | } |
| | | if (scrapQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | stockUtils.addUnStockWithBatchNo( |
| | | productModel.getId(), |
| | | scrapQty, |
| | | StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), |
| | | productionProductMain.getId(), |
| | | qualifiedBatchNo); |
| | | } |
| | | // 区分出的返工零件生成不合格品待处理单,进入不合格品管理的报工处理闭环。 |
| | | createReworkUnqualifiedRecords(productionProductMain, productModel, reworkMinorQty, reworkMajorQty); |
| | | return true; |
| | | } |
| | | |
| | | private Integer deriveReportType(BigDecimal qualifiedQty, BigDecimal reworkMinorQty, |
| | | BigDecimal reworkMajorQty, BigDecimal scrapQty) { |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | return 0; |
| | | } |
| | | if (reworkMinorQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | return 1; |
| | | } |
| | | if (reworkMajorQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | return 2; |
| | | } |
| | | if (scrapQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | return 3; |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | private void createReworkUnqualifiedRecords(ProductionProductMain productionProductMain, |
| | | ProductModel productModel, |
| | | BigDecimal reworkMinorQty, |
| | | BigDecimal reworkMajorQty) { |
| | | if (reworkMinorQty.compareTo(BigDecimal.ZERO) <= 0 && reworkMajorQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | Product product = productMapper.selectById(productModel.getProductId()); |
| | | if (product == null) { |
| | | throw new ServiceException("产品不存在,无法生成报工返工待处理单"); |
| | | } |
| | | if (reworkMinorQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | insertReworkUnqualified(productionProductMain, product, productModel, reworkMinorQty, 0); |
| | | } |
| | | if (reworkMajorQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | insertReworkUnqualified(productionProductMain, product, productModel, reworkMajorQty, 1); |
| | | } |
| | | } |
| | | |
| | | private void insertReworkUnqualified(ProductionProductMain productionProductMain, |
| | | Product product, |
| | | ProductModel productModel, |
| | | BigDecimal quantity, |
| | | int dealType) { |
| | | QualityUnqualified qualityUnqualified = new QualityUnqualified(); |
| | | qualityUnqualified.setInspectType(1); |
| | | qualityUnqualified.setInspectState(0); |
| | | qualityUnqualified.setCheckTime(new Date()); |
| | | qualityUnqualified.setCheckName(productionProductMain.getUserName()); |
| | | qualityUnqualified.setProductId(product.getId()); |
| | | qualityUnqualified.setProductName(product.getProductName()); |
| | | qualityUnqualified.setModel(productModel.getModel()); |
| | | qualityUnqualified.setUnit(productModel.getUnit()); |
| | | qualityUnqualified.setQuantity(quantity); |
| | | qualityUnqualified.setProductModelId(productModel.getId()); |
| | | qualityUnqualified.setProductMainId(productionProductMain.getId()); |
| | | qualityUnqualified.setDefectivePhenomena("生产报工【" + productionProductMain.getProductNo() |
| | | + "】计数区分-" + (dealType == 0 ? "轻微返工" : "严重返工")); |
| | | qualityUnqualified.setDealType(dealType); |
| | | qualityUnqualified.setDealResult("报工"); |
| | | qualityUnqualifiedMapper.insert(qualityUnqualified); |
| | | } |
| | | |
| | | private void createReportApprovalInstance(ProductionProductMain productionProductMain) { |
| | | if (productionProductMain == null || productionProductMain.getId() == null) { |
| | | return; |
| | | } |
| | | ApprovalTemplateVo templateVo = approvalTemplateService.getEnabledTemplateByBusinessType( |
| | | TypeEnums.PRODUCTION_REPORT_APPROVAL.getCode() |
| | | ); |
| | | if (templateVo == null) { |
| | | throw new ServiceException("请先配置报工审批模板"); |
| | | } |
| | | ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto(); |
| | | approvalInstanceDto.setTemplateId(templateVo.getId()); |
| | | approvalInstanceDto.setTemplateName(templateVo.getTemplateName()); |
| | | approvalInstanceDto.setBusinessId(productionProductMain.getId()); |
| | | approvalInstanceDto.setBusinessType(TypeEnums.PRODUCTION_REPORT_APPROVAL.getCode()); |
| | | approvalInstanceDto.setTitle(productionProductMain.getProductNo()); |
| | | approvalInstanceDto.setApplicantId(productionProductMain.getUserId()); |
| | | approvalInstanceDto.setApplicantName(productionProductMain.getUserName()); |
| | | approvalInstanceDto.setApplyTime(LocalDateTime.now()); |
| | | approvalInstanceDto.setStatus("PENDING"); |
| | | approvalInstanceDto.setFormConfig(JSON.toJSONString(buildReportApprovalFormConfig(productionProductMain))); |
| | | approvalInstanceService.add(approvalInstanceDto); |
| | | } |
| | | |
| | | private Map<String, Object> buildReportApprovalFormConfig(ProductionProductMain productionProductMain) { |
| | | Map<String, Object> formConfig = new LinkedHashMap<>(); |
| | | formConfig.put("summaryPlaceholder", "报工审批"); |
| | | formConfig.put("approvalType", "production_report"); |
| | | |
| | | ProductionOperationTask task = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId()); |
| | | ProductionProductOutput output = productionProductOutputMapper.selectOne( |
| | | Wrappers.<ProductionProductOutput>lambdaQuery() |
| | | .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()) |
| | | .last("limit 1")); |
| | | BigDecimal schedulingNum = task == null ? BigDecimal.ZERO : defaultDecimal(task.getPlanQuantity()); |
| | | BigDecimal finishedNum = output == null ? BigDecimal.ZERO : defaultDecimal(output.getQuantity()); |
| | | BigDecimal scrapQty = output == null ? BigDecimal.ZERO : defaultDecimal(output.getScrapQty()); |
| | | BigDecimal reworkMinorNum = output == null ? BigDecimal.ZERO : defaultDecimal(output.getReworkMinorQty()); |
| | | BigDecimal reworkMajorNum = output == null ? BigDecimal.ZERO : defaultDecimal(output.getReworkMajorQty()); |
| | | BigDecimal pendingNum = schedulingNum.subtract(finishedNum); |
| | | if (pendingNum.compareTo(BigDecimal.ZERO) < 0) { |
| | | pendingNum = BigDecimal.ZERO; |
| | | } |
| | | |
| | | List<Map<String, Object>> fields = new ArrayList<>(); |
| | | fields.add(buildReportField("productNo", "报工单号", "text", productionProductMain.getProductNo())); |
| | | fields.add(buildReportField("userName", "报工人员", "text", productionProductMain.getUserName())); |
| | | fields.add(buildReportField("teamInfo", "班组信息", "text", productionProductMain.getUserName())); |
| | | fields.add(buildReportField("workOrderNo", "工单编号", "text", resolveWorkOrderNo(productionProductMain.getProductionOperationTaskId()))); |
| | | fields.add(buildReportField("schedulingNum", "排产数量", "number", schedulingNum)); |
| | | fields.add(buildReportField("pendingNum", "待生产数量", "number", pendingNum)); |
| | | fields.add(buildReportField("finishedNum", "合格数量", "number", finishedNum)); |
| | | fields.add(buildReportField("reworkMinorQty", "轻微返工数量", "number", reworkMinorNum)); |
| | | fields.add(buildReportField("reworkMajorQty", "严重返工数量", "number", reworkMajorNum)); |
| | | fields.add(buildReportField("scrapQty", "报废数量", "number", scrapQty)); |
| | | fields.add(buildReportField("reportType", "报工结果", "select", resolveReportTypeLabel(productionProductMain.getReportType()))); |
| | | fields.add(buildReportField("workHour", "工时", "number", productionProductMain.getWorkHour())); |
| | | fields.add(buildReportField("auditStatus", "审核状态", "select", resolveAuditStatusLabel(productionProductMain.getAuditStatus()))); |
| | | fields.add(buildReportField("auditRemark", "审核意见", "textarea", productionProductMain.getAuditRemark())); |
| | | formConfig.put("fields", fields); |
| | | |
| | | Map<String, Object> formPayload = new LinkedHashMap<>(); |
| | | formPayload.put("productNo", productionProductMain.getProductNo()); |
| | | formPayload.put("userName", productionProductMain.getUserName()); |
| | | formPayload.put("teamInfo", productionProductMain.getUserName()); |
| | | formPayload.put("workOrderNo", resolveWorkOrderNo(productionProductMain.getProductionOperationTaskId())); |
| | | formPayload.put("schedulingNum", schedulingNum); |
| | | formPayload.put("pendingNum", pendingNum); |
| | | formPayload.put("finishedNum", finishedNum); |
| | | formPayload.put("scrapQty", scrapQty); |
| | | formPayload.put("reworkMinorQty", reworkMinorNum); |
| | | formPayload.put("reworkMajorQty", reworkMajorNum); |
| | | formPayload.put("reportType", resolveReportTypeLabel(productionProductMain.getReportType())); |
| | | formPayload.put("workHour", productionProductMain.getWorkHour()); |
| | | formPayload.put("auditStatus", resolveAuditStatusLabel(productionProductMain.getAuditStatus())); |
| | | formPayload.put("auditRemark", productionProductMain.getAuditRemark()); |
| | | formConfig.put("formPayload", formPayload); |
| | | return formConfig; |
| | | } |
| | | |
| | | private Map<String, Object> buildReportField(String key, String label, String type, Object defaultValue) { |
| | | Map<String, Object> field = new LinkedHashMap<>(); |
| | | field.put("key", key); |
| | | field.put("label", label); |
| | | field.put("type", type); |
| | | field.put("required", Boolean.FALSE); |
| | | field.put("defaultValue", defaultValue); |
| | | return field; |
| | | } |
| | | |
| | | private String resolveWorkOrderNo(Long productionOperationTaskId) { |
| | | if (productionOperationTaskId == null) { |
| | | return null; |
| | | } |
| | | ProductionOperationTask task = productionOperationTaskMapper.selectById(productionOperationTaskId); |
| | | if (task != null && task.getWorkOrderNo() != null) { |
| | | return task.getWorkOrderNo(); |
| | | } |
| | | if (task == null || task.getProductionOrderRoutingOperationId() == null) { |
| | | return null; |
| | | } |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(task.getProductionOrderRoutingOperationId()); |
| | | if (routingOperation == null || routingOperation.getProductionOrderId() == null) { |
| | | return null; |
| | | } |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(routingOperation.getProductionOrderId()); |
| | | return productionOrder == null ? null : productionOrder.getNpsNo(); |
| | | } |
| | | |
| | | private String resolveReportTypeLabel(Integer reportType) { |
| | | if (reportType == null) { |
| | | return "—"; |
| | | } |
| | | switch (reportType) { |
| | | case 0: |
| | | return "合格"; |
| | | case 1: |
| | | return "轻微返工"; |
| | | case 2: |
| | | return "严重返工"; |
| | | case 3: |
| | | return "报废"; |
| | | default: |
| | | return "—"; |
| | | } |
| | | } |
| | | |
| | | private String resolveAuditStatusLabel(Integer auditStatus) { |
| | | if (auditStatus == null) { |
| | | return "待审核"; |
| | | } |
| | | switch (auditStatus) { |
| | | case 0: |
| | | return "待审核"; |
| | | case 1: |
| | | return "审核通过"; |
| | | case 2: |
| | | return "审核不通过"; |
| | | default: |
| | | return "—"; |
| | | } |
| | | } |
| | | |
| | | private String resolveLatestStockInBatchNo(Long recordId, |
| | | String recordType, |
| | | Long productModelId, |
| | | String stockType) { |
| | | if (recordId == null || productModelId == null) { |
| | | return null; |
| | | } |
| | | StockInRecord stockInRecord = stockInRecordService.getOne( |
| | | Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getRecordId, recordId) |
| | | .eq(StockInRecord::getRecordType, recordType) |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .eq(StockInRecord::getType, stockType) |
| | | .orderByDesc(StockInRecord::getId) |
| | | .last("limit 1"), |
| | | false); |
| | | if (stockInRecord == null) { |
| | | throw new ServiceException("未找到对应的入库申请记录"); |
| | | } |
| | | return stockInRecord.getBatchNo(); |
| | | } |
| | | |
| | | private void syncOperationParamInputValue(ProductionProductMainDto dto, |
| | |
| | | return target; |
| | | } |
| | | |
| | | private List<ProductStructureDto> resolveInputStructures(Long productionOrderId, |
| | | ProductionOrderRoutingOperation routingOperation, |
| | | Long outputProductModelId) { |
| | | if (productionOrderId == null || routingOperation == null || routingOperation.getTechnologyOperationId() == null) { |
| | | return new ArrayList<>(); |
| | | /** |
| | | * 需求:生产报工只区分产品大类(小提琴/大提琴/贝斯),不再区分零件。 |
| | | * 从产出产品自身名称开始沿产品树向上做关键字匹配,命中即取对应规范大类;未命中返回 null。 |
| | | * 产品树顶层为“成品/半成品/原料”等库存分类,不能直接当作大类,因此必须自底向上取第一个命中的层级。 |
| | | */ |
| | | private String resolveMajorCategory(ProductModel productModel) { |
| | | if (productModel == null || productModel.getProductId() == null) { |
| | | return null; |
| | | } |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( |
| | | Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) |
| | | .orderByDesc(ProductionOrderBom::getId) |
| | | .last("limit 1")); |
| | | if (orderBom == null || orderBom.getId() == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | List<ProductionBomStructure> bomNodeList = productionBomStructureMapper.selectList( |
| | | Wrappers.<ProductionBomStructure>lambdaQuery() |
| | | .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId()) |
| | | .orderByAsc(ProductionBomStructure::getId)); |
| | | if (bomNodeList.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | Map<Long, ProductionBomStructure> nodeMap = bomNodeList.stream() |
| | | .filter(item -> item != null && item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left)); |
| | | Long currentOutputModelId = routingOperation.getProductModelId() != null |
| | | ? routingOperation.getProductModelId() |
| | | : outputProductModelId; |
| | | |
| | | Map<Long, BigDecimal> unitQtyByProductModel = new LinkedHashMap<>(); |
| | | for (ProductionBomStructure node : bomNodeList) { |
| | | if (node == null || node.getParentId() == null || node.getProductModelId() == null) { |
| | | continue; |
| | | Product product = productMapper.selectById(productModel.getProductId()); |
| | | Set<Long> visited = new LinkedHashSet<>(); |
| | | while (product != null && visited.add(product.getId())) { |
| | | String category = matchMajorCategory(product.getProductName()); |
| | | if (category != null) { |
| | | return category; |
| | | } |
| | | if (!Objects.equals(node.getTechnologyOperationId(), routingOperation.getTechnologyOperationId())) { |
| | | continue; |
| | | if (product.getParentId() == null || product.getParentId() == 0L) { |
| | | break; |
| | | } |
| | | ProductionBomStructure parent = nodeMap.get(node.getParentId()); |
| | | if (parent == null || !Objects.equals(parent.getProductModelId(), currentOutputModelId)) { |
| | | continue; |
| | | } |
| | | unitQtyByProductModel.merge(node.getProductModelId(), defaultDecimal(node.getUnitQuantity()), BigDecimal::add); |
| | | product = productMapper.selectById(product.getParentId()); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | List<ProductStructureDto> result = new ArrayList<>(); |
| | | for (Map.Entry<Long, BigDecimal> entry : unitQtyByProductModel.entrySet()) { |
| | | if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | ProductStructureDto item = new ProductStructureDto(); |
| | | item.setProductModelId(entry.getKey()); |
| | | item.setUnitQuantity(entry.getValue()); |
| | | result.add(item); |
| | | static String matchMajorCategory(String productName) { |
| | | if (productName == null || productName.isEmpty()) { |
| | | return null; |
| | | } |
| | | return result; |
| | | // 琴盒/琴套等配件名称可能带“贝司”等字样(如 SA贝司套),排除后避免误归大类。 |
| | | if (productName.contains("琴盒") || productName.contains("琴套") || productName.contains("弓盒")) { |
| | | return null; |
| | | } |
| | | for (Map.Entry<String, List<String>> entry : MAJOR_CATEGORY_PATTERNS.entrySet()) { |
| | | for (String alias : entry.getValue()) { |
| | | if (productName.contains(alias)) { |
| | | return entry.getKey(); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) { |
| | |
| | | if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) { |
| | | throw new ServiceException("该条报工已经不合格处理了,不允许删除"); |
| | | } |
| | | } |
| | | // 报工计数区分直接产生的返工待处理单:已处理的不允许删除报工,未处理的随报工一并删除。 |
| | | List<QualityUnqualified> reworkUnqualifieds = qualityUnqualifiedMapper.selectList( |
| | | Wrappers.<QualityUnqualified>lambdaQuery() |
| | | .eq(QualityUnqualified::getProductMainId, productionProductMain.getId())); |
| | | if (reworkUnqualifieds.stream().anyMatch(q -> Integer.valueOf(1).equals(q.getInspectState()))) { |
| | | throw new ServiceException("该条报工的返工零件已做不合格处理,不允许删除"); |
| | | } |
| | | ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList( |
| | | Wrappers.<ProductionProductOutput>lambdaQuery() |
| | |
| | | } |
| | | } |
| | | |
| | | qualityUnqualifiedMapper.delete(new LambdaQueryWrapper<QualityUnqualified>() |
| | | .eq(QualityUnqualified::getProductMainId, productionProductMain.getId())); |
| | | qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>() |
| | | .eq(QualityInspect::getProductMainId, productionProductMain.getId())).forEach(q -> { |
| | | qualityInspectParamMapper.delete(new LambdaQueryWrapper<QualityInspectParam>() |
| | |
| | | return dto.getProductionOperationTaskId(); |
| | | } |
| | | |
| | | private boolean isCountingWageReport(ProductionProductMain productionProductMain) { |
| | | return productionProductMain != null |
| | | && productionProductMain.getAuditStatus() != null |
| | | && productionProductMain.getAuditStatus() == 1 |
| | | && productionProductMain.getReportType() != null |
| | | && (productionProductMain.getReportType() == 0 || productionProductMain.getReportType() == 1); |
| | | } |
| | | |
| | | private boolean isExcludedFromWage(ProductionProductMain productionProductMain) { |
| | | return productionProductMain != null |
| | | && productionProductMain.getReportType() != null |
| | | && productionProductMain.getReportType() != 0 |
| | | && productionProductMain.getReportType() != 1; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean auditProductMain(ProductionProductMainDto productionProductMainDto) { |
| | | if (productionProductMainDto == null || productionProductMainDto.getId() == null) { |
| | | throw new ServiceException("参数错误"); |
| | | } |
| | | ProductionProductMain productionProductMain = productionProductMainMapper.selectById(productionProductMainDto.getId()); |
| | | if (productionProductMain == null) { |
| | | throw new ServiceException("报工不存在"); |
| | | } |
| | | Integer auditStatus = productionProductMainDto.getAuditStatus(); |
| | | if (auditStatus == null) { |
| | | throw new ServiceException("审核状态不能为空"); |
| | | } |
| | | productionProductMain.setAuditStatus(auditStatus); |
| | | productionProductMain.setAuditRemark(productionProductMainDto.getAuditRemark()); |
| | | productionProductMainMapper.updateById(productionProductMain); |
| | | productionAccountMapper.delete(new LambdaQueryWrapper<ProductionAccount>() |
| | | .eq(ProductionAccount::getProductionProductMainId, productionProductMain.getId())); |
| | | if (auditStatus == 1 && !isExcludedFromWage(productionProductMain)) { |
| | | rebuildProductionAccount(productionProductMain); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private void rebuildProductionAccount(ProductionProductMain productionProductMain) { |
| | | if (productionProductMain == null || productionProductMain.getId() == null) { |
| | | return; |
| | | } |
| | | ProductionProductOutput output = productionProductOutputMapper.selectOne( |
| | | Wrappers.<ProductionProductOutput>lambdaQuery() |
| | | .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()) |
| | | .last("limit 1")); |
| | | if (output == null) { |
| | | return; |
| | | } |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId()); |
| | | if (productionOperationTask == null) { |
| | | return; |
| | | } |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId()); |
| | | if (routingOperation == null) { |
| | | return; |
| | | } |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId()); |
| | | if (productionOrder == null) { |
| | | return; |
| | | } |
| | | TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId()); |
| | | TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null |
| | | : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId()); |
| | | SysUser user = userMapper.selectUserById(productionProductMain.getUserId()); |
| | | BigDecimal productQty = defaultDecimal(output.getQuantity()); |
| | | if (productQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | ProductionAccount productionAccount = new ProductionAccount(); |
| | | productionAccount.setProductionProductMainId(productionProductMain.getId()); |
| | | productionAccount.setSchedulingUserId(user == null ? productionProductMain.getUserId() : user.getUserId()); |
| | | productionAccount.setSchedulingUserName(user == null ? productionProductMain.getUserName() : user.getNickName()); |
| | | productionAccount.setFinishedNum(productQty); |
| | | productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null); |
| | | productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName()); |
| | | productionAccount.setSchedulingDate(LocalDateTime.now()); |
| | | productionAccountMapper.insert(productionAccount); |
| | | } |
| | | |
| | | @Override |
| | | public ArrayList<Long> listMain(List<Long> idList) { |
| | | // 查询主表ID集合 |