完成采购订单功能。

This commit is contained in:
c
2026-03-07 17:30:41 +08:00
parent 8d2afd8d47
commit 263902969d
12 changed files with 660 additions and 68 deletions

View File

@@ -7,20 +7,34 @@ import com.niuan.erp.common.annotation.ModuleLog;
import com.niuan.erp.common.base.BaseDeleteBody;
import com.niuan.erp.common.base.BasePageReqParams;
import com.niuan.erp.common.base.BaseResult;
import com.niuan.erp.common.base.CommonValidateGroup.*;
import com.niuan.erp.common.base.CommonValidateGroup.Add;
import com.niuan.erp.common.base.OperationType;
import com.niuan.erp.module.common.entity.Document;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderAddDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderInboundDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderItemDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderQrCodeDto;
import com.niuan.erp.module.purchase.service.PurchaseOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "PurchaseOrder")
@ModuleLog("PurchaseOrder管理")
import java.util.List;
@Tag(name = "采购订单管理")
@ModuleLog("采购订单管理")
@RestController
@RequestMapping("/purchase/purchaseorder")
@RequiredArgsConstructor
@@ -28,49 +42,79 @@ public class PurchaseOrderController {
private final PurchaseOrderService purchaseOrderService;
@Operation(summary = "分页查询PurchaseOrder数据", operationId = "getPurchaseOrderPage")
@Operation(summary = "分页查询采购订单数据", operationId = "getPurchaseOrderPage")
@GetMapping("/getPurchaseOrderPage")
@PreAuthorize("hasAuthority('purchase_order:index')")
public BaseResult<IPage<PurchaseOrderDto>> getPurchaseOrderPage(@Validated BasePageReqParams pageParams,
@Validated(Get.class) PurchaseOrderDto searchParams) {
public BaseResult<IPage<PurchaseOrderDto>> getPurchaseOrderPage(
@Validated BasePageReqParams pageParams,
@Parameter(description = "单据编号/供应商名称") @RequestParam(required = false) String searchCode) {
var wrapper = new LambdaQueryWrapper<Document>();
if (StringUtils.hasText(searchCode)) {
wrapper.like(Document::getFormCode, searchCode).or().like(Document::getVendorName, searchCode);
}
return BaseResult.successWithData(purchaseOrderService.getPurchaseOrderPage(pageParams, wrapper));
}
@Operation(summary = "查询采购订单明细数据", operationId = "getPurchaseOrderItems")
@GetMapping("/getPurchaseOrderItems")
@PreAuthorize("hasAuthority('purchase_order:index')")
public BaseResult<List<PurchaseOrderItemDto>> getPurchaseOrderItems(
@Parameter(description = "订单ID", required = true)
@RequestParam("orderId") @NotNull(message = "{purchase.purchase_order.validate.order_id.not_null}") Long orderId) {
return BaseResult.successWithData(purchaseOrderService.getPurchaseOrderItems(orderId));
}
@ApiLog(type = OperationType.ADD, remark = "新增一条PurchaseOrder记录")
@Operation(summary = "新增PurchaseOrder", operationId = "addPurchaseOrder")
@ApiLog(type = OperationType.ADD, remark = "新增一条采购订单记录")
@Operation(summary = "新增采购订单", operationId = "addPurchaseOrder")
@PostMapping("/addPurchaseOrder")
@PreAuthorize("hasAuthority('purchase_order:add')")
public BaseResult<?> addPurchaseOrder(@Validated(Add.class) @RequestBody PurchaseOrderDto dto) {
public BaseResult<?> addPurchaseOrder(@Validated(Add.class) @RequestBody PurchaseOrderAddDto dto) {
purchaseOrderService.addPurchaseOrder(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.UPDATE, remark = "更新一条PurchaseOrder记录")
@Operation(summary = "更新PurchaseOrder", operationId = "updatePurchaseOrder")
@ApiLog(type = OperationType.UPDATE, remark = "更新一条采购订单记录")
@Operation(summary = "更新采购订单", operationId = "updatePurchaseOrder")
@PostMapping("/updatePurchaseOrder")
@PreAuthorize("hasAuthority('purchase_order:update')")
public BaseResult<?> updatePurchaseOrder(@Validated(Update.class) @RequestBody PurchaseOrderDto dto) {
public BaseResult<?> updatePurchaseOrder(@Validated @RequestBody PurchaseOrderAddDto dto) {
purchaseOrderService.updatePurchaseOrder(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "删除一条PurchaseOrder记录")
@Operation(summary = "删除PurchaseOrder", operationId = "deletePurchaseOrder")
@ApiLog(type = OperationType.DELETE, remark = "删除一条采购订单记录")
@Operation(summary = "删除采购订单", operationId = "deletePurchaseOrder")
@PostMapping("/deletePurchaseOrder")
@PreAuthorize("hasAuthority('purchase_order:delete')")
public BaseResult<?> deletePurchaseOrder(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) {
public BaseResult<?> deletePurchaseOrder(@RequestBody BaseDeleteBody req) {
purchaseOrderService.deletePurchaseOrder(req.id());
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "批量删除PurchaseOrder记录")
@Operation(summary = "批量删除PurchaseOrder", operationId = "deletePurchaseOrderBatch")
@ApiLog(type = OperationType.DELETE, remark = "批量删除采购订单记录")
@Operation(summary = "批量删除采购订单", operationId = "deletePurchaseOrderBatch")
@PostMapping("/deletePurchaseOrderBatch")
@PreAuthorize("hasAuthority('purchase_order:deleteBatch')")
public BaseResult<?> deletePurchaseOrderBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) {
public BaseResult<?> deletePurchaseOrderBatch(@RequestBody BaseDeleteBody req) {
purchaseOrderService.deleteBatch(req.ids());
return BaseResult.success();
}
@ApiLog(type = OperationType.UPDATE, remark = "采购订单入库")
@Operation(summary = "采购订单入库", operationId = "inbound")
@PostMapping("/inbound")
@PreAuthorize("hasAuthority('purchase_order:inbound')")
public BaseResult<?> inbound(@Validated @RequestBody PurchaseOrderInboundDto dto) {
purchaseOrderService.inbound(dto);
return BaseResult.success();
}
@Operation(summary = "获取采购订单二维码数据", operationId = "getQrCodeData")
@GetMapping("/getQrCodeData")
@PreAuthorize("hasAuthority('purchase_order:printQrCode')")
public BaseResult<List<PurchaseOrderQrCodeDto>> getQrCodeData(
@Parameter(description = "订单ID", required = true)
@RequestParam("orderId") @NotNull(message = "{purchase.purchase_order.validate.order_id.not_null}") Long orderId) {
return BaseResult.successWithData(purchaseOrderService.getQrCodeData(orderId));
}
}

View File

@@ -1,29 +1,54 @@
package com.niuan.erp.module.purchase.controller.dto;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.niuan.erp.module.common.enums.FormStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import java.util.List;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "采购订单DTO")
public record PurchaseOrderDto(
@Schema(description = "订单ID")
Long id,
Integer status,
LocalDateTime createDate,
Long createUserId,
String createUserName,
LocalDateTime updateDate,
Long updateUserId,
String updateUserName,
Integer storeNo,
String storeName,
Integer formType,
@Schema(description = "单据编号")
String formCode,
@Schema(description = "单据名称")
String formName,
Integer formStatus,
String formMark,
Integer reserve1,
String reserve2,
Integer vendorNo,
@Schema(description = "供应商名称")
String vendorName,
@Schema(description = "供应商ID")
Integer vendorNo,
@Schema(description = "订单总额")
Double totalValue,
Integer outStoreNo,
String outStoreName,
Integer groupId,
Integer customerId) {}
@Schema(description = "单据备注")
String formMark,
@Schema(description = "单据状态")
Integer formStatus,
@Schema(description = "创建时间")
LocalDateTime createDate,
@Schema(description = "创建人")
String createUserName,
@Schema(description = "仓库编号")
Integer storeNo,
@Schema(description = "仓库名称")
String storeName,
@Schema(description = "关联采购计划ID")
Long planId,
@Schema(description = "订单明细列表")
List<PurchaseOrderItemDto> items
) {}

View File

@@ -1,25 +1,55 @@
package com.niuan.erp.module.purchase.controller.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "采购订单明细DTO")
public record PurchaseOrderItemDto(
@Schema(description = "明细ID")
Long id,
Integer status,
LocalDateTime createDate,
Long createUserId,
String createUserName,
LocalDateTime updateDate,
Long updateUserId,
String updateUserName,
Integer documentNo,
@Schema(description = "关联订单ID")
Long documentNo,
@Schema(description = "订单编号")
String documentCode,
@Schema(description = "项目名称")
String projectName,
@Schema(description = "物料编号")
String partNumber,
@Schema(description = "物料型号")
String productSpecs,
@Schema(description = "采购数量")
Integer purchaseCount,
Double price,
Double totalPrice,
String purchaseMark,
Integer reserve1,
String reserve2,
@Schema(description = "已入库数量")
Integer receiptCount,
Long partId) {}
@Schema(description = "剩余待入库量")
Integer remainingCount,
@Schema(description = "单价")
Double price,
@Schema(description = "总价")
Double totalPrice,
@Schema(description = "备注")
String purchaseMark,
@Schema(description = "物料ID")
Long partId,
@Schema(description = "创建时间")
LocalDateTime createDate,
@Schema(description = "创建人")
String createUserName
) {}

View File

@@ -3,13 +3,18 @@ package com.niuan.erp.module.purchase.converter;
import com.niuan.erp.module.common.entity.Document;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import java.util.List;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface PurchaseOrderConverter {
Document toEntity(PurchaseOrderDto dto);
@Mapping(target = "planId", expression = "java(entity.getGroupId() != null ? entity.getGroupId().longValue() : null)")
PurchaseOrderDto toDto(Document entity);
List<PurchaseOrderDto> toDtoList(List<Document> entities);
}
}

View File

@@ -12,4 +12,4 @@ public interface PurchaseOrderItemConverter {
PurchaseOrderItem toEntity(PurchaseOrderItemDto dto);
PurchaseOrderItemDto toDto(PurchaseOrderItem entity);
List<PurchaseOrderItemDto> toDtoList(List<PurchaseOrderItem> entities);
}
}

