Explorar el Código

采购计划导出功能

fgd hace 1 año
padre
commit
d583d73176

+ 10 - 0
sd-business/src/main/java/com/sd/business/controller/purchase/PurchaseController.java

@@ -106,4 +106,14 @@ public class PurchaseController {
         List<PurchaseBomImportDataDto> list = ExcelUtil.read(file, PurchaseBomImportDataDto.class);
         purchaseService.purchaseImport(list);
     }
+
+    /**
+     * 采购计划导出
+     */
+    @PostMapping("/purchasePlanExportExcel")
+    public void purchasePlanExportExcel(MultipartFile file) {
+        List<PurchasePlanImportDataDto> list = ExcelUtil.read(file, PurchasePlanImportDataDto.class);
+        purchaseService.purchasePlanExportExcel(list);
+    }
+
 }

+ 27 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchasePlanImportDataDto.java

@@ -0,0 +1,27 @@
+package com.sd.business.entity.purchase.dto;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 采购计划导入入参实体
+ */
+@Getter
+@Setter
+public class PurchasePlanImportDataDto {
+
+    /**
+     * bom品号
+     */
+    @ExcelProperty("品号")
+    private String bomSpecCode;
+
+    /**
+     * 预测需求(60天)
+     */
+    @ExcelProperty("预测需求\n(60天)")
+    private BigDecimal forecastDemandQuantity;
+}

+ 154 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchasePlanExportVo.java

@@ -0,0 +1,154 @@
+package com.sd.business.entity.purchase.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.HeadFontStyle;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 采购计划列表导出返回值实体
+ *
+ * @author
+ * @since 2023-10-12
+ */
+@HeadFontStyle(fontHeightInPoints = 12)
+@Getter
+@Setter
+public class PurchasePlanExportVo {
+
+    /**
+     * bom品号
+     */
+    @ColumnWidth(13)
+    @ExcelProperty("品号")
+    private String bomSpecCode;
+
+    /**
+     * bom品名
+     */
+    @ColumnWidth(18)
+    @ExcelProperty("品名")
+    private String bomSpecName;
+
+    /**
+     * 规格说明
+     */
+    @ColumnWidth(15)
+    @ExcelProperty(value = "规格")
+    private String specification;
+
+    /**
+     * colour
+     */
+    @ColumnWidth(10)
+    @ExcelProperty(value = "颜色")
+    private String colour;
+
+    /**
+     * 单位
+     */
+    @ColumnWidth(8)
+    @ExcelProperty(value = "单位")
+    private String unit;
+
+    /**
+     * 备货模式
+     */
+    @ColumnWidth(13)
+    @ExcelProperty(value = "备货模式")
+    private String stockingMode;
+
+    /**
+     * 安全库存
+     */
+    @ColumnWidth(12)
+    @ExcelProperty(value = "安全库存")
+    private BigDecimal safetyStock;
+
+    /**
+     * 起订量
+     */
+    @ColumnWidth(10)
+    @ExcelProperty(value = "起订量")
+    private BigDecimal minimumQuantity;
+
+    /**
+     * 采购周期(天)
+     */
+    @ColumnWidth(13)
+    @ExcelProperty(value = "采购周期\n(天)")
+    private BigDecimal purchaseCycle;
+
+    /**
+     * 库存数量
+     */
+    @ColumnWidth(13)
+    @ExcelProperty(value = "库存数量")
+    private BigDecimal stockQuantity;
+
+    /**
+     * 在途数量
+     */
+    @ColumnWidth(13)
+    @ExcelProperty(value = "在途数量")
+    private BigDecimal inTransitQuantity;
+
+    /**
+     * 预测需求(60天)
+     */
+    @ColumnWidth(13)
+    @ExcelProperty("预测需求\n(60天)")
+    private BigDecimal forecastDemandQuantity;
+
+    /**
+     * 预测库销天数
+     */
+    @ColumnWidth(16)
+    @ExcelProperty("预测库销天数")
+    private BigDecimal forecastInventorySalesDays;
+
+    /**
+     * 实际日销(近30天)
+     */
+    @ColumnWidth(13)
+    @ExcelProperty("实际日销\n(近30天)")
+    private BigDecimal actualDailySales;
+
+    /**
+     * 实际库销天数
+     */
+    @ColumnWidth(16)
+    @ExcelProperty("实际库销天数")
+    private BigDecimal actualInventorySalesDays;
+
+    /**
+     * 短缺数量合计(销售预测)
+     */
+    @ColumnWidth(16)
+    @ExcelProperty("短缺数量合计\n(销售预测)")
+    private BigDecimal forecastShortageQuantity;
+
+    /**
+     * 短缺数量合计(实际销量)
+     */
+    @ColumnWidth(16)
+    @ExcelProperty("短缺数量合计\n(实际销量)")
+    private BigDecimal actualShortageQuantity;
+
+    /**
+     * 经济采购批量
+     */
+    @ColumnWidth(16)
+    @ExcelProperty("经济采购批量")
+    private BigDecimal economicOrderQuantity;
+
+    /**
+     * 采购批次
+     */
+    @ColumnWidth(13)
+    @ExcelProperty("采购批次")
+    private BigDecimal purchaseBatches;
+}

