| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesLineMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum; |
| | |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.stockreserve.MesWmStockReserveService; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | |
| | | @Resource |
| | | private MesWmProductSalesDetailService productSalesDetailService; |
| | | @Resource |
| | | private MesWmOqcConcessionGuard oqcConcessionGuard; |
| | | @Resource |
| | | private CrmCustomerApi customerApi; |
| | | @Resource |
| | | private MesWmSalesNoticeService salesNoticeService; |
| | |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | @Resource |
| | | private MesWmSalesReelGuard salesReelGuard; |
| | | @Resource |
| | | private MesMdItemService mdItemService; |
| | | |
| | | @Override |
| | | public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) { |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void stockProductSales(Long id) { |
| | | // 校验存在 |
| | | MesWmProductSalesDO sales = validateProductSalesExists(id); |
| | | validateProductSalesExists(id); |
| | | MesWmProductSalesDO sales = productSalesMapper.selectOne(new LambdaQueryWrapperX<MesWmProductSalesDO>() |
| | | .eq(MesWmProductSalesDO::getId, id).last("FOR UPDATE")); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_PICK); |
| | | } |
| | |
| | | if (CollUtil.isEmpty(details)) { |
| | | throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY); |
| | | } |
| | | oqcConcessionGuard.validateQuantity(line, CollectionUtils.getSumValue(details, |
| | | MesWmProductSalesDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO)); |
| | | // 校验库存充足并创建占用 |
| | | for (MesWmProductSalesDetailDO detail : details) { |
| | | salesReelGuard.validate(line, detail.getBatchId()); |
| | | oqcConcessionGuard.validate(line, detail.getMaterialStockId(), detail.getBatchCode(), detail.getQuantity()); |
| | | materialStockService.validateSelectedStock( |
| | | detail.getMaterialStockId(), |
| | | detail.getItemId(), |
| | |
| | | |
| | | private void createTransactionList(MesWmProductSalesDO sales) { |
| | | List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId()); |
| | | for (MesWmProductSalesLineDO line : productSalesLineService.getProductSalesLineListBySalesId(sales.getId())) { |
| | | BigDecimal quantity = details.stream().filter(detail -> ObjUtil.equal(detail.getLineId(), line.getId())) |
| | | .map(MesWmProductSalesDetailDO::getQuantity).filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | oqcConcessionGuard.validateQuantity(line, quantity); |
| | | } |
| | | for (MesWmProductSalesDetailDO detail : details) { |
| | | MesWmProductSalesLineDO line = productSalesLineService.getProductSalesLine(detail.getLineId()); |
| | | if (line == null || !ObjUtil.equal(line.getSalesId(), sales.getId())) { |
| | | throw exception(WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH); |
| | | } |
| | | salesReelGuard.validate(line, detail.getBatchId()); |
| | | oqcConcessionGuard.validate(line, detail.getMaterialStockId(), detail.getBatchCode(), detail.getQuantity()); |
| | | } |
| | | wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId()) |
| | |
| | | if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.CONFIRMED.getStatus(), sales.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_CONFIRM); |
| | | } |
| | | // 出货检验不合格的出库行必须先完成不合格品处置,禁止手工确认绕过返工/让步闭环 |
| | | List<MesWmProductSalesLineDO> lines = productSalesLineMapper.selectListBySalesId(id); |
| | | if (CollUtil.isNotEmpty(lines)) { |
| | | int rowNo = 0; |
| | | for (MesWmProductSalesLineDO line : lines) { |
| | | rowNo++; |
| | | if (Objects.equals(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus()) |
| | | || line.getNcrDispositionId() != null) { |
| | | throw new ServiceException(400, String.format( |
| | | "销售出库单[%s]第[%d]行物料[%s]出货检验不合格,尚未完成不合格品处置;" |
| | | + "请先在不合格品处理单中完成返工/返修/重新生产或让步放行后再确认出库", |
| | | sales.getCode(), rowNo, itemLabel(line.getItemId()))); |
| | | } |
| | | } |
| | | } |
| | | // OQC 检验完成,流转到待拣货 |
| | | productSalesMapper.updateById(new MesWmProductSalesDO() |
| | | .setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus())); |
| | | } |
| | | |
| | | private String itemLabel(Long itemId) { |
| | | MesMdItemDO item = itemId == null ? null : mdItemService.getItem(itemId); |
| | | if (item == null) { |
| | | return "ID=" + itemId; |
| | | } |
| | | String label = (StrUtil.blankToDefault(item.getCode(), "") + " " |
| | | + StrUtil.blankToDefault(item.getName(), "")).trim(); |
| | | return label.isEmpty() ? "ID=" + itemId : label; |
| | | } |
| | | |
| | | @Override |
| | | public List<MesWmProductSalesDO> getProductSalesListByClientId(Long clientId) { |
| | | return productSalesMapper.selectListByClientId(clientId); |