|
@@ -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) {
|