From 263902969de91445e480a69abf193a6eac9cb337 Mon Sep 17 00:00:00 2001 From: c Date: Sat, 7 Mar 2026 17:30:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=87=87=E8=B4=AD=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseOrderController.java | 82 +++- .../controller/dto/PurchaseOrderDto.java | 65 ++- .../controller/dto/PurchaseOrderItemDto.java | 58 ++- .../converter/PurchaseOrderConverter.java | 7 +- .../converter/PurchaseOrderItemConverter.java | 2 +- .../purchase/entity/PurchasePlanItem.java | 3 + .../service/PurchaseOrderService.java | 14 +- .../impl/PurchaseOrderServiceImpl.java | 456 +++++++++++++++++- .../service/impl/PurchasePlanServiceImpl.java | 12 + .../controller/WarehouseItemController.java | 2 +- .../resources/i18n/messages_zh_CN.properties | 23 + src/main/resources/sql/dev/id.sql | 4 +- 12 files changed, 660 insertions(+), 68 deletions(-) diff --git a/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java b/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java index cd91974..27b71dd 100644 --- a/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java +++ b/src/main/java/com/niuan/erp/module/purchase/controller/PurchaseOrderController.java @@ -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> getPurchaseOrderPage(@Validated BasePageReqParams pageParams, - @Validated(Get.class) PurchaseOrderDto searchParams) { + public BaseResult> getPurchaseOrderPage( + @Validated BasePageReqParams pageParams, + @Parameter(description = "单据编号/供应商名称") @RequestParam(required = false) String searchCode) { var wrapper = new LambdaQueryWrapper(); + 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> 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> 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)); + } } diff --git a/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderDto.java b/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderDto.java index 78687f9..33eef42 100644 --- a/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderDto.java +++ b/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderDto.java @@ -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) {} \ No newline at end of file + + @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 items +) {} diff --git a/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderItemDto.java b/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderItemDto.java index 41f7625..66edebb 100644 --- a/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderItemDto.java +++ b/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchaseOrderItemDto.java @@ -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) {} \ No newline at end of file + + @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 +) {} diff --git a/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java b/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java index 010ec05..39e23ca 100644 --- a/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java +++ b/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderConverter.java @@ -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 toDtoList(List entities); -} \ No newline at end of file +} diff --git a/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderItemConverter.java b/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderItemConverter.java index b0f26ea..c59b3ab 100644 --- a/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderItemConverter.java +++ b/src/main/java/com/niuan/erp/module/purchase/converter/PurchaseOrderItemConverter.java @@ -12,4 +12,4 @@ public interface PurchaseOrderItemConverter { PurchaseOrderItem toEntity(PurchaseOrderItemDto dto); PurchaseOrderItemDto toDto(PurchaseOrderItem entity); List toDtoList(List entities); -} \ No newline at end of file +} diff --git a/src/main/java/com/niuan/erp/module/purchase/entity/PurchasePlanItem.java b/src/main/java/com/niuan/erp/module/purchase/entity/PurchasePlanItem.java index f435e8a..7e16fbd 100644 --- a/src/main/java/com/niuan/erp/module/purchase/entity/PurchasePlanItem.java +++ b/src/main/java/com/niuan/erp/module/purchase/entity/PurchasePlanItem.java @@ -57,4 +57,7 @@ public class PurchasePlanItem implements Serializable { @TableField("PartId") private Long partId; + + @TableField("Order_item_id") + private Long orderItemId; } diff --git a/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java b/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java index b15b105..a1dc215 100644 --- a/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java +++ b/src/main/java/com/niuan/erp/module/purchase/service/PurchaseOrderService.java @@ -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 getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addPurchaseOrder(PurchaseOrderDto dto); + void addPurchaseOrder(PurchaseOrderAddDto dto); - void updatePurchaseOrder(PurchaseOrderDto dto); + void updatePurchaseOrder(PurchaseOrderAddDto dto); void deletePurchaseOrder(long id); void deleteBatch(List ids); + List getPurchaseOrderItems(Long orderId); + + void inbound(PurchaseOrderInboundDto dto); + + List getQrCodeData(Long orderId); + } diff --git a/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java b/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java index ce48fb7..bf4cea4 100644 --- a/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchaseOrderServiceImpl.java @@ -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 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 getPurchaseOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.PURCHASE_ORDER); + wrapper.orderByDesc(Document::getCreateDate); IPage 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 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() + .eq(PurchaseOrderItem::getDocumentNo, dto.id()) + ); + + List 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 items = purchaseOrderItemMapper.selectList( + new LambdaQueryWrapper() + .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() + .eq(PurchaseOrderItem::getDocumentNo, id) + ); + this.baseMapper.deleteById(id); + + rollbackPlanItemStatus(items, order.getGroupId()); } @Override public void deleteBatch(List ids) { - this.baseMapper.deleteByIds(ids); + for (Long id : ids) { + deletePurchaseOrder(id); + } } -} \ No newline at end of file + @Override + public List getPurchaseOrderItems(Long orderId) { + List items = purchaseOrderItemMapper.selectList( + new LambdaQueryWrapper() + .eq(PurchaseOrderItem::getDocumentNo, orderId) + ); + + if (items.isEmpty()) { + return new ArrayList<>(); + } + + List partNumbers = items.stream() + .map(PurchaseOrderItem::getPartNumber) + .collect(Collectors.toList()); + + LambdaQueryWrapper warehouseItemWrapper = new LambdaQueryWrapper<>(); + warehouseItemWrapper.in(WarehouseItem::getPartNumber, partNumbers); + List warehouseItems = warehouseItemMapper.selectList(warehouseItemWrapper); + + Map 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 items = purchaseOrderItemMapper.selectList( + new LambdaQueryWrapper() + .eq(PurchaseOrderItem::getDocumentNo, dto.orderId()) + ); + + Map 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 getQrCodeData(Long orderId) { + List items = purchaseOrderItemMapper.selectList( + new LambdaQueryWrapper() + .eq(PurchaseOrderItem::getDocumentNo, orderId) + ); + + if (items.isEmpty()) { + return new ArrayList<>(); + } + + List partNumbers = items.stream() + .map(PurchaseOrderItem::getPartNumber) + .collect(Collectors.toList()); + + LambdaQueryWrapper warehouseItemWrapper = new LambdaQueryWrapper<>(); + warehouseItemWrapper.in(WarehouseItem::getPartNumber, partNumbers); + List warehouseItems = warehouseItemMapper.selectList(warehouseItemWrapper); + + Map 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 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 wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PurchasePlanItem::getPlanId, planId); + List 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 allOrders = this.baseMapper.selectList( + new LambdaQueryWrapper() + .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 items, Integer planId) { + if (planId == null) { + return; + } + + for (PurchaseOrderItem item : items) { + LambdaQueryWrapper 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 orderItems = purchaseOrderItemMapper.selectList( + new LambdaQueryWrapper() + .eq(PurchaseOrderItem::getDocumentNo, order.getId()) + ); + + Map orderItemMap = orderItems.stream() + .collect(Collectors.toMap(PurchaseOrderItem::getId, i -> i)); + + List 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); + } + } +} diff --git a/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchasePlanServiceImpl.java b/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchasePlanServiceImpl.java index 63f6d20..91ce680 100644 --- a/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchasePlanServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/purchase/service/impl/PurchasePlanServiceImpl.java @@ -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 planItemMap = allPlanItems.stream() .collect(Collectors.toMap(PurchasePlanItem::getPartNumber, item -> item, (v1, v2) -> v1)); + // 创建 partNumber 到 PurchaseOrderItem 的映射 + Map 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= planItem.getPurchaseCount()) { planItem.setCompleteStatus(PurchasePlanItemStatus.ORDERED); } + // 关联采购订单明细ID + PurchaseOrderItem orderItem = orderItemMap.get(item.partNumber()); + if (orderItem != null) { + planItem.setOrderItemId(orderItem.getId()); + } purchasePlanItemMapper.updateById(planItem); } diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseItemController.java b/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseItemController.java index 51c4aeb..d5dcb64 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseItemController.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseItemController.java @@ -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 existsWarehouseItem(@Validated @NotNull(message = "warehouse.warehouse_item.part_number.not_null") @RequestParam("partNumber") String partNumber) { diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 21ef143..a429499 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -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=订单已入库,不能删除 diff --git a/src/main/resources/sql/dev/id.sql b/src/main/resources/sql/dev/id.sql index da1c92d..d911a61 100644 --- a/src/main/resources/sql/dev/id.sql +++ b/src/main/resources/sql/dev/id.sql @@ -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; \ No newline at end of file +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"; \ No newline at end of file