View File

@@ -57,4 +57,7 @@ public class PurchasePlanItem implements Serializable {
@TableField("PartId")
private Long partId;
@TableField("Order_item_id")
private Long orderItemId;
}

View File

@@ -4,7 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.niuan.erp.common.base.BasePageReqParams;
import com.niuan.erp.module.common.entity.Document;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderAddDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderInboundDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderItemDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderQrCodeDto;
import java.util.List;
@@ -12,12 +16,18 @@ public interface PurchaseOrderService {
IPage<PurchaseOrderDto> getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper<Document> wrapper);
void addPurchaseOrder(PurchaseOrderDto dto);
void addPurchaseOrder(PurchaseOrderAddDto dto);
void updatePurchaseOrder(PurchaseOrderDto dto);
void updatePurchaseOrder(PurchaseOrderAddDto dto);
void deletePurchaseOrder(long id);
void deleteBatch(List<Long> ids);
List<PurchaseOrderItemDto> getPurchaseOrderItems(Long orderId);
void inbound(PurchaseOrderInboundDto dto);
List<PurchaseOrderQrCodeDto> getQrCodeData(Long orderId);
}

View File

@@ -5,61 +5,499 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.niuan.erp.common.base.BasePageReqParams;
import com.niuan.erp.common.exception.BusinessException;
import com.niuan.erp.common.utils.SecurityUtils;
import com.niuan.erp.module.common.entity.Document;
import com.niuan.erp.module.common.entity.DocumentMaterial;
import com.niuan.erp.module.common.enums.DocumentType;
import com.niuan.erp.module.common.enums.FormStatus;
import com.niuan.erp.module.common.mapper.DocumentMapper;
import com.niuan.erp.module.common.mapper.DocumentMaterialMapper;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderAddDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderInboundDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderInboundItemDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderItemAddDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderItemDto;
import com.niuan.erp.module.purchase.controller.dto.PurchaseOrderQrCodeDto;
import com.niuan.erp.module.purchase.converter.PurchaseOrderConverter;
import com.niuan.erp.module.purchase.converter.PurchaseOrderItemConverter;
import com.niuan.erp.module.purchase.entity.PurchaseOrderItem;
import com.niuan.erp.module.purchase.entity.PurchasePlan;
import com.niuan.erp.module.purchase.entity.PurchasePlanItem;
import com.niuan.erp.module.purchase.enums.PurchasePlanItemStatus;
import com.niuan.erp.module.purchase.enums.PurchasePlanStatus;
import com.niuan.erp.module.purchase.mapper.PurchaseOrderItemMapper;
import com.niuan.erp.module.purchase.mapper.PurchasePlanItemMapper;
import com.niuan.erp.module.purchase.mapper.PurchasePlanMapper;
import com.niuan.erp.module.purchase.service.PurchaseOrderService;
import com.niuan.erp.module.warehouse.entity.WarehouseItem;
import com.niuan.erp.module.warehouse.mapper.WarehouseItemMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@Transactional
@RequiredArgsConstructor
public class PurchaseOrderServiceImpl extends ServiceImpl<DocumentMapper, Document> implements PurchaseOrderService {
private final PurchaseOrderConverter purchaseOrderConverter;
private final PurchaseOrderItemConverter purchaseOrderItemConverter;
private final PurchaseOrderItemMapper purchaseOrderItemMapper;
private final PurchasePlanMapper purchasePlanMapper;
private final PurchasePlanItemMapper purchasePlanItemMapper;
private final WarehouseItemMapper warehouseItemMapper;
private final DocumentMaterialMapper documentMaterialMapper;
@Override
public IPage<PurchaseOrderDto> getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper<Document> wrapper) {
wrapper.eq(Document::getFormType, DocumentType.PURCHASE_ORDER);
wrapper.orderByDesc(Document::getCreateDate);
IPage<Document> result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper);
return result.convert(purchaseOrderConverter::toDto);
}
@Override
public void addPurchaseOrder(PurchaseOrderDto dto) {
Document entity = purchaseOrderConverter.toEntity(dto);
public void addPurchaseOrder(PurchaseOrderAddDto dto) {
Document entity = new Document();
entity.setFormCode(generateFormCode());
entity.setFormName("采购订单-" + dto.vendorName() + "-" + entity.getFormCode());
entity.setVendorName(dto.vendorName());
entity.setVendorNo(dto.vendorNo());
entity.setStoreNo(dto.storeNo());
entity.setStoreName(dto.storeName());
entity.setFormMark(dto.formMark());
entity.setFormType(DocumentType.PURCHASE_ORDER);
entity.setFormStatus(FormStatus.NO_APPROVE);
entity.setStatus(0);
entity.setCreateUserId(SecurityUtils.getUserId());
entity.setCreateUserName(SecurityUtils.getUserName());
entity.setCreateDate(LocalDateTime.now());
entity.setStatus(0);
entity.setGroupId(dto.planId() != null ? dto.planId().intValue() : null);
double totalValue = dto.items().stream()
.mapToDouble(item -> item.price().multiply(new BigDecimal(item.purchaseCount())).doubleValue())
.sum();
entity.setTotalValue(totalValue);
this.baseMapper.insert(entity);
List<PurchaseOrderItem> orderItems = new ArrayList<>();
for (PurchaseOrderItemAddDto itemDto : dto.items()) {
PurchaseOrderItem item = new PurchaseOrderItem();
item.setStatus(0);
item.setCreateDate(LocalDateTime.now());
item.setCreateUserId(SecurityUtils.getUserId());
item.setCreateUserName(SecurityUtils.getUserName());
item.setDocumentNo(entity.getId().intValue());
item.setDocumentCode(entity.getFormCode());
item.setPartNumber(itemDto.partNumber());
item.setPurchaseCount(itemDto.purchaseCount());
item.setPrice(itemDto.price().doubleValue());
item.setTotalPrice(itemDto.price().multiply(new BigDecimal(itemDto.purchaseCount())).doubleValue());
item.setReceiptCount(0);
item.setPartId(itemDto.partId());
item.setPurchaseMark(itemDto.purchaseMark());
orderItems.add(item);
}
if (!orderItems.isEmpty()) {
purchaseOrderItemMapper.insert(orderItems);
}
}
@Override
public void updatePurchaseOrder(PurchaseOrderDto dto) {
Document entity = purchaseOrderConverter.toEntity(dto);
public void updatePurchaseOrder(PurchaseOrderAddDto dto) {
if (dto.id() == null) {
throw new BusinessException("purchase.purchase_order.validate.id.not_null");
}
Document entity = this.baseMapper.selectById(dto.id());
if (entity == null) {
throw new BusinessException("purchase.purchase_order.exception.order_not_exists");
}
entity.setVendorName(dto.vendorName());
entity.setVendorNo(dto.vendorNo());
entity.setStoreNo(dto.storeNo());
entity.setStoreName(dto.storeName());
entity.setFormMark(dto.formMark());
entity.setUpdateUserId(SecurityUtils.getUserId());
entity.setUpdateUserName(SecurityUtils.getUserName());
entity.setUpdateDate(LocalDateTime.now());
double totalValue = dto.items().stream()
.mapToDouble(item -> item.price().multiply(new BigDecimal(item.purchaseCount())).doubleValue())
.sum();
entity.setTotalValue(totalValue);
this.baseMapper.updateById(entity);
purchaseOrderItemMapper.delete(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, dto.id())
);
List<PurchaseOrderItem> orderItems = new ArrayList<>();
for (PurchaseOrderItemAddDto itemDto : dto.items()) {
PurchaseOrderItem item = new PurchaseOrderItem();
item.setStatus(0);
item.setCreateDate(LocalDateTime.now());
item.setCreateUserId(SecurityUtils.getUserId());
item.setCreateUserName(SecurityUtils.getUserName());
item.setDocumentNo(entity.getId().intValue());
item.setDocumentCode(entity.getFormCode());
item.setPartNumber(itemDto.partNumber());
item.setPurchaseCount(itemDto.purchaseCount());
item.setPrice(itemDto.price().doubleValue());
item.setTotalPrice(itemDto.price().multiply(new BigDecimal(itemDto.purchaseCount())).doubleValue());
item.setReceiptCount(0);
item.setPartId(itemDto.partId());
item.setPurchaseMark(itemDto.purchaseMark());
orderItems.add(item);
}
if (!orderItems.isEmpty()) {
purchaseOrderItemMapper.insert(orderItems);
}
}
@Override
public void deletePurchaseOrder(long id) {
Document order = this.baseMapper.selectById(id);
if (order == null) {
throw new BusinessException("purchase.purchase_order.exception.order_not_exists");
}
List<PurchaseOrderItem> items = purchaseOrderItemMapper.selectList(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, id)
);
boolean hasInbound = items.stream()
.anyMatch(item -> item.getReceiptCount() != null && item.getReceiptCount() > 0);
if (hasInbound) {
throw new BusinessException("purchase.purchase_order.exception.order_has_inbound");
}
purchaseOrderItemMapper.delete(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, id)
);
this.baseMapper.deleteById(id);
rollbackPlanItemStatus(items, order.getGroupId());
}
@Override
public void deleteBatch(List<Long> ids) {
this.baseMapper.deleteByIds(ids);
for (Long id : ids) {
deletePurchaseOrder(id);
}
}
}
@Override
public List<PurchaseOrderItemDto> getPurchaseOrderItems(Long orderId) {
List<PurchaseOrderItem> items = purchaseOrderItemMapper.selectList(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, orderId)
);
if (items.isEmpty()) {
return new ArrayList<>();
}
List<String> partNumbers = items.stream()
.map(PurchaseOrderItem::getPartNumber)
.collect(Collectors.toList());
LambdaQueryWrapper<WarehouseItem> warehouseItemWrapper = new LambdaQueryWrapper<>();
warehouseItemWrapper.in(WarehouseItem::getPartNumber, partNumbers);
List<WarehouseItem> warehouseItems = warehouseItemMapper.selectList(warehouseItemWrapper);
Map<String, WarehouseItem> warehouseItemMap = warehouseItems.stream()
.collect(Collectors.toMap(WarehouseItem::getPartNumber, item -> item, (v1, v2) -> v1));
return items.stream()
.map(item -> {
PurchaseOrderItemDto dto = purchaseOrderItemConverter.toDto(item);
WarehouseItem warehouseItem = warehouseItemMap.get(item.getPartNumber());
String productSpecs = warehouseItem != null ? warehouseItem.getProductSpecs() : null;
Integer remainingCount = item.getPurchaseCount() - (item.getReceiptCount() != null ? item.getReceiptCount() : 0);
return new PurchaseOrderItemDto(
dto.id(),
dto.documentNo(),
dto.documentCode(),
dto.projectName(),
dto.partNumber(),
productSpecs,
dto.purchaseCount(),
dto.receiptCount(),
remainingCount,
dto.price(),
dto.totalPrice(),
dto.purchaseMark(),
dto.partId(),
dto.createDate(),
dto.createUserName()
);
})
.collect(Collectors.toList());
}
@Override
public void inbound(PurchaseOrderInboundDto dto) {
Document order = this.baseMapper.selectById(dto.orderId());
if (order == null) {
throw new BusinessException("purchase.purchase_order.exception.order_not_exists");
}
if (order.getFormStatus() == FormStatus.COMPLETE) {
throw new BusinessException("purchase.purchase_order.exception.order_already_completed");
}
List<PurchaseOrderItem> items = purchaseOrderItemMapper.selectList(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, dto.orderId())
);
Map<Long, PurchaseOrderItem> itemMap = items.stream()
.collect(Collectors.toMap(PurchaseOrderItem::getId, i -> i));
boolean allItemsCompleted = true;
for (PurchaseOrderInboundItemDto inboundItem : dto.items()) {
PurchaseOrderItem item = itemMap.get(inboundItem.itemId());
if (item == null) {
throw new BusinessException("purchase.purchase_order.exception.item_not_exists");
}
int currentReceiptCount = item.getReceiptCount() != null ? item.getReceiptCount() : 0;
int remaining = item.getPurchaseCount() - currentReceiptCount;
if (inboundItem.inboundCount() > remaining) {
throw new BusinessException("purchase.purchase_order.exception.inbound_count_exceed");
}
item.setReceiptCount(currentReceiptCount + inboundItem.inboundCount());
if (!item.getPurchaseCount().equals(item.getReceiptCount())) {
allItemsCompleted = false;
} else {
// 采购订单明细完成时,更新关联的采购计划明细状态
updatePlanItemStatusByOrderItemId(item.getId());
}
purchaseOrderItemMapper.updateById(item);
}
if (allItemsCompleted) {
order.setFormStatus(FormStatus.COMPLETE);
checkAndUpdatePlanStatus(order);
} else {
order.setFormStatus(FormStatus.RECEIPTING);
}
this.baseMapper.updateById(order);
createWarehouseReceipt(dto, order);
}
@Override
public List<PurchaseOrderQrCodeDto> getQrCodeData(Long orderId) {
List<PurchaseOrderItem> items = purchaseOrderItemMapper.selectList(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, orderId)
);
if (items.isEmpty()) {
return new ArrayList<>();
}
List<String> partNumbers = items.stream()
.map(PurchaseOrderItem::getPartNumber)
.collect(Collectors.toList());
LambdaQueryWrapper<WarehouseItem> warehouseItemWrapper = new LambdaQueryWrapper<>();
warehouseItemWrapper.in(WarehouseItem::getPartNumber, partNumbers);
List<WarehouseItem> warehouseItems = warehouseItemMapper.selectList(warehouseItemWrapper);
Map<String, WarehouseItem> warehouseItemMap = warehouseItems.stream()
.collect(Collectors.toMap(WarehouseItem::getPartNumber, item -> item, (v1, v2) -> v1));
return items.stream()
.map(item -> {
WarehouseItem warehouseItem = warehouseItemMap.get(item.getPartNumber());
String productSpecs = warehouseItem != null ? warehouseItem.getProductSpecs() : "";
String qrContent = String.format("物料编号:%s|数量:%d|单价:%.2f",
item.getPartNumber(),
item.getPurchaseCount(),
item.getPrice());
return new PurchaseOrderQrCodeDto(
item.getId(),
item.getPartNumber(),
productSpecs,
item.getPurchaseCount(),
item.getPrice(),
qrContent,
null
);
})
.collect(Collectors.toList());
}
private String generateFormCode() {
return "PO" + System.currentTimeMillis();
}
private void updatePlanItemStatusByOrderItemId(Long orderItemId) {
if (orderItemId == null) {
return;
}
LambdaQueryWrapper<PurchasePlanItem> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PurchasePlanItem::getOrderItemId, orderItemId);
PurchasePlanItem planItem = purchasePlanItemMapper.selectOne(wrapper);
if (planItem == null) {
return;
}
planItem.setCompleteStatus(PurchasePlanItemStatus.COMPLETED);
purchasePlanItemMapper.updateById(planItem);
checkAndUpdatePlanStatusByPlanId(planItem.getPlanId());
}
private void checkAndUpdatePlanStatusByPlanId(Long planId) {
if (planId == null) {
return;
}
LambdaQueryWrapper<PurchasePlanItem> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PurchasePlanItem::getPlanId, planId);
List<PurchasePlanItem> allPlanItems = purchasePlanItemMapper.selectList(wrapper);
boolean allItemsCompleted = allPlanItems.stream()
.allMatch(item -> item.getCompleteStatus() == PurchasePlanItemStatus.COMPLETED);
if (allItemsCompleted && !allPlanItems.isEmpty()) {
PurchasePlan plan = purchasePlanMapper.selectById(planId);
if (plan != null) {
plan.setPlanStatus(PurchasePlanStatus.COMPLETED);
purchasePlanMapper.updateById(plan);
}
}
}
private void checkAndUpdatePlanStatus(Document order) {
Integer planId = order.getGroupId();
if (planId == null) {
return;
}
List<Document> allOrders = this.baseMapper.selectList(
new LambdaQueryWrapper<Document>()
.eq(Document::getFormType, DocumentType.PURCHASE_ORDER)
.eq(Document::getGroupId, planId)
);
boolean allOrdersCompleted = allOrders.stream()
.allMatch(o -> o.getFormStatus() == FormStatus.COMPLETE);
if (allOrdersCompleted) {
PurchasePlan plan = purchasePlanMapper.selectById(planId.longValue());
if (plan != null) {
plan.setPlanStatus(PurchasePlanStatus.COMPLETED);
purchasePlanMapper.updateById(plan);
}
}
}
private void rollbackPlanItemStatus(List<PurchaseOrderItem> items, Integer planId) {
if (planId == null) {
return;
}
for (PurchaseOrderItem item : items) {
LambdaQueryWrapper<PurchasePlanItem> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PurchasePlanItem::getPlanId, planId.longValue())
.eq(PurchasePlanItem::getPartNumber, item.getPartNumber());
PurchasePlanItem planItem = purchasePlanItemMapper.selectOne(wrapper);
if (planItem != null) {
int currentCompleteCount = planItem.getCompleteCount() != null ? planItem.getCompleteCount() : 0;
planItem.setCompleteCount(Math.max(0, currentCompleteCount - item.getPurchaseCount()));
if (planItem.getCompleteCount() < planItem.getPurchaseCount()) {
planItem.setCompleteStatus(PurchasePlanItemStatus.NO_START);
}
purchasePlanItemMapper.updateById(planItem);
}
}
}
private void createWarehouseReceipt(PurchaseOrderInboundDto dto, Document order) {
Document receipt = new Document();
receipt.setFormCode("RK" + System.currentTimeMillis());
receipt.setFormName(dto.vendorName() + "-采购入库-由【" + dto.orderCode() + "】生成");
receipt.setFormType(DocumentType.WAREHOUSE_RECEIPT);
receipt.setFormStatus(FormStatus.NO_APPROVE);
receipt.setStatus(0);
receipt.setStoreNo(dto.storeNo());
receipt.setStoreName(dto.storeName());
receipt.setFormMark(dto.formMark());
receipt.setVendorName(dto.vendorName());
receipt.setVendorNo(order.getVendorNo());
receipt.setCustomerId(SecurityUtils.getCustomerId());
receipt.setCreateUserId(SecurityUtils.getUserId());
receipt.setCreateUserName(SecurityUtils.getUserName());
receipt.setCreateDate(LocalDateTime.now());
receipt.setGroupId(order.getId().intValue());
this.baseMapper.insert(receipt);
List<PurchaseOrderItem> orderItems = purchaseOrderItemMapper.selectList(
new LambdaQueryWrapper<PurchaseOrderItem>()
.eq(PurchaseOrderItem::getDocumentNo, order.getId())
);
Map<Long, PurchaseOrderItem> orderItemMap = orderItems.stream()
.collect(Collectors.toMap(PurchaseOrderItem::getId, i -> i));
List<DocumentMaterial> materials = new ArrayList<>();
for (PurchaseOrderInboundItemDto inboundItem : dto.items()) {
PurchaseOrderItem orderItem = orderItemMap.get(inboundItem.itemId());
if (orderItem != null) {
DocumentMaterial material = new DocumentMaterial();
material.setStatus(0);
material.setCreateDate(LocalDateTime.now());
material.setCreateUserId(SecurityUtils.getUserId());
material.setCreateUserName(SecurityUtils.getUserName());
material.setDocumentNo(receipt.getId().intValue());
material.setPartNumber(orderItem.getPartNumber());
material.setProductCount(inboundItem.inboundCount());
material.setPartId(orderItem.getPartId());
material.setStoreNo(dto.storeNo());
materials.add(material);
}
}
if (!materials.isEmpty()) {
documentMaterialMapper.insert(materials);
}
}
}

