Commit 30de2176a4d547bc754943e40efaa27609146188

Authored by YunaiV
1 parent 94247add

✨ ERP:增加 ERP 销售订单的实现 50%

Showing 14 changed files with 522 additions and 264 deletions
yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/sale/ErpSaleOrderStatusEnum.java deleted
1 -package cn.iocoder.yudao.module.erp.enums.sale;  
2 -  
3 -import cn.iocoder.yudao.framework.common.core.IntArrayValuable;  
4 -import lombok.AllArgsConstructor;  
5 -import lombok.Getter;  
6 -  
7 -import java.util.Arrays;  
8 -  
9 -/**  
10 - * ERP 销售订单的状态枚举  
11 - *  
12 - * @author 芋道源码  
13 - */  
14 -@AllArgsConstructor  
15 -@Getter  
16 -public enum ErpSaleOrderStatusEnum implements IntArrayValuable {  
17 -  
18 - AUDIT_NONE(0, "未审核"),  
19 - AUDIT_PASS(10, "已审核"),  
20 - SALE_PART(20, "部分销售"),  
21 - SALE_ALL(21, "完成销售"),  
22 - ;  
23 -  
24 - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(ErpSaleOrderStatusEnum::getStatus).toArray();  
25 -  
26 - /**  
27 - * 状态  
28 - */  
29 - private final Integer status;  
30 - /**  
31 - * 状态名  
32 - */  
33 - private final String name;  
34 -  
35 - @Override  
36 - public int[] array() {  
37 - return ARRAYS;  
38 - }  
39 -  
40 -}  
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
1 package cn.iocoder.yudao.module.erp.controller.admin.sale; 1 package cn.iocoder.yudao.module.erp.controller.admin.sale;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageParam; 5 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 9 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 10 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  11 +import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
9 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; 12 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
10 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO; 13 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO;
11 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; 14 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;
  15 +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpCustomerDO;
12 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; 16 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
  17 +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
  18 +import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
  19 +import cn.iocoder.yudao.module.erp.service.sale.ErpCustomerService;
13 import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService; 20 import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService;
  21 +import cn.iocoder.yudao.module.erp.service.stock.ErpStockService;
  22 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  23 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
14 import io.swagger.v3.oas.annotations.Operation; 24 import io.swagger.v3.oas.annotations.Operation;
15 import io.swagger.v3.oas.annotations.Parameter; 25 import io.swagger.v3.oas.annotations.Parameter;
16 import io.swagger.v3.oas.annotations.tags.Tag; 26 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -22,9 +32,13 @@ import org.springframework.validation.annotation.Validated; @@ -22,9 +32,13 @@ import org.springframework.validation.annotation.Validated;
22 import org.springframework.web.bind.annotation.*; 32 import org.springframework.web.bind.annotation.*;
23 33
24 import java.io.IOException; 34 import java.io.IOException;
  35 +import java.math.BigDecimal;
25 import java.util.List; 36 import java.util.List;
  37 +import java.util.Map;
26 38
27 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
  41 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 42 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
29 43
30 44
@@ -36,65 +50,116 @@ public class ErpSaleOrderController { @@ -36,65 +50,116 @@ public class ErpSaleOrderController {
36 50
37 @Resource 51 @Resource
38 private ErpSaleOrderService saleOrderService; 52 private ErpSaleOrderService saleOrderService;
  53 + @Resource
  54 + private ErpStockService stockService;
  55 + @Resource
  56 + private ErpProductService productService;
  57 + @Resource
  58 + private ErpCustomerService customerService;
  59 +
  60 + @Resource
  61 + private AdminUserApi adminUserApi;
39 62
40 - // TODO 芋艿:待 review  
41 @PostMapping("/create") 63 @PostMapping("/create")
42 - @Operation(summary = "创建ERP 销售订单")  
43 - @PreAuthorize("@ss.hasPermission('erp:sale-order:create')") 64 + @Operation(summary = "创建销售订单")
  65 + @PreAuthorize("@ss.hasPermission('erp:stock-out:create')")
44 public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) { 66 public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {
45 return success(saleOrderService.createSaleOrder(createReqVO)); 67 return success(saleOrderService.createSaleOrder(createReqVO));
46 } 68 }
47 69
48 - // TODO 芋艿:待 review  
49 @PutMapping("/update") 70 @PutMapping("/update")
50 - @Operation(summary = "更新ERP 销售订单")  
51 - @PreAuthorize("@ss.hasPermission('erp:sale-order:update')") 71 + @Operation(summary = "更新销售订单")
  72 + @PreAuthorize("@ss.hasPermission('erp:stock-out:update')")
52 public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) { 73 public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {
53 saleOrderService.updateSaleOrder(updateReqVO); 74 saleOrderService.updateSaleOrder(updateReqVO);
54 return success(true); 75 return success(true);
55 } 76 }
56 77
57 - // TODO 芋艿:待 review 78 + @PutMapping("/update-status")
  79 + @Operation(summary = "更新销售订单的状态")
  80 + @PreAuthorize("@ss.hasPermission('erp:stock-out:update-status')")
  81 + public CommonResult<Boolean> updateSaleOrderStatus(@RequestParam("id") Long id,
  82 + @RequestParam("status") Integer status) {
  83 + saleOrderService.updateSaleOrderStatus(id, status);
  84 + return success(true);
  85 + }
  86 +
58 @DeleteMapping("/delete") 87 @DeleteMapping("/delete")
59 - @Operation(summary = "删除ERP 销售订单")  
60 - @Parameter(name = "id", description = "编号", required = true)  
61 - @PreAuthorize("@ss.hasPermission('erp:sale-order:delete')")  
62 - public CommonResult<Boolean> deleteSaleOrder(@RequestParam("id") Long id) {  
63 - saleOrderService.deleteSaleOrder(id); 88 + @Operation(summary = "删除销售订单")
  89 + @Parameter(name = "ids", description = "编号数组", required = true)
  90 + @PreAuthorize("@ss.hasPermission('erp:stock-out:delete')")
  91 + public CommonResult<Boolean> deleteSaleOrder(@RequestParam("ids") List<Long> ids) {
  92 + saleOrderService.deleteSaleOrder(ids);
64 return success(true); 93 return success(true);
65 } 94 }
66 95
67 - // TODO 芋艿:待 review  
68 @GetMapping("/get") 96 @GetMapping("/get")
69 - @Operation(summary = "获得ERP 销售订单") 97 + @Operation(summary = "获得销售订单")
70 @Parameter(name = "id", description = "编号", required = true, example = "1024") 98 @Parameter(name = "id", description = "编号", required = true, example = "1024")
71 - @PreAuthorize("@ss.hasPermission('erp:sale-order:query')") 99 + @PreAuthorize("@ss.hasPermission('erp:stock-out:query')")
72 public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) { 100 public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
73 ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id); 101 ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
74 - return success(BeanUtils.toBean(saleOrder, ErpSaleOrderRespVO.class)); 102 + if (saleOrder == null) {
  103 + return success(null);
  104 + }
  105 + List<ErpSaleOrderItemDO> saleOrderItemList = saleOrderService.getSaleOrderItemListByOrderId(id);
  106 + Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(
  107 + convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId));
  108 + return success(BeanUtils.toBean(saleOrder, ErpSaleOrderRespVO.class, saleOrderVO ->
  109 + saleOrderVO.setItems(BeanUtils.toBean(saleOrderItemList, ErpSaleOrderRespVO.Item.class, item -> {
  110 + BigDecimal stockCount = stockService.getStockCount(item.getProductId());
  111 + item.setStockCount(stockCount != null ? stockCount : BigDecimal.ZERO);
  112 + MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())
  113 + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()));
  114 + }))));
