package cn.iocoder.yudao.module.mes.controller.admin.wm.scrap; 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.module.mes.dal.dataobject.wm.scrap.MesWmScrapReceiptDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper; import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapReceiptMapper; import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.Digits; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import lombok.Data; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.YearMonth; import java.util.List; import java.util.Map; import cn.iocoder.yudao.framework.common.exception.ServiceException; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** 独立废料入库:记录和库存流水在同一个事务中提交。 */ @RestController @RequestMapping("/mes/wm/scrap-receipt") @Validated public class MesWmScrapReceiptController { @Resource private MesWmScrapReceiptMapper receiptMapper; @Resource private MesProWorkOrderMapper workOrderMapper; @Resource private MesWmTransactionService transactionService; @Resource private MesWmWarehouseAreaService areaService; @Resource private cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService warehouseService; @Resource private cn.iocoder.yudao.module.mdm.api.item.MdmItemApi itemApi; @GetMapping("/order-outputs") @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:create')") public CommonResult> orderOutputs(@RequestParam("workOrderId") Long workOrderId) { var order = workOrderMapper.selectById(workOrderId); if (order == null || !java.util.Objects.equals(order.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.FINISHED.getStatus())) throw new ServiceException(400, "仅已完成的生产订单可以登记废料"); var list = receiptMapper.selectOrderOutputs(workOrderId, cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum.FINISHED.getStatus()); var items = itemApi.getItemMap(list.stream().map(OutputItem::getItemId).collect(java.util.stream.Collectors.toSet())); list.forEach(output -> { var item = items.get(output.getItemId()); if (item != null) { output.setItemName(item.getName()); output.setItemCode(item.getCode()); } }); return success(list); } @PostMapping("/create-from-output") @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:create')") @Transactional(rollbackFor = Exception.class) public CommonResult createFromOutput(@Valid @RequestBody CreateReq req) { var locked = workOrderMapper.lockScheduleOrders(List.of(req.getWorkOrderId())); if (locked.isEmpty() || !java.util.Objects.equals(locked.get(0).getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.FINISHED.getStatus())) throw new ServiceException(400, "仅已完成的生产订单可以登记废料"); if (receiptMapper.selectOrderOutputs(req.getWorkOrderId(), cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum.FINISHED.getStatus()).stream() .noneMatch(item -> java.util.Objects.equals(item.getItemId(), req.getItemId()) && java.util.Objects.equals(item.getBatchId(), req.getBatchId()) && java.util.Objects.equals(cn.hutool.core.util.StrUtil.blankToDefault(item.getBatchCode(), ""), cn.hutool.core.util.StrUtil.blankToDefault(req.getBatchCode(), "")))) throw new ServiceException(400, "所选物料不是该订单的已完成产出"); var warehouse = warehouseService.validateWarehouseExists(req.getWarehouseId()); if (!java.util.Objects.equals("废料仓", warehouse.getWarehouseType()) && !java.util.Objects.equals("废料", warehouse.getWarehouseType())) throw new ServiceException(400, "请选择仓库类型为废料仓的仓库"); return create(req); } @Data public static class OutputItem { private Long itemId; private String itemCode; private String itemName; private Long batchId; private String batchCode; private BigDecimal quantity; } @PostMapping("/create") @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:create')") @Transactional(rollbackFor = Exception.class) public CommonResult create(@Valid @RequestBody CreateReq req) { var order = workOrderMapper.selectById(req.getWorkOrderId()); if (order == null) throw new ServiceException(400, "生产工单不存在"); areaService.validateWarehouseAreaExists(req.getWarehouseId(), req.getLocationId(), req.getAreaId()); String code = "SCR-" + req.getRequestId(); if (receiptMapper.selectByCode(code) != null) throw new ServiceException(400, "请求已提交,请刷新入库记录"); MesWmScrapReceiptDO receipt = new MesWmScrapReceiptDO(); receipt.setCode(code); receipt.setWorkOrderId(order.getId()); receipt.setWorkOrderCode(order.getCode()); receipt.setItemId(req.getItemId()); receipt.setQuantity(req.getQuantity()); receipt.setWarehouseId(req.getWarehouseId()); receipt.setLocationId(req.getLocationId()); receipt.setAreaId(req.getAreaId()); receipt.setBatchId(req.getBatchId()); receipt.setBatchCode(req.getBatchCode()); receipt.setReceiptTime(LocalDateTime.now()); receipt.setRemark(req.getRemark()); receiptMapper.insert(receipt); Long transactionId = transactionService.createTransaction(new MesWmTransactionSaveReqDTO() .setType(MesWmTransactionTypeEnum.IN.getType()).setBizType(cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants.WM_SCRAP_RECEIPT) .setBizId(receipt.getId()).setBizLineId(receipt.getId()).setBizCode(receipt.getCode()) .setItemId(receipt.getItemId()).setQuantity(receipt.getQuantity()) .setWarehouseId(receipt.getWarehouseId()).setLocationId(receipt.getLocationId()) .setAreaId(receipt.getAreaId()).setBatchId(receipt.getBatchId()).setBatchCode(receipt.getBatchCode()) .setReceiptTime(receipt.getReceiptTime()).setForceCreateNewStock(true)); receipt.setTransactionId(transactionId); receiptMapper.updateById(receipt); return success(receipt.getId()); } @GetMapping("/monthly-summary") @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:query')") public CommonResult>> monthlySummary(@RequestParam String month) { YearMonth period = YearMonth.parse(month); return success(receiptMapper.summarize(period.atDay(1).atStartOfDay(), period.plusMonths(1).atDay(1).atStartOfDay())); } @GetMapping("/page") @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:query')") public CommonResult> page(@Valid PageParam page, @RequestParam(required = false) Long workOrderId) { return success(receiptMapper.selectReceiptPage(page, workOrderId)); } @Data public static class CreateReq { @NotBlank @Pattern(regexp = "[A-Za-z0-9-]{1,60}") private String requestId; @NotNull private Long workOrderId; @NotNull private Long itemId; @NotNull @DecimalMin(value = "0", inclusive = false) @Digits(integer = 16, fraction = 4) private BigDecimal quantity; @NotNull private Long warehouseId; @NotNull private Long locationId; @NotNull private Long areaId; private Long batchId; @Size(max = 100) private String batchCode; @Size(max = 500) private String remark; } }