diff --git a/build.gradle.kts b/build.gradle.kts index 513f5a1..3b07427 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,7 +36,6 @@ dependencies { implementation("com.baomidou:mybatis-plus-jsqlparser:3.5.16") developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("com.mysql:mysql-connector-j") - annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.5") testImplementation("org.springframework.batch:spring-batch-test") @@ -67,6 +66,10 @@ dependencies { // 日志 implementation("org.zalando:logbook-spring-boot-starter:4.0.1") + + // Excel处理 + implementation("org.apache.poi:poi:5.2.5") + implementation("org.apache.poi:poi-ooxml:5.2.5") } tasks.withType { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fcdfe2a..4f32b20 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://mirrors.aliyun.com/gradle/gradle-8.14.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/niuan/erp/common/base/BaseApproveAndRejectDto.java b/src/main/java/com/niuan/erp/common/base/BaseApproveAndRejectDto.java index fddc4cd..d3956e4 100644 --- a/src/main/java/com/niuan/erp/common/base/BaseApproveAndRejectDto.java +++ b/src/main/java/com/niuan/erp/common/base/BaseApproveAndRejectDto.java @@ -1,4 +1,6 @@ package com.niuan.erp.common.base; -public class BaseApproveAndRejectDto { -} + +public record BaseApproveAndRejectDto( + Long id +) {} diff --git a/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java b/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java index 7c1a99f..75f4bd7 100644 --- a/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java +++ b/src/main/java/com/niuan/erp/common/handler/CustomerTenantHandler.java @@ -13,15 +13,16 @@ import java.util.Set; */ public class CustomerTenantHandler implements TenantLineHandler { - private final static Set activeTables = new HashSet(); + private final static Set customerTables = new HashSet(); /* 这里添加需要进行账户验证的数据表 */ static { - activeTables.add("product"); - activeTables.add("storage_list"); - activeTables.add("bom_list"); + customerTables.add("product"); + customerTables.add("storage_list"); + customerTables.add("bom_list"); + customerTables.add("vendor"); } @Override @@ -37,6 +38,6 @@ public class CustomerTenantHandler implements TenantLineHandler { @Override public boolean ignoreTable(String tableName) { - return !activeTables.contains(tableName); + return !customerTables.contains(tableName); } } diff --git a/src/main/java/com/niuan/erp/module/auth/service/impl/SystemAuthServiceImpl.java b/src/main/java/com/niuan/erp/module/auth/service/impl/SystemAuthServiceImpl.java index a4e3803..96ae64d 100644 --- a/src/main/java/com/niuan/erp/module/auth/service/impl/SystemAuthServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/auth/service/impl/SystemAuthServiceImpl.java @@ -24,7 +24,10 @@ public class SystemAuthServiceImpl implements SystemAuthService { @Override public List getRouterConfigRawList() { - List sysPermissions = sysPermissionMapper.selectByUserId(SecurityUtils.getUserId()); + List sysPermissions = sysPermissionMapper.selectByUserId(SecurityUtils.getUserId()) + .stream() + .filter(p -> !p.getHidden()) + .toList(); return SystemAuthFactory.generateTeekFrameMenuBySysPermissions(sysPermissions); } } diff --git a/src/main/java/com/niuan/erp/module/common/entity/Document.java b/src/main/java/com/niuan/erp/module/common/entity/Document.java index 8c52b72..d234348 100644 --- a/src/main/java/com/niuan/erp/module/common/entity/Document.java +++ b/src/main/java/com/niuan/erp/module/common/entity/Document.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.niuan.erp.module.common.enums.DocumentType; +import com.niuan.erp.module.common.enums.FormStatus; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -68,7 +69,7 @@ public class Document implements Serializable { private String formName; @TableField("FormStatus") - private Integer formStatus; + private FormStatus formStatus; @TableField("FormMark") private String formMark; diff --git a/src/main/java/com/niuan/erp/module/common/enums/DocumentType.java b/src/main/java/com/niuan/erp/module/common/enums/DocumentType.java index 3e525cb..489cc8a 100644 --- a/src/main/java/com/niuan/erp/module/common/enums/DocumentType.java +++ b/src/main/java/com/niuan/erp/module/common/enums/DocumentType.java @@ -19,7 +19,8 @@ public enum DocumentType implements IEnum { PRODUCTION_RETURN(6, "生产退料单"), FINISHED_PRODUCT_SHIPMENT(7, "成品出货单"), STOCK_TRANSFER_ORDER(8, "仓库调拨单"), - INVENTORY_COUNT(9, "库存盘点单"); + INVENTORY_COUNT(9, "库存盘点单"), + FINISHED_PRODUCT_RECEIPT(10, "成品入库单"); final int code; final String description; DocumentType(int code, String description) { diff --git a/src/main/java/com/niuan/erp/module/common/enums/FormStatus.java b/src/main/java/com/niuan/erp/module/common/enums/FormStatus.java index 62c3247..ef5ec9b 100644 --- a/src/main/java/com/niuan/erp/module/common/enums/FormStatus.java +++ b/src/main/java/com/niuan/erp/module/common/enums/FormStatus.java @@ -1,4 +1,28 @@ package com.niuan.erp.module.common.enums; -public enum FormStatus { +import com.baomidou.mybatisplus.annotation.IEnum; + +public enum FormStatus implements IEnum { + NO_APPROVE(0, "未审核"), + APPROVE(1, "已审核"), + REJECT(2, "驳回"), + NO_COMPLETE(3, "未完成"), + COMPLETE(4, "已完成"), + RECEIPTING(5, "入库中"), + IMPORTED(6, "已导入"), + APPROVING(7, "审批中"), + RETURNED(8, "已退料"); + final int code; + final String description; + + FormStatus(int code, String description) { + this.code = code; + this.description = description; + } + + + @Override + public Integer getValue() { + return code; + } } diff --git a/src/main/java/com/niuan/erp/module/production/controller/FinishedProductReceiptController.java b/src/main/java/com/niuan/erp/module/production/controller/FinishedProductReceiptController.java index a7f9a8a..05cc9d4 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/FinishedProductReceiptController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/FinishedProductReceiptController.java @@ -10,14 +10,20 @@ 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.FinishedProductReceiptAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductReceiptDto; import com.niuan.erp.module.production.service.FinishedProductReceiptService; +import com.niuan.erp.module.sale.controller.dto.DeviceDto; 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; @Tag(name = "FinishedProductReceipt") @ModuleLog("FinishedProductReceipt管理") @@ -30,7 +36,7 @@ public class FinishedProductReceiptController { @Operation(summary = "分页查询FinishedProductReceipt数据", operationId = "getFinishedProductReceiptPage") @GetMapping("/getFinishedProductReceiptPage") - @PreAuthorize("hasAuthority('finishedproductreceipt:index')") + @PreAuthorize("hasAuthority('finished_product_receipt:index')") public BaseResult> getFinishedProductReceiptPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) FinishedProductReceiptDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -41,8 +47,8 @@ public class FinishedProductReceiptController { @ApiLog(type = OperationType.ADD, remark = "新增一条FinishedProductReceipt记录") @Operation(summary = "新增FinishedProductReceipt", operationId = "addFinishedProductReceipt") @PostMapping("/addFinishedProductReceipt") - @PreAuthorize("hasAuthority('finishedproductreceipt:add')") - public BaseResult addFinishedProductReceipt(@Validated(Add.class) @RequestBody FinishedProductReceiptDto dto) { + @PreAuthorize("hasAuthority('finished_product_receipt:add')") + public BaseResult addFinishedProductReceipt(@Validated(Add.class) @RequestBody FinishedProductReceiptAddDto dto) { finishedProductReceiptService.addFinishedProductReceipt(dto); return BaseResult.success(); } @@ -50,7 +56,7 @@ public class FinishedProductReceiptController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条FinishedProductReceipt记录") @Operation(summary = "更新FinishedProductReceipt", operationId = "updateFinishedProductReceipt") @PostMapping("/updateFinishedProductReceipt") - @PreAuthorize("hasAuthority('finishedproductreceipt:update')") + @PreAuthorize("hasAuthority('finished_product_receipt:update')") public BaseResult updateFinishedProductReceipt(@Validated(Update.class) @RequestBody FinishedProductReceiptDto dto) { finishedProductReceiptService.updateFinishedProductReceipt(dto); return BaseResult.success(); @@ -59,7 +65,7 @@ public class FinishedProductReceiptController { @ApiLog(type = OperationType.DELETE, remark = "删除一条FinishedProductReceipt记录") @Operation(summary = "删除FinishedProductReceipt", operationId = "deleteFinishedProductReceipt") @PostMapping("/deleteFinishedProductReceipt") - @PreAuthorize("hasAuthority('finishedproductreceipt:delete')") + @PreAuthorize("hasAuthority('finished_product_receipt:delete')") public BaseResult deleteFinishedProductReceipt(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { finishedProductReceiptService.deleteFinishedProductReceipt(req.id()); return BaseResult.success(); @@ -68,9 +74,45 @@ public class FinishedProductReceiptController { @ApiLog(type = OperationType.DELETE, remark = "批量删除FinishedProductReceipt记录") @Operation(summary = "批量删除FinishedProductReceipt", operationId = "deleteFinishedProductReceiptBatch") @PostMapping("/deleteFinishedProductReceiptBatch") - @PreAuthorize("hasAuthority('finishedproductreceipt:deleteBatch')") + @PreAuthorize("hasAuthority('finished_product_receipt:deleteBatch')") public BaseResult deleteFinishedProductReceiptBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { finishedProductReceiptService.deleteBatch(req.ids()); return BaseResult.success(); } + + @ApiLog(type = OperationType.UPDATE, remark = "审核FinishedProductReceipt") + @Operation(summary = "审核FinishedProductReceipt", operationId = "approveFinishedProductReceipt") + @PostMapping("/approveFinishedProductReceipt") + @PreAuthorize("hasAuthority('finished_product_receipt:approve')") + public BaseResult approveFinishedProductReceipt( + @Parameter(description = "成品入库单ID") @RequestParam Long id) { + finishedProductReceiptService.approve(id); + return BaseResult.success(); + } + + @ApiLog(type = OperationType.UPDATE, remark = "反审核FinishedProductReceipt") + @Operation(summary = "反审核FinishedProductReceipt", operationId = "unapproveFinishedProductReceipt") + @PostMapping("/unapproveFinishedProductReceipt") + @PreAuthorize("hasAuthority('finished_product_receipt:unapprove')") + public BaseResult unapproveFinishedProductReceipt( + @Parameter(description = "成品入库单ID") @RequestParam Long id) { + finishedProductReceiptService.unapprove(id); + return BaseResult.success(); + } + + @Operation(summary = "获取FinishedProductReceipt明细", operationId = "getFinishedProductReceiptDetail") + @GetMapping("/getFinishedProductReceiptDetail") + @PreAuthorize("hasAuthority('finishedproductreceipt:index')") + public BaseResult> getFinishedProductReceiptDetail( + @Parameter(description = "成品入库单ID") @RequestParam Long id) { + return BaseResult.successWithData(finishedProductReceiptService.getDetail(id)); + } + + @Operation(summary = "导入FinishedProductReceipt明细", operationId = "importFinishedProductReceiptItems") + @PostMapping("/importFinishedProductReceiptItems") + @PreAuthorize("hasAuthority('finished_product_receipt:import')") + public BaseResult> importFinishedProductReceiptItems( + @Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) { + return BaseResult.successWithData(finishedProductReceiptService.importItems(file)); + } } 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 6e93ed2..7511808 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 @@ -10,14 +10,20 @@ 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.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; @Tag(name = "FinishedProductShipment") @ModuleLog("FinishedProductShipment管理") @@ -30,7 +36,7 @@ public class FinishedProductShipmentController { @Operation(summary = "分页查询FinishedProductShipment数据", operationId = "getFinishedProductShipmentPage") @GetMapping("/getFinishedProductShipmentPage") - @PreAuthorize("hasAuthority('finishedproductshipment:index')") + @PreAuthorize("hasAuthority('finished_product_shipment:index')") public BaseResult> getFinishedProductShipmentPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) FinishedProductShipmentDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -41,8 +47,8 @@ public class FinishedProductShipmentController { @ApiLog(type = OperationType.ADD, remark = "新增一条FinishedProductShipment记录") @Operation(summary = "新增FinishedProductShipment", operationId = "addFinishedProductShipment") @PostMapping("/addFinishedProductShipment") - @PreAuthorize("hasAuthority('finishedproductshipment:add')") - public BaseResult addFinishedProductShipment(@Validated(Add.class) @RequestBody FinishedProductShipmentDto dto) { + @PreAuthorize("hasAuthority('finished_product_shipment:add')") + public BaseResult addFinishedProductShipment(@Validated(Add.class) @RequestBody FinishedProductShipmentAddDto dto) { finishedProductShipmentService.addFinishedProductShipment(dto); return BaseResult.success(); } @@ -50,7 +56,7 @@ public class FinishedProductShipmentController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条FinishedProductShipment记录") @Operation(summary = "更新FinishedProductShipment", operationId = "updateFinishedProductShipment") @PostMapping("/updateFinishedProductShipment") - @PreAuthorize("hasAuthority('finishedproductshipment:update')") + @PreAuthorize("hasAuthority('finished_product_shipment:update')") public BaseResult updateFinishedProductShipment(@Validated(Update.class) @RequestBody FinishedProductShipmentDto dto) { finishedProductShipmentService.updateFinishedProductShipment(dto); return BaseResult.success(); @@ -59,7 +65,7 @@ public class FinishedProductShipmentController { @ApiLog(type = OperationType.DELETE, remark = "删除一条FinishedProductShipment记录") @Operation(summary = "删除FinishedProductShipment", operationId = "deleteFinishedProductShipment") @PostMapping("/deleteFinishedProductShipment") - @PreAuthorize("hasAuthority('finishedproductshipment:delete')") + @PreAuthorize("hasAuthority('finished_product_shipment:delete')") public BaseResult deleteFinishedProductShipment(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { finishedProductShipmentService.deleteFinishedProductShipment(req.id()); return BaseResult.success(); @@ -68,9 +74,45 @@ public class FinishedProductShipmentController { @ApiLog(type = OperationType.DELETE, remark = "批量删除FinishedProductShipment记录") @Operation(summary = "批量删除FinishedProductShipment", operationId = "deleteFinishedProductShipmentBatch") @PostMapping("/deleteFinishedProductShipmentBatch") - @PreAuthorize("hasAuthority('finishedproductshipment:deleteBatch')") + @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); + 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); + return BaseResult.success(); + } + + @Operation(summary = "获取FinishedProductShipment明细", operationId = "getFinishedProductShipmentDetail") + @GetMapping("/getFinishedProductShipmentDetail") + @PreAuthorize("hasAuthority('finishedproductshipment:index')") + public BaseResult> getFinishedProductShipmentDetail( + @Parameter(description = "成品出库单ID") @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/ProductionIssueController.java b/src/main/java/com/niuan/erp/module/production/controller/ProductionIssueController.java index b6f558d..5b8481f 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/ProductionIssueController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/ProductionIssueController.java @@ -4,74 +4,77 @@ 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.BaseDeleteBody; -import com.niuan.erp.common.base.BasePageReqParams; -import com.niuan.erp.common.base.BaseResult; +import com.niuan.erp.common.base.*; 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.ProductionIssueAddDto; import com.niuan.erp.module.production.controller.dto.ProductionIssueDto; +import com.niuan.erp.module.production.controller.dto.ProductionIssueItemDto; import com.niuan.erp.module.production.service.ProductionIssueService; +import com.niuan.erp.module.production.service.ProductionReturnService; 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.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -@Tag(name = "ProductionIssue") -@ModuleLog("ProductionIssue管理") +import java.util.List; + +@Tag(name = "生产发料单") +@ModuleLog("生产发料单管理") @RestController @RequestMapping("/production/productionissue") @RequiredArgsConstructor public class ProductionIssueController { private final ProductionIssueService productionIssueService; + private final ProductionReturnService productionReturnService; - @Operation(summary = "分页查询ProductionIssue数据", operationId = "getProductionIssuePage") + @Operation(summary = "分页查询生产发料单数据", operationId = "getProductionIssuePage") @GetMapping("/getProductionIssuePage") - @PreAuthorize("hasAuthority('productionissue:index')") + @PreAuthorize("hasAuthority('production_issue:index')") public BaseResult> getProductionIssuePage(@Validated BasePageReqParams pageParams, @Validated(Get.class) ProductionIssueDto searchParams) { var wrapper = new LambdaQueryWrapper(); return BaseResult.successWithData(productionIssueService.getProductionIssuePage(pageParams, wrapper)); } + @Operation(summary = "查询生产发料单明细数据", operationId = "getProductionIssueItemList") + @GetMapping("/getProductionIssueItemList") + @PreAuthorize("hasAuthority('production_issue:index')") + public BaseResult> getProductionIssueItemList(@RequestParam("issueId") @NotNull Long issueId) { + return BaseResult.successWithData(productionIssueService.getProductionIssueItemList(issueId)); + } - @ApiLog(type = OperationType.ADD, remark = "新增一条ProductionIssue记录") - @Operation(summary = "新增ProductionIssue", operationId = "addProductionIssue") - @PostMapping("/addProductionIssue") - @PreAuthorize("hasAuthority('productionissue:add')") - public BaseResult addProductionIssue(@Validated(Add.class) @RequestBody ProductionIssueAddDto dto) { - productionIssueService.addProductionIssue(dto); + @ApiLog(type = OperationType.UPDATE, remark = "审核一条生产发料单记录") + @Operation(summary = "审核生产发料单", operationId = "approvingProductionIssue") + @PostMapping("/approvingProductionIssue") + @PreAuthorize("hasAuthority('production_issue:approve')") + public BaseResult approvingProductionIssue(@Validated @RequestBody BaseApproveAndRejectDto dto) { + productionIssueService.approvingProductionIssue(dto.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.UPDATE, remark = "更新一条ProductionIssue记录") - @Operation(summary = "更新ProductionIssue", operationId = "updateProductionIssue") - @PostMapping("/updateProductionIssue") - @PreAuthorize("hasAuthority('productionissue:update')") - public BaseResult updateProductionIssue(@Validated(Update.class) @RequestBody ProductionIssueDto dto) { - productionIssueService.updateProductionIssue(dto); + @ApiLog(type = OperationType.UPDATE, remark = "反审一条生产发料单记录") + @Operation(summary = "反审生产发料单", operationId = "rejectProductionIssue") + @PostMapping("/rejectProductionIssue") + @PreAuthorize("hasAuthority('production_issue:reject')") + public BaseResult rejectProductionIssue(@Validated @RequestBody BaseApproveAndRejectDto dto) { + productionIssueService.rejectProductionIssue(dto.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "删除一条ProductionIssue记录") - @Operation(summary = "删除ProductionIssue", operationId = "deleteProductionIssue") - @PostMapping("/deleteProductionIssue") - @PreAuthorize("hasAuthority('productionissue:delete')") - public BaseResult deleteProductionIssue(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { - productionIssueService.deleteProductionIssue(req.id()); - return BaseResult.success(); - } - - @ApiLog(type = OperationType.DELETE, remark = "批量删除ProductionIssue记录") - @Operation(summary = "批量删除ProductionIssue", operationId = "deleteProductionIssueBatch") - @PostMapping("/deleteProductionIssueBatch") - @PreAuthorize("hasAuthority('productionissue:deleteBatch')") - public BaseResult deleteProductionIssueBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { - productionIssueService.deleteBatch(req.ids()); + @ApiLog(type = OperationType.ADD, remark = "生成一条退料单") + @Operation(summary = "生成退料单", operationId = "productionReturn") + @PostMapping("/productionReturn") + @PreAuthorize("hasAuthority('production_issue:productionReturn')") + public BaseResult productionReturn( + @Parameter(description = "发料单ID") @RequestParam("id") Long issueId, + @Validated(Update.class) @RequestBody ProductionIssueAddDto dto) { + productionReturnService.createProductionReturn(issueId, dto); return BaseResult.success(); } } diff --git a/src/main/java/com/niuan/erp/module/production/controller/ProductionPlanController.java b/src/main/java/com/niuan/erp/module/production/controller/ProductionPlanController.java index 37fa0c5..c5e5225 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/ProductionPlanController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/ProductionPlanController.java @@ -72,7 +72,7 @@ public class ProductionPlanController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条生产计划记录") @Operation(summary = "更新生产计划", operationId = "updateProductionPlan") @PostMapping("/updateProductionPlan") - @PreAuthorize("hasAuthority('production_plan:update')") + @PreAuthorize("hasAuthority('production_plan:edit')") public BaseResult updateProductionPlan(@Validated(Update.class) @RequestBody ProductionPlanDto dto) { productionPlanService.updateProductionPlan(dto); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java b/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java index 7eafeca..de661b5 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java +++ b/src/main/java/com/niuan/erp/module/production/controller/ProductionReturnController.java @@ -4,13 +4,16 @@ 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.common.enums.DocumentType; import com.niuan.erp.module.production.controller.dto.ProductionReturnDto; +import com.niuan.erp.module.production.controller.dto.ProductionReturnItemDto; import com.niuan.erp.module.production.service.ProductionReturnService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -19,8 +22,10 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -@Tag(name = "ProductionReturn") -@ModuleLog("ProductionReturn管理") +import java.util.List; + +@Tag(name = "退料单") +@ModuleLog("退料单管理") @RestController @RequestMapping("/production/productionreturn") @RequiredArgsConstructor @@ -28,49 +33,38 @@ public class ProductionReturnController { private final ProductionReturnService productionReturnService; - @Operation(summary = "分页查询ProductionReturn数据", operationId = "getProductionReturnPage") + @Operation(summary = "分页查询退料单数据", operationId = "getProductionReturnPage") @GetMapping("/getProductionReturnPage") - @PreAuthorize("hasAuthority('productionreturn:index')") + @PreAuthorize("hasAuthority('production_return:index')") public BaseResult> getProductionReturnPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) ProductionReturnDto searchParams) { var wrapper = new LambdaQueryWrapper(); + wrapper.eq(Document::getFormType, DocumentType.PRODUCTION_RETURN.getCode()); return BaseResult.successWithData(productionReturnService.getProductionReturnPage(pageParams, wrapper)); } - - @ApiLog(type = OperationType.ADD, remark = "新增一条ProductionReturn记录") - @Operation(summary = "新增ProductionReturn", operationId = "addProductionReturn") - @PostMapping("/addProductionReturn") - @PreAuthorize("hasAuthority('productionreturn:add')") - public BaseResult addProductionReturn(@Validated(Add.class) @RequestBody ProductionReturnDto dto) { - productionReturnService.addProductionReturn(dto); + @ApiLog(type = OperationType.UPDATE, remark = "审核生产退料单") + @Operation(summary = "审核生产退料单", operationId = "approvingProductionReturn") + @PostMapping("/approvingProductionReturn") + @PreAuthorize("hasAuthority('production_return:approve')") + public BaseResult approvingProductionReturn(@Validated @RequestBody BaseApproveAndRejectDto dto) { + productionReturnService.approveProductionReturn(dto.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.UPDATE, remark = "更新一条ProductionReturn记录") - @Operation(summary = "更新ProductionReturn", operationId = "updateProductionReturn") - @PostMapping("/updateProductionReturn") - @PreAuthorize("hasAuthority('productionreturn:update')") - public BaseResult updateProductionReturn(@Validated(Update.class) @RequestBody ProductionReturnDto dto) { - productionReturnService.updateProductionReturn(dto); + @ApiLog(type = OperationType.UPDATE, remark = "取消审核生产退料单") + @Operation(summary = "取消审核生产退料单", operationId = "rejectProductionReturn") + @PostMapping("/rejectProductionReturn") + @PreAuthorize("hasAuthority('production_return:reject')") + public BaseResult rejectProductionReturn(@Validated @RequestBody BaseApproveAndRejectDto dto) { + productionReturnService.rejectProductionReturn(dto.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "删除一条ProductionReturn记录") - @Operation(summary = "删除ProductionReturn", operationId = "deleteProductionReturn") - @PostMapping("/deleteProductionReturn") - @PreAuthorize("hasAuthority('productionreturn:delete')") - public BaseResult deleteProductionReturn(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { - productionReturnService.deleteProductionReturn(req.id()); - return BaseResult.success(); + @Operation(summary = "获取退料单明细", operationId = "getProductionReturnItemList") + @GetMapping("/getProductionReturnItemList") + @PreAuthorize("hasAuthority('production_return:index')") + public BaseResult> getProductionReturnItemList(@RequestParam("returnId") Long returnId) { + return BaseResult.successWithData(productionReturnService.getProductionReturnItemList(returnId)); } - - @ApiLog(type = OperationType.DELETE, remark = "批量删除ProductionReturn记录") - @Operation(summary = "批量删除ProductionReturn", operationId = "deleteProductionReturnBatch") - @PostMapping("/deleteProductionReturnBatch") - @PreAuthorize("hasAuthority('productionreturn:deleteBatch')") - public BaseResult deleteProductionReturnBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { - productionReturnService.deleteBatch(req.ids()); - return BaseResult.success(); - } -} +} \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemAddDto.java b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemAddDto.java index ee018e3..3070a58 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemAddDto.java +++ b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemAddDto.java @@ -5,7 +5,7 @@ import jakarta.validation.constraints.NotNull; /** * 发料单明细 Dto - * @param partNumber 物料便哈 + * @param partNumber 物料编号 * @param storeNo 发料仓库 * @param productCount 实发数量 * @param demandCount 需求数量 diff --git a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemDto.java b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemDto.java index d78baae..445b7a0 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemDto.java +++ b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionIssueItemDto.java @@ -1,4 +1,8 @@ package com.niuan.erp.module.production.controller.dto; -public class ProductionIssueItemDto { -} +public record ProductionIssueItemDto( + String partNumber, + String productSpecs, + Integer requiredQty, + Integer actualQty +) {} diff --git a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnAddDto.java b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnAddDto.java index 61e2068..15ebee5 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnAddDto.java +++ b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnAddDto.java @@ -1,4 +1,10 @@ package com.niuan.erp.module.production.controller.dto; -public record ProductionReturnAddDto() { -} +public record ProductionReturnAddDto( + String formCode, + String formMark, + String formName, + + Long storeNo, + String storeName +) {} diff --git a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnDto.java b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnDto.java index 1231e6a..f9dea55 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnDto.java +++ b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnDto.java @@ -4,26 +4,11 @@ import java.time.LocalDateTime; public record ProductionReturnDto( Long id, - Integer status, LocalDateTime createDate, - Long createUserId, - String createUserName, - LocalDateTime updateDate, - Long updateUserId, - String updateUserName, - Integer storeNo, String storeName, - Integer formType, String formCode, - String formName, Integer formStatus, String formMark, - Integer reserve1, - String reserve2, - Integer vendorNo, - String vendorName, - Double totalValue, - Integer outStoreNo, - String outStoreName, - Integer groupId, - Integer customerId) {} \ No newline at end of file + String partNumber, + String productSpecs, + Integer returnQty) {} \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnItemAddDto.java b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnItemAddDto.java index 88fa51f..e40f22d 100644 --- a/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnItemAddDto.java +++ b/src/main/java/com/niuan/erp/module/production/controller/dto/ProductionReturnItemAddDto.java @@ -1,4 +1,8 @@ package com.niuan.erp.module.production.controller.dto; -public record ProductionReturnItemAddDto() { -} +public record ProductionReturnItemAddDto( + String partNumber, + // 实退数量 + Integer productCount, + // 损耗数量 + Integer demandCount) {} diff --git a/src/main/java/com/niuan/erp/module/production/converter/FinishedProductReceiptConverter.java b/src/main/java/com/niuan/erp/module/production/converter/FinishedProductReceiptConverter.java index b31b411..ac5278d 100644 --- a/src/main/java/com/niuan/erp/module/production/converter/FinishedProductReceiptConverter.java +++ b/src/main/java/com/niuan/erp/module/production/converter/FinishedProductReceiptConverter.java @@ -1,8 +1,13 @@ package com.niuan.erp.module.production.converter; import com.niuan.erp.module.common.entity.Document; +import com.niuan.erp.module.production.controller.dto.FinishedProductReceiptAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductReceiptDto; +import com.niuan.erp.module.sale.controller.dto.DeviceAddDto; +import com.niuan.erp.module.sale.controller.dto.DeviceDto; +import com.niuan.erp.module.sale.entity.Device; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import java.util.List; @@ -10,6 +15,13 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface FinishedProductReceiptConverter { Document toEntity(FinishedProductReceiptDto dto); + Document toEntity(FinishedProductReceiptAddDto dto); FinishedProductReceiptDto toDto(Document entity); - List toDtoList(List entities); + + @Mapping(target = "alStatus", expression = "java(\"已激活\".equals(dto.alTxt()))") + Device toEntity(DeviceAddDto dto); + List toEntityList(List dtoList); + + DeviceDto toDto(Device entity); + List toDtoList(List entities); } \ No newline at end of file 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 41608e7..68d3729 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,7 +1,11 @@ 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.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 org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; @@ -10,6 +14,13 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface FinishedProductShipmentConverter { Document toEntity(FinishedProductShipmentDto dto); + Document toEntity(FinishedProductShipmentAddDto dto); FinishedProductShipmentDto toDto(Document entity); List toDtoList(List entities); + + Device toEntity(DeviceAddDto dto); + List toEntityList(List dtoList); + + DeviceShipmentDto toDeviceShipmentDto(Device entity); + List toDeviceShipmentDtoList(List entities); } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/production/service/FinishedProductReceiptService.java b/src/main/java/com/niuan/erp/module/production/service/FinishedProductReceiptService.java index 8b68919..d8ad867 100644 --- a/src/main/java/com/niuan/erp/module/production/service/FinishedProductReceiptService.java +++ b/src/main/java/com/niuan/erp/module/production/service/FinishedProductReceiptService.java @@ -4,7 +4,10 @@ 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.FinishedProductReceiptAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductReceiptDto; +import com.niuan.erp.module.sale.controller.dto.DeviceDto; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -12,7 +15,7 @@ public interface FinishedProductReceiptService { IPage getFinishedProductReceiptPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addFinishedProductReceipt(FinishedProductReceiptDto dto); + void addFinishedProductReceipt(FinishedProductReceiptAddDto dto); void updateFinishedProductReceipt(FinishedProductReceiptDto dto); @@ -20,4 +23,11 @@ public interface FinishedProductReceiptService { void deleteBatch(List ids); + void approve(long id); + + void unapprove(long id); + + List getDetail(long id); + + List importItems(MultipartFile file); } 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 26cf8dd..4c52982 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,7 +4,10 @@ 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 org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -12,7 +15,7 @@ public interface FinishedProductShipmentService { IPage getFinishedProductShipmentPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addFinishedProductShipment(FinishedProductShipmentDto dto); + void addFinishedProductShipment(FinishedProductShipmentAddDto dto); void updateFinishedProductShipment(FinishedProductShipmentDto dto); @@ -20,4 +23,11 @@ public interface FinishedProductShipmentService { void deleteBatch(List ids); + void approve(long id); + + void unapprove(long id); + + List getDetail(long id); + + List importItems(MultipartFile file); } diff --git a/src/main/java/com/niuan/erp/module/production/service/ProductionIssueService.java b/src/main/java/com/niuan/erp/module/production/service/ProductionIssueService.java index 4e23b95..1e581be 100644 --- a/src/main/java/com/niuan/erp/module/production/service/ProductionIssueService.java +++ b/src/main/java/com/niuan/erp/module/production/service/ProductionIssueService.java @@ -6,6 +6,8 @@ import com.niuan.erp.common.base.BasePageReqParams; import com.niuan.erp.module.common.entity.Document; import com.niuan.erp.module.production.controller.dto.ProductionIssueAddDto; import com.niuan.erp.module.production.controller.dto.ProductionIssueDto; +import com.niuan.erp.module.production.controller.dto.ProductionIssueItemAddDto; +import com.niuan.erp.module.production.controller.dto.ProductionIssueItemDto; import java.util.List; @@ -13,12 +15,19 @@ public interface ProductionIssueService { IPage getProductionIssuePage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); + List getProductionIssueItemList(Long id); + void addProductionIssue(ProductionIssueAddDto dto); void updateProductionIssue(ProductionIssueDto dto); - void deleteProductionIssue(long id); void deleteBatch(List ids); + void approvingProductionIssue(long id); + + void rejectProductionIssue(long id); + + void productionReturn(long id); + } diff --git a/src/main/java/com/niuan/erp/module/production/service/ProductionReturnService.java b/src/main/java/com/niuan/erp/module/production/service/ProductionReturnService.java index 04ed2ba..5b16c38 100644 --- a/src/main/java/com/niuan/erp/module/production/service/ProductionReturnService.java +++ b/src/main/java/com/niuan/erp/module/production/service/ProductionReturnService.java @@ -4,7 +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.ProductionIssueAddDto; import com.niuan.erp.module.production.controller.dto.ProductionReturnDto; +import com.niuan.erp.module.production.controller.dto.ProductionReturnItemDto; import java.util.List; @@ -12,12 +14,12 @@ public interface ProductionReturnService { IPage getProductionReturnPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addProductionReturn(ProductionReturnDto dto); + List getProductionReturnItemList(Long returnId); - void updateProductionReturn(ProductionReturnDto dto); + void approveProductionReturn(Long id); - void deleteProductionReturn(long id); + void rejectProductionReturn(Long id); - void deleteBatch(List ids); + void createProductionReturn(Long issueId, ProductionIssueAddDto dto); } diff --git a/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductReceiptServiceImpl.java b/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductReceiptServiceImpl.java index 8591717..f81546d 100644 --- a/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductReceiptServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/production/service/impl/FinishedProductReceiptServiceImpl.java @@ -1,22 +1,37 @@ package com.niuan.erp.module.production.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.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.FinishedProductReceiptAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductReceiptDto; import com.niuan.erp.module.production.converter.FinishedProductReceiptConverter; import com.niuan.erp.module.production.service.FinishedProductReceiptService; +import com.niuan.erp.module.sale.controller.dto.DeviceAddDto; +import com.niuan.erp.module.sale.controller.dto.DeviceDto; +import com.niuan.erp.module.sale.converter.DeviceConverter; +import com.niuan.erp.module.sale.entity.Device; +import com.niuan.erp.module.sale.mapper.DeviceMapper; 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.List; +import java.util.*; +import java.util.stream.Collectors; @Service @@ -24,42 +39,260 @@ import java.util.List; @RequiredArgsConstructor public class FinishedProductReceiptServiceImpl extends ServiceImpl implements FinishedProductReceiptService { + private final DocumentMapper documentMapper; + + private final DeviceMapper deviceMapper; private final FinishedProductReceiptConverter finishedProductReceiptConverter; + private final DeviceConverter deviceConverter; + @Override public IPage getFinishedProductReceiptPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.FINISHED_PRODUCT_RECEIPT); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(finishedProductReceiptConverter::toDto); } @Override - public void addFinishedProductReceipt(FinishedProductReceiptDto dto) { + public void addFinishedProductReceipt(FinishedProductReceiptAddDto dto) { + List deviceItems = dto.deviceItems(); + + if (deviceItems == null || deviceItems.isEmpty()) { + throw new BusinessException("production.finished_product_receipt.exception.no_device_items"); + } + + List snList = deviceItems.stream() + .map(DeviceAddDto::productSn) + .toList(); + + Set uniqueSnSet = new HashSet<>(snList); + if (uniqueSnSet.size() != snList.size()) { + throw new BusinessException("production.finished_product_receipt.exception.duplicate_sn_in_request"); + } + + List invalidStatusSn = deviceItems.stream() + .filter(item -> !"已激活".equals(item.alTxt())) + .map(item -> "SN号:" + item.productSn()) + .toList(); + + if (!invalidStatusSn.isEmpty()) { + throw new BusinessException("production.finished_product_receipt.exception.invalid_activation_status"); + } + + LambdaQueryWrapper snWrapper = new LambdaQueryWrapper<>(); + snWrapper.in(Device::getProductSn, snList); + List existingSnList = deviceMapper.selectList(snWrapper); + Set existingSnSet = existingSnList.stream() + .map(Device::getProductSn) + .collect(Collectors.toSet()); + + List duplicateSnInDb = snList.stream() + .filter(existingSnSet::contains) + .map(sn -> "SN号:" + sn) + .toList(); + + if (!duplicateSnInDb.isEmpty()) { + throw new BusinessException("production.finished_product_receipt.exception.sn_already_exists"); + } + Document entity = finishedProductReceiptConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); entity.setCreateDate(LocalDateTime.now()); entity.setStatus(0); + entity.setFormType(DocumentType.FINISHED_PRODUCT_RECEIPT); + entity.setFormStatus(FormStatus.NO_APPROVE); + entity.setTotalValue(Double.valueOf(deviceItems.size())); + entity.setCustomerId(SecurityUtils.getCustomerId()); this.baseMapper.insert(entity); + + List deviceEntities = deviceConverter.toEntityList(deviceItems); + + 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.setRepairStatus(0); + device.setCustomerId(SecurityUtils.getCustomerId()); + device.setOutStatus(false); + } + + deviceMapper.insert(deviceEntities); } @Override public void updateFinishedProductReceipt(FinishedProductReceiptDto dto) { + Document existingEntity = this.baseMapper.selectById(dto.id()); + if (existingEntity == null) { + throw new BusinessException("production.finished_product_receipt.exception.not_found"); + } + if (existingEntity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_receipt.exception.cannot_update_approved"); + } + Document entity = finishedProductReceiptConverter.toEntity(dto); + entity.setUpdateDate(LocalDateTime.now()); entity.setUpdateUserId(SecurityUtils.getUserId()); entity.setUpdateUserName(SecurityUtils.getUserName()); - entity.setUpdateDate(LocalDateTime.now()); this.baseMapper.updateById(entity); } @Override public void deleteFinishedProductReceipt(long id) { + Document existingEntity = this.baseMapper.selectById(id); + if (existingEntity == null) { + throw new BusinessException("production.finished_product_receipt.exception.not_found"); + } + if (existingEntity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_receipt.exception.cannot_delete_approved"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Device::getDocumentNo, id); + deviceMapper.delete(wrapper); + this.baseMapper.deleteById(id); } @Override public void deleteBatch(List ids) { - this.baseMapper.deleteByIds(ids); + if (ids == null || ids.isEmpty()) { + throw new BusinessException("production.finished_product_receipt.exception.ids_empty"); + } + + List entities = this.baseMapper.selectBatchIds(ids); + boolean hasApproved = entities.stream() + .anyMatch(e -> e.getFormStatus() == FormStatus.APPROVE); + if (hasApproved) { + throw new BusinessException("production.finished_product_receipt.exception.cannot_delete_approved_batch"); + } + + for (Long id : ids) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Device::getDocumentNo, id); + deviceMapper.delete(wrapper); + } + + this.baseMapper.deleteBatchIds(ids); } -} \ No newline at end of file + @Override + public void approve(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("production.finished_product_receipt.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_receipt.exception.already_approved"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Device::getDocumentNo, id); + List items = deviceMapper.selectList(wrapper); + if (items == null || items.isEmpty()) { + throw new BusinessException("production.finished_product_receipt.exception.no_device_items"); + } + + entity.setFormStatus(FormStatus.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::getUpdateDate, LocalDateTime.now()) + .set(Device::getUpdateUserId, SecurityUtils.getUserId()) + .set(Device::getUpdateUserName, SecurityUtils.getUserName()); + deviceMapper.update(null, updateWrapper); + } + + @Override + public void unapprove(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("production.finished_product_receipt.exception.not_found"); + } + if (entity.getFormStatus() != FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_receipt.exception.not_approved"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Device::getDocumentNo, id); + List items = deviceMapper.selectList(wrapper); + + boolean hasShipped = items.stream() + .anyMatch(item -> item.getOutStatus() != null && item.getOutStatus()); + if (hasShipped) { + throw new BusinessException("production.finished_product_receipt.exception.cannot_unapprove_with_shipped"); + } + + 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::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 deviceConverter.toDtoList(items); + } + + @Override + public List importItems(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("production.finished_product_receipt.exception.file_empty"); + } + + 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)); + } + + return items; + } catch (IOException e) { + throw new BusinessException("production.finished_product_receipt.exception.import_failed"); + } + } + + 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 -> ""; + }; + } +} 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 526ff7b..32abe96 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 @@ -1,22 +1,35 @@ package com.niuan.erp.module.production.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.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.production.controller.dto.FinishedProductShipmentAddDto; import com.niuan.erp.module.production.controller.dto.FinishedProductShipmentDto; import com.niuan.erp.module.production.converter.FinishedProductShipmentConverter; 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 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.List; +import java.util.*; +import java.util.stream.Collectors; @Service @@ -27,24 +40,98 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl getFinishedProductShipmentPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.FINISHED_PRODUCT_SHIPMENT); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(finishedProductShipmentConverter::toDto); } @Override - public void addFinishedProductShipment(FinishedProductShipmentDto dto) { + 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"); + } + + List snList = deviceItems.stream() + .map(com.niuan.erp.module.sale.controller.dto.DeviceAddDto::productSn) + .filter(StringUtils::hasText) + .toList(); + + if (snList.isEmpty()) { + throw new BusinessException("production.finished_product_shipment.exception.no_device_items"); + } + + Set uniqueSnSet = new HashSet<>(snList); + if (uniqueSnSet.size() != snList.size()) { + throw new BusinessException("production.finished_product_shipment.exception.duplicate_sn_in_request"); + } + + LambdaQueryWrapper snWrapper = new LambdaQueryWrapper<>(); + snWrapper.in(Device::getProductSn, snList); + List existingSnList = deviceMapper.selectList(snWrapper); + Set existingSnSet = existingSnList.stream() + .map(Device::getProductSn) + .collect(Collectors.toSet()); + + List notFoundSn = snList.stream() + .filter(sn -> !existingSnSet.contains(sn)) + .map(sn -> "SN号:" + sn) + .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"); + } + Document entity = finishedProductShipmentConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); entity.setCreateDate(LocalDateTime.now()); entity.setStatus(0); + entity.setFormType(DocumentType.FINISHED_PRODUCT_SHIPMENT); + entity.setFormStatus(FormStatus.NO_APPROVE); + entity.setTotalValue(Double.valueOf(deviceItems.size())); + entity.setCustomerId(SecurityUtils.getCustomerId()); this.baseMapper.insert(entity); + + List deviceEntities = finishedProductShipmentConverter.toEntityList(deviceItems); + + 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); + } + + deviceMapper.insert(deviceEntities); } @Override public void updateFinishedProductShipment(FinishedProductShipmentDto dto) { + Document existingEntity = this.baseMapper.selectById(dto.id()); + if (existingEntity == null) { + throw new BusinessException("production.finished_product_shipment.exception.not_found"); + } + if (existingEntity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_shipment.exception.cannot_update_approved"); + } + Document entity = finishedProductShipmentConverter.toEntity(dto); entity.setUpdateUserId(SecurityUtils.getUserId()); entity.setUpdateUserName(SecurityUtils.getUserName()); @@ -54,12 +141,172 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(Device::getDocumentNo, id); + deviceMapper.delete(itemWrapper); } @Override public void deleteBatch(List ids) { - this.baseMapper.deleteByIds(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); } + @Override + public void approve(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("production.finished_product_shipment.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_shipment.exception.already_approved"); + } + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(Device::getDocumentNo, id); + List items = deviceMapper.selectList(itemWrapper); + if (items == null || items.isEmpty()) { + throw new BusinessException("production.finished_product_shipment.exception.no_device_items"); + } + + List alreadyShippedSn = items.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"); + } + + entity.setFormStatus(FormStatus.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, 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 + public void unapprove(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("production.finished_product_shipment.exception.not_found"); + } + if (entity.getFormStatus() != FormStatus.APPROVE) { + throw new BusinessException("production.finished_product_shipment.exception.not_approved"); + } + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(Device::getDocumentNo, id); + List items = deviceMapper.selectList(itemWrapper); + + 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); + } + + @Override + public List importItems(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("production.finished_product_shipment.exception.file_empty"); + } + + 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"); + } + } + + 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/java/com/niuan/erp/module/production/service/impl/ProductionIssueServiceImpl.java b/src/main/java/com/niuan/erp/module/production/service/impl/ProductionIssueServiceImpl.java index d4109a6..5e84478 100644 --- a/src/main/java/com/niuan/erp/module/production/service/impl/ProductionIssueServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/production/service/impl/ProductionIssueServiceImpl.java @@ -5,24 +5,33 @@ 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.production.controller.dto.ProductionIssueAddDto; import com.niuan.erp.module.production.controller.dto.ProductionIssueDto; +import com.niuan.erp.module.production.controller.dto.ProductionIssueItemDto; import com.niuan.erp.module.production.converter.ProductionIssueConverter; import com.niuan.erp.module.production.service.ProductionIssueService; +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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; @Service @@ -36,20 +45,47 @@ public class ProductionIssueServiceImpl extends ServiceImpl getProductionIssuePage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.PRODUCTION_ISSUE) + .eq(Document::getGroupId, SecurityUtils.getLoginUser().getUser().getProjectId()) + .orderByDesc(Document::getCreateDate); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(productionIssueConverter::toDto); } + @Override + public List getProductionIssueItemList(Long id) { + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, id)); + if (materials == null || materials.isEmpty()) { + return Collections.emptyList(); + } + var warehouseItemMap = warehouseItemMapper.selectList( + new LambdaQueryWrapper().in(WarehouseItem::getPartNumber, + materials.stream().map(DocumentMaterial::getPartNumber).toList())) + .stream().collect(Collectors.groupingBy(WarehouseItem::getPartNumber)); + System.out.println(warehouseItemMap); + return materials.stream() + .map(m -> + new ProductionIssueItemDto(m.getPartNumber(), + warehouseItemMap.containsKey(m.getPartNumber()) ? + warehouseItemMap.get(m.getPartNumber()).getFirst().getProductSpecs() : "", + m.getDemandCount(), + m.getProductCount())) + .toList(); + } + @Override public void addProductionIssue(ProductionIssueAddDto dto) { - System.out.println(dto); Document entity = productionIssueConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); entity.setCreateDate(LocalDateTime.now()); entity.setStatus(0); + entity.setFormStatus(FormStatus.NO_APPROVE); entity.setFormType(DocumentType.PRODUCTION_ISSUE); entity.setCustomerId(SecurityUtils.getCustomerId()); entity.setGroupId(SecurityUtils.getLoginUser().getUser().getProjectId().intValue()); @@ -82,14 +118,144 @@ public class ProductionIssueServiceImpl extends ServiceImpl ids) { this.baseMapper.deleteByIds(ids); } + @Override + public void approvingProductionIssue(long id) { + Document issue = this.baseMapper.selectById(id); + if (Objects.isNull(issue)) { + throw new BusinessException("没有发料单"); + } + if (!issue.getFormStatus().equals(FormStatus.NO_APPROVE)) { + throw new BusinessException("已审核"); + } + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, issue.getId())); + List stock = stockMapper + .selectList(new LambdaQueryWrapper() + .eq(Stock::getStoreNo, issue.getStoreNo()) + .in(Stock::getPartNumber, materials.stream().map(DocumentMaterial::getPartNumber).toList())); + Map> materialMap = materials.stream() + .collect(Collectors.groupingBy(DocumentMaterial::getPartNumber)); + stock.forEach(s -> { + int actualQty = materialMap.get(s.getPartNumber()).getFirst().getProductCount(); + s.setProductOccupyTotal(s.getProductOccupyTotal() - actualQty); + s.setProductCount(s.getProductCount() - actualQty); + s.setUpdateDate(LocalDateTime.now()); + s.setUpdateUserId(SecurityUtils.getUserId()); + s.setUpdateUserName(SecurityUtils.getUserName()); + }); + issue.setUpdateDate(LocalDateTime.now()); + issue.setUpdateUserId(SecurityUtils.getUserId()); + issue.setUpdateUserName(SecurityUtils.getUserName()); + issue.setFormStatus(FormStatus.APPROVE); + this.baseMapper.updateById(issue); + stockMapper.updateById(stock); + } + + @Override + public void rejectProductionIssue(long id) { + Document issue = this.baseMapper.selectById(id); + if (Objects.isNull(issue)) { + throw new BusinessException("没有发料单"); + } + if (!issue.getFormStatus().equals(FormStatus.APPROVE)) { + throw new BusinessException("已审核"); + } + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, issue.getId())); + List stock = stockMapper + .selectList(new LambdaQueryWrapper() + .eq(Stock::getStoreNo, issue.getStoreNo()) + .in(Stock::getPartNumber, materials.stream().map(DocumentMaterial::getPartNumber).toList())); + Map> materialMap = materials.stream() + .collect(Collectors.groupingBy(DocumentMaterial::getPartNumber)); + stock.forEach(s -> { + int actualQty = materialMap.get(s.getPartNumber()).getFirst().getProductCount(); + s.setProductOccupyTotal(s.getProductOccupyTotal() + actualQty); + s.setProductCount(s.getProductCount() + actualQty); + s.setUpdateDate(LocalDateTime.now()); + s.setUpdateUserId(SecurityUtils.getUserId()); + s.setUpdateUserName(SecurityUtils.getUserName()); + }); + issue.setUpdateDate(LocalDateTime.now()); + issue.setUpdateUserId(SecurityUtils.getUserId()); + issue.setUpdateUserName(SecurityUtils.getUserName()); + issue.setFormStatus(FormStatus.NO_APPROVE); + this.baseMapper.updateById(issue); + stockMapper.updateById(stock); + } + + @Override + public void productionReturn(long id) { + // 获取发料单 + Document issue = this.baseMapper.selectById(id); + if (Objects.isNull(issue)) { + throw new BusinessException("没有发料单"); + } + + // 检查发料单状态,只有审核通过的才能生成退料单 + if (!issue.getFormStatus().equals(FormStatus.APPROVE)) { + throw new BusinessException("只有审核通过的发料单才能生成退料单"); + } + + // 获取发料单明细 + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, issue.getId())); + if (materials == null || materials.isEmpty()) { + throw new BusinessException("发料单没有明细"); + } + + // 创建退料单 + Document returnDoc = new Document(); + returnDoc.setCreateUserId(SecurityUtils.getUserId()); + returnDoc.setCreateUserName(SecurityUtils.getUserName()); + returnDoc.setCreateDate(LocalDateTime.now()); + returnDoc.setStatus(0); + returnDoc.setFormStatus(FormStatus.NO_APPROVE); + returnDoc.setFormType(DocumentType.PRODUCTION_RETURN); + returnDoc.setCustomerId(SecurityUtils.getCustomerId()); + returnDoc.setGroupId(SecurityUtils.getLoginUser().getUser().getProjectId().intValue()); + returnDoc.setStoreNo(issue.getStoreNo()); + this.baseMapper.insert(returnDoc); + + // 创建退料单明细 + List returnMaterials = materials.stream().map(m -> { + DocumentMaterial returnMaterial = new DocumentMaterial(); + returnMaterial.setCreateUserId(SecurityUtils.getUserId()); + returnMaterial.setCreateUserName(SecurityUtils.getUserName()); + returnMaterial.setCreateDate(LocalDateTime.now()); + returnMaterial.setStatus(0); + returnMaterial.setDocumentNo(returnDoc.getId().intValue()); + returnMaterial.setPartNumber(m.getPartNumber()); + returnMaterial.setDemandCount(m.getProductCount()); + returnMaterial.setProductCount(m.getProductCount()); + return returnMaterial; + }).toList(); + + documentMaterialMapper.insert(returnMaterials); + + // 更新库存,将退料的数量加回库存 + List stock = stockMapper + .selectList(new LambdaQueryWrapper() + .eq(Stock::getStoreNo, issue.getStoreNo()) + .in(Stock::getPartNumber, materials.stream().map(DocumentMaterial::getPartNumber).toList())); + + Map> materialMap = materials.stream() + .collect(Collectors.groupingBy(DocumentMaterial::getPartNumber)); + + stock.forEach(s -> { + int returnQty = materialMap.get(s.getPartNumber()).getFirst().getProductCount(); + s.setProductCount(s.getProductCount() + returnQty); + s.setUpdateDate(LocalDateTime.now()); + s.setUpdateUserId(SecurityUtils.getUserId()); + s.setUpdateUserName(SecurityUtils.getUserName()); + }); + + stockMapper.updateById(stock); + } + } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/production/service/impl/ProductionReturnServiceImpl.java b/src/main/java/com/niuan/erp/module/production/service/impl/ProductionReturnServiceImpl.java index cbefb3a..da4e3d2 100644 --- a/src/main/java/com/niuan/erp/module/production/service/impl/ProductionReturnServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/production/service/impl/ProductionReturnServiceImpl.java @@ -5,18 +5,33 @@ 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.production.controller.dto.ProductionIssueAddDto; import com.niuan.erp.module.production.controller.dto.ProductionReturnDto; +import com.niuan.erp.module.production.controller.dto.ProductionReturnItemDto; +import com.niuan.erp.module.production.converter.ProductionIssueConverter; import com.niuan.erp.module.production.converter.ProductionReturnConverter; import com.niuan.erp.module.production.service.ProductionReturnService; +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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; @Service @@ -24,8 +39,11 @@ import java.util.List; @RequiredArgsConstructor public class ProductionReturnServiceImpl extends ServiceImpl implements ProductionReturnService { - private final ProductionReturnConverter productionReturnConverter; + private final ProductionIssueConverter productionIssueConverter; + private final DocumentMaterialMapper documentMaterialMapper; + private final StockMapper stockMapper; + private final WarehouseItemMapper warehouseItemMapper; @Override public IPage getProductionReturnPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { @@ -34,32 +52,187 @@ public class ProductionReturnServiceImpl extends ServiceImpl getProductionReturnItemList(Long returnId) { + // 获取退料单明细 + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, returnId)); + + if (materials == null || materials.isEmpty()) { + return List.of(); + } + + // 获取所有物料编号 + List partNumbers = materials.stream() + .map(DocumentMaterial::getPartNumber) + .collect(Collectors.toList()); + + // 批量查询物料信息 + Map itemMap = warehouseItemMapper + .selectList(new LambdaQueryWrapper().in(WarehouseItem::getPartNumber, partNumbers)) + .stream() + .collect(Collectors.toMap(WarehouseItem::getPartNumber, item -> item)); + + // 转换为DTO + return materials.stream() + .map(material -> { + WarehouseItem item = itemMap.get(material.getPartNumber()); + String productSpecs = item != null ? item.getProductSpecs() : null; + return new ProductionReturnItemDto( + material.getPartNumber(), + productSpecs, + material.getProductCount() + ); + }) + .collect(Collectors.toList()); } @Override - public void updateProductionReturn(ProductionReturnDto dto) { - Document entity = productionReturnConverter.toEntity(dto); - entity.setUpdateUserId(SecurityUtils.getUserId()); - entity.setUpdateUserName(SecurityUtils.getUserName()); - entity.setUpdateDate(LocalDateTime.now()); - this.baseMapper.updateById(entity); + public void createProductionReturn(Long issueId, ProductionIssueAddDto dto) { + Document issue = this.baseMapper.selectById(issueId); + if (Objects.isNull(issue)) { + throw new BusinessException("production.production_issue.exception.issue_not_exists"); + } + + if (!issue.getFormStatus().equals(FormStatus.APPROVE)) { + throw new BusinessException("production.production_issue.exception.only_approved_can_return"); + } + + if (dto.items() == null || dto.items().isEmpty()) { + throw new BusinessException("production.production_issue.exception.no_return_items"); + } + + // 使用Converter转换主表信息 + Document returnDoc = productionIssueConverter.toEntity(dto); + returnDoc.setCreateUserId(SecurityUtils.getUserId()); + returnDoc.setCreateUserName(SecurityUtils.getUserName()); + returnDoc.setCreateDate(LocalDateTime.now()); + returnDoc.setStatus(0); + returnDoc.setFormStatus(FormStatus.NO_APPROVE); + returnDoc.setFormType(DocumentType.PRODUCTION_RETURN); + returnDoc.setCustomerId(SecurityUtils.getCustomerId()); + returnDoc.setGroupId(SecurityUtils.getLoginUser().getUser().getProjectId().intValue()); + returnDoc.setStoreNo(issue.getStoreNo()); + returnDoc.setStoreName(issue.getStoreName()); + this.baseMapper.insert(returnDoc); + + // 使用Converter转换明细信息 + List returnMaterials = productionIssueConverter.toEntityList( + dto.items().stream() + .filter(item -> item.productCount() != null && item.productCount() > 0) + .collect(Collectors.toList()) + ); + + // 设置额外字段 + returnMaterials.forEach(material -> { + material.setCreateUserId(SecurityUtils.getUserId()); + material.setCreateUserName(SecurityUtils.getUserName()); + material.setCreateDate(LocalDateTime.now()); + material.setStatus(0); + material.setDocumentNo(returnDoc.getId().intValue()); + }); + + if (!returnMaterials.isEmpty()) { + documentMaterialMapper.insert(returnMaterials); + } + + // 更新发料单状态为已退料 + issue.setFormStatus(FormStatus.RETURNED); + issue.setUpdateDate(LocalDateTime.now()); + issue.setUpdateUserId(SecurityUtils.getUserId()); + issue.setUpdateUserName(SecurityUtils.getUserName()); + this.baseMapper.updateById(issue); } @Override - public void deleteProductionReturn(long id) { - this.baseMapper.deleteById(id); + public void approveProductionReturn(Long id) { + // 获取退料单 + Document returnDoc = this.baseMapper.selectById(id); + if (Objects.isNull(returnDoc)) { + throw new BusinessException("退料单不存在"); + } + if (!returnDoc.getFormStatus().equals(FormStatus.NO_APPROVE)) { + throw new BusinessException("退料单已经审核"); + } + + // 获取退料单明细 + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, returnDoc.getId())); + if (materials == null || materials.isEmpty()) { + throw new BusinessException("退料单没有明细"); + } + + // 更新库存 + List stock = stockMapper + .selectList(new LambdaQueryWrapper() + .eq(Stock::getStoreNo, returnDoc.getStoreNo()) + .in(Stock::getPartNumber, materials.stream().map(DocumentMaterial::getPartNumber).toList())); + + Map> materialMap = materials.stream() + .collect(Collectors.groupingBy(DocumentMaterial::getPartNumber)); + + stock.forEach(s -> { + int returnQty = materialMap.get(s.getPartNumber()).getFirst().getProductCount(); + s.setProductCount(s.getProductCount() + returnQty); + s.setUpdateDate(LocalDateTime.now()); + s.setUpdateUserId(SecurityUtils.getUserId()); + s.setUpdateUserName(SecurityUtils.getUserName()); + }); + + // 更新退料单状态 + returnDoc.setUpdateDate(LocalDateTime.now()); + returnDoc.setUpdateUserId(SecurityUtils.getUserId()); + returnDoc.setUpdateUserName(SecurityUtils.getUserName()); + returnDoc.setFormStatus(FormStatus.APPROVE); + + // 执行更新 + this.baseMapper.updateById(returnDoc); + stockMapper.updateById(stock); } @Override - public void deleteBatch(List ids) { - this.baseMapper.deleteByIds(ids); + public void rejectProductionReturn(Long id) { + // 获取退料单 + Document returnDoc = this.baseMapper.selectById(id); + if (Objects.isNull(returnDoc)) { + throw new BusinessException("退料单不存在"); + } + if (!returnDoc.getFormStatus().equals(FormStatus.APPROVE)) { + throw new BusinessException("退料单不是已审核状态,不能反审"); + } + + // 获取退料单明细 + List materials = documentMaterialMapper + .selectList(new LambdaQueryWrapper().eq(DocumentMaterial::getDocumentNo, returnDoc.getId())); + if (materials == null || materials.isEmpty()) { + throw new BusinessException("退料单没有明细"); + } + + // 更新库存 + List stock = stockMapper + .selectList(new LambdaQueryWrapper() + .eq(Stock::getStoreNo, returnDoc.getStoreNo()) + .in(Stock::getPartNumber, materials.stream().map(DocumentMaterial::getPartNumber).toList())); + + Map> materialMap = materials.stream() + .collect(Collectors.groupingBy(DocumentMaterial::getPartNumber)); + + stock.forEach(s -> { + int returnQty = materialMap.get(s.getPartNumber()).getFirst().getProductCount(); + s.setProductCount(s.getProductCount() - returnQty); + s.setUpdateDate(LocalDateTime.now()); + s.setUpdateUserId(SecurityUtils.getUserId()); + s.setUpdateUserName(SecurityUtils.getUserName()); + }); + + // 更新退料单状态 + returnDoc.setUpdateDate(LocalDateTime.now()); + returnDoc.setUpdateUserId(SecurityUtils.getUserId()); + returnDoc.setUpdateUserName(SecurityUtils.getUserName()); + returnDoc.setFormStatus(FormStatus.NO_APPROVE); + + // 执行更新 + this.baseMapper.updateById(returnDoc); + stockMapper.updateById(stock); } } \ No newline at end of file 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 72f0712..cd91974 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 @@ -30,7 +30,7 @@ public class PurchaseOrderController { @Operation(summary = "分页查询PurchaseOrder数据", operationId = "getPurchaseOrderPage") @GetMapping("/getPurchaseOrderPage") - @PreAuthorize("hasAuthority('purchaseorder:index')") + @PreAuthorize("hasAuthority('purchase_order:index')") public BaseResult> getPurchaseOrderPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) PurchaseOrderDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -41,7 +41,7 @@ public class PurchaseOrderController { @ApiLog(type = OperationType.ADD, remark = "新增一条PurchaseOrder记录") @Operation(summary = "新增PurchaseOrder", operationId = "addPurchaseOrder") @PostMapping("/addPurchaseOrder") - @PreAuthorize("hasAuthority('purchaseorder:add')") + @PreAuthorize("hasAuthority('purchase_order:add')") public BaseResult addPurchaseOrder(@Validated(Add.class) @RequestBody PurchaseOrderDto dto) { purchaseOrderService.addPurchaseOrder(dto); return BaseResult.success(); @@ -50,7 +50,7 @@ public class PurchaseOrderController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条PurchaseOrder记录") @Operation(summary = "更新PurchaseOrder", operationId = "updatePurchaseOrder") @PostMapping("/updatePurchaseOrder") - @PreAuthorize("hasAuthority('purchaseorder:update')") + @PreAuthorize("hasAuthority('purchase_order:update')") public BaseResult updatePurchaseOrder(@Validated(Update.class) @RequestBody PurchaseOrderDto dto) { purchaseOrderService.updatePurchaseOrder(dto); return BaseResult.success(); @@ -59,7 +59,7 @@ public class PurchaseOrderController { @ApiLog(type = OperationType.DELETE, remark = "删除一条PurchaseOrder记录") @Operation(summary = "删除PurchaseOrder", operationId = "deletePurchaseOrder") @PostMapping("/deletePurchaseOrder") - @PreAuthorize("hasAuthority('purchaseorder:delete')") + @PreAuthorize("hasAuthority('purchase_order:delete')") public BaseResult deletePurchaseOrder(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { purchaseOrderService.deletePurchaseOrder(req.id()); return BaseResult.success(); @@ -68,7 +68,7 @@ public class PurchaseOrderController { @ApiLog(type = OperationType.DELETE, remark = "批量删除PurchaseOrder记录") @Operation(summary = "批量删除PurchaseOrder", operationId = "deletePurchaseOrderBatch") @PostMapping("/deletePurchaseOrderBatch") - @PreAuthorize("hasAuthority('purchaseorder:deleteBatch')") + @PreAuthorize("hasAuthority('purchase_order:deleteBatch')") public BaseResult deletePurchaseOrderBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { purchaseOrderService.deleteBatch(req.ids()); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/purchase/controller/PurchasePlanController.java b/src/main/java/com/niuan/erp/module/purchase/controller/PurchasePlanController.java index 02e8d3f..9dab607 100644 --- a/src/main/java/com/niuan/erp/module/purchase/controller/PurchasePlanController.java +++ b/src/main/java/com/niuan/erp/module/purchase/controller/PurchasePlanController.java @@ -9,8 +9,12 @@ 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.purchase.controller.dto.GeneratePurchaseOrderDto; import com.niuan.erp.module.purchase.controller.dto.PurchasePlanAddDto; import com.niuan.erp.module.purchase.controller.dto.PurchasePlanDto; +import com.niuan.erp.module.purchase.controller.dto.PurchasePlanItemDto; +import com.niuan.erp.module.purchase.controller.dto.PurchasePlanItemsWithVendorSuggestionsDto; +import com.niuan.erp.module.purchase.controller.dto.VendorSuggestionDto; import com.niuan.erp.module.purchase.entity.PurchasePlan; import com.niuan.erp.module.purchase.service.PurchasePlanService; import io.swagger.v3.oas.annotations.Operation; @@ -21,8 +25,10 @@ import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -@Tag(name = "PurchasePlan") -@ModuleLog("PurchasePlan管理") +import java.util.List; + +@Tag(name = "采购计划") +@ModuleLog("采购计划管理") @RestController @RequestMapping("/purchase/purchaseplan") @RequiredArgsConstructor @@ -30,9 +36,9 @@ public class PurchasePlanController { private final PurchasePlanService purchasePlanService; - @Operation(summary = "分页查询PurchasePlan数据", operationId = "getPurchasePlanPage") + @Operation(summary = "分页查询采购计划数据", operationId = "getPurchasePlanPage") @GetMapping("/getPurchasePlanPage") - @PreAuthorize("hasAuthority('purchaseplan:index')") + @PreAuthorize("hasAuthority('purchase_plan:index')") public BaseResult> getPurchasePlanPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) PurchasePlanDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -41,43 +47,84 @@ public class PurchasePlanController { wrapper.like(PurchasePlan::getVendorName, searchParams.vendorName()); } } + // 按创建时间倒序排序 + wrapper.orderByDesc(PurchasePlan::getCreateDate); return BaseResult.successWithData(purchasePlanService.getPurchasePlanPage(pageParams, wrapper)); } - @ApiLog(type = OperationType.ADD, remark = "新增一条PurchasePlan记录") - @Operation(summary = "新增PurchasePlan", operationId = "addPurchasePlan") + @ApiLog(type = OperationType.ADD, remark = "新增一条采购计划记录") + @Operation(summary = "新增采购计划", operationId = "addPurchasePlan") @PostMapping("/addPurchasePlan") - @PreAuthorize("hasAuthority('purchaseplan:add')") + @PreAuthorize("hasAuthority('purchase_plan:add')") public BaseResult addPurchasePlan(@Validated(Add.class) @RequestBody PurchasePlanAddDto dto) { purchasePlanService.addPurchasePlan(dto); return BaseResult.success(); } - @ApiLog(type = OperationType.UPDATE, remark = "更新一条PurchasePlan记录") - @Operation(summary = "更新PurchasePlan", operationId = "updatePurchasePlan") + @ApiLog(type = OperationType.UPDATE, remark = "更新一条采购计划记录") + @Operation(summary = "更新采购计划", operationId = "updatePurchasePlan") @PostMapping("/updatePurchasePlan") - @PreAuthorize("hasAuthority('purchaseplan:update')") + @PreAuthorize("hasAuthority('purchase_plan:update')") public BaseResult updatePurchasePlan(@Validated(Update.class) @RequestBody PurchasePlanDto dto) { purchasePlanService.updatePurchasePlan(dto); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "删除一条PurchasePlan记录") - @Operation(summary = "删除PurchasePlan", operationId = "deletePurchasePlan") + @ApiLog(type = OperationType.DELETE, remark = "删除一条采购计划记录") + @Operation(summary = "删除采购计划", operationId = "deletePurchasePlan") @PostMapping("/deletePurchasePlan") - @PreAuthorize("hasAuthority('purchaseplan:delete')") + @PreAuthorize("hasAuthority('purchase_plan:delete')") public BaseResult deletePurchasePlan(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { purchasePlanService.deletePurchasePlan(req.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "批量删除PurchasePlan记录") - @Operation(summary = "批量删除PurchasePlan", operationId = "deletePurchasePlanBatch") + @ApiLog(type = OperationType.DELETE, remark = "批量删除采购计划记录") + @Operation(summary = "批量删除采购计划", operationId = "deletePurchasePlanBatch") @PostMapping("/deletePurchasePlanBatch") - @PreAuthorize("hasAuthority('purchaseplan:deleteBatch')") + @PreAuthorize("hasAuthority('purchase_plan:deleteBatch')") public BaseResult deletePurchasePlanBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { purchasePlanService.deleteBatch(req.ids()); return BaseResult.success(); } + + @Operation(summary = "获取采购计划明细", operationId = "getPurchasePlanItems") + @GetMapping("/getPurchasePlanItems") + @PreAuthorize("hasAuthority('purchase_plan:index')") + public BaseResult> getPurchasePlanItems(@RequestParam Long planId) { + return BaseResult.successWithData(purchasePlanService.getPurchasePlanItems(planId)); + } + + @Operation(summary = "获取采购计划明细及供应商建议", operationId = "getPurchasePlanItemsWithVendorSuggestions") + @GetMapping("/getPurchasePlanItemsWithVendorSuggestions") + @PreAuthorize("hasAuthority('purchase_plan:generatePurchaseOrder')") + public BaseResult getPurchasePlanItemsWithVendorSuggestions(@RequestParam Long planId) { + return BaseResult.successWithData(purchasePlanService.getPurchasePlanItemsWithVendorSuggestions(planId)); + } + + @ApiLog(type = OperationType.UPDATE, remark = "生成采购订单") + @Operation(summary = "生成采购订单", operationId = "generatePurchaseOrder") + @PostMapping("/generatePurchaseOrder") + @PreAuthorize("hasAuthority('purchase_plan:generatePurchaseOrder')") + public BaseResult generatePurchaseOrder(@Validated @RequestBody GeneratePurchaseOrderDto dto) { + purchasePlanService.generatePurchaseOrder( + dto.planId(), + dto.selectedItems(), + dto.vendorId(), + dto.vendorName(), + dto.formCode(), + dto.formName(), + dto.formMark(), + dto.totalValue() + ); + return BaseResult.success(); + } + + @Operation(summary = "获取供应商推荐列表", operationId = "getVendorSuggestions") + @GetMapping("/getVendorSuggestions") + @PreAuthorize("hasAuthority('purchase_plan:generatePurchaseOrder')") + public BaseResult> getVendorSuggestions(@RequestParam String partNumber) { + return BaseResult.successWithData(purchasePlanService.getVendorSuggestions(partNumber)); + } } diff --git a/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchasePlanItemDto.java b/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchasePlanItemDto.java index 33dca35..28a6891 100644 --- a/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchasePlanItemDto.java +++ b/src/main/java/com/niuan/erp/module/purchase/controller/dto/PurchasePlanItemDto.java @@ -12,4 +12,11 @@ public record PurchasePlanItemDto( Integer completeCount, Integer currentCount, Integer completeStatus, - Long partId) {} \ No newline at end of file + Long partId, + String productSpecs, + Integer productPacking, + Long vendorId, + String vendorName, + Integer vendorCode1, + Integer vendorCode2, + Integer vendorCode3) {} \ 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 40ad278..e0b02c7 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 @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.niuan.erp.module.purchase.enums.PurchasePlanItemStatus; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -23,7 +24,7 @@ import java.math.BigDecimal; @Setter @ToString @TableName("purchaseplandetails") -public class PurchasePlanItem implements Serializable { +public class PurchasePlanItem implements Serializable { private static final long serialVersionUID = 1L; @@ -52,7 +53,7 @@ public class PurchasePlanItem implements Serializable { private Integer currentCount; @TableField("completeStatus") - private Integer completeStatus; + private PurchasePlanItemStatus completeStatus; @TableField("PartId") private Long partId; diff --git a/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanItemStatus.java b/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanItemStatus.java index 1b5a824..3a3fd82 100644 --- a/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanItemStatus.java +++ b/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanItemStatus.java @@ -1,4 +1,31 @@ package com.niuan.erp.module.purchase.enums; -public enum PurchasePlanItemStatus { +import com.baomidou.mybatisplus.annotation.IEnum; +import lombok.Getter; + +@Getter +public enum PurchasePlanItemStatus implements IEnum { + NO_START(0, "未开始"), + ORDERED (1, "已下单"), + COMPLETED(2, "已完成"); + final int value; + final String description; + PurchasePlanItemStatus(int value, String description) { + this.value = value; + this.description = description; + } + + /** + * + * @param value + * @return + */ + public static PurchasePlanItemStatus fromCode(int value) { + return PurchasePlanItemStatus.values()[value]; + } + + @Override + public Integer getValue() { + return value; + } } diff --git a/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanStatus.java b/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanStatus.java index 739de35..66889d5 100644 --- a/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanStatus.java +++ b/src/main/java/com/niuan/erp/module/purchase/enums/PurchasePlanStatus.java @@ -6,7 +6,7 @@ import com.niuan.erp.common.base.BaseStatus; public enum PurchasePlanStatus implements IEnum { UNSTART(0, "未开始"), IN_PROGRESS(1, "执行中"), - Completed(2, "已完成"); + COMPLETED(2, "已完成"); final int code; final String description; PurchasePlanStatus(int code, String description) { diff --git a/src/main/java/com/niuan/erp/module/purchase/service/PurchasePlanService.java b/src/main/java/com/niuan/erp/module/purchase/service/PurchasePlanService.java index c76a1cd..12c82ea 100644 --- a/src/main/java/com/niuan/erp/module/purchase/service/PurchasePlanService.java +++ b/src/main/java/com/niuan/erp/module/purchase/service/PurchasePlanService.java @@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.niuan.erp.common.base.BasePageReqParams; import com.niuan.erp.module.purchase.controller.dto.PurchasePlanAddDto; import com.niuan.erp.module.purchase.controller.dto.PurchasePlanDto; +import com.niuan.erp.module.purchase.controller.dto.PurchasePlanItemDto; +import com.niuan.erp.module.purchase.controller.dto.PurchasePlanItemsWithVendorSuggestionsDto; +import com.niuan.erp.module.purchase.controller.dto.VendorSuggestionDto; import com.niuan.erp.module.purchase.entity.PurchasePlan; import java.util.List; @@ -21,4 +24,13 @@ public interface PurchasePlanService { void deleteBatch(List ids); + List getPurchasePlanItems(Long planId); + + PurchasePlanItemsWithVendorSuggestionsDto getPurchasePlanItemsWithVendorSuggestions(Long planId); + + void generatePurchaseOrder(Long planId, List selectedItems, Long vendorId, String vendorName, + String formCode, String formName, String formMark, java.math.BigDecimal totalValue); + + List getVendorSuggestions(String partNumber); + } 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 d2acc82..63f6d20 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 @@ -6,22 +6,47 @@ 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.utils.SecurityUtils; +import com.niuan.erp.module.common.entity.Document; +import com.niuan.erp.module.common.enums.DocumentType; +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; +import com.niuan.erp.module.purchase.controller.dto.PurchasePlanItemDto; +import com.niuan.erp.module.purchase.controller.dto.PurchasePlanItemsWithVendorSuggestionsDto; +import com.niuan.erp.module.purchase.controller.dto.VendorInfoDto; +import com.niuan.erp.module.purchase.controller.dto.VendorSuggestionDto; +import com.niuan.erp.module.purchase.controller.dto.PartVendorMappingDto; import com.niuan.erp.module.purchase.converter.PurchasePlanConverter; import com.niuan.erp.module.purchase.converter.PurchasePlanItemConverter; +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.PurchasePlanService; +import com.niuan.erp.module.sys.entity.Vendor; +import com.niuan.erp.module.sys.mapper.VendorMapper; +import com.niuan.erp.module.warehouse.entity.ProductVendorMap; +import com.niuan.erp.module.warehouse.entity.WarehouseItem; +import com.niuan.erp.module.warehouse.mapper.ProductVendorMapMapper; +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.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; @Service @@ -35,6 +60,16 @@ public class PurchasePlanServiceImpl extends ServiceImpl getPurchasePlanPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); @@ -54,6 +89,11 @@ public class PurchasePlanServiceImpl extends ServiceImpl planItems = purchasePlanItemConverter.toEntityList(dto.planItems()); planItems.forEach(item -> { item.setPlanId(entity.getId()); + item.setCompleteCount(0); + item.setPrice(new BigDecimal(0)); + item.setTotalPrice(new BigDecimal(0)); + item.setCompleteStatus(PurchasePlanItemStatus.NO_START); + item.setCurrentCount(item.getPurchaseCount()); }); purchasePlanItemMapper.insert(planItems); } @@ -76,4 +116,476 @@ public class PurchasePlanServiceImpl extends ServiceImpl getPurchasePlanItems(Long planId) { + // 1. 查询采购计划明细 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PurchasePlanItem::getPlanId, planId); + List items = purchasePlanItemMapper.selectList(wrapper); + + if (items.isEmpty()) { + return new ArrayList<>(); + } + + // 2. 一次性查询所有相关的 WarehouseItem + List partNumbers = items.stream() + .map(PurchasePlanItem::getPartNumber) + .collect(Collectors.toList()); + + LambdaQueryWrapper warehouseItemWrapper = new LambdaQueryWrapper<>(); + warehouseItemWrapper.in(WarehouseItem::getPartNumber, partNumbers); + List warehouseItems = warehouseItemMapper.selectList(warehouseItemWrapper); + + // 创建 partNumber 到 WarehouseItem 的映射 + Map warehouseItemMap = warehouseItems.stream() + .collect(Collectors.toMap(WarehouseItem::getPartNumber, item -> item, (v1, v2) -> v1)); + + // 3. 一次性查询所有相关的 ProductVendorMap + LambdaQueryWrapper vendorMapWrapper = new LambdaQueryWrapper<>(); + vendorMapWrapper.in(ProductVendorMap::getPartNumber, partNumbers); + vendorMapWrapper.orderByDesc(ProductVendorMap::getId); + List productVendorMaps = productVendorMapMapper.selectList(vendorMapWrapper); + + // 创建 partNumber 到 ProductVendorMap 的映射(取每个 partNumber 的第一条,即最新的) + Map vendorMap = new java.util.LinkedHashMap<>(); + for (ProductVendorMap pvm : productVendorMaps) { + if (!vendorMap.containsKey(pvm.getPartNumber())) { + vendorMap.put(pvm.getPartNumber(), pvm); + } + } + + // 4. 组装 DTO + return items.stream() + .map(item -> { + PurchasePlanItemDto dto = purchasePlanItemConverter.toDto(item); + + // 从 Map 中获取 WarehouseItem + WarehouseItem warehouseItem = warehouseItemMap.get(item.getPartNumber()); + Integer vendorCode1 = null; + Integer vendorCode2 = null; + Integer vendorCode3 = null; + String productSpecs = null; + Integer productPacking = null; + + if (warehouseItem != null) { + vendorCode1 = warehouseItem.getVendorCode1(); + vendorCode2 = warehouseItem.getVendorCode2(); + vendorCode3 = warehouseItem.getVendorCode3(); + productSpecs = warehouseItem.getProductSpecs(); + productPacking = warehouseItem.getProductPacking(); + } + + // 从 Map 中获取 ProductVendorMap + ProductVendorMap productVendorMap = vendorMap.get(item.getPartNumber()); + Long vendorId = null; + String vendorName = null; + if (productVendorMap != null) { + vendorId = productVendorMap.getVendorId(); + } + + return new PurchasePlanItemDto( + dto.id(), + dto.planId(), + dto.partNumber(), + dto.price(), + dto.purchaseCount(), + dto.totalPrice(), + dto.completeCount(), + dto.currentCount(), + dto.completeStatus(), + dto.partId(), + productSpecs, + productPacking, + vendorId, + vendorName, + vendorCode1, + vendorCode2, + vendorCode3 + ); + }) + .collect(Collectors.toList()); + } + + @Override + public PurchasePlanItemsWithVendorSuggestionsDto getPurchasePlanItemsWithVendorSuggestions(Long planId) { + // 1. 查询采购计划明细 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PurchasePlanItem::getPlanId, planId); + List items = purchasePlanItemMapper.selectList(wrapper); + + if (items.isEmpty()) { + return new PurchasePlanItemsWithVendorSuggestionsDto(new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + + // 2. 一次性查询所有相关的 WarehouseItem + List partNumbers = items.stream() + .map(PurchasePlanItem::getPartNumber) + .collect(Collectors.toList()); + + LambdaQueryWrapper warehouseItemWrapper = new LambdaQueryWrapper<>(); + warehouseItemWrapper.in(WarehouseItem::getPartNumber, partNumbers); + List warehouseItems = warehouseItemMapper.selectList(warehouseItemWrapper); + + // 创建 partNumber 到 WarehouseItem 的映射 + Map warehouseItemMap = warehouseItems.stream() + .collect(Collectors.toMap(WarehouseItem::getPartNumber, item -> item, (v1, v2) -> v1)); + + // 3. 一次性查询所有相关的 ProductVendorMap + LambdaQueryWrapper vendorMapWrapper = new LambdaQueryWrapper<>(); + vendorMapWrapper.in(ProductVendorMap::getPartNumber, partNumbers); + vendorMapWrapper.orderByDesc(ProductVendorMap::getId); + List productVendorMaps = productVendorMapMapper.selectList(vendorMapWrapper); + + // 创建 partNumber 到 ProductVendorMap 的映射(取每个 partNumber 的第一条,即最新的) + Map productVendorMap = new java.util.LinkedHashMap<>(); + for (ProductVendorMap pvm : productVendorMaps) { + if (!productVendorMap.containsKey(pvm.getPartNumber())) { + productVendorMap.put(pvm.getPartNumber(), pvm); + } + } + + // 4. 一次性查询所有启用的供应商 + LambdaQueryWrapper allVendorsWrapper = new LambdaQueryWrapper<>(); + allVendorsWrapper.eq(Vendor::getStatus, 0) + .orderByDesc(Vendor::getCreateDate); + List allVendors = vendorMapper.selectList(allVendorsWrapper); + + // 创建 vendorId 到 Vendor 的映射 + Map vendorMap = allVendors.stream() + .collect(Collectors.toMap(Vendor::getId, vendor -> vendor, (v1, v2) -> v1)); + + // 5. 组装 DTO + List itemDtos = items.stream() + .map(item -> { + PurchasePlanItemDto dto = purchasePlanItemConverter.toDto(item); + + WarehouseItem warehouseItem = warehouseItemMap.get(item.getPartNumber()); + Integer vendorCode1 = null; + Integer vendorCode2 = null; + Integer vendorCode3 = null; + String productSpecs = null; + Integer productPacking = null; + + if (warehouseItem != null) { + vendorCode1 = warehouseItem.getVendorCode1(); + vendorCode2 = warehouseItem.getVendorCode2(); + vendorCode3 = warehouseItem.getVendorCode3(); + productSpecs = warehouseItem.getProductSpecs(); + productPacking = warehouseItem.getProductPacking(); + } + + // 对于已采购的明细,从 ProductVendorMap 和 allVendors 中获取供应商名称 + String vendorName = null; + if (item.getCompleteStatus() != null && item.getCompleteStatus().getValue() != 0) { + ProductVendorMap pvm = productVendorMap.get(item.getPartNumber()); + if (pvm != null) { + Vendor vendor = vendorMap.get(pvm.getVendorId()); + if (vendor != null) { + vendorName = vendor.getVendorName(); + } + } + } + + return new PurchasePlanItemDto( + dto.id(), + dto.planId(), + dto.partNumber(), + dto.price(), + dto.purchaseCount(), + dto.totalPrice(), + dto.completeCount(), + dto.currentCount(), + dto.completeStatus(), + dto.partId(), + productSpecs, + productPacking, + null, + vendorName, + vendorCode1, + vendorCode2, + vendorCode3 + ); + }) + .collect(Collectors.toList()); + + // 7. 组装供应商列表 + List vendorInfoDtos = allVendors.stream() + .map(vendor -> new VendorInfoDto( + vendor.getId(), + vendor.getVendorName(), + vendor.getCreateDate() != null ? vendor.getCreateDate().toString() : null + )) + .collect(Collectors.toList()); + + // 8. 组装物料供应商映射关系 + List partVendorMappingDtos = productVendorMaps.stream() + .map(pvm -> new PartVendorMappingDto( + pvm.getPartNumber(), + pvm.getVendorId(), + pvm.getCostPrice(), + pvm.getProcureDate() != null ? pvm.getProcureDate().toString() : null + )) + .collect(Collectors.toList()); + + return new PurchasePlanItemsWithVendorSuggestionsDto(itemDtos, vendorInfoDtos, partVendorMappingDtos); + } + + @Override + public void generatePurchaseOrder(Long planId, List selectedItems, Long vendorId, String vendorName, + String formCode, String formName, String formMark, java.math.BigDecimal totalValue) { + // 1. 获取采购计划 + PurchasePlan plan = this.baseMapper.selectById(planId); + if (plan == null) { + throw new IllegalArgumentException("purchase.purchase_plan.exception.plan_not_exists"); + } + + // 2. 检查选中的物料是否已经生成采购订单 + List partNumbers = selectedItems.stream() + .map(PurchasePlanItemDto::partNumber) + .collect(Collectors.toList()); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PurchasePlanItem::getPlanId, planId) + .in(PurchasePlanItem::getPartNumber, partNumbers) + .eq(PurchasePlanItem::getCompleteStatus, 1); + + List existingItems = purchasePlanItemMapper.selectList(wrapper); + if (!existingItems.isEmpty()) { + String existingPartNumbers = existingItems.stream() + .map(PurchasePlanItem::getPartNumber) + .collect(Collectors.joining(",")); + throw new IllegalArgumentException("purchase.purchase_plan.exception.items_already_ordered: " + existingPartNumbers); + } + + // 3. 生成采购订单主表 + Document purchaseOrder = new Document(); + purchaseOrder.setCreateDate(LocalDateTime.now()); + purchaseOrder.setCreateUserId(SecurityUtils.getUserId()); + purchaseOrder.setCreateUserName(SecurityUtils.getUserName()); + purchaseOrder.setFormCode(formCode); + purchaseOrder.setFormName(formName); + purchaseOrder.setVendorName(vendorName); + purchaseOrder.setVendorNo(vendorId.intValue()); + purchaseOrder.setFormMark(formMark); + purchaseOrder.setTotalValue(totalValue.doubleValue()); + purchaseOrder.setFormType(DocumentType.PURCHASE_ORDER); + purchaseOrder.setStatus(0); + + // 4. 保存采购订单 + documentMapper.insert(purchaseOrder); + + // 5. 生成采购订单明细 + List orderItems = new ArrayList<>(); + for (PurchasePlanItemDto item : selectedItems) { + PurchaseOrderItem orderItem = new PurchaseOrderItem(); + orderItem.setStatus(0); + orderItem.setCreateDate(LocalDateTime.now()); + orderItem.setCreateUserId(SecurityUtils.getUserId()); + orderItem.setCreateUserName(SecurityUtils.getUserName()); + orderItem.setDocumentNo(purchaseOrder.getId().intValue()); + orderItem.setDocumentCode(formCode); + orderItem.setPartNumber(item.partNumber()); + orderItem.setPurchaseCount(item.currentCount()); + orderItem.setPrice(item.price().doubleValue()); + orderItem.setTotalPrice(item.currentCount() * item.price().doubleValue()); + orderItem.setReceiptCount(0); + orderItem.setPartId(item.partId()); + orderItems.add(orderItem); + } + + if (!orderItems.isEmpty()) { + purchaseOrderItemMapper.insert(orderItems); + } + + // 6. 更新采购计划明细状态并处理供应商关联 + // 一次性查询所有采购计划明细 + LambdaQueryWrapper allItemsWrapper = new LambdaQueryWrapper<>(); + allItemsWrapper.eq(PurchasePlanItem::getPlanId, planId) + .in(PurchasePlanItem::getPartNumber, partNumbers); + List allPlanItems = purchasePlanItemMapper.selectList(allItemsWrapper); + + // 创建 partNumber 到 PurchasePlanItem 的映射 + Map planItemMap = allPlanItems.stream() + .collect(Collectors.toMap(PurchasePlanItem::getPartNumber, item -> item, (v1, v2) -> v1)); + + selectedItems.forEach(item -> { + // 更新采购计划明细 + PurchasePlanItem planItem = planItemMap.get(item.partNumber()); + if (planItem != null) { + Integer currentCompleteCount = planItem.getCompleteCount() != null ? planItem.getCompleteCount() : 0; + planItem.setCompleteCount(currentCompleteCount + item.currentCount()); + if (planItem.getCompleteCount() >= planItem.getPurchaseCount()) { + planItem.setCompleteStatus(PurchasePlanItemStatus.ORDERED); + } + purchasePlanItemMapper.updateById(planItem); + } + + // 处理供应商关联逻辑 + if (item.vendorId() != null) { + processVendorMapping(item.partNumber(), item.vendorId(), item.price()); + } + }); + + // 8. 更新采购计划状态为采购中 + plan.setPlanStatus(PurchasePlanStatus.IN_PROGRESS); + this.baseMapper.updateById(plan); + } + + /** + * 处理供应商映射关系 + * 1. 更新 WarehouseItem 中的 VendorCode1/2/3 + * 2. 插入或更新 ProductVendorMap + */ + private void processVendorMapping(String partNumber, Long vendorId, BigDecimal price) { + // 1. 获取 WarehouseItem + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(WarehouseItem::getPartNumber, partNumber); + List warehouseItems = warehouseItemMapper.selectList(itemWrapper); + if (warehouseItems.isEmpty()) { + return; + } + WarehouseItem warehouseItem = warehouseItems.get(0); + + // 2. 获取当前供应商在 VendorCode 中的位置 + Integer vendorCode1 = warehouseItem.getVendorCode1(); + Integer vendorCode2 = warehouseItem.getVendorCode2(); + Integer vendorCode3 = warehouseItem.getVendorCode3(); + + // 3. 检查供应商是否已存在于 VendorCode 中 + boolean existsInVendorCodes = (vendorCode1 != null && vendorCode1.equals(vendorId.intValue())) || + (vendorCode2 != null && vendorCode2.equals(vendorId.intValue())) || + (vendorCode3 != null && vendorCode3.equals(vendorId.intValue())); + + if (!existsInVendorCodes) { + // 供应商不存在于 VendorCode 中,需要重新排序 + // 将现有供应商往后移,新供应商放在第一位 + Integer newVendorCode3 = vendorCode2; + Integer newVendorCode2 = vendorCode1; + Integer newVendorCode1 = vendorId.intValue(); + + warehouseItem.setVendorCode1(newVendorCode1); + warehouseItem.setVendorCode2(newVendorCode2); + warehouseItem.setVendorCode3(newVendorCode3); + warehouseItemMapper.updateById(warehouseItem); + } else { + // 供应商已存在于 VendorCode 中,重新排序将该供应商放在第一位 + if (vendorCode1 != null && vendorCode1.equals(vendorId.intValue())) { + // 已经在第一位,不需要移动 + } else if (vendorCode2 != null && vendorCode2.equals(vendorId.intValue())) { + // 在第二位,移到第一位 + warehouseItem.setVendorCode2(vendorCode1); + warehouseItem.setVendorCode1(vendorId.intValue()); + warehouseItemMapper.updateById(warehouseItem); + } else if (vendorCode3 != null && vendorCode3.equals(vendorId.intValue())) { + // 在第三位,移到第一位 + warehouseItem.setVendorCode3(vendorCode2); + warehouseItem.setVendorCode2(vendorCode1); + warehouseItem.setVendorCode1(vendorId.intValue()); + warehouseItemMapper.updateById(warehouseItem); + } + } + + // 4. 处理 ProductVendorMap + LambdaQueryWrapper mapWrapper = new LambdaQueryWrapper<>(); + mapWrapper.eq(ProductVendorMap::getPartNumber, partNumber) + .eq(ProductVendorMap::getVendorId, vendorId); + ProductVendorMap existingMap = productVendorMapMapper.selectOne(mapWrapper); + + if (existingMap == null) { + // 不存在则新增 + ProductVendorMap newMap = new ProductVendorMap(); + newMap.setPartNumber(partNumber); + newMap.setVendorId(vendorId); + newMap.setCostPrice(price != null ? price : BigDecimal.ZERO); + newMap.setProcureDate(LocalDateTime.now()); + productVendorMapMapper.insert(newMap); + } else { + // 存在则更新价格和时间 + existingMap.setCostPrice(price != null ? price : BigDecimal.ZERO); + existingMap.setProcureDate(LocalDateTime.now()); + productVendorMapMapper.updateById(existingMap); + } + } + + @Override + public List getVendorSuggestions(String partNumber) { + // 1. 一次性查询所有需要的数据 + // 查询 WarehouseItem + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(WarehouseItem::getPartNumber, partNumber); + List warehouseItems = warehouseItemMapper.selectList(itemWrapper); + + // 查询 ProductVendorMap + LambdaQueryWrapper mapWrapper = new LambdaQueryWrapper<>(); + mapWrapper.eq(ProductVendorMap::getPartNumber, partNumber) + .orderByDesc(ProductVendorMap::getProcureDate); + List productVendorMaps = productVendorMapMapper.selectList(mapWrapper); + + // 查询所有启用的供应商 + LambdaQueryWrapper allVendorsWrapper = new LambdaQueryWrapper<>(); + allVendorsWrapper.eq(Vendor::getStatus, 0) + .orderByDesc(Vendor::getCreateDate); + List allVendors = vendorMapper.selectList(allVendorsWrapper); + + // 创建供应商 ID 到 Vendor 的映射 + Map vendorMap = allVendors.stream() + .collect(Collectors.toMap(Vendor::getId, v -> v)); + + // 创建 partNumber+vendorId 到 ProductVendorMap 的映射 + Map vendorPriceMap = productVendorMaps.stream() + .collect(Collectors.toMap(ProductVendorMap::getVendorId, v -> v, (v1, v2) -> v1)); + + List result = new ArrayList<>(); + Set addedVendorIds = new java.util.HashSet<>(); + + // 2. 获取 WarehouseItem 中的 VendorCode1/2/3 + if (!warehouseItems.isEmpty()) { + WarehouseItem warehouseItem = warehouseItems.get(0); + Integer vendorCode1 = warehouseItem.getVendorCode1(); + Integer vendorCode2 = warehouseItem.getVendorCode2(); + Integer vendorCode3 = warehouseItem.getVendorCode3(); + + // 按优先级添加 VendorCode1/2/3 + Integer[] vendorCodes = {vendorCode1, vendorCode2, vendorCode3}; + for (int i = 0; i < vendorCodes.length; i++) { + if (vendorCodes[i] != null && !addedVendorIds.contains(vendorCodes[i].longValue())) { + Vendor vendor = vendorMap.get(vendorCodes[i].longValue()); + if (vendor != null) { + ProductVendorMap pvm = vendorPriceMap.get(vendor.getId()); + BigDecimal price = pvm != null && pvm.getCostPrice() != null + ? pvm.getCostPrice() : BigDecimal.ZERO; + result.add(new VendorSuggestionDto(vendor.getId(), vendor.getVendorName(), i + 1, price)); + addedVendorIds.add(vendor.getId()); + } + } + } + } + + // 3. 添加 ProductVendorMap 中的供应商(优先级 2) + for (ProductVendorMap pvm : productVendorMaps) { + if (!addedVendorIds.contains(pvm.getVendorId())) { + Vendor vendor = vendorMap.get(pvm.getVendorId()); + if (vendor != null) { + result.add(new VendorSuggestionDto( + vendor.getId(), + vendor.getVendorName(), + 2, + pvm.getCostPrice() != null ? pvm.getCostPrice() : BigDecimal.ZERO + )); + addedVendorIds.add(vendor.getId()); + } + } + } + + // 4. 添加其他供应商(优先级 3) + for (Vendor vendor : allVendors) { + if (!addedVendorIds.contains(vendor.getId())) { + result.add(new VendorSuggestionDto(vendor.getId(), vendor.getVendorName(), 3, BigDecimal.ZERO)); + addedVendorIds.add(vendor.getId()); + } + } + + return result; + } + } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/sale/controller/DeviceController.java b/src/main/java/com/niuan/erp/module/sale/controller/DeviceController.java index bf245da..b267c74 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/DeviceController.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/DeviceController.java @@ -7,6 +7,7 @@ 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.BaseSelectDto; import com.niuan.erp.common.base.CommonValidateGroup.*; import com.niuan.erp.common.base.OperationType; import com.niuan.erp.module.sale.controller.dto.DeviceDto; @@ -20,6 +21,8 @@ import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + @Tag(name = "Device") @ModuleLog("Device管理") @RestController @@ -35,15 +38,41 @@ public class DeviceController { public BaseResult> getDevicePage(@Validated BasePageReqParams pageParams, @Validated(Get.class) DeviceDto searchParams) { var wrapper = new LambdaQueryWrapper(); - if (searchParams != null) { - if (StringUtils.hasText(searchParams.searchCode())) { - wrapper.like(Device::getProductSn, searchParams.searchCode()) - .or().like(Device::getMac, searchParams.mac()); - } + if (searchParams != null) { + if (StringUtils.hasText(searchParams.searchCode())) { + wrapper.like(Device::getProductSn, searchParams.searchCode()) + .or().like(Device::getMac, searchParams.searchCode()); } + if (searchParams.keyAccountId() != null) { + wrapper.eq(Device::getKeyAccountId, searchParams.keyAccountId()); + } + if (searchParams.status() != null) { + wrapper.eq(Device::getStatus, searchParams.status()); + } + if (searchParams.startDate() != null) { + wrapper.gt(Device::getCreateDate, searchParams.startDate()); + } + if (searchParams.endDate() != null) { + wrapper.lt(Device::getCreateDate, searchParams.endDate()); + } + if (StringUtils.hasText(searchParams.orderFiled())) { + boolean isDesc = "desc".equalsIgnoreCase(searchParams.orderByType()); + if (isDesc) { + wrapper.orderByDesc(true, Device::getId); + } else { + wrapper.orderByAsc(true, Device::getId); + } + } + } return BaseResult.successWithData(deviceService.getDevicePage(pageParams, wrapper)); } + @Operation(summary = "获取客户信息", operationId = "getKeyAccount") + @GetMapping("/getKeyAccount") + @PreAuthorize("hasAuthority('device:index')") + public BaseResult> getKeyAccount() { + return BaseResult.successWithData(deviceService.getKeyAccount()); + } @ApiLog(type = OperationType.ADD, remark = "新增一条Device记录") @Operation(summary = "新增Device", operationId = "addDevice") diff --git a/src/main/java/com/niuan/erp/module/sale/controller/RepairRecordController.java b/src/main/java/com/niuan/erp/module/sale/controller/RepairRecordController.java index 6b51209..eea4262 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/RepairRecordController.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/RepairRecordController.java @@ -12,7 +12,7 @@ import com.niuan.erp.module.sale.entity.RepairRecord; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory;import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; @@ -26,28 +26,54 @@ import java.util.List; @RequiredArgsConstructor public class RepairRecordController { - private final RepairRecordService repairRecordService;private final ManagementContextFactory managementContextFactory; + private final RepairRecordService repairRecordService; @Operation(summary = "分页查询RepairRecord数据", operationId = "getRepairRecordPage") @GetMapping("/getRepairRecordPage") - @PreAuthorize("hasAuthority('repairrecord:index')") + @PreAuthorize("hasAuthority('repair_record:index')") public BaseResult> getRepairRecordPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) RepairRecordDto searchParams) { var wrapper = new LambdaQueryWrapper(); - if (searchParams != null) { - if (StringUtils.hasText(searchParams.searchCode())) { - wrapper.like(RepairRecord::getProductSn, searchParams.searchCode()) - .or().like(RepairRecord::getMac, searchParams.searchCode()); - } + if (searchParams != null) { + if (StringUtils.hasText(searchParams.searchCode())) { + wrapper.like(RepairRecord::getProductSn, searchParams.searchCode()) + .or().like(RepairRecord::getMac, searchParams.searchCode()); } + if (searchParams.keyAccountIdSearch() != null) { + wrapper.eq(RepairRecord::getKeyAccountId, searchParams.keyAccountIdSearch()); + } + if (searchParams.status() != null) { + wrapper.eq(RepairRecord::getStatus, searchParams.status()); + } + if (searchParams.startDate() != null) { + wrapper.gt(RepairRecord::getCreateDate, searchParams.startDate()); + } + if (searchParams.endDate() != null) { + wrapper.lt(RepairRecord::getCreateDate, searchParams.endDate()); + } + if (StringUtils.hasText(searchParams.orderFiled())) { + boolean isDesc = "desc".equalsIgnoreCase(searchParams.orderByType()); + if (isDesc) { + wrapper.orderByDesc(true, RepairRecord::getId); + } else { + wrapper.orderByAsc(true, RepairRecord::getId); + } + } + } return BaseResult.successWithData(repairRecordService.getRepairRecordPage(pageParams, wrapper)); } + @Operation(summary = "获取客户信息", operationId = "getKeyAccount") + @GetMapping("/getKeyAccount") + @PreAuthorize("hasAuthority('repair_record:index')") + public BaseResult> getKeyAccount() { + return BaseResult.successWithData(repairRecordService.getKeyAccount()); + } @ApiLog(type = OperationType.ADD, remark = "新增一条RepairRecord记录") @Operation(summary = "新增RepairRecord", operationId = "addRepairRecord") @PostMapping("/addRepairRecord") - @PreAuthorize("hasAuthority('repairrecord:add')") + @PreAuthorize("hasAuthority('repair_record:add')") public BaseResult addRepairRecord(@Validated(Add.class) @RequestBody RepairRecordDto dto) { repairRecordService.addRepairRecord(dto); return BaseResult.success(); @@ -56,7 +82,7 @@ public class RepairRecordController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条RepairRecord记录") @Operation(summary = "更新RepairRecord", operationId = "updateRepairRecord") @PostMapping("/updateRepairRecord") - @PreAuthorize("hasAuthority('repairrecord:update')") + @PreAuthorize("hasAuthority('repair_record:update')") public BaseResult updateRepairRecord(@Validated(Update.class) @RequestBody RepairRecordDto dto) { repairRecordService.updateRepairRecord(dto); return BaseResult.success(); @@ -65,7 +91,7 @@ public class RepairRecordController { @ApiLog(type = OperationType.DELETE, remark = "删除一条RepairRecord记录") @Operation(summary = "删除RepairRecord", operationId = "deleteRepairRecord") @PostMapping("/deleteRepairRecord") - @PreAuthorize("hasAuthority('repairrecord:delete')") + @PreAuthorize("hasAuthority('repair_record:delete')") public BaseResult deleteRepairRecord(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { repairRecordService.deleteRepairRecord(req.id()); return BaseResult.success(); @@ -74,7 +100,7 @@ public class RepairRecordController { @ApiLog(type = OperationType.DELETE, remark = "批量删除RepairRecord记录") @Operation(summary = "批量删除RepairRecord", operationId = "deleteRepairRecordBatch") @PostMapping("/deleteRepairRecordBatch") - @PreAuthorize("hasAuthority('repairrecord:deleteBatch')") + @PreAuthorize("hasAuthority('repair_record:deleteBatch')") public BaseResult deleteRepairRecordBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { repairRecordService.deleteBatch(req.ids()); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderController.java b/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderController.java index 837e488..1ded67f 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderController.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderController.java @@ -10,15 +10,21 @@ 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.sale.controller.dto.SaleOrderAddDto; import com.niuan.erp.module.sale.controller.dto.SaleOrderDto; +import com.niuan.erp.module.sale.controller.dto.SaleOrderItemDto; import com.niuan.erp.module.sale.service.SaleOrderService; 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.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; @Tag(name = "SaleOrder") @ModuleLog("SaleOrder管理") @@ -31,7 +37,7 @@ public class SaleOrderController { @Operation(summary = "分页查询SaleOrder数据", operationId = "getSaleOrderPage") @GetMapping("/getSaleOrderPage") - @PreAuthorize("hasAuthority('saleorder:index')") + @PreAuthorize("hasAuthority('sale_order:index')") public BaseResult> getSaleOrderPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) SaleOrderDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -47,8 +53,8 @@ public class SaleOrderController { @ApiLog(type = OperationType.ADD, remark = "新增一条SaleOrder记录") @Operation(summary = "新增SaleOrder", operationId = "addSaleOrder") @PostMapping("/addSaleOrder") - @PreAuthorize("hasAuthority('saleorder:add')") - public BaseResult addSaleOrder(@Validated(Add.class) @RequestBody SaleOrderDto dto) { + @PreAuthorize("hasAuthority('sale_order:add')") + public BaseResult addSaleOrder(@Validated(Add.class) @RequestBody SaleOrderAddDto dto) { saleOrderService.addSaleOrder(dto); return BaseResult.success(); } @@ -56,7 +62,7 @@ public class SaleOrderController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条SaleOrder记录") @Operation(summary = "更新SaleOrder", operationId = "updateSaleOrder") @PostMapping("/updateSaleOrder") - @PreAuthorize("hasAuthority('saleorder:update')") + @PreAuthorize("hasAuthority('sale_order:update')") public BaseResult updateSaleOrder(@Validated(Update.class) @RequestBody SaleOrderDto dto) { saleOrderService.updateSaleOrder(dto); return BaseResult.success(); @@ -65,7 +71,7 @@ public class SaleOrderController { @ApiLog(type = OperationType.DELETE, remark = "删除一条SaleOrder记录") @Operation(summary = "删除SaleOrder", operationId = "deleteSaleOrder") @PostMapping("/deleteSaleOrder") - @PreAuthorize("hasAuthority('saleorder:delete')") + @PreAuthorize("hasAuthority('sale_order:delete')") public BaseResult deleteSaleOrder(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { saleOrderService.deleteSaleOrder(req.id()); return BaseResult.success(); @@ -74,9 +80,45 @@ public class SaleOrderController { @ApiLog(type = OperationType.DELETE, remark = "批量删除SaleOrder记录") @Operation(summary = "批量删除SaleOrder", operationId = "deleteSaleOrderBatch") @PostMapping("/deleteSaleOrderBatch") - @PreAuthorize("hasAuthority('saleorder:deleteBatch')") + @PreAuthorize("hasAuthority('sale_order:deleteBatch')") public BaseResult deleteSaleOrderBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { saleOrderService.deleteBatch(req.ids()); return BaseResult.success(); } + + @ApiLog(type = OperationType.UPDATE, remark = "审核SaleOrder") + @Operation(summary = "审核SaleOrder", operationId = "approveSaleOrder") + @PostMapping("/approveSaleOrder") + @PreAuthorize("hasAuthority('sale_order:approve')") + public BaseResult approveSaleOrder( + @Parameter(description = "销售订单ID") @RequestParam Long id) { + saleOrderService.approve(id); + return BaseResult.success(); + } + + @ApiLog(type = OperationType.UPDATE, remark = "反审核SaleOrder") + @Operation(summary = "反审核SaleOrder", operationId = "unapproveSaleOrder") + @PostMapping("/unapproveSaleOrder") + @PreAuthorize("hasAuthority('sale_order:unapprove')") + public BaseResult unapproveSaleOrder( + @Parameter(description = "销售订单ID") @RequestParam Long id) { + saleOrderService.unapprove(id); + return BaseResult.success(); + } + + @Operation(summary = "获取SaleOrder明细", operationId = "getSaleOrderDetail") + @GetMapping("/getSaleOrderDetail") + @PreAuthorize("hasAuthority('saleorder:index')") + public BaseResult> getSaleOrderDetail( + @Parameter(description = "销售订单ID") @RequestParam Long id) { + return BaseResult.successWithData(saleOrderService.getDetail(id)); + } + + @Operation(summary = "导入SaleOrder明细", operationId = "importSaleOrderItems") + @PostMapping("/importSaleOrderItems") + @PreAuthorize("hasAuthority('sale_order:import')") + public BaseResult> importSaleOrderItems( + @Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) { + return BaseResult.successWithData(saleOrderService.importItems(file)); + } } diff --git a/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderItemController.java b/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderItemController.java index 23f82f1..7ea26a4 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderItemController.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/SaleOrderItemController.java @@ -30,7 +30,7 @@ public class SaleOrderItemController { @Operation(summary = "分页查询SaleOrderItem数据", operationId = "getSaleOrderItemPage") @GetMapping("/getSaleOrderItemPage") - @PreAuthorize("hasAuthority('saleorderitem:index')") + @PreAuthorize("hasAuthority('sale_order_item:index')") public BaseResult> getSaleOrderItemPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) SaleOrderItemDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -41,7 +41,7 @@ public class SaleOrderItemController { @ApiLog(type = OperationType.ADD, remark = "新增一条SaleOrderItem记录") @Operation(summary = "新增SaleOrderItem", operationId = "addSaleOrderItem") @PostMapping("/addSaleOrderItem") - @PreAuthorize("hasAuthority('saleorderitem:add')") + @PreAuthorize("hasAuthority('sale_order_item:add')") public BaseResult addSaleOrderItem(@Validated(Add.class) @RequestBody SaleOrderItemDto dto) { saleOrderItemService.addSaleOrderItem(dto); return BaseResult.success(); @@ -50,7 +50,7 @@ public class SaleOrderItemController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条SaleOrderItem记录") @Operation(summary = "更新SaleOrderItem", operationId = "updateSaleOrderItem") @PostMapping("/updateSaleOrderItem") - @PreAuthorize("hasAuthority('saleorderitem:update')") + @PreAuthorize("hasAuthority('sale_order_item:update')") public BaseResult updateSaleOrderItem(@Validated(Update.class) @RequestBody SaleOrderItemDto dto) { saleOrderItemService.updateSaleOrderItem(dto); return BaseResult.success(); @@ -59,7 +59,7 @@ public class SaleOrderItemController { @ApiLog(type = OperationType.DELETE, remark = "删除一条SaleOrderItem记录") @Operation(summary = "删除SaleOrderItem", operationId = "deleteSaleOrderItem") @PostMapping("/deleteSaleOrderItem") - @PreAuthorize("hasAuthority('saleorderitem:delete')") + @PreAuthorize("hasAuthority('sale_order_item:delete')") public BaseResult deleteSaleOrderItem(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { saleOrderItemService.deleteSaleOrderItem(req.id()); return BaseResult.success(); @@ -68,7 +68,7 @@ public class SaleOrderItemController { @ApiLog(type = OperationType.DELETE, remark = "批量删除SaleOrderItem记录") @Operation(summary = "批量删除SaleOrderItem", operationId = "deleteSaleOrderItemBatch") @PostMapping("/deleteSaleOrderItemBatch") - @PreAuthorize("hasAuthority('saleorderitem:deleteBatch')") + @PreAuthorize("hasAuthority('sale_order_item:deleteBatch')") public BaseResult deleteSaleOrderItemBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { saleOrderItemService.deleteBatch(req.ids()); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java b/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java index c171faa..8ac1572 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/dto/DeviceDto.java @@ -1,32 +1,69 @@ package com.niuan.erp.module.sale.controller.dto; +import io.swagger.v3.oas.annotations.media.Schema; + import java.time.LocalDateTime; +@Schema(description = "设备DTO") public record DeviceDto( + @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 documentNo, + @Schema(description = "产品类型") String productType, + @Schema(description = "产品SN") String productSn, + @Schema(description = "MAC地址") String mac, + @Schema(description = "序列号") String serialNum, + @Schema(description = "软件版本") String softVersion, + @Schema(description = "AL版本") String alVersion, + @Schema(description = "AL编号") String alNum, + @Schema(description = "AL状态") Boolean alStatus, + @Schema(description = "维修状态") Integer repairStatus, + @Schema(description = "备注") String mark, - Integer reserve1, - String reserve2, - Integer keyAccountId, + @Schema(description = "客户ID") Integer customerId, + @Schema(description = "出库状态") Boolean outStatus, + @Schema(description = "出库日期") LocalDateTime outProductDate, + @Schema(description = "维修备注") String repairMark, - String searchCode) {} \ No newline at end of file + @Schema(description = "产品SN显示") + String productSnDisplay, + @Schema(description = "搜索代码") + String searchCode, + @Schema(description = "客户ID") + Integer keyAccountId, + @Schema(description = "开始日期") + LocalDateTime startDate, + @Schema(description = "结束日期") + LocalDateTime endDate, + @Schema(description = "排序字段") + String orderFiled, + @Schema(description = "排序类型") + String orderByType +) {} diff --git a/src/main/java/com/niuan/erp/module/sale/controller/dto/RepairRecordDto.java b/src/main/java/com/niuan/erp/module/sale/controller/dto/RepairRecordDto.java index 2c1ebbe..fbbc73c 100644 --- a/src/main/java/com/niuan/erp/module/sale/controller/dto/RepairRecordDto.java +++ b/src/main/java/com/niuan/erp/module/sale/controller/dto/RepairRecordDto.java @@ -1,27 +1,63 @@ package com.niuan.erp.module.sale.controller.dto; +import io.swagger.v3.oas.annotations.media.Schema; + import java.time.LocalDateTime; +@Schema(description = "返修记录DTO") public record RepairRecordDto( + @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 = "产品类型") String productType, + @Schema(description = "产品SN") String productSn, + @Schema(description = "MAC地址") String mac, + @Schema(description = "维修状态") Integer repairStatus, + @Schema(description = "备注") String mark, + @Schema(description = "保留字段1") Integer reserve1, + @Schema(description = "保留字段2") String reserve2, + @Schema(description = "客户ID") Integer keyAccountId, + @Schema(description = "客户ID") Integer customerId, + @Schema(description = "出库状态") Boolean outStatus, + @Schema(description = "出库日期") LocalDateTime outProductDate, + @Schema(description = "维修日期") LocalDateTime repairDate, + @Schema(description = "维修备注") String repairMark, - String searchCode) {} \ No newline at end of file + @Schema(description = "搜索代码") + String searchCode, + @Schema(description = "客户ID") + Integer keyAccountIdSearch, + @Schema(description = "开始日期") + LocalDateTime startDate, + @Schema(description = "结束日期") + LocalDateTime endDate, + @Schema(description = "排序字段") + String orderFiled, + @Schema(description = "排序类型") + String orderByType +) {} diff --git a/src/main/java/com/niuan/erp/module/sale/converter/DeviceConverter.java b/src/main/java/com/niuan/erp/module/sale/converter/DeviceConverter.java index 911798e..5c928d8 100644 --- a/src/main/java/com/niuan/erp/module/sale/converter/DeviceConverter.java +++ b/src/main/java/com/niuan/erp/module/sale/converter/DeviceConverter.java @@ -1,8 +1,10 @@ package com.niuan.erp.module.sale.converter; +import com.niuan.erp.module.sale.controller.dto.DeviceAddDto; import com.niuan.erp.module.sale.controller.dto.DeviceDto; import com.niuan.erp.module.sale.entity.Device; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import java.util.List; @@ -12,4 +14,8 @@ public interface DeviceConverter { Device toEntity(DeviceDto dto); DeviceDto toDto(Device entity); List toDtoList(List entities); -} \ No newline at end of file + + @Mapping(target = "alStatus", expression = "java(\"已激活\".equals(dto.alTxt()))") + Device toEntity(DeviceAddDto dto); + List toEntityList(List dtoList); +} diff --git a/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java b/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java index 3a4586f..e8a0cb3 100644 --- a/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java +++ b/src/main/java/com/niuan/erp/module/sale/converter/SaleOrderConverter.java @@ -1,7 +1,11 @@ package com.niuan.erp.module.sale.converter; import com.niuan.erp.module.common.entity.Document; +import com.niuan.erp.module.sale.controller.dto.SaleOrderAddDto; import com.niuan.erp.module.sale.controller.dto.SaleOrderDto; +import com.niuan.erp.module.sale.controller.dto.SaleOrderItemAddDto; +import com.niuan.erp.module.sale.controller.dto.SaleOrderItemDto; +import com.niuan.erp.module.sale.entity.SaleOrderItem; import org.mapstruct.Mapper; import org.mapstruct.ReportingPolicy; @@ -10,6 +14,13 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface SaleOrderConverter { Document toEntity(SaleOrderDto dto); + Document toEntity(SaleOrderAddDto dto); SaleOrderDto toDto(Document entity); List toDtoList(List entities); + + SaleOrderItem toEntity(SaleOrderItemAddDto dto); + List toEntityList(List dtoList); + + SaleOrderItemDto toDto(SaleOrderItem entity); + List toSaleOrderItemDtoList(List entities); } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/sale/mapper/DeviceMapper.java b/src/main/java/com/niuan/erp/module/sale/mapper/DeviceMapper.java index a711098..ea17c9e 100644 --- a/src/main/java/com/niuan/erp/module/sale/mapper/DeviceMapper.java +++ b/src/main/java/com/niuan/erp/module/sale/mapper/DeviceMapper.java @@ -2,15 +2,8 @@ package com.niuan.erp.module.sale.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.niuan.erp.module.sale.entity.Device; +import org.apache.ibatis.annotations.Mapper; -/** - *

- * Mapper 接口 - *

- * - * @author - * @since 2026-02-11 - */ +@Mapper public interface DeviceMapper extends BaseMapper { - } diff --git a/src/main/java/com/niuan/erp/module/sale/service/DeviceService.java b/src/main/java/com/niuan/erp/module/sale/service/DeviceService.java index 056734d..1be69cf 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/DeviceService.java +++ b/src/main/java/com/niuan/erp/module/sale/service/DeviceService.java @@ -3,6 +3,7 @@ package com.niuan.erp.module.sale.service; 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.common.base.BaseSelectDto; import com.niuan.erp.module.sale.controller.dto.DeviceDto; import com.niuan.erp.module.sale.entity.Device; @@ -20,4 +21,5 @@ public interface DeviceService { void deleteBatch(List ids); + List getKeyAccount(); } diff --git a/src/main/java/com/niuan/erp/module/sale/service/RepairRecordService.java b/src/main/java/com/niuan/erp/module/sale/service/RepairRecordService.java index 9798df6..930455d 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/RepairRecordService.java +++ b/src/main/java/com/niuan/erp/module/sale/service/RepairRecordService.java @@ -3,6 +3,7 @@ package com.niuan.erp.module.sale.service; 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.common.base.BaseSelectDto; import com.niuan.erp.module.sale.controller.dto.RepairRecordDto; import com.niuan.erp.module.sale.entity.RepairRecord; @@ -20,4 +21,5 @@ public interface RepairRecordService { void deleteBatch(List ids); + List getKeyAccount(); } diff --git a/src/main/java/com/niuan/erp/module/sale/service/SaleOrderService.java b/src/main/java/com/niuan/erp/module/sale/service/SaleOrderService.java index 4c16f08..542192e 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/SaleOrderService.java +++ b/src/main/java/com/niuan/erp/module/sale/service/SaleOrderService.java @@ -4,7 +4,10 @@ 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.sale.controller.dto.SaleOrderAddDto; import com.niuan.erp.module.sale.controller.dto.SaleOrderDto; +import com.niuan.erp.module.sale.controller.dto.SaleOrderItemDto; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -12,7 +15,7 @@ public interface SaleOrderService { IPage getSaleOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addSaleOrder(SaleOrderDto dto); + void addSaleOrder(SaleOrderAddDto dto); void updateSaleOrder(SaleOrderDto dto); @@ -20,4 +23,11 @@ public interface SaleOrderService { void deleteBatch(List ids); + void approve(long id); + + void unapprove(long id); + + List getDetail(long id); + + List importItems(MultipartFile file); } diff --git a/src/main/java/com/niuan/erp/module/sale/service/impl/DeviceServiceImpl.java b/src/main/java/com/niuan/erp/module/sale/service/impl/DeviceServiceImpl.java index 07ac4b5..1933612 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sale/service/impl/DeviceServiceImpl.java @@ -5,6 +5,7 @@ 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.base.BaseSelectDto; import com.niuan.erp.common.utils.SecurityUtils; import com.niuan.erp.module.sale.controller.dto.DeviceDto; import com.niuan.erp.module.sale.converter.DeviceConverter; @@ -14,6 +15,7 @@ import com.niuan.erp.module.sale.service.DeviceService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.util.List; @@ -59,7 +61,18 @@ public class DeviceServiceImpl extends ServiceImpl impleme @Override public void deleteBatch(List ids) { - this.baseMapper.deleteByIds(ids); + this.baseMapper.deleteBatchIds(ids); } -} \ No newline at end of file + @Override + public List getKeyAccount() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.select(Device::getKeyAccountId); + wrapper.isNotNull(Device::getKeyAccountId); + wrapper.groupBy(Device::getKeyAccountId); + List devices = this.baseMapper.selectList(wrapper); + return devices.stream() + .map(device -> new BaseSelectDto(device.getKeyAccountId().toString(), device.getKeyAccountId().toString())) + .toList(); + } +} diff --git a/src/main/java/com/niuan/erp/module/sale/service/impl/RepairRecordServiceImpl.java b/src/main/java/com/niuan/erp/module/sale/service/impl/RepairRecordServiceImpl.java index b395c92..41082e8 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/impl/RepairRecordServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sale/service/impl/RepairRecordServiceImpl.java @@ -5,6 +5,7 @@ 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.base.BaseSelectDto; import com.niuan.erp.common.utils.SecurityUtils; import com.niuan.erp.module.sale.controller.dto.RepairRecordDto; import com.niuan.erp.module.sale.converter.RepairRecordConverter; @@ -59,7 +60,18 @@ public class RepairRecordServiceImpl extends ServiceImpl ids) { - this.baseMapper.deleteByIds(ids); + this.baseMapper.deleteBatchIds(ids); } -} \ No newline at end of file + @Override + public List getKeyAccount() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.select(RepairRecord::getKeyAccountId); + wrapper.isNotNull(RepairRecord::getKeyAccountId); + wrapper.groupBy(RepairRecord::getKeyAccountId); + List repairRecords = this.baseMapper.selectList(wrapper); + return repairRecords.stream() + .map(repairRecord -> new BaseSelectDto(repairRecord.getKeyAccountId().toString(), repairRecord.getKeyAccountId().toString())) + .toList(); + } +} diff --git a/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java b/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java index d2675f3..474389a 100644 --- a/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sale/service/impl/SaleOrderServiceImpl.java @@ -1,22 +1,36 @@ package com.niuan.erp.module.sale.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.enums.DocumentType; +import com.niuan.erp.module.common.enums.FormStatus; import com.niuan.erp.module.common.mapper.DocumentMapper; +import com.niuan.erp.module.sale.controller.dto.SaleOrderAddDto; import com.niuan.erp.module.sale.controller.dto.SaleOrderDto; +import com.niuan.erp.module.sale.controller.dto.SaleOrderItemAddDto; +import com.niuan.erp.module.sale.controller.dto.SaleOrderItemDto; import com.niuan.erp.module.sale.converter.SaleOrderConverter; +import com.niuan.erp.module.sale.entity.SaleOrderItem; +import com.niuan.erp.module.sale.mapper.SaleOrderItemMapper; import com.niuan.erp.module.sale.service.SaleOrderService; 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.List; +import java.util.*; +import java.util.stream.Collectors; @Service @@ -27,24 +41,58 @@ public class SaleOrderServiceImpl extends ServiceImpl private final SaleOrderConverter saleOrderConverter; + private final SaleOrderItemMapper saleOrderItemMapper; + @Override public IPage getSaleOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.SALES_ORDER); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(saleOrderConverter::toDto); } @Override - public void addSaleOrder(SaleOrderDto dto) { + public void addSaleOrder(SaleOrderAddDto dto) { + List saleOrderItems = dto.saleOrderItems(); + + if (saleOrderItems == null || saleOrderItems.isEmpty()) { + throw new BusinessException("sale.sale_order.exception.no_sale_order_items"); + } + Document entity = saleOrderConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); entity.setCreateDate(LocalDateTime.now()); entity.setStatus(0); + entity.setFormType(DocumentType.SALES_ORDER); + entity.setFormStatus(FormStatus.NO_APPROVE); + entity.setCustomerId(SecurityUtils.getCustomerId()); this.baseMapper.insert(entity); + + List saleOrderItemEntities = saleOrderConverter.toEntityList(saleOrderItems); + + for (SaleOrderItem saleOrderItem : saleOrderItemEntities) { + saleOrderItem.setDocumentNo(entity.getId().intValue()); + saleOrderItem.setDocumentCode(entity.getFormCode()); + saleOrderItem.setCreateDate(LocalDateTime.now()); + saleOrderItem.setCreateUserId(SecurityUtils.getUserId()); + saleOrderItem.setCreateUserName(SecurityUtils.getUserName()); + saleOrderItem.setStatus(0); + saleOrderItem.setSendCount(0); + } + + saleOrderItemMapper.insert(saleOrderItemEntities); } @Override public void updateSaleOrder(SaleOrderDto dto) { + Document existingEntity = this.baseMapper.selectById(dto.id()); + if (existingEntity == null) { + throw new BusinessException("sale.sale_order.exception.not_found"); + } + if (existingEntity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("sale.sale_order.exception.cannot_update_approved"); + } + Document entity = saleOrderConverter.toEntity(dto); entity.setUpdateUserId(SecurityUtils.getUserId()); entity.setUpdateUserName(SecurityUtils.getUserName()); @@ -54,12 +102,164 @@ public class SaleOrderServiceImpl extends ServiceImpl @Override public void deleteSaleOrder(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("sale.sale_order.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("sale.sale_order.exception.cannot_delete_approved"); + } + this.baseMapper.deleteById(id); + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(SaleOrderItem::getDocumentNo, id); + saleOrderItemMapper.delete(itemWrapper); } @Override public void deleteBatch(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException("sale.sale_order.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("sale.sale_order.exception.cannot_delete_approved_batch"); + } + this.baseMapper.deleteByIds(ids); + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.in(SaleOrderItem::getDocumentNo, ids); + saleOrderItemMapper.delete(itemWrapper); } + @Override + public void approve(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("sale.sale_order.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("sale.sale_order.exception.already_approved"); + } + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(SaleOrderItem::getDocumentNo, id); + List items = saleOrderItemMapper.selectList(itemWrapper); + if (items == null || items.isEmpty()) { + throw new BusinessException("sale.sale_order.exception.no_sale_order_items"); + } + + entity.setFormStatus(FormStatus.APPROVE); + entity.setUpdateDate(LocalDateTime.now()); + entity.setUpdateUserId(SecurityUtils.getUserId()); + entity.setUpdateUserName(SecurityUtils.getUserName()); + this.baseMapper.updateById(entity); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SaleOrderItem::getDocumentNo, id) + .set(SaleOrderItem::getUpdateDate, LocalDateTime.now()) + .set(SaleOrderItem::getUpdateUserId, SecurityUtils.getUserId()) + .set(SaleOrderItem::getUpdateUserName, SecurityUtils.getUserName()); + saleOrderItemMapper.update(null, updateWrapper); + } + + @Override + public void unapprove(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("sale.sale_order.exception.not_found"); + } + if (entity.getFormStatus() != FormStatus.APPROVE) { + throw new BusinessException("sale.sale_order.exception.not_approved"); + } + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(SaleOrderItem::getDocumentNo, id); + List items = saleOrderItemMapper.selectList(itemWrapper); + + Long shippedCount = items.stream() + .filter(item -> item.getSendCount() != null && item.getSendCount() > 0) + .count(); + + if (shippedCount > 0) { + throw new BusinessException("sale.sale_order.exception.cannot_unapprove_with_shipped"); + } + + 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(SaleOrderItem::getDocumentNo, id) + .set(SaleOrderItem::getUpdateDate, LocalDateTime.now()) + .set(SaleOrderItem::getUpdateUserId, SecurityUtils.getUserId()) + .set(SaleOrderItem::getUpdateUserName, SecurityUtils.getUserName()); + saleOrderItemMapper.update(null, updateWrapper); + } + + @Override + public List getDetail(long id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SaleOrderItem::getDocumentNo, id); + List items = saleOrderItemMapper.selectList(wrapper); + return saleOrderConverter.toSaleOrderItemDtoList(items); + } + + @Override + public List importItems(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("sale.sale_order.exception.file_empty"); + } + + 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 partNumber = getCellValueAsString(row.getCell(0)); + String saleCountStr = getCellValueAsString(row.getCell(1)); + String priceStr = getCellValueAsString(row.getCell(2)); + String saleMark = getCellValueAsString(row.getCell(3)); + + if (!StringUtils.hasText(partNumber)) continue; + + Integer saleCount = StringUtils.hasText(saleCountStr) ? Integer.parseInt(saleCountStr) : 0; + Double price = StringUtils.hasText(priceStr) ? Double.parseDouble(priceStr) : 0.0; + + SaleOrderItemDto item = new SaleOrderItemDto( + null, null, null, null, null, null, null, null, + null, null, null, partNumber, saleCount, price, + saleCount * price, 0, saleMark, null, null + ); + items.add(item); + } + + return items; + } catch (IOException e) { + throw new BusinessException("sale.sale_order.exception.import_failed"); + } + } + + 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/java/com/niuan/erp/module/sys/controller/VendorController.java b/src/main/java/com/niuan/erp/module/sys/controller/VendorController.java index 488a4e6..1193a92 100644 --- a/src/main/java/com/niuan/erp/module/sys/controller/VendorController.java +++ b/src/main/java/com/niuan/erp/module/sys/controller/VendorController.java @@ -7,6 +7,7 @@ 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.BaseSelectDto; import com.niuan.erp.common.base.CommonValidateGroup.*; import com.niuan.erp.common.base.OperationType; import com.niuan.erp.module.sys.controller.dto.VendorDto; @@ -20,6 +21,8 @@ import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + @Tag(name = "供应商管理") @ModuleLog("供应商管理") @RestController @@ -76,4 +79,19 @@ public class VendorController { vendorService.deleteBatch(req.ids()); return BaseResult.success(); } + + @Operation(summary = "获取供应商列表", operationId = "getVendorList") + @PreAuthorize("hasAnyAuthority('vendor:index', 'purchase_plan:add', 'purchase_plan:edit')") + @GetMapping("/getVendorList") + public BaseResult> getVendorList() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Vendor::getStatus, 0); // 只查询启用的供应商 + List vendors = vendorService.getVendorList(wrapper); + + List selectDtos = vendors.stream() + .map(vendor -> new BaseSelectDto(vendor.getId(), vendor.getVendorName())) + .collect(java.util.stream.Collectors.toList()); + + return BaseResult.successWithData(selectDtos); + } } diff --git a/src/main/java/com/niuan/erp/module/sys/service/VendorService.java b/src/main/java/com/niuan/erp/module/sys/service/VendorService.java index be9e9b7..c5409b9 100644 --- a/src/main/java/com/niuan/erp/module/sys/service/VendorService.java +++ b/src/main/java/com/niuan/erp/module/sys/service/VendorService.java @@ -20,4 +20,6 @@ public interface VendorService { void deleteBatch(List ids); + List getVendorList(LambdaQueryWrapper wrapper); + } diff --git a/src/main/java/com/niuan/erp/module/sys/service/impl/VendorServiceImpl.java b/src/main/java/com/niuan/erp/module/sys/service/impl/VendorServiceImpl.java index 789269e..efe2152 100644 --- a/src/main/java/com/niuan/erp/module/sys/service/impl/VendorServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/sys/service/impl/VendorServiceImpl.java @@ -63,4 +63,9 @@ public class VendorServiceImpl extends ServiceImpl impleme this.baseMapper.deleteByIds(ids); } + @Override + public List getVendorList(LambdaQueryWrapper wrapper) { + return this.baseMapper.selectList(wrapper); + } + } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountController.java b/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountController.java index c504559..47ddf25 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountController.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountController.java @@ -10,15 +10,21 @@ 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.warehouse.controller.dto.InventoryCountAddDto; import com.niuan.erp.module.warehouse.controller.dto.InventoryCountDto; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountItemDto; import com.niuan.erp.module.warehouse.service.InventoryCountService; 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.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; @Tag(name = "InventoryCount") @ModuleLog("InventoryCount管理") @@ -31,7 +37,7 @@ public class InventoryCountController { @Operation(summary = "分页查询InventoryCount数据", operationId = "getInventoryCountPage") @GetMapping("/getInventoryCountPage") - @PreAuthorize("hasAuthority('inventorycount:index')") + @PreAuthorize("hasAuthority('inventory_count:index')") public BaseResult> getInventoryCountPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) InventoryCountDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -47,8 +53,8 @@ public class InventoryCountController { @ApiLog(type = OperationType.ADD, remark = "新增一条InventoryCount记录") @Operation(summary = "新增InventoryCount", operationId = "addInventoryCount") @PostMapping("/addInventoryCount") - @PreAuthorize("hasAuthority('inventorycount:add')") - public BaseResult addInventoryCount(@Validated(Add.class) @RequestBody InventoryCountDto dto) { + @PreAuthorize("hasAuthority('inventory_count:add')") + public BaseResult addInventoryCount(@Validated(Add.class) @RequestBody InventoryCountAddDto dto) { inventoryCountService.addInventoryCount(dto); return BaseResult.success(); } @@ -56,7 +62,7 @@ public class InventoryCountController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条InventoryCount记录") @Operation(summary = "更新InventoryCount", operationId = "updateInventoryCount") @PostMapping("/updateInventoryCount") - @PreAuthorize("hasAuthority('inventorycount:update')") + @PreAuthorize("hasAuthority('inventory_count:update')") public BaseResult updateInventoryCount(@Validated(Update.class) @RequestBody InventoryCountDto dto) { inventoryCountService.updateInventoryCount(dto); return BaseResult.success(); @@ -65,7 +71,7 @@ public class InventoryCountController { @ApiLog(type = OperationType.DELETE, remark = "删除一条InventoryCount记录") @Operation(summary = "删除InventoryCount", operationId = "deleteInventoryCount") @PostMapping("/deleteInventoryCount") - @PreAuthorize("hasAuthority('inventorycount:delete')") + @PreAuthorize("hasAuthority('inventory_count:delete')") public BaseResult deleteInventoryCount(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { inventoryCountService.deleteInventoryCount(req.id()); return BaseResult.success(); @@ -74,9 +80,45 @@ public class InventoryCountController { @ApiLog(type = OperationType.DELETE, remark = "批量删除InventoryCount记录") @Operation(summary = "批量删除InventoryCount", operationId = "deleteInventoryCountBatch") @PostMapping("/deleteInventoryCountBatch") - @PreAuthorize("hasAuthority('inventorycount:deleteBatch')") + @PreAuthorize("hasAuthority('inventory_count:deleteBatch')") public BaseResult deleteInventoryCountBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { inventoryCountService.deleteBatch(req.ids()); return BaseResult.success(); } + + @ApiLog(type = OperationType.UPDATE, remark = "审核InventoryCount") + @Operation(summary = "审核InventoryCount", operationId = "approveInventoryCount") + @PostMapping("/approveInventoryCount") + @PreAuthorize("hasAuthority('inventory_count:approve')") + public BaseResult approveInventoryCount( + @Parameter(description = "盘点单ID") @RequestParam Long id) { + inventoryCountService.approve(id); + return BaseResult.success(); + } + + @ApiLog(type = OperationType.UPDATE, remark = "反审核InventoryCount") + @Operation(summary = "反审核InventoryCount", operationId = "unapproveInventoryCount") + @PostMapping("/unapproveInventoryCount") + @PreAuthorize("hasAuthority('inventory_count:unapprove')") + public BaseResult unapproveInventoryCount( + @Parameter(description = "盘点单ID") @RequestParam Long id) { + inventoryCountService.unapprove(id); + return BaseResult.success(); + } + + @Operation(summary = "获取InventoryCount明细", operationId = "getInventoryCountDetail") + @GetMapping("/getInventoryCountDetail") + @PreAuthorize("hasAuthority('inventorycount:index')") + public BaseResult> getInventoryCountDetail( + @Parameter(description = "盘点单ID") @RequestParam Long id) { + return BaseResult.successWithData(inventoryCountService.getDetail(id)); + } + + @Operation(summary = "导入InventoryCount明细", operationId = "importInventoryCountItems") + @PostMapping("/importInventoryCountItems") + @PreAuthorize("hasAuthority('inventory_count:import')") + public BaseResult> importInventoryCountItems( + @Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) { + return BaseResult.successWithData(inventoryCountService.importItems(file)); + } } diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountItemController.java b/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountItemController.java index f349e21..b48e961 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountItemController.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/InventoryCountItemController.java @@ -30,7 +30,7 @@ public class InventoryCountItemController { @Operation(summary = "分页查询InventoryCountItem数据", operationId = "getInventoryCountItemPage") @GetMapping("/getInventoryCountItemPage") - @PreAuthorize("hasAuthority('inventorycountitem:index')") + @PreAuthorize("hasAuthority('inventory_count_item:index')") public BaseResult> getInventoryCountItemPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) InventoryCountItemDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -41,7 +41,7 @@ public class InventoryCountItemController { @ApiLog(type = OperationType.ADD, remark = "新增一条InventoryCountItem记录") @Operation(summary = "新增InventoryCountItem", operationId = "addInventoryCountItem") @PostMapping("/addInventoryCountItem") - @PreAuthorize("hasAuthority('inventorycountitem:add')") + @PreAuthorize("hasAuthority('inventory_count_item:add')") public BaseResult addInventoryCountItem(@Validated(Add.class) @RequestBody InventoryCountItemDto dto) { inventoryCountItemService.addInventoryCountItem(dto); return BaseResult.success(); @@ -50,7 +50,7 @@ public class InventoryCountItemController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条InventoryCountItem记录") @Operation(summary = "更新InventoryCountItem", operationId = "updateInventoryCountItem") @PostMapping("/updateInventoryCountItem") - @PreAuthorize("hasAuthority('inventorycountitem:update')") + @PreAuthorize("hasAuthority('inventory_count_item:update')") public BaseResult updateInventoryCountItem(@Validated(Update.class) @RequestBody InventoryCountItemDto dto) { inventoryCountItemService.updateInventoryCountItem(dto); return BaseResult.success(); @@ -59,7 +59,7 @@ public class InventoryCountItemController { @ApiLog(type = OperationType.DELETE, remark = "删除一条InventoryCountItem记录") @Operation(summary = "删除InventoryCountItem", operationId = "deleteInventoryCountItem") @PostMapping("/deleteInventoryCountItem") - @PreAuthorize("hasAuthority('inventorycountitem:delete')") + @PreAuthorize("hasAuthority('inventory_count_item:delete')") public BaseResult deleteInventoryCountItem(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { inventoryCountItemService.deleteInventoryCountItem(req.id()); return BaseResult.success(); @@ -68,7 +68,7 @@ public class InventoryCountItemController { @ApiLog(type = OperationType.DELETE, remark = "批量删除InventoryCountItem记录") @Operation(summary = "批量删除InventoryCountItem", operationId = "deleteInventoryCountItemBatch") @PostMapping("/deleteInventoryCountItemBatch") - @PreAuthorize("hasAuthority('inventorycountitem:deleteBatch')") + @PreAuthorize("hasAuthority('inventory_count_item:deleteBatch')") public BaseResult deleteInventoryCountItemBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { inventoryCountItemService.deleteBatch(req.ids()); return BaseResult.success(); diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/StockTransferOrderController.java b/src/main/java/com/niuan/erp/module/warehouse/controller/StockTransferOrderController.java index 14fdc61..4bad0c2 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/StockTransferOrderController.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/StockTransferOrderController.java @@ -12,14 +12,19 @@ import com.niuan.erp.common.base.OperationType; import com.niuan.erp.module.common.entity.Document; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderAddDto; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderDto; +import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderItemDto; import com.niuan.erp.module.warehouse.service.StockTransferOrderService; 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.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; @Tag(name = "StockTransferOrder") @ModuleLog("StockTransferOrder管理") @@ -32,7 +37,7 @@ public class StockTransferOrderController { @Operation(summary = "分页查询StockTransferOrder数据", operationId = "getStockTransferOrderPage") @GetMapping("/getStockTransferOrderPage") - @PreAuthorize("hasAuthority('stocktransferorder:index')") + @PreAuthorize("hasAuthority('stock_transfer_order:index')") public BaseResult> getStockTransferOrderPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) StockTransferOrderDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -48,7 +53,7 @@ public class StockTransferOrderController { @ApiLog(type = OperationType.ADD, remark = "新增一条StockTransferOrder记录") @Operation(summary = "新增StockTransferOrder", operationId = "addStockTransferOrder") @PostMapping("/addStockTransferOrder") - @PreAuthorize("hasAuthority('stocktransferorder:add')") + @PreAuthorize("hasAuthority('stock_transfer_order:add')") public BaseResult addStockTransferOrder(@Validated(Add.class) @RequestBody StockTransferOrderAddDto dto) { stockTransferOrderService.addStockTransferOrder(dto); return BaseResult.success(); @@ -57,7 +62,7 @@ public class StockTransferOrderController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条StockTransferOrder记录") @Operation(summary = "更新StockTransferOrder", operationId = "updateStockTransferOrder") @PostMapping("/updateStockTransferOrder") - @PreAuthorize("hasAuthority('stocktransferorder:update')") + @PreAuthorize("hasAuthority('stock_transfer_order:update')") public BaseResult updateStockTransferOrder(@Validated(Update.class) @RequestBody StockTransferOrderDto dto) { stockTransferOrderService.updateStockTransferOrder(dto); return BaseResult.success(); @@ -66,7 +71,7 @@ public class StockTransferOrderController { @ApiLog(type = OperationType.DELETE, remark = "删除一条StockTransferOrder记录") @Operation(summary = "删除StockTransferOrder", operationId = "deleteStockTransferOrder") @PostMapping("/deleteStockTransferOrder") - @PreAuthorize("hasAuthority('stocktransferorder:delete')") + @PreAuthorize("hasAuthority('stock_transfer_order:delete')") public BaseResult deleteStockTransferOrder(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { stockTransferOrderService.deleteStockTransferOrder(req.id()); return BaseResult.success(); @@ -75,9 +80,45 @@ public class StockTransferOrderController { @ApiLog(type = OperationType.DELETE, remark = "批量删除StockTransferOrder记录") @Operation(summary = "批量删除StockTransferOrder", operationId = "deleteStockTransferOrderBatch") @PostMapping("/deleteStockTransferOrderBatch") - @PreAuthorize("hasAuthority('stocktransferorder:deleteBatch')") + @PreAuthorize("hasAuthority('stock_transfer_order:deleteBatch')") public BaseResult deleteStockTransferOrderBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { stockTransferOrderService.deleteBatch(req.ids()); return BaseResult.success(); } + + @ApiLog(type = OperationType.UPDATE, remark = "审核StockTransferOrder") + @Operation(summary = "审核StockTransferOrder", operationId = "approveStockTransferOrder") + @PostMapping("/approveStockTransferOrder") + @PreAuthorize("hasAuthority('stock_transfer_order:approve')") + public BaseResult approveStockTransferOrder( + @Parameter(description = "调拨单ID") @RequestParam Long id) { + stockTransferOrderService.approve(id); + return BaseResult.success(); + } + + @ApiLog(type = OperationType.UPDATE, remark = "反审核StockTransferOrder") + @Operation(summary = "反审核StockTransferOrder", operationId = "unapproveStockTransferOrder") + @PostMapping("/unapproveStockTransferOrder") + @PreAuthorize("hasAuthority('stock_transfer_order:unapprove')") + public BaseResult unapproveStockTransferOrder( + @Parameter(description = "调拨单ID") @RequestParam Long id) { + stockTransferOrderService.unapprove(id); + return BaseResult.success(); + } + + @Operation(summary = "获取StockTransferOrder明细", operationId = "getStockTransferOrderDetail") + @GetMapping("/getStockTransferOrderDetail") + @PreAuthorize("hasAuthority('stocktransferorder:index')") + public BaseResult> getStockTransferOrderDetail( + @Parameter(description = "调拨单ID") @RequestParam Long id) { + return BaseResult.successWithData(stockTransferOrderService.getDetail(id)); + } + + @Operation(summary = "导入StockTransferOrder明细", operationId = "importStockTransferOrderItems") + @PostMapping("/importStockTransferOrderItems") + @PreAuthorize("hasAuthority('stock_transfer_order:add')") + public BaseResult> importStockTransferOrderItems( + @Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) { + return BaseResult.successWithData(stockTransferOrderService.importItems(file)); + } } 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 3647c47..51c4aeb 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 @@ -6,11 +6,14 @@ import com.niuan.erp.common.annotation.ApiLog; import com.niuan.erp.common.annotation.ModuleLog; import com.niuan.erp.common.base.*; import com.niuan.erp.common.base.CommonValidateGroup.*; +import com.niuan.erp.module.warehouse.controller.dto.ProductVendorMapAddDto; +import com.niuan.erp.module.warehouse.controller.dto.ProductVendorMapDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseItemDto; import com.niuan.erp.module.warehouse.entity.WarehouseItem; import com.niuan.erp.module.warehouse.service.WarehouseItemService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; @@ -20,8 +23,8 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@Tag(name = "WarehouseItem") -@ModuleLog("WarehouseItem管理") +@Tag(name = "物料总表") +@ModuleLog("物料信息管理") @RestController @RequestMapping("/warehouse/warehouseitem") @RequiredArgsConstructor @@ -45,7 +48,7 @@ public class WarehouseItemController { @Operation(summary = "分页查询WarehouseItem数据", operationId = "getWarehouseItemPage") @GetMapping("/getWarehouseItemPage") - @PreAuthorize("hasAuthority('warehouseitem:index')") + @PreAuthorize("hasAuthority('warehouse_item:index')") public BaseResult> getWarehouseItemPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) WarehouseItemDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -54,41 +57,42 @@ public class WarehouseItemController { wrapper.like(WarehouseItem::getPartNumber, searchParams.searchCode()); } } + wrapper.orderByDesc(WarehouseItem::getCreateDate); return BaseResult.successWithData(warehouseItemService.getWarehouseItemPage(pageParams, wrapper)); } - @ApiLog(type = OperationType.ADD, remark = "新增一条WarehouseItem记录") - @Operation(summary = "新增WarehouseItem", operationId = "addWarehouseItem") + @ApiLog(type = OperationType.ADD, remark = "新增一条物料信息记录") + @Operation(summary = "新增物料信息", operationId = "addWarehouseItem") @PostMapping("/addWarehouseItem") - @PreAuthorize("hasAuthority('warehouseitem:add')") + @PreAuthorize("hasAuthority('warehouse_item:add')") public BaseResult addWarehouseItem(@Validated(Add.class) @RequestBody WarehouseItemDto dto) { warehouseItemService.addWarehouseItem(dto); return BaseResult.success(); } - @ApiLog(type = OperationType.UPDATE, remark = "更新一条WarehouseItem记录") - @Operation(summary = "更新WarehouseItem", operationId = "updateWarehouseItem") + @ApiLog(type = OperationType.UPDATE, remark = "更新一条物料信息记录") + @Operation(summary = "更新物料信息", operationId = "updateWarehouseItem") @PostMapping("/updateWarehouseItem") - @PreAuthorize("hasAuthority('warehouseitem:update')") + @PreAuthorize("hasAuthority('warehouse_item:edit')") public BaseResult updateWarehouseItem(@Validated(Update.class) @RequestBody WarehouseItemDto dto) { warehouseItemService.updateWarehouseItem(dto); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "删除一条WarehouseItem记录") - @Operation(summary = "删除WarehouseItem", operationId = "deleteWarehouseItem") + @ApiLog(type = OperationType.DELETE, remark = "删除一条物料信息记录") + @Operation(summary = "删除物料信息", operationId = "deleteWarehouseItem") @PostMapping("/deleteWarehouseItem") - @PreAuthorize("hasAuthority('warehouseitem:delete')") + @PreAuthorize("hasAuthority('warehouse_item:remove')") public BaseResult deleteWarehouseItem(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { warehouseItemService.deleteWarehouseItem(req.id()); return BaseResult.success(); } - @ApiLog(type = OperationType.DELETE, remark = "批量删除WarehouseItem记录") - @Operation(summary = "批量删除WarehouseItem", operationId = "deleteWarehouseItemBatch") + @ApiLog(type = OperationType.DELETE, remark = "批量删除物料信息记录") + @Operation(summary = "批量删除物料信息", operationId = "deleteWarehouseItemBatch") @PostMapping("/deleteWarehouseItemBatch") - @PreAuthorize("hasAuthority('warehouseitem:deleteBatch')") + @PreAuthorize("hasAuthority('warehouse_item:deleteBatch')") public BaseResult deleteWarehouseItemBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { warehouseItemService.deleteBatch(req.ids()); return BaseResult.success(); @@ -102,4 +106,31 @@ public class WarehouseItemController { @RequestParam("partNumber") String partNumber) { return BaseResult.successWithData(warehouseItemService.existsWarehouseItemByPartNumber(partNumber)); } + + @Operation(summary = "获取物料供应商列表", operationId = "getVendorList") + @GetMapping("/getVendorList") + @PreAuthorize("hasAuthority('warehouse_item:editVendors')") + public BaseResult> getVendorList( + @NotNull(message = "warehouse.warehouse_item.validate.id.not_null") + @RequestParam("warehouseItemId") Long warehouseItemId) { + return BaseResult.successWithData(warehouseItemService.getVendorListByWarehouseItemId(warehouseItemId)); + } + + @ApiLog(type = OperationType.UPDATE, remark = "保存物料供应商列表") + @Operation(summary = "保存物料供应商列表", operationId = "saveVendorList") + @PostMapping("/saveVendorList") + @PreAuthorize("hasAuthority('warehouse_item:editVendors')") + public BaseResult saveVendorList(@Valid @RequestBody ProductVendorMapAddDto dto) { + warehouseItemService.saveVendorList(dto); + return BaseResult.success(); + } + + @Operation(summary = "获取物料编号用于打印二维码", operationId = "getPartNumberForQrCode") + @GetMapping("/getPartNumberForQrCode") + @PreAuthorize("hasAuthority('warehouse_item:printQrCode')") + public BaseResult getPartNumberForQrCode( + @NotNull(message = "warehouse.warehouse_item.validate.id.not_null") + @RequestParam("warehouseItemId") Long warehouseItemId) { + return BaseResult.successWithData(warehouseItemService.getPartNumberById(warehouseItemId)); + } } diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java b/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java index 0ca9489..57d0145 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/WarehouseReceiptController.java @@ -10,15 +10,21 @@ 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.warehouse.controller.dto.WarehouseReceiptAddDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptDto; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptItemDto; import com.niuan.erp.module.warehouse.service.WarehouseReceiptService; 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.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; @Tag(name = "WarehouseReceipt") @ModuleLog("WarehouseReceipt管理") @@ -31,7 +37,7 @@ public class WarehouseReceiptController { @Operation(summary = "分页查询WarehouseReceipt数据", operationId = "getWarehouseReceiptPage") @GetMapping("/getWarehouseReceiptPage") - @PreAuthorize("hasAuthority('warehousereceipt:index')") + @PreAuthorize("hasAuthority('warehouse_receipt:index')") public BaseResult> getWarehouseReceiptPage(@Validated BasePageReqParams pageParams, @Validated(Get.class) WarehouseReceiptDto searchParams) { var wrapper = new LambdaQueryWrapper(); @@ -47,8 +53,8 @@ public class WarehouseReceiptController { @ApiLog(type = OperationType.ADD, remark = "新增一条WarehouseReceipt记录") @Operation(summary = "新增WarehouseReceipt", operationId = "addWarehouseReceipt") @PostMapping("/addWarehouseReceipt") - @PreAuthorize("hasAuthority('warehousereceipt:add')") - public BaseResult addWarehouseReceipt(@Validated(Add.class) @RequestBody WarehouseReceiptDto dto) { + @PreAuthorize("hasAuthority('warehouse_receipt:add')") + public BaseResult addWarehouseReceipt(@Validated(Add.class) @RequestBody WarehouseReceiptAddDto dto) { warehouseReceiptService.addWarehouseReceipt(dto); return BaseResult.success(); } @@ -56,7 +62,7 @@ public class WarehouseReceiptController { @ApiLog(type = OperationType.UPDATE, remark = "更新一条WarehouseReceipt记录") @Operation(summary = "更新WarehouseReceipt", operationId = "updateWarehouseReceipt") @PostMapping("/updateWarehouseReceipt") - @PreAuthorize("hasAuthority('warehousereceipt:update')") + @PreAuthorize("hasAuthority('warehouse_receipt:update')") public BaseResult updateWarehouseReceipt(@Validated(Update.class) @RequestBody WarehouseReceiptDto dto) { warehouseReceiptService.updateWarehouseReceipt(dto); return BaseResult.success(); @@ -65,7 +71,7 @@ public class WarehouseReceiptController { @ApiLog(type = OperationType.DELETE, remark = "删除一条WarehouseReceipt记录") @Operation(summary = "删除WarehouseReceipt", operationId = "deleteWarehouseReceipt") @PostMapping("/deleteWarehouseReceipt") - @PreAuthorize("hasAuthority('warehousereceipt:delete')") + @PreAuthorize("hasAuthority('warehouse_receipt:delete')") public BaseResult deleteWarehouseReceipt(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) { warehouseReceiptService.deleteWarehouseReceipt(req.id()); return BaseResult.success(); @@ -74,9 +80,45 @@ public class WarehouseReceiptController { @ApiLog(type = OperationType.DELETE, remark = "批量删除WarehouseReceipt记录") @Operation(summary = "批量删除WarehouseReceipt", operationId = "deleteWarehouseReceiptBatch") @PostMapping("/deleteWarehouseReceiptBatch") - @PreAuthorize("hasAuthority('warehousereceipt:deleteBatch')") + @PreAuthorize("hasAuthority('warehouse_receipt:deleteBatch')") public BaseResult deleteWarehouseReceiptBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) { warehouseReceiptService.deleteBatch(req.ids()); return BaseResult.success(); } + + @ApiLog(type = OperationType.UPDATE, remark = "审核WarehouseReceipt") + @Operation(summary = "审核WarehouseReceipt", operationId = "approveWarehouseReceipt") + @PostMapping("/approveWarehouseReceipt") + @PreAuthorize("hasAuthority('warehouse_receipt:approve')") + public BaseResult approveWarehouseReceipt( + @Parameter(description = "入库单ID") @RequestParam Long id) { + warehouseReceiptService.approve(id); + return BaseResult.success(); + } + + @ApiLog(type = OperationType.UPDATE, remark = "反审核WarehouseReceipt") + @Operation(summary = "反审核WarehouseReceipt", operationId = "unapproveWarehouseReceipt") + @PostMapping("/unapproveWarehouseReceipt") + @PreAuthorize("hasAuthority('warehouse_receipt:unapprove')") + public BaseResult unapproveWarehouseReceipt( + @Parameter(description = "入库单ID") @RequestParam Long id) { + warehouseReceiptService.unapprove(id); + return BaseResult.success(); + } + + @Operation(summary = "获取WarehouseReceipt明细", operationId = "getWarehouseReceiptDetail") + @GetMapping("/getWarehouseReceiptDetail") + @PreAuthorize("hasAuthority('warehousereceipt:index')") + public BaseResult> getWarehouseReceiptDetail( + @Parameter(description = "入库单ID") @RequestParam Long id) { + return BaseResult.successWithData(warehouseReceiptService.getDetail(id)); + } + + @Operation(summary = "导入WarehouseReceipt明细", operationId = "importWarehouseReceiptItems") + @PostMapping("/importWarehouseReceiptItems") + @PreAuthorize("hasAuthority('warehouse_receipt:add')") + public BaseResult> importWarehouseReceiptItems( + @Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) { + return BaseResult.successWithData(warehouseReceiptService.importItems(file)); + } } diff --git a/src/main/java/com/niuan/erp/module/warehouse/controller/dto/InventoryCountItemDto.java b/src/main/java/com/niuan/erp/module/warehouse/controller/dto/InventoryCountItemDto.java index fb4be63..b966faf 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/controller/dto/InventoryCountItemDto.java +++ b/src/main/java/com/niuan/erp/module/warehouse/controller/dto/InventoryCountItemDto.java @@ -20,4 +20,6 @@ public record InventoryCountItemDto( Integer reserve1, String reserve2, Integer documentNo, - Integer originalProductCount) {} \ No newline at end of file + Integer originalProductCount, + String productSpec +) {} \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/warehouse/converter/InventoryCountConverter.java b/src/main/java/com/niuan/erp/module/warehouse/converter/InventoryCountConverter.java index af12a0f..99c9b68 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/converter/InventoryCountConverter.java +++ b/src/main/java/com/niuan/erp/module/warehouse/converter/InventoryCountConverter.java @@ -1,8 +1,13 @@ package com.niuan.erp.module.warehouse.converter; import com.niuan.erp.module.common.entity.Document; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountAddDto; import com.niuan.erp.module.warehouse.controller.dto.InventoryCountDto; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountItemAddDto; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountItemDto; +import com.niuan.erp.module.warehouse.entity.InventoryCountItem; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import java.util.List; @@ -10,6 +15,14 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface InventoryCountConverter { Document toEntity(InventoryCountDto dto); + Document toEntity(InventoryCountAddDto dto); InventoryCountDto toDto(Document entity); List toDtoList(List entities); + + InventoryCountItem toEntity(InventoryCountItemAddDto dto); + List toEntityList(List dtoList); + + @Mapping(target = "productSpec", ignore = true) + InventoryCountItemDto toItemDto(InventoryCountItem entity); + List toItemDtoList(List entities); } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/warehouse/converter/StockTransferOrderConverter.java b/src/main/java/com/niuan/erp/module/warehouse/converter/StockTransferOrderConverter.java index 198c0c7..2859d32 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/converter/StockTransferOrderConverter.java +++ b/src/main/java/com/niuan/erp/module/warehouse/converter/StockTransferOrderConverter.java @@ -2,11 +2,12 @@ package com.niuan.erp.module.warehouse.converter; import com.niuan.erp.module.common.entity.Document; import com.niuan.erp.module.common.entity.DocumentMaterial; -import com.niuan.erp.module.common.mapper.DocumentMapper; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderAddDto; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderDto; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderItemAddDto; +import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderItemDto; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import java.util.List; @@ -20,4 +21,8 @@ public interface StockTransferOrderConverter { DocumentMaterial toEntity(StockTransferOrderItemAddDto dto); List toEntityList(List dtoList); + + @Mapping(target = "productSpec", ignore = true) + StockTransferOrderItemDto toItemDto(DocumentMaterial entity); + List toItemDtoList(List entities); } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/warehouse/converter/WarehouseReceiptConverter.java b/src/main/java/com/niuan/erp/module/warehouse/converter/WarehouseReceiptConverter.java index 47bc42c..ebead1e 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/converter/WarehouseReceiptConverter.java +++ b/src/main/java/com/niuan/erp/module/warehouse/converter/WarehouseReceiptConverter.java @@ -1,8 +1,13 @@ package com.niuan.erp.module.warehouse.converter; import com.niuan.erp.module.common.entity.Document; +import com.niuan.erp.module.common.entity.DocumentMaterial; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptAddDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptDto; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptItemAddDto; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptItemDto; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import java.util.List; @@ -10,6 +15,14 @@ import java.util.List; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface WarehouseReceiptConverter { Document toEntity(WarehouseReceiptDto dto); + Document toEntity(WarehouseReceiptAddDto dto); WarehouseReceiptDto toDto(Document entity); List toDtoList(List entities); + + DocumentMaterial toEntity(WarehouseReceiptItemAddDto dto); + List toEntityList(List dtoList); + + @Mapping(target = "productSpec", ignore = true) + WarehouseReceiptItemDto toItemDto(DocumentMaterial entity); + List toItemDtoList(List entities); } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/InventoryCountService.java b/src/main/java/com/niuan/erp/module/warehouse/service/InventoryCountService.java index 1f4c61d..99de317 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/InventoryCountService.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/InventoryCountService.java @@ -4,7 +4,10 @@ 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.warehouse.controller.dto.InventoryCountAddDto; import com.niuan.erp.module.warehouse.controller.dto.InventoryCountDto; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountItemDto; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -12,7 +15,7 @@ public interface InventoryCountService { IPage getInventoryCountPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addInventoryCount(InventoryCountDto dto); + void addInventoryCount(InventoryCountAddDto dto); void updateInventoryCount(InventoryCountDto dto); @@ -20,4 +23,11 @@ public interface InventoryCountService { void deleteBatch(List ids); + void approve(long id); + + void unapprove(long id); + + List getDetail(long id); + + List importItems(MultipartFile file); } diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/StockTransferOrderService.java b/src/main/java/com/niuan/erp/module/warehouse/service/StockTransferOrderService.java index acbc7a9..0896f71 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/StockTransferOrderService.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/StockTransferOrderService.java @@ -6,6 +6,8 @@ import com.niuan.erp.common.base.BasePageReqParams; import com.niuan.erp.module.common.entity.Document; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderAddDto; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderDto; +import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderItemDto; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -21,4 +23,11 @@ public interface StockTransferOrderService { void deleteBatch(List ids); + void approve(long id); + + void unapprove(long id); + + List getDetail(long id); + + List importItems(MultipartFile file); } diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseItemService.java b/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseItemService.java index 4e9b33d..f12c291 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseItemService.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseItemService.java @@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.niuan.erp.common.base.BasePageReqParams; import com.niuan.erp.common.base.BaseSelectDto; import com.niuan.erp.common.base.BaseTree; +import com.niuan.erp.module.warehouse.controller.dto.ProductVendorMapAddDto; +import com.niuan.erp.module.warehouse.controller.dto.ProductVendorMapDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseItemDto; import com.niuan.erp.module.warehouse.entity.WarehouseItem; @@ -28,4 +30,10 @@ public interface WarehouseItemService { boolean existsWarehouseItemByPartNumber(String partNumber); + List getVendorListByWarehouseItemId(Long warehouseItemId); + + void saveVendorList(ProductVendorMapAddDto dto); + + String getPartNumberById(Long warehouseItemId); + } diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseReceiptService.java b/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseReceiptService.java index 575d8e6..cd2333b 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseReceiptService.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/WarehouseReceiptService.java @@ -4,7 +4,10 @@ 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.warehouse.controller.dto.WarehouseReceiptAddDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptDto; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptItemDto; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -12,7 +15,7 @@ public interface WarehouseReceiptService { IPage getWarehouseReceiptPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper); - void addWarehouseReceipt(WarehouseReceiptDto dto); + void addWarehouseReceipt(WarehouseReceiptAddDto dto); void updateWarehouseReceipt(WarehouseReceiptDto dto); @@ -20,4 +23,11 @@ public interface WarehouseReceiptService { void deleteBatch(List ids); + void approve(long id); + + void unapprove(long id); + + List getDetail(long id); + + List importItems(MultipartFile file); } diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/impl/InventoryCountServiceImpl.java b/src/main/java/com/niuan/erp/module/warehouse/service/impl/InventoryCountServiceImpl.java index c365256..1ba026a 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/impl/InventoryCountServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/impl/InventoryCountServiceImpl.java @@ -1,22 +1,38 @@ package com.niuan.erp.module.warehouse.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.enums.DocumentType; +import com.niuan.erp.module.common.enums.FormStatus; import com.niuan.erp.module.common.mapper.DocumentMapper; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountAddDto; import com.niuan.erp.module.warehouse.controller.dto.InventoryCountDto; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountItemAddDto; +import com.niuan.erp.module.warehouse.controller.dto.InventoryCountItemDto; import com.niuan.erp.module.warehouse.converter.InventoryCountConverter; +import com.niuan.erp.module.warehouse.entity.InventoryCountItem; +import com.niuan.erp.module.warehouse.entity.Stock; +import com.niuan.erp.module.warehouse.mapper.InventoryCountItemMapper; +import com.niuan.erp.module.warehouse.mapper.StockMapper; import com.niuan.erp.module.warehouse.service.InventoryCountService; 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.List; +import java.util.*; +import java.util.stream.Collectors; @Service @@ -27,24 +43,115 @@ public class InventoryCountServiceImpl extends ServiceImpl getInventoryCountPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.INVENTORY_COUNT); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(inventoryCountConverter::toDto); } @Override - public void addInventoryCount(InventoryCountDto dto) { + public void addInventoryCount(InventoryCountAddDto dto) { + Integer isInit = dto.isInit() != null ? dto.isInit() : 0; + + if (isInit == 1) { + LambdaQueryWrapper initWrapper = new LambdaQueryWrapper<>(); + initWrapper.eq(Document::getFormType, DocumentType.INVENTORY_COUNT) + .eq(Document::getStoreNo, dto.storeNo()) + .eq(Document::getReserve1, 1); + Document existingInit = this.baseMapper.selectOne(initWrapper); + if (existingInit != null) { + throw new BusinessException("warehouse.inventory_count.exception.init_already_exists"); + } + } + Document entity = inventoryCountConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); entity.setCreateDate(LocalDateTime.now()); entity.setStatus(0); + entity.setCustomerId(SecurityUtils.getCustomerId()); + entity.setFormType(DocumentType.INVENTORY_COUNT); + entity.setFormStatus(FormStatus.NO_APPROVE); + entity.setReserve1(isInit); this.baseMapper.insert(entity); + + List items = inventoryCountConverter.toEntityList(dto.countItems()); + + if (isInit == 0) { + List partNumbers = items.stream() + .map(InventoryCountItem::getPartNumber) + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, dto.storeNo()) + .in(Stock::getPartNumber, partNumbers); + List stockList = stockMapper.selectList(stockWrapper); + Map stockMap = stockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (InventoryCountItem item : items) { + Stock stock = stockMap.get(item.getPartNumber()); + if (stock == null) { + throw new BusinessException("warehouse.inventory_count.exception.stock_not_found"); + } + item.setOriginalProductCount(stock.getProductCount()); + item.setDiffCount(item.getProductCount() - stock.getProductCount()); + } + } else { + for (InventoryCountItem item : items) { + item.setOriginalProductCount(0); + item.setDiffCount(0); + } + } + + for (InventoryCountItem item : items) { + item.setDocumentNo(entity.getId().intValue()); + item.setStoreNo(dto.storeNo()); + item.setStoreName(dto.storeName()); + item.setCreateDate(LocalDateTime.now()); + item.setCreateUserId(SecurityUtils.getUserId()); + item.setCreateUserName(SecurityUtils.getUserName()); + item.setStatus(0); + item.setReserve1(isInit); + } + + inventoryCountItemMapper.insert(items); + + if (isInit == 1) { + List newStocks = new ArrayList<>(); + for (InventoryCountItem item : items) { + Stock stock = new Stock(); + stock.setPartNumber(item.getPartNumber()); + stock.setProductCount(item.getProductCount()); + stock.setStoreNo(dto.storeNo()); + stock.setStoreName(dto.storeName()); + stock.setCreateDate(LocalDateTime.now()); + stock.setCreateUserId(SecurityUtils.getUserId()); + stock.setCreateUserName(SecurityUtils.getUserName()); + stock.setStatus(0); + newStocks.add(stock); + } + for (Stock stock : newStocks) { + stockMapper.insert(stock); + } + } } @Override public void updateInventoryCount(InventoryCountDto dto) { + Document existingEntity = this.baseMapper.selectById(dto.id()); + if (existingEntity == null) { + throw new BusinessException("warehouse.inventory_count.exception.not_found"); + } + if (existingEntity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("warehouse.inventory_count.exception.cannot_update_approved"); + } + Document entity = inventoryCountConverter.toEntity(dto); entity.setUpdateUserId(SecurityUtils.getUserId()); entity.setUpdateUserName(SecurityUtils.getUserName()); @@ -54,12 +161,225 @@ public class InventoryCountServiceImpl extends ServiceImpl itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(InventoryCountItem::getDocumentNo, id); + inventoryCountItemMapper.delete(itemWrapper); } @Override public void deleteBatch(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException("warehouse.inventory_count.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("warehouse.inventory_count.exception.cannot_delete_approved_batch"); + } + this.baseMapper.deleteByIds(ids); + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.in(InventoryCountItem::getDocumentNo, ids); + inventoryCountItemMapper.delete(itemWrapper); } + @Override + public void approve(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("warehouse.inventory_count.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("warehouse.inventory_count.exception.already_approved"); + } + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(InventoryCountItem::getDocumentNo, id); + List items = inventoryCountItemMapper.selectList(itemWrapper); + if (items == null || items.isEmpty()) { + throw new BusinessException("warehouse.inventory_count.exception.no_items"); + } + + Integer storeNo = entity.getStoreNo(); + List partNumbers = items.stream() + .map(InventoryCountItem::getPartNumber) + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, storeNo) + .in(Stock::getPartNumber, partNumbers); + List existingStockList = stockMapper.selectList(stockWrapper); + Map existingStockMap = existingStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + List newStocks = new ArrayList<>(); + for (InventoryCountItem item : items) { + Stock existingStock = existingStockMap.get(item.getPartNumber()); + if (existingStock == null) { + Stock newStock = new Stock(); + newStock.setPartNumber(item.getPartNumber()); + newStock.setProductCount(item.getProductCount()); + newStock.setStoreNo(storeNo); + newStock.setStoreName(entity.getStoreName()); + newStock.setCreateDate(LocalDateTime.now()); + newStock.setCreateUserId(SecurityUtils.getUserId()); + newStock.setCreateUserName(SecurityUtils.getUserName()); + newStock.setStatus(0); + newStocks.add(newStock); + } else { + existingStock.setProductCount(item.getProductCount()); + existingStock.setUpdateDate(LocalDateTime.now()); + existingStock.setUpdateUserId(SecurityUtils.getUserId()); + existingStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(existingStock); + } + } + + if (!newStocks.isEmpty()) { + for (Stock newStock : newStocks) { + stockMapper.insert(newStock); + } + } + + entity.setFormStatus(FormStatus.APPROVE); + entity.setUpdateDate(LocalDateTime.now()); + entity.setUpdateUserId(SecurityUtils.getUserId()); + entity.setUpdateUserName(SecurityUtils.getUserName()); + this.baseMapper.updateById(entity); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(InventoryCountItem::getDocumentNo, id) + .set(InventoryCountItem::getUpdateDate, LocalDateTime.now()) + .set(InventoryCountItem::getUpdateUserId, SecurityUtils.getUserId()) + .set(InventoryCountItem::getUpdateUserName, SecurityUtils.getUserName()); + inventoryCountItemMapper.update(null, updateWrapper); + } + + @Override + public void unapprove(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("warehouse.inventory_count.exception.not_found"); + } + if (entity.getFormStatus() != FormStatus.APPROVE) { + throw new BusinessException("warehouse.inventory_count.exception.not_approved"); + } + + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.eq(InventoryCountItem::getDocumentNo, id); + List items = inventoryCountItemMapper.selectList(itemWrapper); + + Integer storeNo = entity.getStoreNo(); + List partNumbers = items.stream() + .map(InventoryCountItem::getPartNumber) + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, storeNo) + .in(Stock::getPartNumber, partNumbers); + List existingStockList = stockMapper.selectList(stockWrapper); + Map existingStockMap = existingStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (InventoryCountItem item : items) { + Stock existingStock = existingStockMap.get(item.getPartNumber()); + if (existingStock == null) { + throw new BusinessException("warehouse.inventory_count.exception.stock_not_found_for_unapprove"); + } + existingStock.setProductCount(item.getOriginalProductCount()); + existingStock.setUpdateDate(LocalDateTime.now()); + existingStock.setUpdateUserId(SecurityUtils.getUserId()); + existingStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(existingStock); + } + + 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(InventoryCountItem::getDocumentNo, id) + .set(InventoryCountItem::getUpdateDate, LocalDateTime.now()) + .set(InventoryCountItem::getUpdateUserId, SecurityUtils.getUserId()) + .set(InventoryCountItem::getUpdateUserName, SecurityUtils.getUserName()); + inventoryCountItemMapper.update(null, updateWrapper); + } + + @Override + public List getDetail(long id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(InventoryCountItem::getDocumentNo, id); + List items = inventoryCountItemMapper.selectList(wrapper); + return inventoryCountConverter.toItemDtoList(items); + } + + @Override + public List importItems(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("warehouse.inventory_count.exception.file_empty"); + } + + 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 partNumber = getCellValueAsString(row.getCell(0)); + String productSpec = getCellValueAsString(row.getCell(1)); + String countStr = getCellValueAsString(row.getCell(2)); + + if (!StringUtils.hasText(partNumber)) continue; + + int productCount = 0; + if (StringUtils.hasText(countStr)) { + try { + productCount = Integer.parseInt(countStr); + } catch (NumberFormatException e) { + productCount = 0; + } + } + + InventoryCountItemDto item = new InventoryCountItemDto( + null, null, null, null, null, null, null, null, + null, null, partNumber, productCount, null, null, null, null, null, null, productSpec + ); + items.add(item); + } + + return items; + } catch (IOException e) { + throw new BusinessException("warehouse.inventory_count.exception.import_failed"); + } + } + + 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/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java b/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java index 8629f8d..a8e3bc7 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/impl/StockTransferOrderServiceImpl.java @@ -1,30 +1,40 @@ package com.niuan.erp.module.warehouse.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.warehouse.controller.dto.StockTransferOrderAddDto; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderDto; import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderItemAddDto; +import com.niuan.erp.module.warehouse.controller.dto.StockTransferOrderItemDto; import com.niuan.erp.module.warehouse.converter.StockTransferOrderConverter; +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 com.niuan.erp.module.warehouse.service.StockTransferOrderService; 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.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; @Service @@ -39,14 +49,19 @@ public class StockTransferOrderServiceImpl extends ServiceImpl getStockTransferOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.STOCK_TRANSFER_ORDER); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(stockTransferOrderConverter::toDto); } @Override public void addStockTransferOrder(StockTransferOrderAddDto dto) { + validateTransferOrder(dto); + Document entity = stockTransferOrderConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); @@ -54,6 +69,7 @@ public class StockTransferOrderServiceImpl extends ServiceImpl materials = stockTransferOrderConverter.toEntityList(dto.transferOrderItems()); @@ -69,8 +85,10 @@ public class StockTransferOrderServiceImpl extends ServiceImpl stockInfo = warehouseItems.get(m.getPartNumber()); + if (stockInfo != null) { + m.setDemandCount(((Long) stockInfo.get("productCount")).intValue()); + } }); documentMaterialMapper.insert(materials); @@ -78,6 +96,14 @@ public class StockTransferOrderServiceImpl extends ServiceImpl materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.eq(DocumentMaterial::getDocumentNo, id); + documentMaterialMapper.delete(materialWrapper); } @Override public void deleteBatch(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException("warehouse.stock_transfer_order.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("warehouse.stock_transfer_order.exception.cannot_delete_approved_batch"); + } + this.baseMapper.deleteByIds(ids); + + LambdaQueryWrapper materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.in(DocumentMaterial::getDocumentNo, ids); + documentMaterialMapper.delete(materialWrapper); } + @Override + public void approve(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("warehouse.stock_transfer_order.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("warehouse.stock_transfer_order.exception.already_approved"); + } + + LambdaQueryWrapper materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.eq(DocumentMaterial::getDocumentNo, id); + List materialList = documentMaterialMapper.selectList(materialWrapper); + if (materialList == null || materialList.isEmpty()) { + throw new BusinessException("warehouse.stock_transfer_order.exception.no_materials"); + } + + Integer outStoreNo = entity.getOutStoreNo(); + List partNumbers = materialList.stream() + .map(DocumentMaterial::getPartNumber) + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, outStoreNo) + .in(Stock::getPartNumber, partNumbers); + List outStockList = stockMapper.selectList(stockWrapper); + + Map outStockMap = outStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (DocumentMaterial material : materialList) { + Stock outStock = outStockMap.get(material.getPartNumber()); + if (outStock == null || outStock.getProductCount() < material.getProductCount()) { + throw new BusinessException("warehouse.stock_transfer_order.exception.insufficient_stock"); + } + } + + for (DocumentMaterial material : materialList) { + Stock outStock = outStockMap.get(material.getPartNumber()); + outStock.setProductCount(outStock.getProductCount() - material.getProductCount()); + outStock.setUpdateDate(LocalDateTime.now()); + outStock.setUpdateUserId(SecurityUtils.getUserId()); + outStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(outStock); + } + + Integer inStoreNo = entity.getStoreNo(); + LambdaQueryWrapper inStockWrapper = new LambdaQueryWrapper<>(); + inStockWrapper.eq(Stock::getStoreNo, inStoreNo) + .in(Stock::getPartNumber, partNumbers); + List inStockList = stockMapper.selectList(inStockWrapper); + Map inStockMap = inStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + List newInStocks = new ArrayList<>(); + for (DocumentMaterial material : materialList) { + Stock inStock = inStockMap.get(material.getPartNumber()); + if (inStock == null) { + Stock newStock = new Stock(); + newStock.setPartNumber(material.getPartNumber()); + newStock.setProductCount(material.getProductCount()); + newStock.setStoreNo(inStoreNo); + newStock.setCreateDate(LocalDateTime.now()); + newStock.setCreateUserId(SecurityUtils.getUserId()); + newStock.setCreateUserName(SecurityUtils.getUserName()); + newStock.setStatus(0); + newInStocks.add(newStock); + } else { + inStock.setProductCount(inStock.getProductCount() + material.getProductCount()); + inStock.setUpdateDate(LocalDateTime.now()); + inStock.setUpdateUserId(SecurityUtils.getUserId()); + inStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(inStock); + } + } + + if (!newInStocks.isEmpty()) { + for (Stock newStock : newInStocks) { + stockMapper.insert(newStock); + } + } + + entity.setFormStatus(FormStatus.APPROVE); + entity.setUpdateDate(LocalDateTime.now()); + entity.setUpdateUserId(SecurityUtils.getUserId()); + entity.setUpdateUserName(SecurityUtils.getUserName()); + this.baseMapper.updateById(entity); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(DocumentMaterial::getDocumentNo, id) + .set(DocumentMaterial::getUpdateDate, LocalDateTime.now()) + .set(DocumentMaterial::getUpdateUserId, SecurityUtils.getUserId()) + .set(DocumentMaterial::getUpdateUserName, SecurityUtils.getUserName()); + documentMaterialMapper.update(null, updateWrapper); + } + + @Override + public void unapprove(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("warehouse.stock_transfer_order.exception.not_found"); + } + if (entity.getFormStatus() != FormStatus.APPROVE) { + throw new BusinessException("warehouse.stock_transfer_order.exception.not_approved"); + } + + LambdaQueryWrapper materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.eq(DocumentMaterial::getDocumentNo, id); + List materialList = documentMaterialMapper.selectList(materialWrapper); + + List partNumbers = materialList.stream() + .map(DocumentMaterial::getPartNumber) + .toList(); + + Integer outStoreNo = entity.getOutStoreNo(); + LambdaQueryWrapper outStockWrapper = new LambdaQueryWrapper<>(); + outStockWrapper.eq(Stock::getStoreNo, outStoreNo) + .in(Stock::getPartNumber, partNumbers); + List outStockList = stockMapper.selectList(outStockWrapper); + Map outStockMap = outStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (DocumentMaterial material : materialList) { + Stock outStock = outStockMap.get(material.getPartNumber()); + if (outStock == null) { + Stock newStock = new Stock(); + newStock.setPartNumber(material.getPartNumber()); + newStock.setProductCount(material.getProductCount()); + newStock.setStoreNo(outStoreNo); + newStock.setCreateDate(LocalDateTime.now()); + newStock.setCreateUserId(SecurityUtils.getUserId()); + newStock.setCreateUserName(SecurityUtils.getUserName()); + newStock.setStatus(0); + stockMapper.insert(newStock); + } else { + outStock.setProductCount(outStock.getProductCount() + material.getProductCount()); + outStock.setUpdateDate(LocalDateTime.now()); + outStock.setUpdateUserId(SecurityUtils.getUserId()); + outStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(outStock); + } + } + + Integer inStoreNo = entity.getStoreNo(); + LambdaQueryWrapper inStockWrapper = new LambdaQueryWrapper<>(); + inStockWrapper.eq(Stock::getStoreNo, inStoreNo) + .in(Stock::getPartNumber, partNumbers); + List inStockList = stockMapper.selectList(inStockWrapper); + Map inStockMap = inStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (DocumentMaterial material : materialList) { + Stock inStock = inStockMap.get(material.getPartNumber()); + if (inStock == null) { + throw new BusinessException("warehouse.stock_transfer_order.exception.stock_not_found"); + } + if (inStock.getProductCount() < material.getProductCount()) { + throw new BusinessException("warehouse.stock_transfer_order.exception.insufficient_stock_for_unapprove"); + } + inStock.setProductCount(inStock.getProductCount() - material.getProductCount()); + inStock.setUpdateDate(LocalDateTime.now()); + inStock.setUpdateUserId(SecurityUtils.getUserId()); + inStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(inStock); + } + + 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(DocumentMaterial::getDocumentNo, id) + .set(DocumentMaterial::getUpdateDate, LocalDateTime.now()) + .set(DocumentMaterial::getUpdateUserId, SecurityUtils.getUserId()) + .set(DocumentMaterial::getUpdateUserName, SecurityUtils.getUserName()); + documentMaterialMapper.update(null, updateWrapper); + } + + @Override + public List getDetail(long id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DocumentMaterial::getDocumentNo, id); + List materials = documentMaterialMapper.selectList(wrapper); + return stockTransferOrderConverter.toItemDtoList(materials); + } + + @Override + public List importItems(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("warehouse.stock_transfer_order.exception.file_empty"); + } + + 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 partNumber = getCellValueAsString(row.getCell(0)); + String productSpec = getCellValueAsString(row.getCell(1)); + String countStr = getCellValueAsString(row.getCell(2)); + + if (!StringUtils.hasText(partNumber)) continue; + + int productCount = 0; + if (StringUtils.hasText(countStr)) { + try { + productCount = Integer.parseInt(countStr); + } catch (NumberFormatException e) { + productCount = 0; + } + } + + StockTransferOrderItemDto item = new StockTransferOrderItemDto( + null, null, null, null, null, null, null, null, + null, partNumber, null, productCount, null, null, null, null, null, null, productSpec + ); + items.add(item); + } + + return items; + } catch (IOException e) { + throw new BusinessException("warehouse.stock_transfer_order.exception.import_failed"); + } + } + + private void validateTransferOrder(StockTransferOrderAddDto dto) { + if (dto.storeNo().equals(dto.outStoreNo())) { + throw new BusinessException("warehouse.stock_transfer_order.exception.same_warehouse"); + } + + Map partCountMap = new HashMap<>(); + for (StockTransferOrderItemAddDto item : dto.transferOrderItems()) { + partCountMap.merge(item.partNumber(), item.productCount(), Integer::sum); + } + + List partNumbers = new ArrayList<>(partCountMap.keySet()); + Map> warehouseItems = + warehouseItemMapper.getWarehouseItemStockListByPartNumbers(partNumbers); + + for (Map.Entry entry : partCountMap.entrySet()) { + String partNumber = entry.getKey(); + Integer totalTransfer = entry.getValue(); + + Map stockInfo = warehouseItems.get(partNumber); + if (stockInfo == null) { + throw new BusinessException("warehouse.stock_transfer_order.exception.part_not_found"); + } + + Long stockCount = (Long) stockInfo.get("productCount"); + if (stockCount == null || stockCount < totalTransfer) { + throw new BusinessException("warehouse.stock_transfer_order.exception.insufficient_stock"); + } + } + } + + 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/java/com/niuan/erp/module/warehouse/service/impl/WarehouseItemServiceImpl.java b/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseItemServiceImpl.java index 46d849e..980366b 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseItemServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseItemServiceImpl.java @@ -7,10 +7,17 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.niuan.erp.common.base.BasePageReqParams; import com.niuan.erp.common.base.BaseSelectDto; import com.niuan.erp.common.base.BaseTree; +import com.niuan.erp.common.exception.BusinessException; import com.niuan.erp.common.utils.SecurityUtils; +import com.niuan.erp.module.sys.entity.Vendor; +import com.niuan.erp.module.sys.service.VendorService; +import com.niuan.erp.module.warehouse.controller.dto.ProductVendorMapAddDto; +import com.niuan.erp.module.warehouse.controller.dto.ProductVendorMapDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseItemDto; import com.niuan.erp.module.warehouse.converter.WarehouseItemConverter; +import com.niuan.erp.module.warehouse.entity.ProductVendorMap; import com.niuan.erp.module.warehouse.entity.WarehouseItem; +import com.niuan.erp.module.warehouse.mapper.ProductVendorMapMapper; import com.niuan.erp.module.warehouse.mapper.WarehouseItemMapper; import com.niuan.erp.module.warehouse.service.WarehouseItemService; import lombok.RequiredArgsConstructor; @@ -18,7 +25,13 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; @Service @@ -28,6 +41,8 @@ public class WarehouseItemServiceImpl extends ServiceImpl getProductTypeSelectList() { @@ -84,4 +99,104 @@ public class WarehouseItemServiceImpl extends ServiceImpl 0; } + @Override + public List getVendorListByWarehouseItemId(Long warehouseItemId) { + WarehouseItem warehouseItem = this.baseMapper.selectById(warehouseItemId); + if (warehouseItem == null) { + throw new BusinessException("warehouse.warehouse_item.exception.not_found"); + } + String partNumber = warehouseItem.getPartNumber(); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ProductVendorMap::getPartNumber, partNumber); + List vendorMaps = productVendorMapMapper.selectList(wrapper); + + if (vendorMaps.isEmpty()) { + return new ArrayList<>(); + } + + Set vendorIds = vendorMaps.stream() + .map(ProductVendorMap::getVendorId) + .collect(Collectors.toSet()); + + LambdaQueryWrapper vendorWrapper = new LambdaQueryWrapper<>(); + vendorWrapper.in(Vendor::getId, vendorIds); + List vendors = vendorService.getVendorList(vendorWrapper); + + Map vendorMap = vendors.stream() + .collect(Collectors.toMap(Vendor::getId, Function.identity())); + + return vendorMaps.stream() + .map(vm -> { + Vendor vendor = vendorMap.get(vm.getVendorId()); + return new ProductVendorMapDto( + vm.getId(), + vm.getPartNumber(), + vm.getVendorId(), + vendor != null ? vendor.getVendorName() : null, + vendor != null ? vendor.getContactPerson() : null, + vendor != null ? vendor.getTel() : null, + vendor != null ? vendor.getAddress() : null, + vm.getCostPrice(), + vm.getProcureDate() + ); + }) + .collect(Collectors.toList()); + } + + @Override + public void saveVendorList(ProductVendorMapAddDto dto) { + WarehouseItem warehouseItem = this.baseMapper.selectById(dto.warehouseItemId()); + if (warehouseItem == null) { + throw new BusinessException("warehouse.warehouse_item.exception.not_found"); + } + String partNumber = warehouseItem.getPartNumber(); + + if (dto.vendorList() != null && !dto.vendorList().isEmpty()) { + Set vendorIds = new HashSet<>(); + for (ProductVendorMapAddDto.ProductVendorMapItemDto item : dto.vendorList()) { + if (!vendorIds.add(item.vendorId())) { + throw new BusinessException("warehouse.warehouse_item.exception.vendor_duplicate"); + } + } + } + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ProductVendorMap::getPartNumber, partNumber); + List existingMaps = productVendorMapMapper.selectList(queryWrapper); + Map existingMapByVendorId = existingMaps.stream() + .collect(Collectors.toMap(ProductVendorMap::getVendorId, m -> m)); + + productVendorMapMapper.delete(queryWrapper); + + if (dto.vendorList() != null && !dto.vendorList().isEmpty()) { + List newMaps = dto.vendorList().stream() + .map(item -> { + ProductVendorMap map = new ProductVendorMap(); + map.setPartNumber(partNumber); + map.setVendorId(item.vendorId()); + map.setCostPrice(item.costPrice()); + ProductVendorMap existing = existingMapByVendorId.get(item.vendorId()); + if (existing != null) { + map.setProcureDate(existing.getProcureDate()); + } + return map; + }) + .collect(Collectors.toList()); + + for (ProductVendorMap map : newMaps) { + productVendorMapMapper.insert(map); + } + } + } + + @Override + public String getPartNumberById(Long warehouseItemId) { + WarehouseItem warehouseItem = this.baseMapper.selectById(warehouseItemId); + if (warehouseItem == null) { + throw new BusinessException("warehouse.warehouse_item.exception.not_found"); + } + return warehouseItem.getPartNumber(); + } + } \ No newline at end of file diff --git a/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseReceiptServiceImpl.java b/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseReceiptServiceImpl.java index 610e41d..c29a728 100644 --- a/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseReceiptServiceImpl.java +++ b/src/main/java/com/niuan/erp/module/warehouse/service/impl/WarehouseReceiptServiceImpl.java @@ -1,22 +1,38 @@ package com.niuan.erp.module.warehouse.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 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.warehouse.controller.dto.WarehouseReceiptAddDto; import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptDto; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptItemAddDto; +import com.niuan.erp.module.warehouse.controller.dto.WarehouseReceiptItemDto; import com.niuan.erp.module.warehouse.converter.WarehouseReceiptConverter; +import com.niuan.erp.module.warehouse.entity.Stock; +import com.niuan.erp.module.warehouse.mapper.StockMapper; import com.niuan.erp.module.warehouse.service.WarehouseReceiptService; 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.List; +import java.util.*; +import java.util.stream.Collectors; @Service @@ -27,24 +43,51 @@ public class WarehouseReceiptServiceImpl extends ServiceImpl getWarehouseReceiptPage(BasePageReqParams pageParams, LambdaQueryWrapper wrapper) { + wrapper.eq(Document::getFormType, DocumentType.WAREHOUSE_RECEIPT); IPage result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper); return result.convert(warehouseReceiptConverter::toDto); } @Override - public void addWarehouseReceipt(WarehouseReceiptDto dto) { + public void addWarehouseReceipt(WarehouseReceiptAddDto dto) { Document entity = warehouseReceiptConverter.toEntity(dto); entity.setCreateUserId(SecurityUtils.getUserId()); entity.setCreateUserName(SecurityUtils.getUserName()); entity.setCreateDate(LocalDateTime.now()); entity.setStatus(0); + entity.setCustomerId(SecurityUtils.getCustomerId()); + entity.setFormType(DocumentType.WAREHOUSE_RECEIPT); + entity.setFormStatus(FormStatus.NO_APPROVE); this.baseMapper.insert(entity); + + List materials = warehouseReceiptConverter.toEntityList(dto.receiptItems()); + materials.forEach(m -> { + m.setDocumentNo(entity.getId().intValue()); + m.setCreateUserId(SecurityUtils.getUserId()); + m.setCreateUserName(SecurityUtils.getUserName()); + m.setCreateDate(LocalDateTime.now()); + m.setStatus(0); + }); + + documentMaterialMapper.insert(materials); } @Override public void updateWarehouseReceipt(WarehouseReceiptDto dto) { + Document existingEntity = this.baseMapper.selectById(dto.id()); + if (existingEntity == null) { + throw new BusinessException("warehouse.warehouse_receipt.exception.not_found"); + } + if (existingEntity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("warehouse.warehouse_receipt.exception.cannot_update_approved"); + } + Document entity = warehouseReceiptConverter.toEntity(dto); entity.setUpdateUserId(SecurityUtils.getUserId()); entity.setUpdateUserName(SecurityUtils.getUserName()); @@ -54,12 +97,227 @@ public class WarehouseReceiptServiceImpl extends ServiceImpl materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.eq(DocumentMaterial::getDocumentNo, id); + documentMaterialMapper.delete(materialWrapper); } @Override public void deleteBatch(List ids) { + if (ids == null || ids.isEmpty()) { + throw new BusinessException("warehouse.warehouse_receipt.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("warehouse.warehouse_receipt.exception.cannot_delete_approved_batch"); + } + this.baseMapper.deleteByIds(ids); + + LambdaQueryWrapper materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.in(DocumentMaterial::getDocumentNo, ids); + documentMaterialMapper.delete(materialWrapper); } + @Override + public void approve(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("warehouse.warehouse_receipt.exception.not_found"); + } + if (entity.getFormStatus() == FormStatus.APPROVE) { + throw new BusinessException("warehouse.warehouse_receipt.exception.already_approved"); + } + + LambdaQueryWrapper materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.eq(DocumentMaterial::getDocumentNo, id); + List materialList = documentMaterialMapper.selectList(materialWrapper); + if (materialList == null || materialList.isEmpty()) { + throw new BusinessException("warehouse.warehouse_receipt.exception.no_materials"); + } + + Integer storeNo = entity.getStoreNo(); + List partNumbers = materialList.stream() + .map(DocumentMaterial::getPartNumber) + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, storeNo) + .in(Stock::getPartNumber, partNumbers); + List existingStockList = stockMapper.selectList(stockWrapper); + Map existingStockMap = existingStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + List newStocks = new ArrayList<>(); + for (DocumentMaterial material : materialList) { + Stock existingStock = existingStockMap.get(material.getPartNumber()); + if (existingStock == null) { + Stock newStock = new Stock(); + newStock.setPartNumber(material.getPartNumber()); + newStock.setProductCount(material.getProductCount()); + newStock.setStoreNo(storeNo); + newStock.setCreateDate(LocalDateTime.now()); + newStock.setCreateUserId(SecurityUtils.getUserId()); + newStock.setCreateUserName(SecurityUtils.getUserName()); + newStock.setStatus(0); + newStocks.add(newStock); + } else { + existingStock.setProductCount(existingStock.getProductCount() + material.getProductCount()); + existingStock.setUpdateDate(LocalDateTime.now()); + existingStock.setUpdateUserId(SecurityUtils.getUserId()); + existingStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(existingStock); + } + } + + if (!newStocks.isEmpty()) { + for (Stock newStock : newStocks) { + stockMapper.insert(newStock); + } + } + + entity.setFormStatus(FormStatus.APPROVE); + entity.setUpdateDate(LocalDateTime.now()); + entity.setUpdateUserId(SecurityUtils.getUserId()); + entity.setUpdateUserName(SecurityUtils.getUserName()); + this.baseMapper.updateById(entity); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(DocumentMaterial::getDocumentNo, id) + .set(DocumentMaterial::getUpdateDate, LocalDateTime.now()) + .set(DocumentMaterial::getUpdateUserId, SecurityUtils.getUserId()) + .set(DocumentMaterial::getUpdateUserName, SecurityUtils.getUserName()); + documentMaterialMapper.update(null, updateWrapper); + } + + @Override + public void unapprove(long id) { + Document entity = this.baseMapper.selectById(id); + if (entity == null) { + throw new BusinessException("warehouse.warehouse_receipt.exception.not_found"); + } + if (entity.getFormStatus() != FormStatus.APPROVE) { + throw new BusinessException("warehouse.warehouse_receipt.exception.not_approved"); + } + + LambdaQueryWrapper materialWrapper = new LambdaQueryWrapper<>(); + materialWrapper.eq(DocumentMaterial::getDocumentNo, id); + List materialList = documentMaterialMapper.selectList(materialWrapper); + + Integer storeNo = entity.getStoreNo(); + List partNumbers = materialList.stream() + .map(DocumentMaterial::getPartNumber) + .toList(); + + LambdaQueryWrapper stockWrapper = new LambdaQueryWrapper<>(); + stockWrapper.eq(Stock::getStoreNo, storeNo) + .in(Stock::getPartNumber, partNumbers); + List existingStockList = stockMapper.selectList(stockWrapper); + Map existingStockMap = existingStockList.stream() + .collect(Collectors.toMap(Stock::getPartNumber, s -> s)); + + for (DocumentMaterial material : materialList) { + Stock existingStock = existingStockMap.get(material.getPartNumber()); + if (existingStock == null) { + throw new BusinessException("warehouse.warehouse_receipt.exception.stock_not_found"); + } + if (existingStock.getProductCount() < material.getProductCount()) { + throw new BusinessException("warehouse.warehouse_receipt.exception.insufficient_stock_for_unapprove"); + } + existingStock.setProductCount(existingStock.getProductCount() - material.getProductCount()); + existingStock.setUpdateDate(LocalDateTime.now()); + existingStock.setUpdateUserId(SecurityUtils.getUserId()); + existingStock.setUpdateUserName(SecurityUtils.getUserName()); + stockMapper.updateById(existingStock); + } + + 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(DocumentMaterial::getDocumentNo, id) + .set(DocumentMaterial::getUpdateDate, LocalDateTime.now()) + .set(DocumentMaterial::getUpdateUserId, SecurityUtils.getUserId()) + .set(DocumentMaterial::getUpdateUserName, SecurityUtils.getUserName()); + documentMaterialMapper.update(null, updateWrapper); + } + + @Override + public List getDetail(long id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DocumentMaterial::getDocumentNo, id); + List materials = documentMaterialMapper.selectList(wrapper); + return warehouseReceiptConverter.toItemDtoList(materials); + } + + @Override + public List importItems(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new BusinessException("warehouse.warehouse_receipt.exception.file_empty"); + } + + 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 partNumber = getCellValueAsString(row.getCell(0)); + String productSpec = getCellValueAsString(row.getCell(1)); + String countStr = getCellValueAsString(row.getCell(2)); + + if (!StringUtils.hasText(partNumber)) continue; + + int productCount = 0; + if (StringUtils.hasText(countStr)) { + try { + productCount = Integer.parseInt(countStr); + } catch (NumberFormatException e) { + productCount = 0; + } + } + + WarehouseReceiptItemDto item = new WarehouseReceiptItemDto( + null, null, null, null, null, null, null, null, + null, partNumber, null, productCount, null, null, null, null, null, null, productSpec + ); + items.add(item); + } + + return items; + } catch (IOException e) { + throw new BusinessException("warehouse.warehouse_receipt.exception.import_failed"); + } + } + + 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_en_US.properties b/src/main/resources/i18n/messages_en_US.properties index e69de29..ae325ab 100644 --- a/src/main/resources/i18n/messages_en_US.properties +++ b/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,28 @@ +result.success=Request successful +result.error=Request failed +production.productionreturn.exception.not_exist=Return order does not exist +production.productionreturn.exception.already_approved=Return order has been approved +production.productionreturn.exception.no_material=Return order has no details +production.productionreturn.exception.not_approved=Return order is not approved, cannot reject +production.production_issue.exception.issue_not_exists=Issue order does not exist +production.production_issue.exception.only_approved_can_return=Only approved issue orders can generate return orders +production.production_issue.exception.no_return_items=No return items +production.production_issue.exception.already_returned=Issue order has already generated a return order, cannot return again +purchase.purchase_plan.validate.plan_id.not_null=Purchase plan ID cannot be empty +purchase.purchase_plan.validate.vendor_id.not_null=Vendor ID cannot be empty +purchase.purchase_plan.validate.vendor_name.not_null=Vendor name cannot be empty +purchase.purchase_plan.validate.selected_items.not_null=Please select items to purchase +purchase.purchase_plan.validate.form_code.not_null=Form code cannot be empty +purchase.purchase_plan.validate.form_name.not_null=Form name cannot be empty +purchase.purchase_plan.validate.form_mark.not_null=Form mark cannot be empty +purchase.purchase_plan.validate.total_value.not_null=Total value cannot be empty +purchase.purchase_plan.exception.plan_not_exists=Purchase plan does not exist +purchase.purchase_plan.exception.items_already_ordered=Items have already generated purchase orders, cannot purchase again +warehouse.warehouse_item.validate.id.not_null=Warehouse item ID cannot be empty +warehouse.warehouse_item.validate.vendor_id.not_null=Vendor ID cannot be empty +warehouse.warehouse_item.validate.cost_price.min=Cost price cannot be less than 0 +warehouse.warehouse_item.validate.part_number.not_null=Part number cannot be empty +warehouse.warehouse_item.validate.product_type.not_null=Product type cannot be empty +warehouse.warehouse_item.validate.product_specs.not_null=Product specs cannot be empty +warehouse.warehouse_item.exception.not_found=Warehouse item not found +warehouse.warehouse_item.exception.vendor_duplicate=Vendor cannot be duplicated diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 294a39c..7abd6c8 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -1,2 +1,163 @@ result.success=请求成功 -result.error=请求失败 \ No newline at end of file +result.error=请求失败 +production.productionreturn.exception.not_exist=退料单不存在 +production.productionreturn.exception.already_approved=退料单已经审核 +production.productionreturn.exception.no_material=退料单没有明细 +production.productionreturn.exception.not_approved=退料单不是已审核状态,不能反审 +production.production_issue.exception.issue_not_exists=发料单不存在 +production.production_issue.exception.only_approved_can_return=只有审核通过的发料单才能生成退料单 +production.production_issue.exception.no_return_items=没有退料明细 +production.production_issue.exception.already_returned=发料单已经生成退料单,不能再次退料 +purchase.purchase_plan.validate.plan_id.not_null=采购计划 ID 不能为空 +purchase.purchase_plan.validate.vendor_id.not_null=供应商 ID 不能为空 +purchase.purchase_plan.validate.vendor_name.not_null=供应商名称不能为空 +purchase.purchase_plan.validate.selected_items.not_null=请选择要采购的物料 +purchase.purchase_plan.validate.form_code.not_null=单据编号不能为空 +purchase.purchase_plan.validate.form_name.not_null=单据名称不能为空 +purchase.purchase_plan.validate.form_mark.not_null=单据备注不能为空 +purchase.purchase_plan.validate.total_value.not_null=订单总额不能为空 +purchase.purchase_plan.exception.plan_not_exists=采购计划不存在 +purchase.purchase_plan.exception.items_already_ordered=物料已经生成采购订单,不能再次采购 +warehouse.warehouse_item.validate.id.not_null=物料 ID 不能为空 +warehouse.warehouse_item.validate.vendor_id.not_null=供应商 ID 不能为空 +warehouse.warehouse_item.validate.cost_price.min=成本价不能小于 0 +warehouse.warehouse_item.validate.part_number.not_null=物料编号不能为空 +warehouse.warehouse_item.validate.product_type.not_null=物料规格不能为空 +warehouse.warehouse_item.validate.product_specs.not_null=物料型号不能为空 +warehouse.warehouse_item.exception.not_found=物料不存在 +warehouse.warehouse_item.exception.vendor_duplicate=供应商不能重复 +warehouse.stock_transfer_order.validate.form_code.not_null=单据编号不能为空 +warehouse.stock_transfer_order.validate.form_name.not_null=单据名称不能为空 +warehouse.stock_transfer_order.validate.store_no.not_null=入库仓库不能为空 +warehouse.stock_transfer_order.validate.store_name.not_null=入库仓库名称不能为空 +warehouse.stock_transfer_order.validate.out_store_no.not_null=出库仓库不能为空 +warehouse.stock_transfer_order.validate.out_store_name.not_null=出库仓库名称不能为空 +warehouse.stock_transfer_order.validate.transfer_order_items.not_null=调拨明细不能为空 +warehouse.stock_transfer_order.validate.part_number.not_null=物料编号不能为空 +warehouse.stock_transfer_order.validate.product_spec.not_null=物料规格不能为空 +warehouse.stock_transfer_order.validate.product_count.not_null=调拨数量不能为空 +warehouse.stock_transfer_order.validate.product_count.min=调拨数量必须大于 0 +warehouse.stock_transfer_order.validate.part_id.not_null=物料 ID 不能为空 +warehouse.stock_transfer_order.exception.not_found=调拨单不存在 +warehouse.stock_transfer_order.exception.cannot_update_approved=已审核的调拨单不能修改 +warehouse.stock_transfer_order.exception.cannot_delete_approved=已审核的调拨单不能删除 +warehouse.stock_transfer_order.exception.cannot_delete_approved_batch=选中的数据中存在已审核的调拨单,不能删除 +warehouse.stock_transfer_order.exception.ids_empty=请选择要删除的调拨单 +warehouse.stock_transfer_order.exception.already_approved=调拨单已经审核 +warehouse.stock_transfer_order.exception.no_materials=调拨单没有明细 +warehouse.stock_transfer_order.exception.insufficient_stock=物料 {0} 库存不足 +warehouse.stock_transfer_order.exception.not_approved=调拨单不是已审核状态,不能反审 +warehouse.stock_transfer_order.exception.stock_not_found=物料 {0} 在入库仓库中不存在 +warehouse.stock_transfer_order.exception.insufficient_stock_for_unapprove=物料 {0} 在入库仓库中库存不足,无法反审 +warehouse.stock_transfer_order.exception.file_empty=请选择要上传的文件 +warehouse.stock_transfer_order.exception.import_failed=导入文件失败 +warehouse.stock_transfer_order.exception.same_warehouse=入库仓库和出库仓库不能相同 +warehouse.stock_transfer_order.exception.part_not_found=物料 {0} 不存在 +warehouse.warehouse_receipt.validate.form_code.not_null=单据编号不能为空 +warehouse.warehouse_receipt.validate.form_name.not_null=单据名称不能为空 +warehouse.warehouse_receipt.validate.store_no.not_null=仓库不能为空 +warehouse.warehouse_receipt.validate.store_name.not_null=仓库名称不能为空 +warehouse.warehouse_receipt.validate.receipt_items.not_null=入库明细不能为空 +warehouse.warehouse_receipt.validate.part_number.not_null=物料编号不能为空 +warehouse.warehouse_receipt.validate.product_spec.not_null=物料规格不能为空 +warehouse.warehouse_receipt.validate.product_count.not_null=入库数量不能为空 +warehouse.warehouse_receipt.validate.product_count.min=入库数量必须大于 0 +warehouse.warehouse_receipt.validate.part_id.not_null=物料 ID 不能为空 +warehouse.warehouse_receipt.exception.not_found=入库单不存在 +warehouse.warehouse_receipt.exception.cannot_update_approved=已审核的入库单不能修改 +warehouse.warehouse_receipt.exception.cannot_delete_approved=已审核的入库单不能删除 +warehouse.warehouse_receipt.exception.cannot_delete_approved_batch=选中的数据中存在已审核的入库单,不能删除 +warehouse.warehouse_receipt.exception.ids_empty=请选择要删除的入库单 +warehouse.warehouse_receipt.exception.already_approved=入库单已经审核 +warehouse.warehouse_receipt.exception.no_materials=入库单没有明细 +warehouse.warehouse_receipt.exception.not_approved=入库单不是已审核状态,不能反审 +warehouse.warehouse_receipt.exception.stock_not_found=物料 {0} 在仓库中不存在 +warehouse.warehouse_receipt.exception.insufficient_stock_for_unapprove=物料 {0} 在仓库中库存不足,无法反审 +warehouse.warehouse_receipt.exception.file_empty=请选择要上传的文件 +warehouse.warehouse_receipt.exception.import_failed=导入文件失败 +warehouse.inventory_count.validate.form_code.not_null=单据编号不能为空 +warehouse.inventory_count.validate.form_name.not_null=单据名称不能为空 +warehouse.inventory_count.validate.store_no.not_null=仓库不能为空 +warehouse.inventory_count.validate.store_name.not_null=仓库名称不能为空 +warehouse.inventory_count.validate.count_items.not_null=盘点明细不能为空 +warehouse.inventory_count.validate.part_number.not_null=物料编号不能为空 +warehouse.inventory_count.validate.product_count.not_null=盘点数量不能为空 +warehouse.inventory_count.validate.product_count.min=盘点数量不能小于 0 +warehouse.inventory_count.exception.not_found=盘点单不存在 +warehouse.inventory_count.exception.cannot_update_approved=已审核的盘点单不能修改 +warehouse.inventory_count.exception.cannot_delete_approved=已审核的盘点单不能删除 +warehouse.inventory_count.exception.cannot_delete_approved_batch=选中的数据中存在已审核的盘点单,不能删除 +warehouse.inventory_count.exception.ids_empty=请选择要删除的盘点单 +warehouse.inventory_count.exception.already_approved=盘点单已经审核 +warehouse.inventory_count.exception.no_items=盘点单没有明细 +warehouse.inventory_count.exception.not_approved=盘点单不是已审核状态,不能反审 +warehouse.inventory_count.exception.stock_not_found=物料 {0} 在仓库中不存在库存 +warehouse.inventory_count.exception.stock_not_found_for_unapprove=物料 {0} 在仓库中不存在库存,无法反审 +warehouse.inventory_count.exception.file_empty=请选择要上传的文件 +warehouse.inventory_count.exception.import_failed=导入文件失败 +warehouse.inventory_count.exception.init_already_exists=当前仓库已经有初始库存单 +production.finished_product_receipt.validate.form_code.not_null=单据编号不能为空 +production.finished_product_receipt.validate.form_name.not_null=单据名称不能为空 +production.finished_product_receipt.validate.store_no.not_null=仓库不能为空 +production.finished_product_receipt.validate.store_name.not_null=仓库名称不能为空 +production.finished_product_receipt.validate.module_sn_items.not_null=成品明细不能为空 +production.finished_product_receipt.exception.not_found=成品入库单不存在 +production.finished_product_receipt.exception.cannot_update_approved=已审核的成品入库单不能修改 +production.finished_product_receipt.exception.cannot_delete_approved=已审核的成品入库单不能删除 +production.finished_product_receipt.exception.cannot_delete_approved_batch=选中的数据中存在已审核的成品入库单,不能删除 +production.finished_product_receipt.exception.ids_empty=请选择要删除的成品入库单 +production.finished_product_receipt.exception.already_approved=成品入库单已经审核 +production.finished_product_receipt.exception.not_approved=成品入库单不是已审核状态,不能反审 +production.finished_product_receipt.exception.no_module_sn_items=成品入库单没有明细 +production.finished_product_receipt.exception.duplicate_sn_in_request=导入数据中存在重复SN号 +production.finished_product_receipt.exception.invalid_activation_status=存在未激活的SN号:{0} +production.finished_product_receipt.exception.sn_already_exists=系统中已存在的SN号:{0} +production.finished_product_receipt.exception.cannot_unapprove_with_shipped=成品入库单已出货,不能反审 +production.finished_product_receipt.exception.file_empty=请选择要上传的文件 +production.finished_product_receipt.exception.import_failed=导入文件失败 +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.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_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.file_empty=请选择要上传的文件 +production.finished_product_shipment.exception.import_failed=导入文件失败 +sale.sale_order.validate.form_code.not_null=单据编号不能为空 +sale.sale_order.validate.form_name.not_null=单据名称不能为空 +sale.sale_order.validate.customer_id.not_null=客户不能为空 +sale.sale_order.validate.customer_name.not_null=客户名称不能为空 +sale.sale_order.validate.sale_order_items.not_null=销售明细不能为空 +sale.sale_order.validate.part_number.not_null=物料编号不能为空 +sale.sale_order.validate.sale_count.not_null=销售数量不能为空 +sale.sale_order.validate.price.not_null=单价不能为空 +sale.sale_order.exception.not_found=销售订单不存在 +sale.sale_order.exception.cannot_update_approved=已审核的销售订单不能修改 +sale.sale_order.exception.cannot_delete_approved=已审核的销售订单不能删除 +sale.sale_order.exception.cannot_delete_approved_batch=选中的数据中存在已审核的销售订单,不能删除 +sale.sale_order.exception.ids_empty=请选择要删除的销售订单 +sale.sale_order.exception.already_approved=销售订单已经审核 +sale.sale_order.exception.not_approved=销售订单不是已审核状态,不能反审 +sale.sale_order.exception.no_sale_order_items=销售订单没有明细 +sale.sale_order.exception.cannot_unapprove_with_shipped=销售订单已出货,不能反审 +sale.sale_order.exception.file_empty=请选择要上传的文件 +sale.sale_order.exception.import_failed=导入文件失败 +production.devicesn.validate.product_sn.not_null=SN号不能为空 +production.devicesn.validate.mac.not_null=MAC地址不能为空 +production.devicesn.exception.not_found=SN号不存在 +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=维修记录不存在