75 } 115 }
76 116
77 - // TODO 芋艿:待 review  
78 @GetMapping("/page") 117 @GetMapping("/page")
79 - @Operation(summary = "获得ERP 销售订单分页")  
80 - @PreAuthorize("@ss.hasPermission('erp:sale-order:query')") 118 + @Operation(summary = "获得销售订单分页")
  119 + @PreAuthorize("@ss.hasPermission('erp:stock-out:query')")
81 public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) { 120 public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {
82 PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO); 121 PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
83 - return success(BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class)); 122 + return success(buildSaleOrderVOPageResult(pageResult));
84 } 123 }
85 124
86 - // TODO 芋艿:待 review  
87 @GetMapping("/export-excel") 125 @GetMapping("/export-excel")
88 - @Operation(summary = "导出ERP 销售订单 Excel")  
89 - @PreAuthorize("@ss.hasPermission('erp:sale-order:export')") 126 + @Operation(summary = "导出销售订单 Excel")
  127 + @PreAuthorize("@ss.hasPermission('erp:stock-out:export')")
90 @OperateLog(type = EXPORT) 128 @OperateLog(type = EXPORT)
91 public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO, 129 public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,
92 - HttpServletResponse response) throws IOException { 130 + HttpServletResponse response) throws IOException {
93 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); 131 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
94 - List<ErpSaleOrderDO> list = saleOrderService.getSaleOrderPage(pageReqVO).getList(); 132 + List<ErpSaleOrderRespVO> list = buildSaleOrderVOPageResult(saleOrderService.getSaleOrderPage(pageReqVO)).getList();
95 // 导出 Excel 133 // 导出 Excel
96 - ExcelUtils.write(response, "ERP 销售订单.xls", "数据", ErpSaleOrderRespVO.class,  
97 - BeanUtils.toBean(list, ErpSaleOrderRespVO.class)); 134 + ExcelUtils.write(response, "销售订单.xls", "数据", ErpSaleOrderRespVO.class, list);
  135 + }
  136 +
  137 + private PageResult<ErpSaleOrderRespVO> buildSaleOrderVOPageResult(PageResult<ErpSaleOrderDO> pageResult) {
  138 + if (CollUtil.isEmpty(pageResult.getList())) {
  139 + return PageResult.empty(pageResult.getTotal());
  140 + }
  141 + // 1.1 出库项
  142 + List<ErpSaleOrderItemDO> saleOrderItemList = saleOrderService.getSaleOrderItemListByOrderIds(
  143 + convertSet(pageResult.getList(), ErpSaleOrderDO::getId));
  144 + Map<Long, List<ErpSaleOrderItemDO>> saleOrderItemMap = convertMultiMap(saleOrderItemList, ErpSaleOrderItemDO::getOrderId);
  145 + // 1.2 商品信息
  146 + Map<Long, ErpProductRespVO> productMap = productService.getProductVOMap(
  147 + convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId));
  148 + // 1.3 客户信息
  149 + Map<Long, ErpCustomerDO> customerMap = customerService.getCustomerMap(
  150 + convertSet(pageResult.getList(), ErpSaleOrderDO::getCustomerId));
  151 + // 1.4 管理员信息
  152 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
  153 + convertSet(pageResult.getList(), erpStockRecordDO -> Long.parseLong(erpStockRecordDO.getCreator())));
  154 + // 2. 开始拼接
  155 + return BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class, saleOrder -> {
  156 + saleOrder.setItems(BeanUtils.toBean(saleOrderItemMap.get(saleOrder.getId()), ErpSaleOrderRespVO.Item.class,
  157 + item -> MapUtils.findAndThen(productMap, item.getProductId(), product -> item.setProductName(product.getName())
  158 + .setProductBarCode(product.getBarCode()).setProductUnitName(product.getUnitName()))));
  159 + saleOrder.setProductNames(CollUtil.join(saleOrder.getItems(), ",", ErpSaleOrderRespVO.Item::getProductName));
  160 + MapUtils.findAndThen(customerMap, saleOrder.getCustomerId(), supplier -> saleOrder.setCustomerName(supplier.getName()));
  161 + MapUtils.findAndThen(userMap, Long.parseLong(saleOrder.getCreator()), user -> saleOrder.setCreatorName(user.getNickname()));
  162 + });
