产品入库,半成品委外单

This commit is contained in:
朱昊天
2026-05-11 17:59:12 +08:00
parent d1aab9dba1
commit f94ddb433d
31 changed files with 2119 additions and 77 deletions

View File

@@ -0,0 +1,71 @@
package com.gear.mat.controller;
import com.gear.common.annotation.Log;
import com.gear.common.annotation.RepeatSubmit;
import com.gear.common.core.controller.BaseController;
import com.gear.common.core.domain.PageQuery;
import com.gear.common.core.domain.R;
import com.gear.common.core.page.TableDataInfo;
import com.gear.common.core.validate.AddGroup;
import com.gear.common.core.validate.EditGroup;
import com.gear.common.enums.BusinessType;
import com.gear.common.utils.poi.ExcelUtil;
import com.gear.mat.domain.bo.MatProductInDetailBo;
import com.gear.mat.domain.vo.MatProductInDetailVo;
import com.gear.mat.service.IMatProductInDetailService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mat/productInDetail")
public class MatProductInDetailController extends BaseController {
private final IMatProductInDetailService iMatProductInDetailService;
@GetMapping("/list")
public TableDataInfo<MatProductInDetailVo> list(MatProductInDetailBo bo, PageQuery pageQuery) {
return iMatProductInDetailService.queryPageList(bo, pageQuery);
}
@Log(title = "产品入库单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(MatProductInDetailBo bo, HttpServletResponse response) {
List<MatProductInDetailVo> list = iMatProductInDetailService.queryList(bo);
ExcelUtil.exportExcel(list, "产品入库单", MatProductInDetailVo.class, response);
}
@GetMapping("/{detailId}")
public R<MatProductInDetailVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long detailId) {
return R.ok(iMatProductInDetailService.queryById(detailId));
}
@Log(title = "产品入库单", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody MatProductInDetailBo bo) {
return toAjax(iMatProductInDetailService.insertByBoWithInventory(bo));
}
@Log(title = "产品入库单", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody MatProductInDetailBo bo) {
return toAjax(iMatProductInDetailService.updateByBo(bo));
}
@Log(title = "产品入库单", businessType = BusinessType.DELETE)
@DeleteMapping("/{detailIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] detailIds) {
return toAjax(iMatProductInDetailService.deleteWithValidByIdsWithInventory(Arrays.asList(detailIds), true));
}
}

View File

@@ -0,0 +1,105 @@
package com.gear.mat.controller;
import com.gear.common.annotation.Log;
import com.gear.common.annotation.RepeatSubmit;
import com.gear.common.core.controller.BaseController;
import com.gear.common.core.domain.PageQuery;
import com.gear.common.core.domain.R;
import com.gear.common.core.page.TableDataInfo;
import com.gear.common.core.validate.AddGroup;
import com.gear.common.enums.BusinessType;
import com.gear.common.utils.poi.ExcelUtil;
import com.gear.mat.domain.bo.MatProductOutsourceOrderBo;
import com.gear.mat.domain.vo.MatProductOutsourceOrderVo;
import com.gear.mat.service.IMatProductOutsourceOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Arrays;
import java.util.List;
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/mat/productOutsourceOrder")
public class MatProductOutsourceOrderController extends BaseController {
private final IMatProductOutsourceOrderService iMatProductOutsourceOrderService;
@GetMapping("/list")
public TableDataInfo<MatProductOutsourceOrderVo> list(MatProductOutsourceOrderBo bo, PageQuery pageQuery) {
return iMatProductOutsourceOrderService.queryPageList(bo, pageQuery);
}
@Log(title = "半成品委外单", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(MatProductOutsourceOrderBo bo, HttpServletResponse response) {
List<MatProductOutsourceOrderVo> list = iMatProductOutsourceOrderService.queryList(bo);
ExcelUtil.exportExcel(list, "半成品委外单", MatProductOutsourceOrderVo.class, response);
}
@GetMapping("/{orderId}")
public R<MatProductOutsourceOrderVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long orderId) {
return R.ok(iMatProductOutsourceOrderService.queryById(orderId));
}
@Log(title = "半成品委外单", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody MatProductOutsourceOrderBo bo) {
return toAjax(iMatProductOutsourceOrderService.insertByBoWithInventory(bo));
}
@Log(title = "半成品委外单", businessType = BusinessType.DELETE)
@DeleteMapping("/{orderIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] orderIds) {
return toAjax(iMatProductOutsourceOrderService.deleteWithValidByIdsWithInventory(Arrays.asList(orderIds), true));
}
@Log(title = "半成品委外单", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/receive/{orderId}")
public R<Void> receive(@NotNull(message = "主键不能为空") @PathVariable Long orderId,
@RequestBody ReceiveReq req) {
BigDecimal qty = req == null ? null : req.getReceiveQty();
Date time = req == null ? null : req.getReceiveTime();
String by = req == null ? null : req.getReceiveBy();
return toAjax(iMatProductOutsourceOrderService.receive(orderId, qty, time, by));
}
public static class ReceiveReq {
private BigDecimal receiveQty;
private Date receiveTime;
private String receiveBy;
public BigDecimal getReceiveQty() {
return receiveQty;
}
public void setReceiveQty(BigDecimal receiveQty) {
this.receiveQty = receiveQty;
}
public Date getReceiveTime() {
return receiveTime;
}
public void setReceiveTime(Date receiveTime) {
this.receiveTime = receiveTime;
}
public String getReceiveBy() {
return receiveBy;
}
public void setReceiveBy(String receiveBy) {
this.receiveBy = receiveBy;
}
}
}

View File

@@ -44,6 +44,12 @@ public class MatProduct extends BaseEntity {
* 产品单价 可手动维护
*/
private BigDecimal unitPrice;
@TableField("product_type")
private String productType;
@TableField("current_stock")
private BigDecimal currentStock;
/**
* 删除标志0=正常1=已删除)
*/

View File

@@ -0,0 +1,36 @@
package com.gear.mat.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.gear.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("mat_product_in_detail")
public class MatProductInDetail extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "detail_id")
private Long detailId;
private Long productId;
private BigDecimal inNum;
private Date inTime;
private String operator;
@TableLogic
private Integer delFlag;
private String remark;
}