+ 1 - 1
sd-business/src/main/java/com/sd/business/service/board/impl/SalesBoardServiceImpl.java

@@ -299,7 +299,7 @@ public class SalesBoardServiceImpl implements SalesBoardService {
             BzSkuSalesBoardVo vo = new BzSkuSalesBoardVo();
             vo.setSkuSpecCode(item.getSkuSpecCode());
             vo.setSkuSpecName(item.getSkuSpecName());
-            vo.setDailySales(item.getSalesQuantity().divide(BigDecimal.valueOf(30), 2, RoundingMode.HALF_UP));
+            vo.setDailySales(item.getSalesQuantity().divide(BigDecimal.valueOf(30), 0, RoundingMode.HALF_UP));
             vo.setSafetyStock(vo.getDailySales().multiply(BigDecimal.valueOf(3)));
             vo.setInventoryQuantity(inventoryFinishedMap.getOrDefault(item.getSkuSpecId(), BigDecimal.ZERO));
             return vo;

+ 15 - 5
sd-business/src/main/java/com/sd/business/service/order/impl/OrderProofingRegistrationServiceImpl.java

@@ -1,6 +1,8 @@
 package com.sd.business.service.order.impl;
 
 import cn.hutool.core.util.StrUtil;
+import com.ruoyi.common.core.domain.BaseIdPo;
+import com.ruoyi.common.core.domain.BasePo;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.sd.business.entity.department.po.Department;
 import com.sd.business.entity.order.po.OrderInfo;
@@ -22,6 +24,7 @@ import com.sd.business.entity.order.dto.OrderProofingRegistrationDto;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 /**
  * <p>
@@ -66,11 +69,18 @@ public class OrderProofingRegistrationServiceImpl extends ServiceImpl<OrderProof
         BigDecimal proofingFee = dto.getUnitPrice().add(dto.getCustomProcessingFee());
         // 取出订单任意一个sku,将打样金额保存
         OrderSku orderSku = orderSkuService.getOne(q -> q.eq(OrderSku::getOrderId, orderInfo.getId()));
-        orderSku.setProofingFee(orderSku.getProofingFee().add(proofingFee));
-        orderInfo.setProofingFee(orderInfo.getProofingFee().add(proofingFee));
-        orderInfo.setTotalAmount(orderInfo.getTotalAmount().add(proofingFee));
-        orderService.updateById(orderInfo);
-        orderSkuService.updateById(orderSku);
+
+        orderService.update(q -> q
+                .eq(BaseIdPo::getId, orderInfo.getId())
+                .set(OrderInfo::getProofingFee, orderInfo.getProofingFee().add(proofingFee))
+                .set(OrderInfo::getTotalAmount, orderInfo.getTotalAmount().add(proofingFee))
+                .set(BasePo::getUpdateTime, new Date())
+                .set(BasePo::getUpdateUser, SecurityUtils.getUserId()));
+        orderSkuService.update(q -> q
+                .eq(BaseIdPo::getId, orderSku.getId())
+                .set(OrderSku::getProofingFee, orderSku.getProofingFee().add(proofingFee))
+                .set(BasePo::getUpdateTime, new Date())
+                .set(BasePo::getUpdateUser, SecurityUtils.getUserId()));
     }
 
     @Override

+ 1 - 0
sd-business/src/main/java/com/sd/business/service/order/impl/OrderSalesShipmentStatisticsServiceImpl.java

@@ -62,6 +62,7 @@ public class OrderSalesShipmentStatisticsServiceImpl implements OrderSalesShipme
         Date startDate = DateUtil.beginOfDay(DateUtil.offsetDay(date, -90));
         IWrapper<OrderSku> wrapper = IWrapper.getWrapper();
         wrapper.orderByDesc("os", OrderSku::getId);
+        wrapper.in("bs", BomSpec::getId, bomSpecIds);
         wrapper.between("oi", OrderInfo::getShippingTime, startDate, date);
         wrapper.groupBy("bs.id");
         List<OrderSalesShipmentStatisticsBo> list = orderSalesShipmentStatisticsMapper.getOrderSalesShipmentStatisticsList(wrapper);

+ 0 - 4
sd-business/src/main/java/com/sd/business/service/production/impl/ProductionExceedReceiveServiceImpl.java

@@ -34,7 +34,6 @@ import com.sd.business.entity.warehouse.constant.WarehouseConstant;
 import com.sd.business.mapper.production.ProductionExceedReceiveMapper;
 import com.sd.business.service.bom.BomSpecService;
 import com.sd.business.service.in.InOutStorageService;
-import com.sd.business.service.inventory.InventoryFinishedOrderService;
 import com.sd.business.service.inventory.InventoryFinishedService;
 import com.sd.business.service.inventory.InventoryService;
 import com.sd.business.service.order.OrderService;
@@ -91,9 +90,6 @@ public class ProductionExceedReceiveServiceImpl extends ServiceImpl<ProductionEx
     @Autowired
     private InventoryFinishedService inventoryFinishedService;
 
-    @Autowired
-    private InventoryFinishedOrderService inventoryFinishedOrderService;
-
     @Override
     public Page<ProductionExceedReceiveVo> getPage(ProductionExceedReceiveSelectDto dto) {
         IWrapper<ProductionExceedReceive> wrapper = getWrapper();

+ 6 - 0
sd-business/src/main/java/com/sd/business/service/purchase/PurchaseService.java

@@ -75,4 +75,10 @@ public interface PurchaseService extends BaseService<Purchase> {
      * @param list
      */
     void purchaseImport(List<PurchaseBomImportDataDto> list);
+
+    /**
+     * 导出采购计划
+     * @param list
+     */
+    void purchasePlanExportExcel(List<PurchasePlanImportDataDto> list);
 }