98 } 163 }
99 164
100 } 165 }
101 \ No newline at end of file 166 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderPageReqVO.java
@@ -17,6 +17,19 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @@ -17,6 +17,19 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 @ToString(callSuper = true) 17 @ToString(callSuper = true)
18 public class ErpSaleOrderPageReqVO extends PageParam { 18 public class ErpSaleOrderPageReqVO extends PageParam {
19 19
  20 + /**
  21 + * 入库状态 - 无
  22 + */
  23 + public static final Integer IN_STATUS_NONE = 0;
  24 + /**
  25 + * 入库状态 - 部分
  26 + */
  27 + public static final Integer IN_STATUS_PART = 1;
  28 + /**
  29 + * 入库状态 - 全部
  30 + */
  31 + public static final Integer IN_STATUS_ALL = 2;
  32 +
20 @Schema(description = "销售单编号", example = "XS001") 33 @Schema(description = "销售单编号", example = "XS001")
21 private String no; 34 private String no;
22 35
@@ -28,7 +41,7 @@ public class ErpSaleOrderPageReqVO extends PageParam { @@ -28,7 +41,7 @@ public class ErpSaleOrderPageReqVO extends PageParam {
28 private LocalDateTime[] orderTime; 41 private LocalDateTime[] orderTime;
29 42
30 @Schema(description = "备注", example = "你猜") 43 @Schema(description = "备注", example = "你猜")
31 - private String description; 44 + private String remark;
32 45
33 @Schema(description = "销售状态", example = "2") 46 @Schema(description = "销售状态", example = "2")
34 private Integer status; 47 private Integer status;
@@ -36,4 +49,10 @@ public class ErpSaleOrderPageReqVO extends PageParam { @@ -36,4 +49,10 @@ public class ErpSaleOrderPageReqVO extends PageParam {
36 @Schema(description = "创建者") 49 @Schema(description = "创建者")
37 private String creator; 50 private String creator;
38 51
  52 + @Schema(description = "产品编号", example = "1")
  53 + private Long productId;
  54 +
  55 + @Schema(description = "入库状态", example = "2")
  56 + private Integer inStatus;
  57 +
39 } 58 }
40 \ No newline at end of file 59 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
@@ -3,12 +3,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; @@ -3,12 +3,13 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order;
3 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; 3 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
4 import com.alibaba.excel.annotation.ExcelProperty; 4 import com.alibaba.excel.annotation.ExcelProperty;
5 import io.swagger.v3.oas.annotations.media.Schema; 5 import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 7 import lombok.Data;
7 8
8 import java.math.BigDecimal; 9 import java.math.BigDecimal;
9 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
  11 +import java.util.List;
10 12
11 -// TODO 芋艿:导出最后搞  
12 @Schema(description = "管理后台 - ERP 销售订单 Response VO") 13 @Schema(description = "管理后台 - ERP 销售订单 Response VO")
13 @Data 14 @Data
14 @ExcelIgnoreUnannotated 15 @ExcelIgnoreUnannotated
@@ -23,56 +24,103 @@ public class ErpSaleOrderRespVO { @@ -23,56 +24,103 @@ public class ErpSaleOrderRespVO {
23 private String no; 24 private String no;
24 25
25 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") 26 @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724")
26 - @ExcelProperty("客户编号")  
27 private Long customerId; 27 private Long customerId;
  28 + @Schema(description = "客户名称", example = "芋道")
  29 + @ExcelProperty("客户名称")
  30 + private String customerName;
28 31
29 @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.REQUIRED) 32 @Schema(description = "下单时间", requiredMode = Schema.RequiredMode.REQUIRED)
30 @ExcelProperty("下单时间") 33 @ExcelProperty("下单时间")
31 private LocalDateTime orderTime; 34 private LocalDateTime orderTime;
32 35
33 - // TODO 芋艿:example 后面  
34 - @Schema(description = "销售员编号数组")  
35 - @ExcelProperty("销售员编号数组")  
36 - private String salePersonIds; 36 + @Schema(description = "销售员编号", example = "1888")
  37 + private Long saleUserId;
37 38
38 - @Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "26094")  
39 - @ExcelProperty("合计价格,单位:元")  
40 - private BigDecimal totalPrice; 39 + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89")
  40 + @ExcelProperty("结算账户编号")
  41 + private Long accountId;
  42 +
  43 + @Schema(description = "合计产品价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
  44 + private BigDecimal totalProductPrice;
  45 +
  46 + @Schema(description = "合计税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
  47 + private BigDecimal totalTaxPrice;
41 48
42 @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") 49 @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88")
43 - @ExcelProperty("优惠率,百分比")  
44 private BigDecimal discountPercent; 50 private BigDecimal discountPercent;
45 51
46 - @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "44.52")  
47 - @ExcelProperty("优惠金额,单位:元") 52 + @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
48 private BigDecimal discountPrice; 53 private BigDecimal discountPrice;
49 54
50 - @Schema(description = "支付金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "322.40")  
51 - @ExcelProperty("支付金额,单位:元")  
52 - private BigDecimal payPrice;  
53 -  
54 - @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "71.27")  
55 - @ExcelProperty("定金金额,单位:元") 55 + @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
  56 + @NotNull(message = "定金金额,单位:元不能为空")
56 private BigDecimal depositPrice; 57 private BigDecimal depositPrice;
57 58
  59 + @Schema(description = "销售状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  60 + @ExcelProperty("销售状态")
  61 + private Integer status;
  62 +
58 @Schema(description = "附件地址", example = "https://www.iocoder.cn") 63 @Schema(description = "附件地址", example = "https://www.iocoder.cn")
59 @ExcelProperty("附件地址") 64 @ExcelProperty("附件地址")
60 private String fileUrl; 65 private String fileUrl;
61 66
62 - @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89")  
63 - @ExcelProperty("结算账户编号")  
64 - private Long accountId;  
65 -  
66 @Schema(description = "备注", example = "你猜") 67 @Schema(description = "备注", example = "你猜")
67 @ExcelProperty("备注") 68 @ExcelProperty("备注")
68 - private String description; 69 + private String remark;
69 70
70 - @Schema(description = "销售状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
71 - @ExcelProperty("销售状态")  
72 - private Integer status; 71 + @Schema(description = "审核人", example = "芋道")
  72 + private String creator;
  73 + @Schema(description = "审核人名称", example = "芋道")
  74 + private String creatorName;
73 75
74 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 76 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
75 @ExcelProperty("创建时间") 77 @ExcelProperty("创建时间")
76 private LocalDateTime createTime; 78 private LocalDateTime createTime;
77 79
  80 + @Schema(description = "订单项列表", requiredMode = Schema.RequiredMode.REQUIRED)
  81 + private List<Item> items;
  82 +
  83 + @Schema(description = "产品信息", requiredMode = Schema.RequiredMode.REQUIRED)
  84 + @ExcelProperty("产品信息")
  85 + private String productNames;
  86 +
  87 + @Data
  88 + public static class Item {
  89 +
  90 + @Schema(description = "订单项编号", example = "11756")
  91 + private Long id;
  92 +
  93 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
  94 + private Long productId;
  95 +
  96 + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
  97 + private Long productUnitId;
  98 +
  99 + @Schema(description = "产品单价", example = "100.00")
  100 + private BigDecimal productPrice;
  101 +
  102 + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  103 + @NotNull(message = "产品数量不能为空")
  104 + private BigDecimal count;
  105 +
  106 + @Schema(description = "税率,百分比", example = "99.88")
  107 + private BigDecimal taxPercent;
  108 +
  109 + @Schema(description = "备注", example = "随便")
  110 + private String remark;
  111 +
  112 + // ========== 关联字段 ==========
  113 +
  114 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力")
  115 + private String productName;
  116 + @Schema(description = "产品条码", requiredMode = Schema.RequiredMode.REQUIRED, example = "A9985")
  117 + private String productBarCode;
  118 + @Schema(description = "产品单位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "盒")
  119 + private String productUnitName;
  120 +
  121 + @Schema(description = "库存数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  122 + private BigDecimal stockCount; // 该字段仅仅在“详情”和“编辑”时使用
  123 +
  124 + }
  125 +
78 } 126 }
79 \ No newline at end of file 127 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
@@ -28,26 +28,16 @@ public class ErpSaleOrderSaveReqVO { @@ -28,26 +28,16 @@ public class ErpSaleOrderSaveReqVO {
28 @NotNull(message = "下单时间不能为空") 28 @NotNull(message = "下单时间不能为空")
29 private LocalDateTime orderTime; 29 private LocalDateTime orderTime;
30 30
31 - @Schema(description = "销售员编号数组")  
32 - private List<Long> salePersonIds; 31 + @Schema(description = "销售员编号", example = "1888")
  32 + private Long saleUserId;
33 33
34 - @Schema(description = "合计价格,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "26094")  
35 - @NotNull(message = "合计价格,单位:元不能为空")  
36 - private BigDecimal totalPrice; 34 + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31189")
  35 + @NotNull(message = "结算账户编号不能为空")
  36 + private Long accountId;
37 37
38 @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") 38 @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88")
39 - @NotNull(message = "优惠率,百分比不能为空")  
40 private BigDecimal discountPercent; 39 private BigDecimal discountPercent;
41 40
42 - @Schema(description = "优惠金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "4452")  
43 - @NotNull(message = "优惠金额,单位:元不能为空")  
44 - private BigDecimal discountPrice;  
45 -  
46 - // TODO 芋艿:后面删除  
47 -// @Schema(description = "支付金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "32240")  
48 -// @NotNull(message = "支付金额,单位:元不能为空")  
49 -// private BigDecimal payPrice;  
50 -  
51 @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") 41 @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
52 @NotNull(message = "定金金额,单位:元不能为空") 42 @NotNull(message = "定金金额,单位:元不能为空")
53 private BigDecimal depositPrice; 43 private BigDecimal depositPrice;
@@ -55,73 +45,38 @@ public class ErpSaleOrderSaveReqVO { @@ -55,73 +45,38 @@ public class ErpSaleOrderSaveReqVO {
55 @Schema(description = "附件地址", example = "https://www.iocoder.cn") 45 @Schema(description = "附件地址", example = "https://www.iocoder.cn")
56 private String fileUrl; 46 private String fileUrl;
57 47
58 - @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31189")  
59 - @NotNull(message = "结算账户编号不能为空")  
60 - private Long accountId;  
61 -  
62 @Schema(description = "备注", example = "你猜") 48 @Schema(description = "备注", example = "你猜")
63 - private String description;  
64 -  
65 - // TODO 芋艿:后面删除  
66 -// @Schema(description = "销售状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
67 -// @NotNull(message = "销售状态不能为空")  
68 -// private Integer status; 49 + private String remark;
69 50
70 - @Schema(description = "ERP 销售订单明细列表")  
71 - private List<Item> salesOrderItems; 51 + @Schema(description = "订单清单列表")
  52 + private List<Item> items;
72 53
73 - @Schema(description = "管理后台 - ERP 销售订单明细新增/修改 Request VO")  
74 @Data 54 @Data
75 - public class Item { 55 + public static class Item {
76 56
77 - @Schema(description = "编号", example = "20704") 57 + @Schema(description = "订单项编号", example = "11756")
78 private Long id; 58 private Long id;
79 59
80 - // TODO 芋艿:后面删除  
81 -// @Schema(description = "销售订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30765")  
82 -// @NotNull(message = "销售订单编号不能为空")  
83 -// private Long orderId;  
84 -  
85 -// @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5574")  
86 -// @NotNull(message = "商品 SPU 编号不能为空")  
87 -// private Long productSpuId;  
88 -  
89 - @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21273")  
90 - @NotNull(message = "商品 SKU 编号不能为空")  
91 - private Long productSkuId; 60 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
  61 + @NotNull(message = "产品编号不能为空")
  62 + private Long productId;
92 63
93 - @Schema(description = "商品单位", requiredMode = Schema.RequiredMode.REQUIRED)  
94 - @NotEmpty(message = "商品单位不能为空")  
95 - private String productUnit; 64 + @Schema(description = "产品单位单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113")
  65 + @NotNull(message = "产品单位单位不能为空")
  66 + private Long productUnitId;
96 67
97 - @Schema(description = "商品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "6897")  
98 - @NotNull(message = "商品单价不能为空") 68 + @Schema(description = "产品单价", example = "100.00")
99 private BigDecimal productPrice; 69 private BigDecimal productPrice;
100 70
101 - @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "22100")  
102 - @NotNull(message = "数量不能为空")  
103 - private Integer count; 71 + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
  72 + @NotNull(message = "产品数量不能为空")
  73 + private BigDecimal count;
104 74
105 - // TODO 芋艿:后面删除  
106 -// @Schema(description = "总价", requiredMode = Schema.RequiredMode.REQUIRED, example = "26868")  
107 -// @NotNull(message = "总价不能为空")  
108 -// private BigDecimal totalPrice;  
109 -  
110 - @Schema(description = "备注", example = "你说的对")  
111 - private String description;  
112 -  
113 - @Schema(description = "税率,百分比", requiredMode = Schema.RequiredMode.REQUIRED)  
114 - @NotNull(message = "税率,百分比不能为空") 75 + @Schema(description = "税率,百分比", example = "99.88")
115 private BigDecimal taxPercent; 76 private BigDecimal taxPercent;
116 77
117 - @Schema(description = "税额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "15791")  
118 - @NotNull(message = "税额,单位:元不能为空")  
119 - private BigDecimal taxPrice;  
120 -  
121 - // TODO 芋艿:后面删除  
122 -// @Schema(description = "支付金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "21930")  
123 -// @NotNull(message = "支付金额,单位:元不能为空")  
124 -// private BigDecimal payPrice; 78 + @Schema(description = "备注", example = "随便")
  79 + private String remark;
125 80
126 } 81 }
127 82
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
1 package cn.iocoder.yudao.module.erp.dal.dataobject.sale; 1 package cn.iocoder.yudao.module.erp.dal.dataobject.sale;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;  
5 -import cn.iocoder.yudao.module.erp.enums.sale.ErpSaleOrderStatusEnum;  
6 import com.baomidou.mybatisplus.annotation.KeySequence; 4 import com.baomidou.mybatisplus.annotation.KeySequence;
7 -import com.baomidou.mybatisplus.annotation.TableField;  
8 import com.baomidou.mybatisplus.annotation.TableId; 5 import com.baomidou.mybatisplus.annotation.TableId;
9 import com.baomidou.mybatisplus.annotation.TableName; 6 import com.baomidou.mybatisplus.annotation.TableName;
10 import lombok.*; 7 import lombok.*;
11 8
12 import java.math.BigDecimal; 9 import java.math.BigDecimal;
13 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
14 -import java.util.List;  
15 11
16 /** 12 /**
17 * ERP 销售订单 DO 13 * ERP 销售订单 DO
18 * 14 *
19 * @author 芋道源码 15 * @author 芋道源码
20 */ 16 */
21 -@TableName(value = "erp_sale_order", autoResultMap = true) 17 +@TableName(value = "erp_sale_order")
22 @KeySequence("erp_sale_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 18 @KeySequence("erp_sale_order_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
23 @Data 19 @Data
24 @EqualsAndHashCode(callSuper = true) 20 @EqualsAndHashCode(callSuper = true)
@@ -40,13 +36,13 @@ public class ErpSaleOrderDO extends BaseDO { @@ -40,13 +36,13 @@ public class ErpSaleOrderDO extends BaseDO {
40 /** 36 /**
41 * 销售状态 37 * 销售状态
42 * 38 *
43 - * 枚举 {@link ErpSaleOrderStatusEnum} 39 + * 枚举 {@link cn.iocoder.yudao.module.erp.enums.ErpAuditStatus}
44 */ 40 */
45 private Integer status; 41 private Integer status;
46 /** 42 /**
47 * 客户编号 43 * 客户编号
48 * 44 *
49 - * TODO 芋艿:关联 45 + * 关联 {@link ErpCustomerDO#getId()}
50 */ 46 */
51 private Long customerId; 47 private Long customerId;
52 /** 48 /**
@@ -56,34 +52,46 @@ public class ErpSaleOrderDO extends BaseDO { @@ -56,34 +52,46 @@ public class ErpSaleOrderDO extends BaseDO {
56 */ 52 */
57 private Long accountId; 53 private Long accountId;
58 /** 54 /**
59 - * 销售员编号数组 55 + * 销售员编号
60 * 56 *
61 - * TODO 芋艿:关联 57 + * 关联 AdminUserDO 的 id 字段
62 */ 58 */
63 - @TableField(typeHandler = LongListTypeHandler.class)  
64 - private List<Long> salePersonIds; 59 + private Long saleUserId;
65 /** 60 /**
66 * 下单时间 61 * 下单时间
67 */ 62 */
68 private LocalDateTime orderTime; 63 private LocalDateTime orderTime;
69 64
70 /** 65 /**
71 - * 合计价格,单位:元 66 + * 合计数量
  67 + */
  68 + private BigDecimal totalCount;
  69 + /**
  70 + * 最终合计价格,单位:元
  71 + *
  72 + * totalPrice = totalProductPrice + totalTaxPrice - discountPrice
72 */ 73 */
73 private BigDecimal totalPrice; 74 private BigDecimal totalPrice;
  75 +
  76 + /**
  77 + * 合计产品价格,单位:元
  78 + */
  79 + private BigDecimal totalProductPrice;
  80 + /**
  81 + * 合计税额,单位:元
  82 + */
  83 + private BigDecimal totalTaxPrice;
74 /** 84 /**
75 * 优惠率,百分比 85 * 优惠率,百分比
76 */ 86 */
77 private BigDecimal discountPercent; 87 private BigDecimal discountPercent;
78 /** 88 /**
79 * 优惠金额,单位:元 89 * 优惠金额,单位:元
  90 + *
  91 + * discountPrice = (totalProductPrice + totalTaxPrice) * discountPercent
80 */ 92 */
81 private BigDecimal discountPrice; 93 private BigDecimal discountPrice;
82 /** 94 /**
83 - * 支付金额,单位:元  
84 - */  
85 - private BigDecimal payPrice;  
86 - /**  
87 * 定金金额,单位:元 95 * 定金金额,单位:元
88 */ 96 */
89 private BigDecimal depositPrice; 97 private BigDecimal depositPrice;
@@ -95,6 +103,18 @@ public class ErpSaleOrderDO extends BaseDO { @@ -95,6 +103,18 @@ public class ErpSaleOrderDO extends BaseDO {
95 /** 103 /**
96 * 备注 104 * 备注
97 */ 105 */
98 - private String description; 106 + private String remark;
  107 +
  108 + // ========== 销售入库 ==========
  109 + /**
  110 + * 销售入库数量
  111 + */
  112 + private BigDecimal inCount;
  113 +
  114 + // ========== 销售退货(出库)) ==========
  115 + /**
  116 + * 销售退货数量
  117 + */
  118 + private BigDecimal returnCount;
99 119
100 } 120 }
101 \ No newline at end of file 121 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSalesOrderItemDO.java renamed to yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java
1 package cn.iocoder.yudao.module.erp.dal.dataobject.sale; 1 package cn.iocoder.yudao.module.erp.dal.dataobject.sale;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
4 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
5 import com.baomidou.mybatisplus.annotation.TableId; 6 import com.baomidou.mybatisplus.annotation.TableId;
6 import com.baomidou.mybatisplus.annotation.TableName; 7 import com.baomidou.mybatisplus.annotation.TableName;
@@ -13,15 +14,15 @@ import java.math.BigDecimal; @@ -13,15 +14,15 @@ import java.math.BigDecimal;
13 * 14 *
14 * @author 芋道源码 15 * @author 芋道源码
15 */ 16 */
16 -@TableName("erp_sales_order_items")  
17 -@KeySequence("erp_sales_order_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 17 +@TableName("erp_sale_order_items")
  18 +@KeySequence("erp_sale_order_items_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
18 @Data 19 @Data
19 @EqualsAndHashCode(callSuper = true) 20 @EqualsAndHashCode(callSuper = true)
20 @ToString(callSuper = true) 21 @ToString(callSuper = true)
21 @Builder 22 @Builder
22 @NoArgsConstructor 23 @NoArgsConstructor
23 @AllArgsConstructor 24 @AllArgsConstructor
24 -public class ErpSalesOrderItemDO extends BaseDO { 25 +public class ErpSaleOrderItemDO extends BaseDO {
25 26
26 /** 27 /**
27 * 编号 28 * 编号
@@ -36,53 +37,58 @@ public class ErpSalesOrderItemDO extends BaseDO { @@ -36,53 +37,58 @@ public class ErpSalesOrderItemDO extends BaseDO {
36 private Long orderId; 37 private Long orderId;
37 38
38 /** 39 /**
39 - * 商品 SPU 编号 40 + * 产品编号
40 * 41 *
41 - * TODO 芋艿 关联 42 + * 关联 {@link ErpProductDO#getId()}
42 */ 43 */
43 - private Long productSpuId; 44 + private Long productId;
44 /** 45 /**
45 - * 商品 SKU 编号 46 + * 产品单位单位
46 * 47 *
47 - * TODO 芋艿 关联 48 + * 冗余 {@link ErpProductDO#getUnitId()}
48 */ 49 */
49 - private Long productSkuId;  
50 - /**  
51 - * 商品单位  
52 - *  
53 - * TODO 芋艿 冗余  
54 - */  
55 - private String productUnit; 50 + private Long productUnitId;
  51 +
56 /** 52 /**
57 - * 商品单价  
58 - *  
59 - * TODO 芋艿 冗余 53 + * 产品单位单价,单位:元
60 */ 54 */
61 private BigDecimal productPrice; 55 private BigDecimal productPrice;
62 -  
63 /** 56 /**
64 * 数量 57 * 数量
65 */ 58 */
66 - private Integer count; 59 + private BigDecimal count;
67 /** 60 /**
68 - * 总价 61 + * 总价,单位:元
  62 + *
  63 + * totalPrice = productPrice * count
69 */ 64 */
70 private BigDecimal totalPrice; 65 private BigDecimal totalPrice;
71 /** 66 /**
72 - * 备注  
73 - */  
74 - private String description;  
75 - /**  
76 * 税率,百分比 67 * 税率,百分比
77 */ 68 */
78 private BigDecimal taxPercent; 69 private BigDecimal taxPercent;
79 /** 70 /**
80 * 税额,单位:元 71 * 税额,单位:元
  72 + *
  73 + * taxPrice = totalPrice * taxPercent
81 */ 74 */
82 private BigDecimal taxPrice; 75 private BigDecimal taxPrice;
  76 +
  77 + /**
  78 + * 备注
  79 + */
  80 + private String remark;
  81 +
  82 + // ========== 销售入库 ==========
  83 + /**
  84 + * 销售入库数量
  85 + */
  86 + private BigDecimal inCount;
  87 +
  88 + // ========== 销售退货(出库)) ==========
83 /** 89 /**
84 - * 支付金额,单位:元 90 + * 销售退货数量
85 */ 91 */
86 - private BigDecimal payPrice; 92 + private BigDecimal returnCount;
87 93
88 } 94 }
89 \ No newline at end of file 95 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSalesOrderItemMapper.java renamed to yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderItemMapper.java
1 package cn.iocoder.yudao.module.erp.dal.mysql.sale; 1 package cn.iocoder.yudao.module.erp.dal.mysql.sale;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 -import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSalesOrderItemDO; 4 +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
5 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
6 6
  7 +import java.util.Collection;
7 import java.util.List; 8 import java.util.List;
8 9
9 /** 10 /**
@@ -12,14 +13,18 @@ import java.util.List; @@ -12,14 +13,18 @@ import java.util.List;
12 * @author 芋道源码 13 * @author 芋道源码
13 */ 14 */
14 @Mapper 15 @Mapper
15 -public interface ErpSalesOrderItemMapper extends BaseMapperX<ErpSalesOrderItemDO> { 16 +public interface ErpSaleOrderItemMapper extends BaseMapperX<ErpSaleOrderItemDO> {
16 17
17 - default List<ErpSalesOrderItemDO> selectListById(Long id) {  
18 - return selectList(ErpSalesOrderItemDO::getId, id); 18 + default List<ErpSaleOrderItemDO> selectListByOrderId(Long orderId) {
  19 + return selectList(ErpSaleOrderItemDO::getOrderId, orderId);
19 } 20 }
20 21
21 - default int deleteById(Long id) {  
22 - return delete(ErpSalesOrderItemDO::getId, id); 22 + default List<ErpSaleOrderItemDO> selectListByOrderIds(Collection<Long> orderIds) {
  23 + return selectList(ErpSaleOrderItemDO::getOrderId, orderIds);
  24 + }
  25 +
  26 + default int deleteByOrderId(Long orderId) {
  27 + return delete(ErpSaleOrderItemDO::getOrderId, orderId);
23 } 28 }
24 29
25 } 30 }
26 \ No newline at end of file 31 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java
@@ -3,9 +3,11 @@ package cn.iocoder.yudao.module.erp.dal.mysql.sale; @@ -3,9 +3,11 @@ package cn.iocoder.yudao.module.erp.dal.mysql.sale;
3 3
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
7 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; 7 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
8 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; 8 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
  9 +import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockOutItemDO;
  10 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
9 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
10 12
11 /** 13 /**
@@ -17,14 +19,29 @@ import org.apache.ibatis.annotations.Mapper; @@ -17,14 +19,29 @@ import org.apache.ibatis.annotations.Mapper;
17 public interface ErpSaleOrderMapper extends BaseMapperX<ErpSaleOrderDO> { 19 public interface ErpSaleOrderMapper extends BaseMapperX<ErpSaleOrderDO> {
18 20
19 default PageResult<ErpSaleOrderDO> selectPage(ErpSaleOrderPageReqVO reqVO) { 21 default PageResult<ErpSaleOrderDO> selectPage(ErpSaleOrderPageReqVO reqVO) {
20 - return selectPage(reqVO, new LambdaQueryWrapperX<ErpSaleOrderDO>()  
21 - .likeIfPresent(ErpSaleOrderDO::getNo, reqVO.getNo()) 22 + MPJLambdaWrapperX<ErpSaleOrderDO> query = new MPJLambdaWrapperX<ErpSaleOrderDO>()
  23 + .eqIfPresent(ErpSaleOrderDO::getNo, reqVO.getNo())
22 .eqIfPresent(ErpSaleOrderDO::getCustomerId, reqVO.getCustomerId()) 24 .eqIfPresent(ErpSaleOrderDO::getCustomerId, reqVO.getCustomerId())
23 .betweenIfPresent(ErpSaleOrderDO::getOrderTime, reqVO.getOrderTime()) 25 .betweenIfPresent(ErpSaleOrderDO::getOrderTime, reqVO.getOrderTime())
24 - .eqIfPresent(ErpSaleOrderDO::getDescription, reqVO.getDescription())  
25 .eqIfPresent(ErpSaleOrderDO::getStatus, reqVO.getStatus()) 26 .eqIfPresent(ErpSaleOrderDO::getStatus, reqVO.getStatus())
  27 + .likeIfPresent(ErpSaleOrderDO::getRemark, reqVO.getRemark())
26 .eqIfPresent(ErpSaleOrderDO::getCreator, reqVO.getCreator()) 28 .eqIfPresent(ErpSaleOrderDO::getCreator, reqVO.getCreator())
27 - .orderByDesc(ErpSaleOrderDO::getId)); 29 + .orderByDesc(ErpSaleOrderDO::getId);
  30 + if (reqVO.getProductId() != null) {
  31 + query.leftJoin(ErpStockOutItemDO.class, ErpStockOutItemDO::getOutId, ErpSaleOrderDO::getId)
  32 + .eq(reqVO.getProductId() != null, ErpStockOutItemDO::getProductId, reqVO.getProductId())
  33 + .groupBy(ErpSaleOrderDO::getId); // 避免 1 对多查询,产生相同的 1
  34 + }
  35 + return selectJoinPage(reqVO, ErpSaleOrderDO.class, query);
  36 + }
  37 +
  38 + default int updateByIdAndStatus(Long id, Integer status, ErpSaleOrderDO updateObj) {
  39 + return update(updateObj, new LambdaUpdateWrapper<ErpSaleOrderDO>()
  40 + .eq(ErpSaleOrderDO::getId, id).eq(ErpSaleOrderDO::getStatus, status));
  41 + }
  42 +
  43 + default ErpSaleOrderDO selectByNo(String no) {
  44 + return selectOne(ErpSaleOrderDO::getNo, no);
28 } 45 }
29 46
30 } 47 }
31 \ No newline at end of file 48 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/stock/ErpStockMapper.java
1 package cn.iocoder.yudao.module.erp.dal.mysql.stock; 1 package cn.iocoder.yudao.module.erp.dal.mysql.stock;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO; 8 import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
7 import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO; 9 import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
  10 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
8 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 11 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
9 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
10 13
11 import java.math.BigDecimal; 14 import java.math.BigDecimal;
  15 +import java.util.List;
  16 +import java.util.Map;
12 17
13 /** 18 /**
14 * ERP 产品库存 Mapper 19 * ERP 产品库存 Mapper
@@ -37,11 +42,23 @@ public interface ErpStockMapper extends BaseMapperX&lt;ErpStockDO&gt; { @@ -37,11 +42,23 @@ public interface ErpStockMapper extends BaseMapperX&lt;ErpStockDO&gt; {
37 updateWrapper.setSql("count = count + " + count); 42 updateWrapper.setSql("count = count + " + count);
38 } else if (count.compareTo(BigDecimal.ZERO) < 0) { 43 } else if (count.compareTo(BigDecimal.ZERO) < 0) {
39 if (!negativeEnable) { 44 if (!negativeEnable) {
40 - updateWrapper.gt(ErpStockDO::getCount, count.abs()); 45 + updateWrapper.ge(ErpStockDO::getCount, count.abs());
41 } 46 }
42 updateWrapper.setSql("count = count - " + count.abs()); 47 updateWrapper.setSql("count = count - " + count.abs());
43 } 48 }
44 return update(null, updateWrapper); 49 return update(null, updateWrapper);
45 } 50 }
46 51
  52 + default BigDecimal selectSumByProductId(Long productId) {
  53 + // SQL sum 查询
  54 + List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpStockDO>()
  55 + .select("SUM(count) AS sumCount")
  56 + .eq("product_id", productId));
  57 + // 获得数量
  58 + if (CollUtil.isEmpty(result)) {
  59 + return BigDecimal.ZERO;
  60 + }
  61 + return BigDecimal.valueOf(MapUtil.getDouble(result.get(0), "sumCount", 0D));
  62 + }
  63 +
47 } 64 }
48 \ No newline at end of file 65 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java
@@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; 4 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
5 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; 5 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;
6 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; 6 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
  7 +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
7 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
8 9
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
9 /** 13 /**
10 * ERP 销售订单 Service 接口 14 * ERP 销售订单 Service 接口
11 * 15 *
@@ -14,7 +18,7 @@ import jakarta.validation.Valid; @@ -14,7 +18,7 @@ import jakarta.validation.Valid;
14 public interface ErpSaleOrderService { 18 public interface ErpSaleOrderService {
15 19
16 /** 20 /**
17 - * 创建ERP 销售订单 21 + * 创建销售订单
18 * 22 *
19 * @param createReqVO 创建信息 23 * @param createReqVO 创建信息
20 * @return 编号 24 * @return 编号
@@ -22,33 +26,59 @@ public interface ErpSaleOrderService { @@ -22,33 +26,59 @@ public interface ErpSaleOrderService {
22 Long createSaleOrder(@Valid ErpSaleOrderSaveReqVO createReqVO); 26 Long createSaleOrder(@Valid ErpSaleOrderSaveReqVO createReqVO);
23 27
24 /** 28 /**
25 - * 更新ERP 销售订单 29 + * 更新销售订单
26 * 30 *
27 * @param updateReqVO 更新信息 31 * @param updateReqVO 更新信息
28 */ 32 */
29 void updateSaleOrder(@Valid ErpSaleOrderSaveReqVO updateReqVO); 33 void updateSaleOrder(@Valid ErpSaleOrderSaveReqVO updateReqVO);
30 34
31 /** 35 /**
32 - * 删除ERP 销售订单 36 + * 更新销售订单的状态
33 * 37 *
34 * @param id 编号 38 * @param id 编号
  39 + * @param status 状态
35 */ 40 */
36 - void deleteSaleOrder(Long id); 41 + void updateSaleOrderStatus(Long id, Integer status);
37 42
38 /** 43 /**
39 - * 获得ERP 销售订单 44 + * 删除销售订单
  45 + *
  46 + * @param ids 编号数组
  47 + */
  48 + void deleteSaleOrder(List<Long> ids);
  49 +
  50 + /**
  51 + * 获得销售订单
40 * 52 *
41 * @param id 编号 53 * @param id 编号
42 - * @return ERP 销售订单 54 + * @return 销售订单
43 */ 55 */
44 ErpSaleOrderDO getSaleOrder(Long id); 56 ErpSaleOrderDO getSaleOrder(Long id);
45 57
46 /** 58 /**
47 - * 获得ERP 销售订单分页 59 + * 获得销售订单分页
48 * 60 *
49 * @param pageReqVO 分页查询 61 * @param pageReqVO 分页查询
50 - * @return ERP 销售订单分页 62 + * @return 销售订单分页
51 */ 63 */
52 PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO); 64 PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO);
53 65
  66 + // ==================== 销售订单项 ====================
  67 +
  68 + /**
  69 + * 获得销售订单项列表
  70 + *
  71 + * @param orderId 销售订单编号
  72 + * @return 销售订单项列表
  73 + */
  74 + List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderId(Long orderId);
  75 +
  76 + /**
  77 + * 获得销售订单项 List
  78 + *
  79 + * @param orderIds 销售订单编号数组
  80 + * @return 销售订单项 List
  81 + */
  82 + List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderIds(Collection<Long> orderIds);
  83 +
54 } 84 }
55 \ No newline at end of file 85 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
1 package cn.iocoder.yudao.module.erp.service.sale; 1 package cn.iocoder.yudao.module.erp.service.sale;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; 7 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
6 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; 8 import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;
  9 +import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
7 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; 10 import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
8 -import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSalesOrderItemDO; 11 +import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
  12 +import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper;
9 import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper; 13 import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
10 -import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSalesOrderItemMapper; 14 +import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO;
  15 +import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
  16 +import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
11 import jakarta.annotation.Resource; 17 import jakarta.annotation.Resource;
12 import org.springframework.stereotype.Service; 18 import org.springframework.stereotype.Service;
13 import org.springframework.transaction.annotation.Transactional; 19 import org.springframework.transaction.annotation.Transactional;
14 import org.springframework.validation.annotation.Validated; 20 import org.springframework.validation.annotation.Validated;
15 21
  22 +import java.math.BigDecimal;
  23 +import java.util.Collection;
  24 +import java.util.Collections;
16 import java.util.List; 25 import java.util.List;
  26 +import java.util.Map;
17 27
18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
19 import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; 30 import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
20 31
  32 +// TODO 芋艿:记录操作日志
  33 +
21 /** 34 /**
22 * ERP 销售订单 Service 实现类 35 * ERP 销售订单 Service 实现类
23 * 36 *
@@ -30,61 +43,142 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { @@ -30,61 +43,142 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService {
30 @Resource 43 @Resource
31 private ErpSaleOrderMapper saleOrderMapper; 44 private ErpSaleOrderMapper saleOrderMapper;
32 @Resource 45 @Resource
33 - private ErpSalesOrderItemMapper salesOrderItemMapper; 46 + private ErpSaleOrderItemMapper saleOrderItemMapper;
  47 +
  48 + @Resource
  49 + private ErpNoRedisDAO noRedisDAO;
  50 +
  51 + @Resource
  52 + private ErpProductService productService;
  53 + @Resource
  54 + private ErpCustomerService customerService;
34 55
35 @Override 56 @Override
36 @Transactional(rollbackFor = Exception.class) 57 @Transactional(rollbackFor = Exception.class)
37 public Long createSaleOrder(ErpSaleOrderSaveReqVO createReqVO) { 58 public Long createSaleOrder(ErpSaleOrderSaveReqVO createReqVO) {
38 - // 插入  
39 - ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class);  
40 - saleOrderMapper.insert(saleOrder); 59 + // 1.1 校验订单项的有效性
  60 + List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(createReqVO.getItems());
  61 + // 1.2 校验客户
  62 + customerService.validateCustomer(createReqVO.getCustomerId());
  63 + // 1.3 生成调拨单号,并校验唯一性
  64 + String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_MOVE_NO_PREFIX);
  65 + if (saleOrderMapper.selectByNo(no) != null) {
  66 + throw exception(STOCK_MOVE_NO_EXISTS);
  67 + }
41 68
42 - // 插入子表  
43 -// createSalesOrderItemsList(saleOrder.getId(), createReqVO.getSalesOrderItems());  
44 - // 返回 69 + // 2.1 插入订单
  70 + ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in
  71 + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())
  72 + .setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add))
  73 + .setTotalPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)));
  74 + saleOrderMapper.insert(saleOrder);
  75 + // 2.2 插入订单项
  76 + saleOrderItems.forEach(o -> o.setOrderId(saleOrder.getId()));
  77 + saleOrderItemMapper.insertBatch(saleOrderItems);
45 return saleOrder.getId(); 78 return saleOrder.getId();
46 } 79 }
47 80
48 - private void createSalesOrderItemsList(Long id, List<ErpSalesOrderItemDO> list) {  
49 - list.forEach(o -> o.setId(id));  
50 - salesOrderItemMapper.insertBatch(list);  
51 - }  
52 -  
53 @Override 81 @Override
54 @Transactional(rollbackFor = Exception.class) 82 @Transactional(rollbackFor = Exception.class)
55 public void updateSaleOrder(ErpSaleOrderSaveReqVO updateReqVO) { 83 public void updateSaleOrder(ErpSaleOrderSaveReqVO updateReqVO) {
56 - // 校验存在  
57 - validateSaleOrderExists(updateReqVO.getId());  
58 - // 更新  
59 - ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class); 84 + // 1.1 校验存在
  85 + ErpSaleOrderDO saleOrder = validateSaleOrderExists(updateReqVO.getId());
  86 + if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
  87 + throw exception(STOCK_MOVE_UPDATE_FAIL_APPROVE, saleOrder.getNo());
  88 + }
  89 + // 1.2 校验客户
  90 + customerService.validateCustomer(updateReqVO.getCustomerId());
  91 + // 1.3 校验订单项的有效性
  92 + List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(updateReqVO.getItems());
  93 +
  94 + // 2.1 更新订单
  95 + ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class, in -> in
  96 + .setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add))
  97 + .setTotalPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add)));
60 saleOrderMapper.updateById(updateObj); 98 saleOrderMapper.updateById(updateObj);
  99 + // 2.2 更新订单项
  100 + updateSaleOrderItemList(updateReqVO.getId(), saleOrderItems);
  101 + }
  102 +
  103 + @Override
  104 + @Transactional(rollbackFor = Exception.class)
  105 + public void updateSaleOrderStatus(Long id, Integer status) {
  106 + boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
  107 + // 1.1 校验存在
  108 + ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
  109 + // 1.2 校验状态
  110 + if (saleOrder.getStatus().equals(status)) {
  111 + throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL);
  112 + }
  113 + // TODO @芋艿:需要校验是不是有入库、有退货
61 114
62 - // 更新子表  
63 -// updateSalesOrderItemsList(updateReqVO.getId(), updateReqVO.getSalesOrderItems()); 115 + // 2. 更新状态
  116 + int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(),
  117 + new ErpSaleOrderDO().setStatus(status));
  118 + if (updateCount == 0) {
  119 + throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL);
  120 + }
64 } 121 }
65 122
66 - private void updateSalesOrderItemsList(Long id, List<ErpSalesOrderItemDO> list) {  
67 - deleteSalesOrderItemsById(id);  
68 - list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新  
69 - createSalesOrderItemsList(id, list); 123 + private List<ErpSaleOrderItemDO> validateSaleOrderItems(List<ErpSaleOrderSaveReqVO.Item> list) {
  124 + // 1. 校验产品存在
  125 + List<ErpProductDO> productList = productService.validProductList(
  126 + convertSet(list, ErpSaleOrderSaveReqVO.Item::getProductId));
  127 + Map<Long, ErpProductDO> productMap = convertMap(productList, ErpProductDO::getId);
  128 + // 2. 转化为 ErpSaleOrderItemDO 列表
  129 + return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> item
  130 + .setProductUnitId(productMap.get(item.getProductId()).getUnitId())
  131 + .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount()))));
  132 + }
  133 +
  134 + private void updateSaleOrderItemList(Long id, List<ErpSaleOrderItemDO> newList) {
  135 + // 第一步,对比新老数据,获得添加、修改、删除的列表
  136 + List<ErpSaleOrderItemDO> oldList = saleOrderItemMapper.selectListByOrderId(id);
  137 + List<List<ErpSaleOrderItemDO>> diffList = diffList(oldList, newList, // id 不同,就认为是不同的记录
  138 + (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));
  139 +
  140 + // 第二步,批量添加、修改、删除
  141 + if (CollUtil.isNotEmpty(diffList.get(0))) {
  142 + diffList.get(0).forEach(o -> o.setOrderId(id));
  143 + saleOrderItemMapper.insertBatch(diffList.get(0));
  144 + }
  145 + if (CollUtil.isNotEmpty(diffList.get(1))) {
  146 + saleOrderItemMapper.updateBatch(diffList.get(1));
  147 + }
  148 + if (CollUtil.isNotEmpty(diffList.get(2))) {
  149 + saleOrderItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpSaleOrderItemDO::getId));
  150 + }
70 } 151 }
71 152
72 @Override 153 @Override
73 @Transactional(rollbackFor = Exception.class) 154 @Transactional(rollbackFor = Exception.class)
74 - public void deleteSaleOrder(Long id) {  
75 - // 校验存在  
76 - validateSaleOrderExists(id);  
77 - // 删除  
78 - saleOrderMapper.deleteById(id);  
79 -  
80 - // 删除子表  
81 - deleteSalesOrderItemsById(id); 155 + public void deleteSaleOrder(List<Long> ids) {
  156 + // 1. 校验不处于已审批
  157 + List<ErpSaleOrderDO> saleOrders = saleOrderMapper.selectBatchIds(ids);
  158 + if (CollUtil.isEmpty(saleOrders)) {
  159 + return;
  160 + }
  161 + saleOrders.forEach(saleOrder -> {
  162 + if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
  163 + throw exception(STOCK_MOVE_DELETE_FAIL_APPROVE, saleOrder.getNo());
  164 + }
  165 + });
  166 +
  167 + // 2. 遍历删除,并记录操作日志
  168 + saleOrders.forEach(saleOrder -> {
  169 + // 2.1 删除订单
  170 + saleOrderMapper.deleteById(saleOrder.getId());
  171 + // 2.2 删除订单项
  172 + saleOrderItemMapper.deleteByOrderId(saleOrder.getId());
  173 + });
82 } 174 }
83 175
84 - private void validateSaleOrderExists(Long id) {  
85 - if (saleOrderMapper.selectById(id) == null) {  
86 - throw exception(SALE_ORDER_NOT_EXISTS); 176 + private ErpSaleOrderDO validateSaleOrderExists(Long id) {
  177 + ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(id);
  178 + if (saleOrder == null) {
  179 + throw exception(STOCK_MOVE_NOT_EXISTS);
87 } 180 }
  181 + return saleOrder;
88 } 182 }
89 183
90 @Override 184 @Override
@@ -97,10 +191,19 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { @@ -97,10 +191,19 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService {
97 return saleOrderMapper.selectPage(pageReqVO); 191 return saleOrderMapper.selectPage(pageReqVO);
98 } 192 }
99 193
100 - // ==================== 子表(ERP 销售订单明细) ==================== 194 + // ==================== 订单项 ====================
101 195
102 - private void deleteSalesOrderItemsById(Long id) {  
103 - salesOrderItemMapper.deleteById(id); 196 + @Override
  197 + public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderId(Long moveId) {
  198 + return saleOrderItemMapper.selectListByOrderId(moveId);
  199 + }
  200 +
  201 + @Override
  202 + public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderIds(Collection<Long> moveIds) {
  203 + if (CollUtil.isEmpty(moveIds)) {
  204 + return Collections.emptyList();
  205 + }
  206 + return saleOrderItemMapper.selectListByOrderIds(moveIds);
104 } 207 }
105 208
106 } 209 }
107 \ No newline at end of file 210 \ No newline at end of file
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockService.java
@@ -31,6 +31,14 @@ public interface ErpStockService { @@ -31,6 +31,14 @@ public interface ErpStockService {
31 ErpStockDO getStock(Long productId, Long warehouseId); 31 ErpStockDO getStock(Long productId, Long warehouseId);
32 32
33 /** 33 /**
  34 + * 获得产品库存数量
  35 + *
  36 + * @param productId 产品编号
  37 + * @return 产品库存数量
  38 + */
  39 + BigDecimal getStockCount(Long productId);
  40 +
  41 + /**
34 * 获得产品库存分页 42 * 获得产品库存分页
35 * 43 *
36 * @param pageReqVO 分页查询 44 * @param pageReqVO 分页查询
yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java
@@ -50,6 +50,11 @@ public class ErpStockServiceImpl implements ErpStockService { @@ -50,6 +50,11 @@ public class ErpStockServiceImpl implements ErpStockService {
50 } 50 }
51 51
52 @Override 52 @Override
  53 + public BigDecimal getStockCount(Long productId) {
  54 + return stockMapper.selectSumByProductId(productId);
  55 + }
  56 +
  57 + @Override
53 public PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO) { 58 public PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO) {
54 return stockMapper.selectPage(pageReqVO); 59 return stockMapper.selectPage(pageReqVO);
55 } 60 }