View File

@@ -0,0 +1,51 @@
package com.gear.mat.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.gear.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("mat_product_outsource_order")
public class MatProductOutsourceOrder extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "order_id")
private Long orderId;
private String orderNo;
private Long productId;
private String productTypeSnapshot;
private Long customerId;
private String customerName;
private BigDecimal quantity;
private Date outTime;
private String operator;
private BigDecimal receivedQty;
private Integer receiveFlag;
private Date receiveTime;
private String receiveBy;
@TableLogic
private Integer delFlag;
private String remark;
}

View File

@@ -47,6 +47,10 @@ public class MatProductBo extends BaseEntity {
*/
private BigDecimal unitPrice;
private String productType;
private BigDecimal currentStock;
/**
* 备注
*/

View File

@@ -0,0 +1,38 @@
package com.gear.mat.domain.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.gear.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
public class MatProductInDetailBo extends BaseEntity {
private Long detailId;
private Long productId;
private BigDecimal inNum;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date inTime;
private String operator;
private String remark;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date beginTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
}

View File

@@ -0,0 +1,45 @@
package com.gear.mat.domain.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.gear.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
public class MatProductOutsourceOrderBo extends BaseEntity {
private Long orderId;
private String orderNo;
private Long productId;
private Long customerId;
private String customerName;
private BigDecimal quantity;
private Integer receiveFlag;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date outTime;
private String operator;
private String remark;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date beginTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
}

View File

@@ -0,0 +1,52 @@
package com.gear.mat.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@Data
@ExcelIgnoreUnannotated
public class MatProductInDetailVo {
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "入库明细ID")
private Long detailId;
@ExcelProperty(value = "产品ID")
private Long productId;
@ExcelProperty(value = "入库数量")
private BigDecimal inNum;
@ExcelProperty(value = "入库时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date inTime;
@ExcelProperty(value = "操作人")
private String operator;
@ExcelProperty(value = "备注")
private String remark;
@ExcelProperty(value = "产品名称")
private String productName;
@ExcelProperty(value = "产品规格")
private String spec;
@ExcelProperty(value = "产品型号")
private String model;
@ExcelProperty(value = "产品类型")
private String productType;
@ExcelProperty(value = "当前库存")
private BigDecimal currentStock;
}

View File

@@ -0,0 +1,72 @@
package com.gear.mat.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@Data
@ExcelIgnoreUnannotated
public class MatProductOutsourceOrderVo {
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "委外单ID")
private Long orderId;
@ExcelProperty(value = "委外单号")
private String orderNo;
@ExcelProperty(value = "产品ID")
private Long productId;
@ExcelProperty(value = "产品名称")
private String productName;
@ExcelProperty(value = "产品规格")
private String spec;
@ExcelProperty(value = "产品型号")
private String model;
@ExcelProperty(value = "产品类型")
private String productTypeSnapshot;
@ExcelProperty(value = "委托公司ID")
private Long customerId;
@ExcelProperty(value = "委托公司")
private String customerName;
@ExcelProperty(value = "委派数量")
private BigDecimal quantity;
@ExcelProperty(value = "已收回数量")
private BigDecimal receivedQty;
@ExcelProperty(value = "收回标志")
private Integer receiveFlag;
@ExcelProperty(value = "委外时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date outTime;
@ExcelProperty(value = "收回时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date receiveTime;
@ExcelProperty(value = "收回人")
private String receiveBy;
@ExcelProperty(value = "操作人")
private String operator;
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -52,6 +52,12 @@ public class MatProductVo {
@ExcelProperty(value = "产品单价 可手动维护")
private BigDecimal unitPrice;
@ExcelProperty(value = "产品类型")
private String productType;
@ExcelProperty(value = "库存")
private BigDecimal currentStock;
/**
* 备注
*/

View File

@@ -51,6 +51,12 @@ public class MatProductWithMaterialsVo {
@ExcelProperty(value = "产品单价 可手动维护")
private BigDecimal unitPrice;
@ExcelProperty(value = "产品类型")
private String productType;
@ExcelProperty(value = "库存")
private BigDecimal currentStock;
/**
* 备注
*/

View File

@@ -0,0 +1,14 @@
package com.gear.mat.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gear.common.core.mapper.BaseMapperPlus;
import com.gear.mat.domain.MatProductInDetail;
import com.gear.mat.domain.vo.MatProductInDetailVo;
import org.apache.ibatis.annotations.Param;
public interface MatProductInDetailMapper extends BaseMapperPlus<MatProductInDetailMapper, MatProductInDetail, MatProductInDetailVo> {
Page<MatProductInDetailVo> selectVoPagePlus(Page<Object> page, @Param("ew") QueryWrapper<MatProductInDetail> qw);
}

View File

@@ -0,0 +1,14 @@
package com.gear.mat.mapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gear.common.core.mapper.BaseMapperPlus;
import com.gear.mat.domain.MatProductOutsourceOrder;
import com.gear.mat.domain.vo.MatProductOutsourceOrderVo;
import org.apache.ibatis.annotations.Param;
public interface MatProductOutsourceOrderMapper extends BaseMapperPlus<MatProductOutsourceOrderMapper, MatProductOutsourceOrder, MatProductOutsourceOrderVo> {
Page<MatProductOutsourceOrderVo> selectVoPagePlus(Page<Object> page, @Param("ew") QueryWrapper<MatProductOutsourceOrder> qw);
}

View File

@@ -0,0 +1,25 @@
package com.gear.mat.service;
import com.gear.common.core.domain.PageQuery;
import com.gear.common.core.page.TableDataInfo;
import com.gear.mat.domain.bo.MatProductInDetailBo;
import com.gear.mat.domain.vo.MatProductInDetailVo;
import java.util.Collection;
import java.util.List;
public interface IMatProductInDetailService {
MatProductInDetailVo queryById(Long detailId);
TableDataInfo<MatProductInDetailVo> queryPageList(MatProductInDetailBo bo, PageQuery pageQuery);
List<MatProductInDetailVo> queryList(MatProductInDetailBo bo);
Boolean insertByBoWithInventory(MatProductInDetailBo bo);
Boolean updateByBo(MatProductInDetailBo bo);
Boolean deleteWithValidByIdsWithInventory(Collection<Long> ids, Boolean isValid);
}

View File

@@ -0,0 +1,24 @@
package com.gear.mat.service;
import com.gear.common.core.domain.PageQuery;
import com.gear.common.core.page.TableDataInfo;
import com.gear.mat.domain.bo.MatProductOutsourceOrderBo;
import com.gear.mat.domain.vo.MatProductOutsourceOrderVo;
import java.util.Collection;
import java.util.List;
public interface IMatProductOutsourceOrderService {
MatProductOutsourceOrderVo queryById(Long orderId);
TableDataInfo<MatProductOutsourceOrderVo> queryPageList(MatProductOutsourceOrderBo bo, PageQuery pageQuery);
List<MatProductOutsourceOrderVo> queryList(MatProductOutsourceOrderBo bo);
Boolean insertByBoWithInventory(MatProductOutsourceOrderBo bo);
Boolean receive(Long orderId, java.math.BigDecimal receiveQty, java.util.Date receiveTime, String receiveBy);
Boolean deleteWithValidByIdsWithInventory(Collection<Long> ids, Boolean isValid);
}

View File

@@ -0,0 +1,163 @@
package com.gear.mat.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gear.common.core.domain.PageQuery;
import com.gear.common.core.page.TableDataInfo;
import com.gear.common.utils.StringUtils;
import com.gear.mat.domain.MatProduct;
import com.gear.mat.domain.MatProductInDetail;
import com.gear.mat.domain.bo.MatProductInDetailBo;
import com.gear.mat.domain.vo.MatProductInDetailVo;
import com.gear.mat.mapper.MatProductInDetailMapper;
import com.gear.mat.mapper.MatProductMapper;
import com.gear.mat.service.IMatProductInDetailService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@RequiredArgsConstructor
@Service
public class MatProductInDetailServiceImpl implements IMatProductInDetailService {
private final MatProductInDetailMapper baseMapper;
private final MatProductMapper matProductMapper;
@Override
public MatProductInDetailVo queryById(Long detailId) {
return baseMapper.selectVoById(detailId);
}
@Override
public TableDataInfo<MatProductInDetailVo> queryPageList(MatProductInDetailBo bo, PageQuery pageQuery) {
QueryWrapper<MatProductInDetail> qw = buildQueryWrapperPlus(bo);
Page<MatProductInDetailVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
return TableDataInfo.build(result);
}
private QueryWrapper<MatProductInDetail> buildQueryWrapperPlus(MatProductInDetailBo bo) {
QueryWrapper<MatProductInDetail> qw = Wrappers.query();
qw.eq(bo.getProductId() != null, "mpid.product_id", bo.getProductId());
qw.eq(bo.getInNum() != null, "mpid.in_num", bo.getInNum());
qw.ge(bo.getBeginTime() != null, "mpid.in_time", bo.getBeginTime());
qw.le(bo.getEndTime() != null, "mpid.in_time", bo.getEndTime());
qw.eq(StringUtils.isNotBlank(bo.getOperator()), "mpid.operator", bo.getOperator());
qw.eq("mpid.del_flag", 0);
qw.orderByDesc("mpid.in_time");
return qw;
}
@Override
public List<MatProductInDetailVo> queryList(MatProductInDetailBo bo) {
LambdaQueryWrapper<MatProductInDetail> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<MatProductInDetail> buildQueryWrapper(MatProductInDetailBo bo) {
LambdaQueryWrapper<MatProductInDetail> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getProductId() != null, MatProductInDetail::getProductId, bo.getProductId());
lqw.eq(bo.getInNum() != null, MatProductInDetail::getInNum, bo.getInNum());
lqw.eq(bo.getInTime() != null, MatProductInDetail::getInTime, bo.getInTime());
lqw.eq(StringUtils.isNotBlank(bo.getOperator()), MatProductInDetail::getOperator, bo.getOperator());
return lqw;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBoWithInventory(MatProductInDetailBo bo) {
if (bo.getProductId() == null) {
throw new IllegalArgumentException("产品不能为空");
}
if (bo.getInNum() == null || bo.getInNum().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("入库数量必须大于0");
}
if (bo.getInTime() == null) {
bo.setInTime(new Date());
}
if (StringUtils.isBlank(bo.getOperator())) {
bo.setOperator("-");
}
MatProductInDetail add = BeanUtil.toBean(bo, MatProductInDetail.class);
boolean ok = baseMapper.insert(add) > 0;
if (!ok) {
throw new IllegalStateException("入库记录保存失败");
}
bo.setDetailId(add.getDetailId());
updateProductStock(bo.getProductId(), bo.getInNum());
return true;
}
@Override
public Boolean updateByBo(MatProductInDetailBo bo) {
MatProductInDetail update = BeanUtil.toBean(bo, MatProductInDetail.class);
return baseMapper.updateById(update) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIdsWithInventory(Collection<Long> ids, Boolean isValid) {
if (CollectionUtils.isEmpty(ids)) {
return false;
}
List<MatProductInDetailVo> records = baseMapper.selectVoBatchIds(ids);
if (records == null || records.isEmpty()) {
return false;
}
boolean ok = baseMapper.deleteBatchIds(ids) > 0;
if (!ok) {
return false;
}
for (MatProductInDetailVo r : records) {
if (r.getProductId() == null || r.getInNum() == null) {
continue;
}
revertProductStock(r.getProductId(), r.getInNum());
}
return true;
}
private void updateProductStock(Long productId, BigDecimal qty) {
MatProduct product = matProductMapper.selectById(productId);
if (product == null) {
throw new IllegalArgumentException("产品不存在productId=" + productId);
}
BigDecimal current = product.getCurrentStock() == null ? BigDecimal.ZERO : product.getCurrentStock();
BigDecimal next = current.add(qty);
product.setCurrentStock(next);
int affected = matProductMapper.updateById(product);
if (affected <= 0) {
throw new IllegalStateException("更新产品库存失败");
}
}
private void revertProductStock(Long productId, BigDecimal qty) {
MatProduct product = matProductMapper.selectById(productId);
if (product == null) {
throw new IllegalArgumentException("产品不存在productId=" + productId);
}
BigDecimal current = product.getCurrentStock() == null ? BigDecimal.ZERO : product.getCurrentStock();
BigDecimal next = current.subtract(qty);
if (next.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("库存不足,无法撤回入库");
}
product.setCurrentStock(next);
int affected = matProductMapper.updateById(product);
if (affected <= 0) {
throw new IllegalStateException("更新产品库存失败");
}
}
}

View File

@@ -0,0 +1,215 @@
package com.gear.mat.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gear.common.core.domain.PageQuery;
import com.gear.common.core.page.TableDataInfo;
import com.gear.common.utils.StringUtils;
import com.gear.mat.domain.MatProduct;
import com.gear.mat.domain.MatProductOutsourceOrder;
import com.gear.mat.domain.bo.MatProductOutsourceOrderBo;
import com.gear.mat.domain.vo.MatProductOutsourceOrderVo;
import com.gear.mat.mapper.MatProductMapper;
import com.gear.mat.mapper.MatProductOutsourceOrderMapper;
import com.gear.mat.service.IMatProductOutsourceOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@RequiredArgsConstructor
@Service
public class MatProductOutsourceOrderServiceImpl implements IMatProductOutsourceOrderService {
private static final String PRODUCT_TYPE_SEMI = "semi";
private final MatProductOutsourceOrderMapper baseMapper;
private final MatProductMapper matProductMapper;
@Override
public MatProductOutsourceOrderVo queryById(Long orderId) {
return baseMapper.selectVoById(orderId);
}
@Override
public TableDataInfo<MatProductOutsourceOrderVo> queryPageList(MatProductOutsourceOrderBo bo, PageQuery pageQuery) {
QueryWrapper<MatProductOutsourceOrder> qw = buildQueryWrapperPlus(bo);
Page<MatProductOutsourceOrderVo> result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
return TableDataInfo.build(result);
}
private QueryWrapper<MatProductOutsourceOrder> buildQueryWrapperPlus(MatProductOutsourceOrderBo bo) {
QueryWrapper<MatProductOutsourceOrder> qw = Wrappers.query();
qw.eq(bo.getProductId() != null, "mo.product_id", bo.getProductId());
qw.eq(bo.getCustomerId() != null, "mo.customer_id", bo.getCustomerId());
qw.like(StringUtils.isNotBlank(bo.getCustomerName()), "mo.customer_name", bo.getCustomerName());
qw.eq(StringUtils.isNotBlank(bo.getOperator()), "mo.operator", bo.getOperator());
qw.ge(bo.getBeginTime() != null, "mo.out_time", bo.getBeginTime());
qw.le(bo.getEndTime() != null, "mo.out_time", bo.getEndTime());
qw.eq("mo.del_flag", 0);
qw.orderByDesc("mo.out_time");
return qw;
}
@Override
public List<MatProductOutsourceOrderVo> queryList(MatProductOutsourceOrderBo bo) {
QueryWrapper<MatProductOutsourceOrder> qw = buildQueryWrapperPlus(bo);
return baseMapper.selectVoList(qw);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBoWithInventory(MatProductOutsourceOrderBo bo) {
if (bo.getProductId() == null) {
throw new IllegalArgumentException("半成品不能为空");
}
if (bo.getQuantity() == null || bo.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("委派数量必须大于0");
}
if (StringUtils.isBlank(bo.getCustomerName()) && bo.getCustomerId() == null) {
throw new IllegalArgumentException("委托公司不能为空");
}
MatProduct product = matProductMapper.selectById(bo.getProductId());
if (product == null) {
throw new IllegalArgumentException("半成品不存在");
}
if (!PRODUCT_TYPE_SEMI.equals(product.getProductType())) {
throw new IllegalArgumentException("只能委外半成品");
}
BigDecimal current = product.getCurrentStock() == null ? BigDecimal.ZERO : product.getCurrentStock();
if (current.compareTo(bo.getQuantity()) < 0) {
throw new IllegalArgumentException("库存不足,无法委外出库");
}
if (bo.getOutTime() == null) {
bo.setOutTime(new Date());
}
if (StringUtils.isBlank(bo.getOperator())) {
bo.setOperator("-");
}
if (StringUtils.isBlank(bo.getOrderNo())) {
bo.setOrderNo(generateOrderNo());
}
MatProductOutsourceOrder add = BeanUtil.toBean(bo, MatProductOutsourceOrder.class);
add.setProductTypeSnapshot(product.getProductType());
add.setReceivedQty(BigDecimal.ZERO);
add.setReceiveFlag(0);
boolean ok = baseMapper.insert(add) > 0;
if (!ok) {
throw new IllegalStateException("委外单保存失败");
}
bo.setOrderId(add.getOrderId());
product.setCurrentStock(current.subtract(bo.getQuantity()));
int affected = matProductMapper.updateById(product);
if (affected <= 0) {
throw new IllegalStateException("扣减库存失败");
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean receive(Long orderId, BigDecimal receiveQty, Date receiveTime, String receiveBy) {
if (orderId == null) {
throw new IllegalArgumentException("委外单不能为空");
}
if (receiveQty == null || receiveQty.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("收回数量必须大于0");
}
MatProductOutsourceOrder order = baseMapper.selectById(orderId);
if (order == null) {
throw new IllegalArgumentException("委外单不存在");
}
if (order.getDelFlag() != null && order.getDelFlag() != 0) {
throw new IllegalArgumentException("委外单已撤回");
}
if (!PRODUCT_TYPE_SEMI.equals(order.getProductTypeSnapshot())) {
throw new IllegalArgumentException("只能收回半成品委外单");
}
BigDecimal total = order.getQuantity() == null ? BigDecimal.ZERO : order.getQuantity();
BigDecimal received = order.getReceivedQty() == null ? BigDecimal.ZERO : order.getReceivedQty();
BigDecimal remaining = total.subtract(received);
if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("已全部收回");
}
if (receiveQty.compareTo(remaining) > 0) {
throw new IllegalArgumentException("收回数量不能大于未收回数量");
}
MatProduct product = matProductMapper.selectById(order.getProductId());
if (product == null) {
throw new IllegalArgumentException("半成品不存在");
}
BigDecimal current = product.getCurrentStock() == null ? BigDecimal.ZERO : product.getCurrentStock();
product.setCurrentStock(current.add(receiveQty));
int affected = matProductMapper.updateById(product);
if (affected <= 0) {
throw new IllegalStateException("更新库存失败");
}
BigDecimal nextReceived = received.add(receiveQty);
order.setReceivedQty(nextReceived);
order.setReceiveTime(receiveTime == null ? new Date() : receiveTime);
order.setReceiveBy(StringUtils.isBlank(receiveBy) ? "-" : receiveBy);
order.setReceiveFlag(nextReceived.compareTo(total) >= 0 ? 1 : 0);
int updated = baseMapper.updateById(order);
if (updated <= 0) {
throw new IllegalStateException("更新委外单失败");
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIdsWithInventory(Collection<Long> ids, Boolean isValid) {
if (CollectionUtils.isEmpty(ids)) {
return false;
}
List<MatProductOutsourceOrder> records = baseMapper.selectBatchIds(ids);
if (records == null || records.isEmpty()) {
return false;
}
for (MatProductOutsourceOrder r : records) {
if (r.getProductId() == null || r.getQuantity() == null) {
continue;
}
BigDecimal received = r.getReceivedQty() == null ? BigDecimal.ZERO : r.getReceivedQty();
if (received.compareTo(BigDecimal.ZERO) > 0) {
throw new IllegalArgumentException("已收回的委外单不可撤回");
}
revertStock(r.getProductId(), r.getQuantity());
}
return baseMapper.deleteBatchIds(ids) > 0;
}
private void revertStock(Long productId, BigDecimal qty) {
MatProduct product = matProductMapper.selectById(productId);
if (product == null) {
throw new IllegalArgumentException("半成品不存在");
}
BigDecimal current = product.getCurrentStock() == null ? BigDecimal.ZERO : product.getCurrentStock();
product.setCurrentStock(current.add(qty));
int affected = matProductMapper.updateById(product);
if (affected <= 0) {
throw new IllegalStateException("回滚库存失败");
}
}
private String generateOrderNo() {
String ts = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
int rnd = (int) (Math.random() * 9000) + 1000;
return "WW" + ts + rnd;
}
}

View File

@@ -23,6 +23,7 @@ import com.gear.mat.service.IMatProductMaterialRelationService;
import com.gear.mat.domain.vo.MatMaterialVo;
import com.gear.mat.domain.vo.MatProductMaterialRelationVo;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Collection;
@@ -40,6 +41,9 @@ import java.util.ArrayList;
@Service
public class MatProductServiceImpl implements IMatProductService {
private static final String PRODUCT_TYPE_PRODUCT = "product";
private static final String PRODUCT_TYPE_SEMI = "semi";
private final MatProductMapper baseMapper;
private final IMatMaterialService matMaterialService;
private final IMatProductMaterialRelationService productMaterialRelationService;
@@ -73,12 +77,12 @@ public class MatProductServiceImpl implements IMatProductService {
}
private LambdaQueryWrapper<MatProduct> buildQueryWrapper(MatProductBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<MatProduct> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getProductName()), MatProduct::getProductName, bo.getProductName());
lqw.eq(StringUtils.isNotBlank(bo.getSpec()), MatProduct::getSpec, bo.getSpec());
lqw.eq(StringUtils.isNotBlank(bo.getModel()), MatProduct::getModel, bo.getModel());
lqw.eq(bo.getUnitPrice() != null, MatProduct::getUnitPrice, bo.getUnitPrice());
lqw.eq(StringUtils.isNotBlank(bo.getProductType()), MatProduct::getProductType, bo.getProductType());
return lqw;
}
@@ -113,6 +117,8 @@ public class MatProductServiceImpl implements IMatProductService {
productWithMaterialsVo.setSpec(productVo.getSpec());
productWithMaterialsVo.setModel(productVo.getModel());
productWithMaterialsVo.setUnitPrice(productVo.getUnitPrice());
productWithMaterialsVo.setProductType(productVo.getProductType());
productWithMaterialsVo.setCurrentStock(productVo.getCurrentStock());
productWithMaterialsVo.setRemark(productVo.getRemark());
productWithMaterialsVo.setProductImages(productVo.getProductImages());
productWithMaterialsVo.setProductPdfs(productVo.getProductPdfs());
@@ -174,6 +180,8 @@ public class MatProductServiceImpl implements IMatProductService {
public Boolean insertByBo(MatProductBo bo) {
System.out.println("插入产品productPdfs: " + bo.getProductPdfs());
MatProduct add = BeanUtil.toBean(bo, MatProduct.class);
add.setProductType(normalizeProductType(add.getProductType()));
add.setCurrentStock(normalizeCurrentStock(add.getCurrentStock()));
System.out.println("转换后productPdfs: " + add.getProductPdfs());
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
@@ -190,6 +198,8 @@ public class MatProductServiceImpl implements IMatProductService {
public Boolean updateByBo(MatProductBo bo) {
System.out.println("修改产品productPdfs: " + bo.getProductPdfs());
MatProduct update = BeanUtil.toBean(bo, MatProduct.class);
update.setProductType(normalizeProductType(update.getProductType()));
update.setCurrentStock(normalizeCurrentStock(update.getCurrentStock()));
System.out.println("转换后productPdfs: " + update.getProductPdfs());
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
@@ -245,6 +255,8 @@ public class MatProductServiceImpl implements IMatProductService {
data.setModel(model);
data.setUnitPrice(row.getUnitPrice());
data.setRemark(row.getRemark());
data.setProductType(normalizeProductType(data.getProductType()));
data.setCurrentStock(normalizeCurrentStock(data.getCurrentStock()));
if (exist == null) {
baseMapper.insert(data);
} else {
@@ -254,4 +266,22 @@ public class MatProductServiceImpl implements IMatProductService {
}
return "导入完成,成功 " + success + " 条,跳过 " + skipped + "";
}
private String normalizeProductType(String productType) {
String val = StringUtils.trim(productType);
if (StringUtils.isBlank(val)) {
return PRODUCT_TYPE_PRODUCT;
}
if (PRODUCT_TYPE_SEMI.equals(val)) {
return PRODUCT_TYPE_SEMI;
}
if (PRODUCT_TYPE_PRODUCT.equals(val)) {
return PRODUCT_TYPE_PRODUCT;
}
return PRODUCT_TYPE_PRODUCT;
}
private BigDecimal normalizeCurrentStock(BigDecimal currentStock) {
return currentStock == null ? BigDecimal.ZERO : currentStock;
}
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gear.mat.mapper.MatProductInDetailMapper">
<resultMap type="com.gear.mat.domain.MatProductInDetail" id="MatProductInDetailResult">
<result property="detailId" column="detail_id"/>
<result property="productId" column="product_id"/>
<result property="inNum" column="in_num"/>
<result property="inTime" column="in_time"/>
<result property="operator" column="operator"/>
<result property="delFlag" column="del_flag"/>
<result property="createTime" column="create_time"/>
<result property="createBy" column="create_by"/>
<result property="updateTime" column="update_time"/>
<result property="updateBy" column="update_by"/>
<result property="remark" column="remark"/>
</resultMap>
<select id="selectVoPagePlus" resultType="com.gear.mat.domain.vo.MatProductInDetailVo">
select mpid.detail_id AS detailId,
mpid.product_id AS productId,
mpid.in_num AS inNum,
mpid.in_time AS inTime,
mpid.operator AS operator,
mpid.create_time AS createTime,
mpid.create_by AS createBy,
mpid.update_time AS updateTime,
mpid.update_by AS updateBy,
mpid.remark AS remark,
mp.product_name AS productName,
mp.spec AS spec,
mp.model AS model,
mp.product_type AS productType,
mp.current_stock AS currentStock
FROM mat_product_in_detail mpid
LEFT JOIN mat_product mp ON mpid.product_id = mp.product_id AND mp.del_flag = 0
${ew.customSqlSegment}
</select>
</mapper>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gear.mat.mapper.MatProductOutsourceOrderMapper">
<resultMap type="com.gear.mat.domain.MatProductOutsourceOrder" id="MatProductOutsourceOrderResult">
<result property="orderId" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="productId" column="product_id"/>
<result property="productTypeSnapshot" column="product_type_snapshot"/>
<result property="customerId" column="customer_id"/>
<result property="customerName" column="customer_name"/>
<result property="quantity" column="quantity"/>
<result property="outTime" column="out_time"/>
<result property="operator" column="operator"/>
<result property="receivedQty" column="received_qty"/>
<result property="receiveFlag" column="receive_flag"/>
<result property="receiveTime" column="receive_time"/>
<result property="receiveBy" column="receive_by"/>
<result property="delFlag" column="del_flag"/>
<result property="createTime" column="create_time"/>
<result property="createBy" column="create_by"/>
<result property="updateTime" column="update_time"/>
<result property="updateBy" column="update_by"/>
<result property="remark" column="remark"/>
</resultMap>
<select id="selectVoPagePlus" resultType="com.gear.mat.domain.vo.MatProductOutsourceOrderVo">
select mo.order_id AS orderId,
mo.order_no AS orderNo,
mo.product_id AS productId,
mp.product_name AS productName,
mp.spec AS spec,
mp.model AS model,
mo.product_type_snapshot AS productTypeSnapshot,
mo.customer_id AS customerId,
mo.customer_name AS customerName,
mo.quantity AS quantity,
mo.received_qty AS receivedQty,
mo.receive_flag AS receiveFlag,
mo.out_time AS outTime,
mo.receive_time AS receiveTime,
mo.receive_by AS receiveBy,
mo.operator AS operator,
mo.remark AS remark,
mo.create_time AS createTime,
mo.create_by AS createBy,
mo.update_time AS updateTime,
mo.update_by AS updateBy
FROM mat_product_outsource_order mo
LEFT JOIN mat_product mp ON mo.product_id = mp.product_id AND mp.del_flag = 0
${ew.customSqlSegment}
</select>
</mapper>