+ 123 - 0
sd-business/src/main/java/com/sd/business/service/purchase/impl/PurchaseServiceImpl.java

@@ -15,28 +15,38 @@ import com.ruoyi.common.core.domain.BaseIdPo;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.wrapper.IWrapper;
 import com.sd.business.entity.apply.po.ApplyBuyBom;
+import com.sd.business.entity.board.bo.OrderSalesShipmentStatisticsBo;
 import com.sd.business.entity.bom.bo.BomSpecBo;
 import com.sd.business.entity.bom.po.BomSpec;
+import com.sd.business.entity.department.constant.DepartmentConstant;
 import com.sd.business.entity.in.po.InOutStorageBom;
+import com.sd.business.entity.inventory.po.Inventory;
 import com.sd.business.entity.purchase.dto.*;
 import com.sd.business.entity.purchase.enums.PurchaseStatusEnum;
 import com.sd.business.entity.purchase.po.Purchase;
 import com.sd.business.entity.purchase.po.PurchaseBom;
 import com.sd.business.entity.purchase.vo.PurchaseBomVo;
+import com.sd.business.entity.purchase.vo.PurchaseInTransitBomVo;
+import com.sd.business.entity.purchase.vo.PurchasePlanExportVo;
 import com.sd.business.entity.purchase.vo.PurchaseVo;
 import com.sd.business.entity.supplier.po.Supplier;
+import com.sd.business.entity.warehouse.constant.WarehouseConstant;
 import com.sd.business.mapper.purchase.PurchaseMapper;
 import com.sd.business.service.apply.ApplyBuyBomService;
 import com.sd.business.service.bom.BomSpecService;
