|
@@ -2,6 +2,11 @@ package cn.iocoder.yudao.module.system.service.biz;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
|
|
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
|
|
|
|
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ImportResultVO;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialIoImportExcelVO;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.util.ImportUtil;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialInBalanceListReqVO;
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialInBalanceListReqVO;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialInBalanceRespVO;
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialInBalanceRespVO;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialIoOrderItemSaveReqVO;
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.materialio.MaterialIoOrderItemSaveReqVO;
|
|
@@ -36,6 +41,7 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
|
import java.time.YearMonth;
|
|
import java.time.YearMonth;
|
|
@@ -44,6 +50,9 @@ import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
+
|
|
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exceptionCustomMsg;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exceptionCustomMsg;
|
|
@@ -1899,8 +1908,16 @@ public class MaterialIoServiceImpl implements MaterialIoService {
|
|
|
LambdaQueryWrapper<MaterialInboundOrderItemDO> wrapper = new LambdaQueryWrapperX<MaterialInboundOrderItemDO>()
|
|
LambdaQueryWrapper<MaterialInboundOrderItemDO> wrapper = new LambdaQueryWrapperX<MaterialInboundOrderItemDO>()
|
|
|
.inIfPresent(MaterialInboundOrderItemDO::getTenantId, (Object[]) reqVO.getTenantIds())
|
|
.inIfPresent(MaterialInboundOrderItemDO::getTenantId, (Object[]) reqVO.getTenantIds())
|
|
|
.eqIfPresent(MaterialInboundOrderItemDO::getStoreId, reqVO.getStoreId())
|
|
.eqIfPresent(MaterialInboundOrderItemDO::getStoreId, reqVO.getStoreId())
|
|
|
- .inSql(MaterialInboundOrderItemDO::getInboundOrderId,
|
|
|
|
|
- "SELECT id FROM material_inbound_order WHERE order_date >= '" + monthStart + "' AND order_date < '" + nextMonthStart + "'");
|
|
|
|
|
|
|
+ // 口径:
|
|
|
|
|
+ // 1) 入库单为当月;
|
|
|
|
|
+ // 2) 或者该入库批次在当月发生过出库(即使入库单不是当月)
|
|
|
|
|
+ .and(w -> w.inSql(MaterialInboundOrderItemDO::getInboundOrderId,
|
|
|
|
|
+ "SELECT id FROM material_inbound_order WHERE order_date >= '" + monthStart + "' AND order_date < '" + nextMonthStart + "'")
|
|
|
|
|
+ .or()
|
|
|
|
|
+ .inSql(MaterialInboundOrderItemDO::getId,
|
|
|
|
|
+ "SELECT oi.ref_inbound_item_id FROM material_outbound_order_item oi " +
|
|
|
|
|
+ "INNER JOIN material_outbound_order o ON oi.outbound_order_id = o.id " +
|
|
|
|
|
+ "WHERE o.order_date >= '" + monthStart + "' AND o.order_date < '" + nextMonthStart + "'"));
|
|
|
|
|
|
|
|
// 关键字匹配物资编码/名称
|
|
// 关键字匹配物资编码/名称
|
|
|
if (StringUtils.isNotBlank(reqVO.getKeyword())) {
|
|
if (StringUtils.isNotBlank(reqVO.getKeyword())) {
|
|
@@ -1953,8 +1970,16 @@ public class MaterialIoServiceImpl implements MaterialIoService {
|
|
|
LambdaQueryWrapper<MaterialInboundOrderItemDO> wrapper = new LambdaQueryWrapperX<MaterialInboundOrderItemDO>()
|
|
LambdaQueryWrapper<MaterialInboundOrderItemDO> wrapper = new LambdaQueryWrapperX<MaterialInboundOrderItemDO>()
|
|
|
.inIfPresent(MaterialInboundOrderItemDO::getTenantId, (Object[]) reqVO.getTenantIds())
|
|
.inIfPresent(MaterialInboundOrderItemDO::getTenantId, (Object[]) reqVO.getTenantIds())
|
|
|
.eqIfPresent(MaterialInboundOrderItemDO::getStoreId, reqVO.getStoreId())
|
|
.eqIfPresent(MaterialInboundOrderItemDO::getStoreId, reqVO.getStoreId())
|
|
|
- .inSql(MaterialInboundOrderItemDO::getInboundOrderId,
|
|
|
|
|
- "SELECT id FROM material_inbound_order WHERE order_date >= '" + monthStart + "' AND order_date < '" + nextMonthStart + "'");
|
|
|
|
|
|
|
+ // 口径:
|
|
|
|
|
+ // 1) 入库单为当月;
|
|
|
|
|
+ // 2) 或者该入库批次在当月发生过出库(即使入库单不是当月)
|
|
|
|
|
+ .and(w -> w.inSql(MaterialInboundOrderItemDO::getInboundOrderId,
|
|
|
|
|
+ "SELECT id FROM material_inbound_order WHERE order_date >= '" + monthStart + "' AND order_date < '" + nextMonthStart + "'")
|
|
|
|
|
+ .or()
|
|
|
|
|
+ .inSql(MaterialInboundOrderItemDO::getId,
|
|
|
|
|
+ "SELECT oi.ref_inbound_item_id FROM material_outbound_order_item oi " +
|
|
|
|
|
+ "INNER JOIN material_outbound_order o ON oi.outbound_order_id = o.id " +
|
|
|
|
|
+ "WHERE o.order_date >= '" + monthStart + "' AND o.order_date < '" + nextMonthStart + "'"));
|
|
|
|
|
|
|
|
if (StringUtils.isNotBlank(reqVO.getKeyword())) {
|
|
if (StringUtils.isNotBlank(reqVO.getKeyword())) {
|
|
|
List<Long> materialIds = materialInfoMapper.selectList(
|
|
List<Long> materialIds = materialInfoMapper.selectList(
|
|
@@ -2296,6 +2321,80 @@ public class MaterialIoServiceImpl implements MaterialIoService {
|
|
|
inboundOrderItemMapper.updateById(inboundItem);
|
|
inboundOrderItemMapper.updateById(inboundItem);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void exportImportTemplate(HttpServletResponse response) throws IOException {
|
|
|
|
|
+ ExcelUtils.write(response, "物资出入库导入模板.xlsx", "模板", MaterialIoImportExcelVO.class, Collections.emptyList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public ImportResultVO importMaterialIo(MultipartFile file) {
|
|
|
|
|
+ List<MaterialIoImportExcelVO> rows = ExcelUtils.read(file, MaterialIoImportExcelVO.class);
|
|
|
|
|
+ int success = 0;
|
|
|
|
|
+ int rowNum = 1;
|
|
|
|
|
+ List<ImportResultVO.RowError> failures = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (MaterialIoImportExcelVO row : rows) {
|
|
|
|
|
+ rowNum++;
|
|
|
|
|
+ try {
|
|
|
|
|
+ MaterialIoOrderSaveReqVO reqVO = buildImportReq(row);
|
|
|
|
|
+ if ("IN".equalsIgnoreCase(reqVO.getBizType())) {
|
|
|
|
|
+ inbound(reqVO);
|
|
|
|
|
+ } else if ("OUT".equalsIgnoreCase(reqVO.getBizType())) {
|
|
|
|
|
+ outbound(reqVO);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new IllegalArgumentException("业务类型仅支持 IN 或 OUT");
|
|
|
|
|
+ }
|
|
|
|
|
+ success++;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ ImportUtil.addFailure(failures, rowNum, null,
|
|
|
|
|
+ row.getMaterialId() == null ? null : String.valueOf(row.getMaterialId()),
|
|
|
|
|
+ e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return ImportUtil.buildResult(success, failures);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private MaterialIoOrderSaveReqVO buildImportReq(MaterialIoImportExcelVO row) {
|
|
|
|
|
+ if (row.getBizType() == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("业务类型不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (row.getOrderDate() == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("单据日期不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (row.getMaterialId() == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("物资ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (row.getQuantity() == null || row.getQuantity() <= 0) {
|
|
|
|
|
+ throw new IllegalArgumentException("数量必须大于0");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ MaterialIoOrderItemSaveReqVO item = new MaterialIoOrderItemSaveReqVO();
|
|
|
|
|
+ item.setMaterialId(row.getMaterialId());
|
|
|
|
|
+ item.setInboundItemId(row.getInboundItemId());
|
|
|
|
|
+ item.setRefInStoreId(row.getRefInStoreId());
|
|
|
|
|
+ item.setQuantity(row.getQuantity());
|
|
|
|
|
+ item.setInUnitPrice(row.getInUnitPrice());
|
|
|
|
|
+ item.setSaleUnitPrice(row.getSaleUnitPrice());
|
|
|
|
|
+ item.setAmount(row.getAmount());
|
|
|
|
|
+ item.setProduceDate(row.getProduceDate());
|
|
|
|
|
+
|
|
|
|
|
+ MaterialIoOrderSaveReqVO reqVO = new MaterialIoOrderSaveReqVO();
|
|
|
|
|
+ reqVO.setBizType(row.getBizType().trim().toUpperCase());
|
|
|
|
|
+ reqVO.setOrderDate(row.getOrderDate());
|
|
|
|
|
+ reqVO.setRemark(row.getRemark());
|
|
|
|
|
+ reqVO.setOutDeptId(row.getOutDeptId());
|
|
|
|
|
+ reqVO.setOutUser(row.getOutUser());
|
|
|
|
|
+ reqVO.setOutReason(row.getOutReason());
|
|
|
|
|
+ reqVO.setTenantId(TenantContextHolder.getTenantId());
|
|
|
|
|
+ reqVO.setItems(Collections.singletonList(item));
|
|
|
|
|
+
|
|
|
|
|
+ if ("OUT".equals(reqVO.getBizType()) && row.getInboundItemId() == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("出库导入时,入库批次明细ID不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ return reqVO;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 批量加载物资信息
|
|
* 批量加载物资信息
|
|
|
*/
|
|
*/
|