View File

@@ -8,6 +8,7 @@ import com.niuan.erp.common.base.BasePageReqParams;
import com.niuan.erp.common.utils.SecurityUtils;
import com.niuan.erp.module.common.entity.Document;
import com.niuan.erp.module.common.enums.DocumentType;
import com.niuan.erp.module.common.enums.FormStatus;
import com.niuan.erp.module.common.mapper.DocumentMapper;
import com.niuan.erp.module.purchase.controller.dto.PurchasePlanAddDto;
import com.niuan.erp.module.purchase.controller.dto.PurchasePlanDto;
@@ -369,7 +370,9 @@ public class PurchasePlanServiceImpl extends ServiceImpl<PurchasePlanMapper, Pur
purchaseOrder.setFormMark(formMark);
purchaseOrder.setTotalValue(totalValue.doubleValue());
purchaseOrder.setFormType(DocumentType.PURCHASE_ORDER);
purchaseOrder.setFormStatus(FormStatus.NO_APPROVE);
purchaseOrder.setStatus(0);
purchaseOrder.setGroupId(planId.intValue());
// 4. 保存采购订单
documentMapper.insert(purchaseOrder);
@@ -408,6 +411,10 @@ public class PurchasePlanServiceImpl extends ServiceImpl<PurchasePlanMapper, Pur
Map<String, PurchasePlanItem> planItemMap = allPlanItems.stream()
.collect(Collectors.toMap(PurchasePlanItem::getPartNumber, item -> item, (v1, v2) -> v1));
// 创建 partNumber 到 PurchaseOrderItem 的映射
Map<String, PurchaseOrderItem> orderItemMap = orderItems.stream()
.collect(Collectors.toMap(PurchaseOrderItem::getPartNumber, item -> item, (v1, v2) -> v1));
selectedItems.forEach(item -> {
// 更新采购计划明细
PurchasePlanItem planItem = planItemMap.get(item.partNumber());
@@ -417,6 +424,11 @@ public class PurchasePlanServiceImpl extends ServiceImpl<PurchasePlanMapper, Pur
if (planItem.getCompleteCount() >= planItem.getPurchaseCount()) {
planItem.setCompleteStatus(PurchasePlanItemStatus.ORDERED);
}
// 关联采购订单明细ID
PurchaseOrderItem orderItem = orderItemMap.get(item.partNumber());
if (orderItem != null) {
planItem.setOrderItemId(orderItem.getId());
}
purchasePlanItemMapper.updateById(planItem);
}

View File

@@ -100,7 +100,7 @@ public class WarehouseItemController {
@Operation(summary = "判断 partNumber 的物料是否存在仓库", operationId = "existsWarehouseItem")
@GetMapping("/existsWarehouseItem")
@PreAuthorize("hasAnyAuthority('bom:add')")
@PreAuthorize("hasAnyAuthority('bom:add', 'purchase_order:add', 'purchase_order:update')")
public BaseResult<Boolean> existsWarehouseItem(@Validated
@NotNull(message = "warehouse.warehouse_item.part_number.not_null")
@RequestParam("partNumber") String partNumber) {

View File

@@ -162,3 +162,26 @@ production.repair_record.validate.product_sn.not_null=SN号不能为空
production.repair_record.validate.mac.not_null=MAC地址不能为空
production.repair_record.validate.repair_status.not_null=维修状态不能为空
production.repair_record.exception.not_found=维修记录不存在
purchase.purchase_order.validate.id.not_null=订单ID不能为空
purchase.purchase_order.validate.vendor_name.not_blank=供应商名称不能为空
purchase.purchase_order.validate.vendor_name.not_null=供应商名称不能为空
purchase.purchase_order.validate.items.not_empty=订单明细不能为空
purchase.purchase_order.validate.items.not_null=订单明细不能为空
purchase.purchase_order.validate.items.size=订单明细至少需要一条
purchase.purchase_order.validate.part_number.not_blank=物料编号不能为空
purchase.purchase_order.validate.purchase_count.not_null=采购数量不能为空
purchase.purchase_order.validate.purchase_count.min=采购数量必须大于0
purchase.purchase_order.validate.price.not_null=单价不能为空
purchase.purchase_order.validate.price.min=单价不能小于0
purchase.purchase_order.validate.order_id.not_null=订单ID不能为空
purchase.purchase_order.validate.store_no.not_null=仓库不能为空
purchase.purchase_order.validate.inbound_items.not_empty=入库明细不能为空
purchase.purchase_order.validate.inbound_items.size=入库明细至少需要一条
purchase.purchase_order.validate.item_id.not_null=明细ID不能为空
purchase.purchase_order.validate.inbound_count.not_null=入库数量不能为空
purchase.purchase_order.validate.inbound_count.min=入库数量必须大于0
purchase.purchase_order.exception.order_not_exists=采购订单不存在
purchase.purchase_order.exception.order_already_completed=采购订单已完成
purchase.purchase_order.exception.item_not_exists=订单明细不存在
purchase.purchase_order.exception.inbound_count_exceed=入库数量超过剩余待入库量
purchase.purchase_order.exception.order_has_inbound=订单已入库,不能删除

View File

@@ -7,4 +7,6 @@ ALTER TABLE yy_sysrolechannelmapping ADD temp_id BIGINT AUTO_INCREMENT PRIMARY K
UPDATE yy_sysrolechannelmapping SET id = temp_id;
ALTER TABLE yy_sysrolechannelmapping DROP temp_id;
ALTER TABLE yy_sysrolechannelmapping ADD PRIMARY KEY (id);
ALTER TABLE yy_sysrolechannelmapping MODIFY id BIGINT NOT NULL AUTO_INCREMENT;
ALTER TABLE yy_sysrolechannelmapping MODIFY id BIGINT NOT NULL AUTO_INCREMENT;
ALTER TABLE purchaseplandetails ADD Order_item_id BIGINT DEFAULT NULL COMMENT "关联 purchasematerial 的 ID 订单明细 ID";