+import com.sd.business.service.inventory.InventoryService;
+import com.sd.business.service.order.OrderSalesShipmentStatisticsService;
 import com.sd.business.service.purchase.PurchaseBomService;
 import com.sd.business.service.purchase.PurchaseService;
 import com.sd.business.service.sku.SkuSpecService;
 import com.sd.business.service.supplier.SupplierService;
 import com.sd.business.util.CodeEnum;
 import com.sd.framework.util.Assert;
+import com.sd.framework.util.excel.util.ExcelUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.*;
@@ -69,6 +79,15 @@ public class PurchaseServiceImpl extends ServiceImpl<PurchaseMapper, Purchase> i
     @Autowired
     private SupplierService supplierService;
 
+    @Autowired
+    private InventoryService inventoryService;
+
+    @Autowired
+    private OrderSalesShipmentStatisticsService orderSalesShipmentStatisticsService;
+
+    @Autowired
+    private HttpServletResponse response;
+
     @Override
     public Page<PurchaseVo> getPage(PurchaseSelectDto dto) {
         IWrapper<Purchase> wrapper = getWrapper();
@@ -429,6 +448,110 @@ public class PurchaseServiceImpl extends ServiceImpl<PurchaseMapper, Purchase> i
         }
     }
 
+    @Override
+    public void purchasePlanExportExcel(List<PurchasePlanImportDataDto> list) {
+        // 验证采购计划导入数据
+        validatedPurchasePlanImportData(list);
+
+        List<String> bomSpecCodes = list.stream().map(PurchasePlanImportDataDto::getBomSpecCode).collect(Collectors.toList());
+        Map<String, BomSpec> bomSpecMap = bomSpecService.mapKEntity(BomSpec::getCode, q -> q.in(BomSpec::getCode, bomSpecCodes));
+        List<Long> bomSpecIds = bomSpecMap.values().stream().map(BaseIdPo::getId).collect(Collectors.toList());
+        // 查询库存数据
+        List<Inventory> inventorList = inventoryService.list(q -> q
+                .in(Inventory::getBomSpecId, bomSpecIds)
+                .eq(Inventory::getDepartmentId, DepartmentConstant.SD_SPORTS)
+                .eq(Inventory::getWarehouseId, WarehouseConstant.SEMI_FINISHED_PRODUCT));
+        Map<Long, BigDecimal> inventorMap = inventorList.stream().collect(Collectors.toMap(Inventory::getBomSpecId, item -> item.getQuantity().add(item.getLockQuantity())));
+
+        // 获取在途数量
+        List<PurchaseInTransitBomVo> purchaseBomInTransitList = purchaseBomService.getPurchaseBomInTransitSum(bomSpecIds);
+        Map<Long, BigDecimal> inTransitMap = purchaseBomInTransitList.stream().collect(Collectors.toMap(PurchaseInTransitBomVo::getBomSpecId, PurchaseInTransitBomVo::getInTransitQuantity));
+
+        // 获取销售量
+        Map<Long, OrderSalesShipmentStatisticsBo> shipmentStatisticsBoMap = orderSalesShipmentStatisticsService.getSalesShipmentStatisticsByDate(bomSpecIds);
+
+        List<PurchasePlanExportVo> exportDataList = new ArrayList<>();
+
+        for (PurchasePlanImportDataDto importDataDto : list) {
+            PurchasePlanExportVo vo = new PurchasePlanExportVo();
+            BomSpec bomSpec = bomSpecMap.get(importDataDto.getBomSpecCode());
+            if (bomSpec == null) {
+                throw new ServiceException("未知bom品号:" + importDataDto.getBomSpecCode());
+            }
+            Long bomSpecId = bomSpec.getId();
+            vo.setStockQuantity(inventorMap.getOrDefault(bomSpecId, BigDecimal.ZERO));
+            vo.setInTransitQuantity(inTransitMap.getOrDefault(bomSpecId, BigDecimal.ZERO));
+            vo.setActualDailySales(BigDecimal.ZERO);
+            OrderSalesShipmentStatisticsBo shipmentStatisticsBo = shipmentStatisticsBoMap.get(bomSpecId);
+            if (shipmentStatisticsBo != null) {
+                vo.setActualDailySales(shipmentStatisticsBo.getThirtyDaysSalesQuantity().divide(BigDecimal.valueOf(30), 0, RoundingMode.HALF_UP));
+            }
+
+            // 规格
+            String length = bomSpec.getLength() == null ? "0" : bomSpec.getLength().stripTrailingZeros().toPlainString();
+            String width = bomSpec.getWidth() == null ? "0" : bomSpec.getWidth().stripTrailingZeros().toPlainString();
+            String height = bomSpec.getHeight() == null ? "0" : bomSpec.getHeight().stripTrailingZeros().toPlainString();
+            // 赋值
+            vo.setBomSpecCode(bomSpec.getCode());
+            vo.setBomSpecName(bomSpec.getName());
+            vo.setSpecification(length + " * " + width + " * " + height);
+            vo.setColour(bomSpec.getColour());
+            vo.setUnit("PCS");
+            vo.setStockingMode("预测备货");
+            vo.setMinimumQuantity(BigDecimal.valueOf(300));
+            vo.setPurchaseCycle(BigDecimal.valueOf(40));
+            vo.setSafetyStock(vo.getActualDailySales().multiply(vo.getPurchaseCycle()));
+            vo.setForecastDemandQuantity(importDataDto.getForecastDemandQuantity());
+            // 预测库销天数 = (库存数量+在途数量)/(预测需求/60)
+            vo.setForecastInventorySalesDays(
+                    vo.getStockQuantity().add(vo.getInTransitQuantity())
+                            .divide(vo.getForecastDemandQuantity().divide(BigDecimal.valueOf(60), 4, RoundingMode.HALF_UP), 0, RoundingMode.DOWN));
+            // 实际库销天数 = (库存数量+在途数量)/实际日销
+            if (vo.getActualDailySales().compareTo(BigDecimal.ZERO) == 0) {
+                vo.setActualInventorySalesDays(BigDecimal.ZERO);
+            } else {
+                vo.setActualInventorySalesDays(
+                        vo.getStockQuantity().add(vo.getInTransitQuantity())
+                                .divide(vo.getActualDailySales(), 0, RoundingMode.DOWN));
+            }
+            // 短缺数量合计(销售预测) = 预测需求-库存数量-在途数量+安全库存
+            vo.setForecastShortageQuantity(
+                    vo.getForecastDemandQuantity()
+                            .subtract(vo.getStockQuantity())
+                            .subtract(vo.getInTransitQuantity())
+                            .add(vo.getSafetyStock()));
+            // 短缺数量合计(实际销量) = 实际日销*60-库存数量-在途数量+安全库存
+            BigDecimal actualShortageQuantity = vo.getActualDailySales()
+                    .multiply(BigDecimal.valueOf(60))
+                    .subtract(vo.getStockQuantity())
+                    .subtract(vo.getInTransitQuantity())
+                    .add(vo.getSafetyStock());
+            vo.setActualShortageQuantity(actualShortageQuantity.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : actualShortageQuantity);
+            // 经济采购批量 = 短缺数量合计(销售预测)/60*采购周期
+            vo.setEconomicOrderQuantity(
+                    vo.getForecastShortageQuantity()
+                            .divide(BigDecimal.valueOf(60), 4, RoundingMode.HALF_UP)
+                            .multiply(vo.getPurchaseCycle())
+                            .setScale(0, RoundingMode.HALF_UP));
+            // 采购批次 = 短缺数量合计(销售预测)/经济采购批量
+            vo.setPurchaseBatches(
+                    vo.getForecastShortageQuantity()
+                            .divide(vo.getEconomicOrderQuantity(), 0, RoundingMode.UP));
+
+            exportDataList.add(vo);
+        }
+
+        ExcelUtil.export(response,"胜德体育滚动采购计划", "胜德体育采购计划", exportDataList, PurchasePlanExportVo.class);
+    }
+
+    private void validatedPurchasePlanImportData(List<PurchasePlanImportDataDto> list) {
+        Assert.notEmpty(list, "采购合同明细不能为空");
+        for (PurchasePlanImportDataDto dto : list) {
+            Assert.notBlank(dto.getBomSpecCode(), "bom品号不能为空");
+            Assert.notNull(dto.getForecastDemandQuantity(), "预测需求格式不正确");
+        }
+    }
+
     private void validatedImportData(List<PurchaseBomImportDataDto> list) {
         Assert.notEmpty(list, "采购合同明细不能为空");
         for (PurchaseBomImportDataDto dto : list) {