diff --git a/src/main/java/com/niuan/erp/module/production/controller/FinishedProductShipmentController.java b/src/main/java/com/niuan/erp/module/production/controller/FinishedProductShipmentController.java index 7511808..831f38b 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/FinishedProductShipmentController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/FinishedProductShipmentController.java @@ -4,24 +4,23 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.niuan.erp.common.annotation.ApiLog; import com.niuan.erp.common.annotation.ModuleLog; +import com.niuan.erp.common.base.BaseApproveAndRejectDto; 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.OperationType; import com.niuan.erp.module.common.entity.Document; -import com.niuan.erp.module.production.controller.dto.DeviceShipmentDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentDto; +import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentItemDto; import com.niuan.erp.module.production.service.FinishedProductShipmentService; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -65,54 +64,35 @@ public class FinishedProductShipmentController { @ApiLog(type = OperationType.DELETE, remark = "删除一条FinishedProductShipment记录") @Operation(summary = "删除FinishedProductShipment", operationId = "deleteFinishedProductShipment") @PostMapping("/deleteFinishedProductShipment") - @PreAuthorize("hasAuthority('finished_product_shipment:delete')") + @PreAuthorize("hasAuthority('finished_product_shipment:remove')") public BaseResult deleteFinishedProductShipment(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { finishedProductShipmentService.deleteFinishedProductShipment(req.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "批量删除FinishedProductShipment记录") - @Operation(summary = "批量删除FinishedProductShipment", operationId = "deleteFinishedProductShipmentBatch") - @PostMapping("/deleteFinishedProductShipmentBatch") - @PreAuthorize("hasAuthority('finished_product_shipment:deleteBatch')") - public BaseResult deleteFinishedProductShipmentBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { - finishedProductShipmentService.deleteBatch(req.ids()); - return BaseResult.success(); - } - @ApiLog(type = OperationType.UPDATE, remark = "审核FinishedProductShipment") @Operation(summary = "审核FinishedProductShipment", operationId = "approveFinishedProductShipment") @PostMapping("/approveFinishedProductShipment") @PreAuthorize("hasAuthority('finished_product_shipment:approve')") - public BaseResult approveFinishedProductShipment( - @Parameter(description = "成品出库单ID") @RequestParam Long id) { - finishedProductShipmentService.approve(id); + public BaseResult approveFinishedProductShipment(@Validated @RequestBody BaseApproveAndRejectDto dto) { + finishedProductShipmentService.approve(dto.id()); return BaseResult.success(); } @ApiLog(type = OperationType.UPDATE, remark = "反审核FinishedProductShipment") - @Operation(summary = "反审核FinishedProductShipment", operationId = "unapproveFinishedProductShipment") - @PostMapping("/unapproveFinishedProductShipment") - @PreAuthorize("hasAuthority('finished_product_shipment:unapprove')") - public BaseResult unapproveFinishedProductShipment( - @Parameter(description = "成品出库单ID") @RequestParam Long id) { - finishedProductShipmentService.unapprove(id); + @Operation(summary = "反审核FinishedProductShipment", operationId = "rejectFinishedProductShipment") + @PostMapping("/rejectFinishedProductShipment") + @PreAuthorize("hasAuthority('finished_product_shipment:reject')") + public BaseResult rejectFinishedProductShipment(@Validated @RequestBody BaseApproveAndRejectDto dto) { + finishedProductShipmentService.unapprove(dto.id()); return BaseResult.success(); } @Operation(summary = "获取FinishedProductShipment明细", operationId = "getFinishedProductShipmentDetail") @GetMapping("/getFinishedProductShipmentDetail") - @PreAuthorize("hasAuthority('finishedproductshipment:index')") - public BaseResult> getFinishedProductShipmentDetail( - @Parameter(description = "成品出库单ID") @RequestParam Long id) { + @PreAuthorize("hasAuthority('finished_product_shipment:showItem')") + public BaseResult> getFinishedProductShipmentDetail( + @RequestParam Long id) { return BaseResult.successWithData(finishedProductShipmentService.getDetail(id)); } - - @Operation(summary = "导入FinishedProductShipment明细", operationId = "importFinishedProductShipmentItems") - @PostMapping("/importFinishedProductShipmentItems") - @PreAuthorize("hasAuthority('finished_product_shipment:import')") - public BaseResult> importFinishedProductShipmentItems( - @Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) { - return BaseResult.successWithData(finishedProductShipmentService.importItems(file)); - } } diff --git a/src/main/java/com/niuan/erp/module/production/controller/dto/FinishedProductShipmentDto.java b/src/main/java/com/niuan/erp/module/production/controller/dto/FinishedProductShipmentDto.java index 84b8f43..9ce2794 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/dto/FinishedProductShipmentDto.java +++ b/src/main/java/com/niuan/erp/module/production/controller/dto/FinishedProductShipmentDto.java @@ -1,29 +1,52 @@ package com.niuan.erp.module.production.controller.dto; +import com.niuan.erp.module.production.enums.OutStockType; +import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDateTime; +@Schema(description = "成品出库DTO") public record FinishedProductShipmentDto( + @Schema(description = "ID") Long id, + @Schema(description = "状态") Integer status, + @Schema(description = "创建时间") LocalDateTime createDate, + @Schema(description = "创建用户ID") Long createUserId, + @Schema(description = "创建用户名") String createUserName, + @Schema(description = "更新时间") LocalDateTime updateDate, + @Schema(description = "更新用户ID") Long updateUserId, + @Schema(description = "更新用户名") String updateUserName, + @Schema(description = "仓库编号") Integer storeNo, + @Schema(description = "仓库名称") String storeName, + @Schema(description = "单据类型") Integer formType, + @Schema(description = "单据编号") String formCode, + @Schema(description = "单据名称") String formName, + @Schema(description = "单据状态") Integer formStatus, + @Schema(description = "单据备注") String formMark, - Integer reserve1, - String reserve2, - Integer vendorNo, - String vendorName, + @Schema(description = "出库类型") + OutStockType outStockType, + @Schema(description = "总价值") Double totalValue, + @Schema(description = "出库仓库编号") Integer outStoreNo, + @Schema(description = "出库仓库名称") String outStoreName, + @Schema(description = "分组ID") Integer groupId, - Integer customerId) {} \ No newline at end of file + @Schema(description = "客户ID") + Integer customerId, + @Schema(description = "出库明细列表") + java.util.List shipmentItems) {} diff --git a/src/main/java/com/niuan/erp/module/production/converter/FinishedProductShipmentConverter.java b/src/main/java/com/niuan/erp/module/production/converter/FinishedProductShipmentConverter.java index 68d3729..964257b 100644 --- a/src/main/java/com/niuan/erp/module/production/converter/FinishedProductShipmentConverter.java +++ b/src/main/java/com/niuan/erp/module/production/converter/FinishedProductShipmentConverter.java @@ -1,12 +1,14 @@ package com.niuan.erp.module.production.converter; import com.niuan.erp.module.common.entity.Document; -import com.niuan.erp.module.production.controller.dto.DeviceShipmentDto; +import com.niuan.erp.module.common.entity.DocumentMaterial; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentDto; -import com.niuan.erp.module.sale.entity.Device; -import com.niuan.erp.module.sale.controller.dto.DeviceAddDto; +import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentItemDto; +import com.niuan.erp.module.production.enums.OutStockType; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; import java.util.List; @@ -14,13 +16,67 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface FinishedProductShipmentConverter { Document toEntity(FinishedProductShipmentDto dto); + + @Mapping(target = "reserve1", source = "outStockType", qualifiedByName = "outStockTypeToInteger") + @Mapping(target = "formType", ignore = true) Document toEntity(FinishedProductShipmentAddDto dto); + + @Mapping(target = "outStockType", source = "reserve1", qualifiedByName = "integerToOutStockType") + @Mapping(target = "formType", source = "formType", qualifiedByName = "documentTypeToInteger") + @Mapping(target = "formStatus", source = "formStatus", qualifiedByName = "formStatusToInteger") FinishedProductShipmentDto toDto(Document entity); + List toDtoList(List entities); - Device toEntity(DeviceAddDto dto); - List toEntityList(List dtoList); + @Mapping(target = "partNumber", source = "partNumber") + @Mapping(target = "productCount", source = "productCount") + @Mapping(target = "productMark", source = "productMark") + FinishedProductShipmentItemDto toItemDto(DocumentMaterial entity); - DeviceShipmentDto toDeviceShipmentDto(Device entity); - List toDeviceShipmentDtoList(List entities); -} \ No newline at end of file + List toItemDtoList(List entities); + + @Mapping(target = "partNumber", source = "partNumber") + @Mapping(target = "productCount", source = "productCount") + @Mapping(target = "productMark", source = "productMark") + @Mapping(target = "documentNo", ignore = true) + DocumentMaterial toMaterialEntity(FinishedProductShipmentItemDto dto); + + List toMaterialEntityList(List dtoList); + + @Named("outStockTypeToInteger") + default Integer outStockTypeToInteger(OutStockType outStockType) { + if (outStockType == null) { + return null; + } + return outStockType.getCode(); + } + + @Named("integerToOutStockType") + default OutStockType integerToOutStockType(Integer code) { + if (code == null) { + return null; + } + for (OutStockType type : OutStockType.values()) { + if (type.getCode() == code) { + return type; + } + } + return null; + } + + @Named("documentTypeToInteger") + default Integer documentTypeToInteger(com.niuan.erp.module.common.enums.DocumentType documentType) { + if (documentType == null) { + return null; + } + return documentType.getValue(); + } + + @Named("formStatusToInteger") + default Integer formStatusToInteger(com.niuan.erp.module.common.enums.FormStatus formStatus) { + if (formStatus == null) { + return null; + } + return formStatus.getValue(); + } +} diff --git a/src/main/java/com/niuan/erp/module/production/service/FinishedProductShipmentService.java b/src/main/java/com/niuan/erp/module/production/service/FinishedProductShipmentService.java index 4c52982..f02af03 100644 --- a/src/main/java/com/niuan/erp/module/production/service/FinishedProductShipmentService.java +++ b/src/main/java/com/niuan/erp/module/production/service/FinishedProductShipmentService.java @@ -4,9 +4,9 @@ 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.production.controller.dto.DeviceShipmentDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentDto; +import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentItemDto; import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -21,13 +21,9 @@ public interface FinishedProductShipmentService { void deleteFinishedProductShipment(long id); - void deleteBatch(List ids); - void approve(long id); void unapprove(long id); - List getDetail(long id); - - List importItems(MultipartFile file); + List getDetail(long id); } diff --git a/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductShipmentServiceImpl.java b/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductShipmentServiceImpl.java index 32abe96..5e3afec 100644 --- a/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductShipmentServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductShipmentServiceImpl.java @@ -9,24 +9,26 @@ 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.production.controller.dto.DeviceShipmentDto; +import com.niuan.erp.module.common.mapper.DocumentMaterialMapper; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentDto; +import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentItemDto; import com.niuan.erp.module.production.converter.FinishedProductShipmentConverter; +import com.niuan.erp.module.production.enums.OutStockType; import com.niuan.erp.module.production.service.FinishedProductShipmentService; -import com.niuan.erp.module.sale.entity.Device; -import com.niuan.erp.module.sale.mapper.DeviceMapper; +import com.niuan.erp.module.warehouse.entity.Stock; +import com.niuan.erp.module.warehouse.entity.WarehouseItem; +import com.niuan.erp.module.warehouse.mapper.StockMapper; +import com.niuan.erp.module.warehouse.mapper.WarehouseItemMapper; import lombok.RequiredArgsConstructor; -import org.apache.poi.ss.usermodel.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -40,60 +42,86 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl getFinishedProductShipmentPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { - wrapper.eq(Document::getFormType, DocumentType.FINISHED_PRODUCT_SHIPMENT); + wrapper.eq(Document::getFormType, DocumentType.FINISHED_PRODUCT_SHIPMENT) + .or() + .eq(Document::getFormType, DocumentType.WAREHOUSE_ISSUE); + // 按创建时间倒序排列 + wrapper.orderByDesc(Document::getCreateDate); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(finishedProductShipmentConverter::toDto); } @Override public void addFinishedProductShipment(FinishedProductShipmentAddDto dto) { - List deviceItems = dto.deviceItems(); - - if (deviceItems == null || deviceItems.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.no_device_items"); + // 基础校验 + if (dto == null) { + throw new BusinessException("production.finished_product_shipment.validate.dto.not_null"); + } + if (dto.outStockType() == null) { + throw new BusinessException("production.finished_product_shipment.validate.out_stock_type.not_null"); + } + if (!StringUtils.hasText(dto.formCode())) { + throw new BusinessException("production.finished_product_shipment.validate.form_code.not_null"); + } + if (!StringUtils.hasText(dto.formName())) { + throw new BusinessException("production.finished_product_shipment.validate.form_name.not_null"); + } + if (dto.storeNo() == null) { + throw new BusinessException("production.finished_product_shipment.validate.store_no.not_null"); } - List snList = deviceItems.stream() - .map(com.niuan.erp.module.sale.controller.dto.DeviceAddDto::productSn) + List shipmentItems = dto.shipmentItems(); + + if (shipmentItems == null || shipmentItems.isEmpty()) { + throw new BusinessException("production.finished_product_shipment.exception.no_shipment_items"); + } + + // 校验明细项 + for (FinishedProductShipmentItemDto item : shipmentItems) { + if (!StringUtils.hasText(item.partNumber())) { + throw new BusinessException("production.finished_product_shipment.validate.part_number.not_blank"); + } + if (item.productCount() == null || item.productCount() <= 0) { + throw new BusinessException("production.finished_product_shipment.validate.product_count.positive"); + } + } + + List partNumberList = shipmentItems.stream() + .map(FinishedProductShipmentItemDto::partNumber) .filter(StringUtils::hasText) .toList(); - if (snList.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.no_device_items"); + if (partNumberList.isEmpty()) { + throw new BusinessException("production.finished_product_shipment.exception.no_shipment_items"); } - Set uniqueSnSet = new HashSet<>(snList); - if (uniqueSnSet.size() != snList.size()) { - throw new BusinessException("production.finished_product_shipment.exception.duplicate_sn_in_request"); + Set uniquePartNumberSet = new HashSet<>(partNumberList); + if (uniquePartNumberSet.size() != partNumberList.size()) { + throw new BusinessException("production.finished_product_shipment.exception.duplicate_part_number_in_request"); } - LambdaQueryWrapper snWrapper = new LambdaQueryWrapper<>(); - snWrapper.in(Device::getProductSn, snList); - List existingSnList = deviceMapper.selectList(snWrapper); - Set existingSnSet = existingSnList.stream() - .map(Device::getProductSn) + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.in(WarehouseItem::getPartNumber, partNumberList); + List existingItems = warehouseItemMapper.selectList(itemWrapper); + Set existingPartNumberSet = existingItems.stream() + .map(WarehouseItem::getPartNumber) .collect(Collectors.toSet()); - List notFoundSn = snList.stream() - .filter(sn -> !existingSnSet.contains(sn)) - .map(sn -> "SN号:" + sn) + List notFoundPartNumbers = partNumberList.stream() + .filter(pn -> !existingPartNumberSet.contains(pn)) + .map(pn -> "物料编号:" + pn) .toList(); - if (!notFoundSn.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.sn_not_found"); - } - - List alreadyShippedSn = existingSnList.stream() - .filter(item -> item.getOutStatus() != null && item.getOutStatus()) - .map(item -> "SN号:" + item.getProductSn()) - .toList(); - - if (!alreadyShippedSn.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.sn_already_shipped"); + if (!notFoundPartNumbers.isEmpty()) { + throw new BusinessException("production.finished_product_shipment.exception.part_number_not_found"); } Document entity = finishedProductShipmentConverter.toEntity(dto); @@ -101,25 +129,31 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl deviceEntities = finishedProductShipmentConverter.toEntityList(deviceItems); + List materialEntities = finishedProductShipmentConverter.toMaterialEntityList(shipmentItems); - for (Device device : deviceEntities) { - device.setDocumentNo(entity.getId().intValue()); - device.setCreateDate(LocalDateTime.now()); - device.setCreateUserId(SecurityUtils.getUserId()); - device.setCreateUserName(SecurityUtils.getUserName()); - device.setStatus(0); - device.setCustomerId(SecurityUtils.getCustomerId()); - device.setOutStatus(false); + for (DocumentMaterial material : materialEntities) { + material.setDocumentNo(entity.getId().intValue()); + material.setCreateDate(LocalDateTime.now()); + material.setCreateUserId(SecurityUtils.getUserId()); + material.setCreateUserName(SecurityUtils.getUserName()); + material.setStatus(0); + material.setStoreNo(dto.storeNo()); } - deviceMapper.insert(deviceEntities); + documentMaterialMapper.insert(materialEntities); } @Override @@ -151,30 +185,9 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl itemWrapper = new LambdaQueryWrapper<>(); - itemWrapper.eq(Device::getDocumentNo, id); - deviceMapper.delete(itemWrapper); - } - - @Override - public void deleteBatch(List ids) { - if (ids == null || ids.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.ids_empty"); - } - - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.in(Document::getId, ids); - wrapper.eq(Document::getFormStatus, FormStatus.APPROVE); - Long approvedCount = this.baseMapper.selectCount(wrapper); - if (approvedCount > 0) { - throw new BusinessException("production.finished_product_shipment.exception.cannot_delete_approved_batch"); - } - - this.baseMapper.deleteBatchIds(ids); - - LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); - itemWrapper.in(Device::getDocumentNo, ids); - deviceMapper.delete(itemWrapper); + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(DocumentMaterial::getDocumentNo, id); + documentMaterialMapper.delete(itemWrapper); } @Override @@ -187,20 +200,40 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl itemWrapper = new LambdaQueryWrapper<>(); - itemWrapper.eq(Device::getDocumentNo, id); - List items = deviceMapper.selectList(itemWrapper); + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(DocumentMaterial::getDocumentNo, id); + List items = documentMaterialMapper.selectList(itemWrapper); if (items == null || items.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.no_device_items"); + throw new BusinessException("production.finished_product_shipment.exception.no_shipment_items"); } - List alreadyShippedSn = items.stream() - .filter(item -> item.getOutStatus() != null && item.getOutStatus()) - .map(item -> "SN号:" + item.getProductSn()) + Integer storeNo = entity.getStoreNo(); + List partNumbers = items.stream() + .map(DocumentMaterial::getPartNumber) + .distinct() .toList(); - if (!alreadyShippedSn.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.sn_already_shipped"); + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, storeNo) + .in(Stock::getPartNumber, partNumbers); + List stockList = stockMapper.selectList(stockWrapper); + Map stockMap = stockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (DocumentMaterial item : items) { + Stock stock = stockMap.get(item.getPartNumber()); + if (stock == null || stock.getProductCount() < item.getProductCount()) { + throw new BusinessException("production.finished_product_shipment.exception.insufficient_stock"); + } + } + + for (DocumentMaterial item : items) { + Stock stock = stockMap.get(item.getPartNumber()); + stock.setProductCount(stock.getProductCount() - item.getProductCount()); + stock.setUpdateDate(LocalDateTime.now()); + stock.setUpdateUserId(SecurityUtils.getUserId()); + stock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(stock); } entity.setFormStatus(FormStatus.APPROVE); @@ -208,15 +241,6 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl updateWrapper = new LambdaUpdateWrapper<>(); - updateWrapper.eq(Device::getDocumentNo, id) - .set(Device::getOutStatus, true) - .set(Device::getOutProductDate, LocalDateTime.now()) - .set(Device::getUpdateDate, LocalDateTime.now()) - .set(Device::getUpdateUserId, SecurityUtils.getUserId()) - .set(Device::getUpdateUserName, SecurityUtils.getUserName()); - deviceMapper.update(null, updateWrapper); } @Override @@ -229,84 +253,84 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl itemWrapper = new LambdaQueryWrapper<>(); - itemWrapper.eq(Device::getDocumentNo, id); - List items = deviceMapper.selectList(itemWrapper); + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(DocumentMaterial::getDocumentNo, id); + List items = documentMaterialMapper.selectList(itemWrapper); + + Integer storeNo = entity.getStoreNo(); + List partNumbers = items.stream() + .map(DocumentMaterial::getPartNumber) + .distinct() + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, storeNo) + .in(Stock::getPartNumber, partNumbers); + List stockList = stockMapper.selectList(stockWrapper); + Map stockMap = stockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (DocumentMaterial item : items) { + Stock stock = stockMap.get(item.getPartNumber()); + if (stock != null) { + stock.setProductCount(stock.getProductCount() + item.getProductCount()); + stock.setUpdateDate(LocalDateTime.now()); + stock.setUpdateUserId(SecurityUtils.getUserId()); + stock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(stock); + } else { + Stock newStock = new Stock(); + newStock.setPartNumber(item.getPartNumber()); + newStock.setProductCount(item.getProductCount()); + newStock.setStoreNo(storeNo); + newStock.setCreateDate(LocalDateTime.now()); + newStock.setCreateUserId(SecurityUtils.getUserId()); + newStock.setCreateUserName(SecurityUtils.getUserName()); + newStock.setStatus(0); + stockMapper.insert(newStock); + } + } entity.setFormStatus(FormStatus.NO_APPROVE); entity.setUpdateDate(LocalDateTime.now()); entity.setUpdateUserId(SecurityUtils.getUserId()); entity.setUpdateUserName(SecurityUtils.getUserName()); this.baseMapper.updateById(entity); - - LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); - updateWrapper.eq(Device::getDocumentNo, id) - .set(Device::getOutStatus, false) - .set(Device::getOutProductDate, (LocalDateTime) null) - .set(Device::getUpdateDate, LocalDateTime.now()) - .set(Device::getUpdateUserId, SecurityUtils.getUserId()) - .set(Device::getUpdateUserName, SecurityUtils.getUserName()); - deviceMapper.update(null, updateWrapper); } @Override - public List getDetail(long id) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(Device::getDocumentNo, id); - List items = deviceMapper.selectList(wrapper); - return finishedProductShipmentConverter.toDeviceShipmentDtoList(items); - } + public List getDetail(long id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DocumentMaterial::getDocumentNo, id); + List items = documentMaterialMapper.selectList(wrapper); - @Override - public List importItems(MultipartFile file) { - if (file == null || file.isEmpty()) { - throw new BusinessException("production.finished_product_shipment.exception.file_empty"); + List partNumbers = items.stream() + .map(DocumentMaterial::getPartNumber) + .filter(StringUtils::hasText) + .distinct() + .toList(); + + if (!partNumbers.isEmpty()) { + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.in(WarehouseItem::getPartNumber, partNumbers); + List warehouseItems = warehouseItemMapper.selectList(itemWrapper); + Map specsMap = warehouseItems.stream() + .collect(Collectors.toMap(WarehouseItem::getPartNumber, WarehouseItem::getProductSpecs, (a, b) -> a)); + + return items.stream() + .map(item -> { + FinishedProductShipmentItemDto dto = finishedProductShipmentConverter.toItemDto(item); + return new FinishedProductShipmentItemDto( + dto.id(), + dto.partNumber(), + specsMap.getOrDefault(dto.partNumber(), ""), + dto.productCount(), + dto.productMark() + ); + }) + .toList(); } - try (Workbook workbook = WorkbookFactory.create(file.getInputStream())) { - Sheet sheet = workbook.getSheetAt(0); - List items = new ArrayList<>(); - - for (int i = 1; i <= sheet.getLastRowNum(); i++) { - Row row = sheet.getRow(i); - if (row == null) continue; - - String productType = getCellValueAsString(row.getCell(0)); - String productSn = getCellValueAsString(row.getCell(1)); - String mac = getCellValueAsString(row.getCell(2)); - String serialNum = getCellValueAsString(row.getCell(3)); - String softVersion = getCellValueAsString(row.getCell(4)); - String alVersion = getCellValueAsString(row.getCell(5)); - String alNum = getCellValueAsString(row.getCell(6)); - String alTxt = getCellValueAsString(row.getCell(7)); - String manufacturingDate = getCellValueAsString(row.getCell(8)); - String mark = getCellValueAsString(row.getCell(9)); - - if (!StringUtils.hasText(productSn)) continue; - - DeviceShipmentDto item = new DeviceShipmentDto( - null, null, null, null, null, null, null, null, - null, productType, productSn, mac, serialNum, softVersion, - alVersion, alNum, null, null, mark, null, null, null, null, null - ); - items.add(item); - } - - return items; - } catch (IOException e) { - throw new BusinessException("production.finished_product_shipment.exception.import_failed"); - } + return finishedProductShipmentConverter.toItemDtoList(items); } - - private String getCellValueAsString(Cell cell) { - if (cell == null) { - return ""; - } - return switch (cell.getCellType()) { - case STRING -> cell.getStringCellValue().trim(); - case NUMERIC -> String.valueOf((long) cell.getNumericCellValue()); - case BOOLEAN -> String.valueOf(cell.getBooleanCellValue()); - default -> ""; - }; - } -} \ No newline at end of file +} diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 49f42ed..fb25af8 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -85,6 +85,28 @@ production.production_plan.validate.demand_count.min=需求数量不能小于 1 production.production_plan.exception.must_no_complete=选中的行其中有不是未完成的状态 production.production_plan.exception.more_than_one_warehouse=选中的行不能有多个仓库 +production.finished_product_shipment.validate.form_code.not_null=单据编号不能为空 +production.finished_product_shipment.validate.form_name.not_null=单据名称不能为空 +production.finished_product_shipment.validate.store_no.not_null=仓库不能为空 +production.finished_product_shipment.validate.store_name.not_null=仓库名称不能为空 +production.finished_product_shipment.validate.out_stock_type.not_null=出库类型不能为空 +production.finished_product_shipment.validate.shipment_items.not_null=出库明细不能为空 +production.finished_product_shipment.validate.part_number.not_blank=物料编号不能为空 +production.finished_product_shipment.validate.product_count.not_null=数量不能为空 +production.finished_product_shipment.exception.not_found=成品出库单不存在 +production.finished_product_shipment.exception.cannot_update_approved=已审核的成品出库单不能修改 +production.finished_product_shipment.exception.cannot_delete_approved=已审核的成品出库单不能删除 +production.finished_product_shipment.exception.cannot_delete_approved_batch=选中的数据中存在已审核的成品出库单,不能删除 +production.finished_product_shipment.exception.ids_empty=请选择要删除的成品出库单 +production.finished_product_shipment.exception.already_approved=成品出库单已经审核 +production.finished_product_shipment.exception.not_approved=成品出库单不是已审核状态,不能反审 +production.finished_product_shipment.exception.no_shipment_items=成品出库单没有明细 +production.finished_product_shipment.exception.duplicate_part_number_in_request=导入数据中存在重复物料编号 +production.finished_product_shipment.exception.part_number_not_found=系统中不存在的物料编号 +production.finished_product_shipment.exception.insufficient_stock=库存不足 +production.finished_product_shipment.exception.file_empty=请选择要上传的文件 +production.finished_product_shipment.exception.import_failed=导入文件失败 + sys.operationType.codeNotExists=编号不存在 # ==========>> 采购管理 diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 0380af5..1bde00c 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -124,9 +124,10 @@ production.finished_product_shipment.validate.form_code.not_null=单据编号不 production.finished_product_shipment.validate.form_name.not_null=单据名称不能为空 production.finished_product_shipment.validate.store_no.not_null=仓库不能为空 production.finished_product_shipment.validate.store_name.not_null=仓库名称不能为空 -production.finished_product_shipment.validate.module_sn_items.not_null=成品明细不能为空 -production.finished_product_shipment.validate.product_type.not_null=型号不能为空 -production.finished_product_shipment.validate.product_sn.not_null=SN号不能为空 +production.finished_product_shipment.validate.out_stock_type.not_null=出库类型不能为空 +production.finished_product_shipment.validate.shipment_items.not_null=出库明细不能为空 +production.finished_product_shipment.validate.part_number.not_blank=物料编号不能为空 +production.finished_product_shipment.validate.product_count.not_null=数量不能为空 production.finished_product_shipment.exception.not_found=成品出库单不存在 production.finished_product_shipment.exception.cannot_update_approved=已审核的成品出库单不能修改 production.finished_product_shipment.exception.cannot_delete_approved=已审核的成品出库单不能删除 @@ -134,10 +135,10 @@ production.finished_product_shipment.exception.cannot_delete_approved_batch=选 production.finished_product_shipment.exception.ids_empty=请选择要删除的成品出库单 production.finished_product_shipment.exception.already_approved=成品出库单已经审核 production.finished_product_shipment.exception.not_approved=成品出库单不是已审核状态,不能反审 -production.finished_product_shipment.exception.no_module_sn_items=成品出库单没有明细 -production.finished_product_shipment.exception.duplicate_sn_in_request=导入数据中存在重复SN号 -production.finished_product_shipment.exception.sn_not_found=系统中不存在的SN号:{0} -production.finished_product_shipment.exception.sn_already_shipped=已经出货的SN号:{0} +production.finished_product_shipment.exception.no_shipment_items=成品出库单没有明细 +production.finished_product_shipment.exception.duplicate_part_number_in_request=导入数据中存在重复物料编号 +production.finished_product_shipment.exception.part_number_not_found=系统中不存在的物料编号 +production.finished_product_shipment.exception.insufficient_stock=库存不足 production.finished_product_shipment.exception.file_empty=请选择要上传的文件 production.finished_product_shipment.exception.import_failed=导入文件失败 sale.sale_order.validate.form_code.not_null=单据编号不能为空