完成 SN 溯源、销售管理和维修记录。

This commit is contained in:
c
2026-03-12 16:26:41 +08:00
parent a9d8c0f7e7
commit 8504fa121d
24 changed files with 574 additions and 499 deletions

View File

@@ -131,7 +131,7 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl<DocumentMapp
entity.setStatus(0);
// 根据出库类型设置不同的单据类型:物料出库使用 WAREHOUSE_ISSUE(4),成品出库使用 FINISHED_PRODUCT_SHIPMENT(7)
// 注意Spring 默认使用枚举的 ordinal 值转换,所以 1 对应 FINISHED_PRODUCT (ordinal=1),需要用 code 比较
if (dto.outStockType() != null && dto.outStockType().getCode() == 1) {
if ( dto.outStockType().getCode() == 1) {
entity.setFormType(DocumentType.WAREHOUSE_ISSUE);
} else {
entity.setFormType(DocumentType.FINISHED_PRODUCT_SHIPMENT);
@@ -139,7 +139,7 @@ public class FinishedProductShipmentServiceImpl extends ServiceImpl<DocumentMapp
entity.setFormStatus(FormStatus.NO_APPROVE);
entity.setTotalValue(Double.valueOf(shipmentItems.size()));
entity.setCustomerId(SecurityUtils.getCustomerId());
entity.setReserve1(dto.outStockType() != null ? dto.outStockType().getValue() : null);
entity.setReserve1(dto.outStockType().getValue());
this.baseMapper.insert(entity);
List<DocumentMaterial> materialEntities = finishedProductShipmentConverter.toMaterialEntityList(shipmentItems);

View File

@@ -1,30 +1,22 @@
package com.niuan.erp.module.sale.controller;
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.BaseSelectDto;
import com.niuan.erp.common.base.CommonValidateGroup.*;
import com.niuan.erp.common.base.OperationType;
import com.niuan.erp.module.sale.controller.dto.DeviceDto;
import com.niuan.erp.module.sale.entity.Device;
import com.niuan.erp.module.sale.controller.dto.DeviceQueryDto;
import com.niuan.erp.module.sale.controller.dto.DeviceResultDto;
import com.niuan.erp.module.sale.service.DeviceService;
import io.swagger.v3.oas.annotations.Operation;
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 java.util.List;
@Tag(name = "Device")
@ModuleLog("Device管理")
@Tag(name = "SN溯源")
@ModuleLog("SN溯源管理")
@RestController
@RequestMapping("/sale/device")
@RequiredArgsConstructor
@@ -32,81 +24,11 @@ public class DeviceController {
private final DeviceService deviceService;
@Operation(summary = "分页查询Device数据", operationId = "getDevicePage")
@GetMapping("/getDevicePage")
@PreAuthorize("hasAuthority('device:index')")
public BaseResult<IPage<DeviceDto>> getDevicePage(@Validated BasePageReqParams pageParams,
@Validated(Get.class) DeviceDto searchParams) {
var wrapper = new LambdaQueryWrapper<Device>();
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<List<BaseSelectDto>> getKeyAccount() {
return BaseResult.successWithData(deviceService.getKeyAccount());
}
@ApiLog(type = OperationType.ADD, remark = "新增一条Device记录")
@Operation(summary = "新增Device", operationId = "addDevice")
@PostMapping("/addDevice")
@PreAuthorize("hasAuthority('device:add')")
public BaseResult<?> addDevice(@Validated(Add.class) @RequestBody DeviceDto dto) {
deviceService.addDevice(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.UPDATE, remark = "更新一条Device记录")
@Operation(summary = "更新Device", operationId = "updateDevice")
@PostMapping("/updateDevice")
@PreAuthorize("hasAuthority('device:update')")
public BaseResult<?> updateDevice(@Validated(Update.class) @RequestBody DeviceDto dto) {
deviceService.updateDevice(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "删除一条Device记录")
@Operation(summary = "删除Device", operationId = "deleteDevice")
@PostMapping("/deleteDevice")
@PreAuthorize("hasAuthority('device:delete')")
public BaseResult<?> deleteDevice(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) {
deviceService.deleteDevice(req.id());
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "批量删除Device记录")
@Operation(summary = "批量删除Device", operationId = "deleteDeviceBatch")
@PostMapping("/deleteDeviceBatch")
@PreAuthorize("hasAuthority('device:deleteBatch')")
public BaseResult<?> deleteDeviceBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) {
deviceService.deleteBatch(req.ids());
return BaseResult.success();
@Operation(summary = "分页查询SN溯源数据", operationId = "getDeviceSnPage")
@GetMapping("/getDeviceSnPage")
@PreAuthorize("hasAuthority('device_sn:index')")
public BaseResult<IPage<DeviceResultDto>> getDeviceSnPage(@Validated BasePageReqParams pageParams,
@Validated(Get.class) DeviceQueryDto searchParams) {
return BaseResult.successWithData(deviceService.getDeviceSnPage(pageParams, searchParams));
}
}

View File

@@ -1,26 +1,25 @@
package com.niuan.erp.module.sale.controller;
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.*;
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.sale.controller.dto.RepairRecordDto;
import com.niuan.erp.module.sale.controller.dto.RepairRecordQueryDto;
import com.niuan.erp.module.sale.controller.dto.RepairRecordResultDto;
import com.niuan.erp.module.sale.service.RepairRecordService;
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.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import java.util.List;
@Tag(name = "RepairRecord")
@ModuleLog("RepairRecord管理")
@Tag(name = "返修报表")
@ModuleLog("返修报表管理")
@RestController
@RequestMapping("/sale/repairrecord")
@RequiredArgsConstructor
@@ -28,46 +27,12 @@ public class RepairRecordController {
private final RepairRecordService repairRecordService;
@Operation(summary = "分页查询RepairRecord数据", operationId = "getRepairRecordPage")
@GetMapping("/getRepairRecordPage")
@Operation(summary = "分页查询返修报表数据", operationId = "getRepairReportPage")
@GetMapping("/getRepairReportPage")
@PreAuthorize("hasAuthority('repair_record:index')")
public BaseResult<IPage<RepairRecordDto>> getRepairRecordPage(@Validated BasePageReqParams pageParams,
@Validated(Get.class) RepairRecordDto searchParams) {
var wrapper = new LambdaQueryWrapper<RepairRecord>();
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<List<BaseSelectDto>> getKeyAccount() {
return BaseResult.successWithData(repairRecordService.getKeyAccount());
public BaseResult<IPage<RepairRecordResultDto>> getRepairReportPage(@Validated BasePageReqParams pageParams,
@Validated(Get.class) RepairRecordQueryDto searchParams) {
return BaseResult.successWithData(repairRecordService.getRepairReportPage(pageParams, searchParams));
}
@ApiLog(type = OperationType.ADD, remark = "新增一条RepairRecord记录")
@@ -78,31 +43,4 @@ public class RepairRecordController {
repairRecordService.addRepairRecord(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.UPDATE, remark = "更新一条RepairRecord记录")
@Operation(summary = "更新RepairRecord", operationId = "updateRepairRecord")
@PostMapping("/updateRepairRecord")
@PreAuthorize("hasAuthority('repair_record:update')")
public BaseResult<?> updateRepairRecord(@Validated(Update.class) @RequestBody RepairRecordDto dto) {
repairRecordService.updateRepairRecord(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "删除一条RepairRecord记录")
@Operation(summary = "删除RepairRecord", operationId = "deleteRepairRecord")
@PostMapping("/deleteRepairRecord")
@PreAuthorize("hasAuthority('repair_record:delete')")
public BaseResult<?> deleteRepairRecord(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) {
repairRecordService.deleteRepairRecord(req.id());
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "批量删除RepairRecord记录")
@Operation(summary = "批量删除RepairRecord", operationId = "deleteRepairRecordBatch")
@PostMapping("/deleteRepairRecordBatch")
@PreAuthorize("hasAuthority('repair_record:deleteBatch')")
public BaseResult<?> deleteRepairRecordBatch(@Validated(DeleteBatch.class) @RequestBody BaseDeleteBody req) {
repairRecordService.deleteBatch(req.ids());
return BaseResult.success();
}
}

View File

@@ -4,6 +4,7 @@ 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;
@@ -13,6 +14,7 @@ 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.controller.dto.SaleOrderUpdateDto;
import com.niuan.erp.module.sale.service.SaleOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -26,8 +28,8 @@ import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Tag(name = "SaleOrder")
@ModuleLog("SaleOrder管理")
@Tag(name = "销售订单")
@ModuleLog("销售订单管理")
@RestController
@RequestMapping("/sale/saleorder")
@RequiredArgsConstructor
@@ -35,23 +37,23 @@ public class SaleOrderController {
private final SaleOrderService saleOrderService;
@Operation(summary = "分页查询SaleOrder数据", operationId = "getSaleOrderPage")
@Operation(summary = "分页查询销售订单数据", operationId = "getSaleOrderPage")
@GetMapping("/getSaleOrderPage")
@PreAuthorize("hasAuthority('sale_order:index')")
public BaseResult<IPage<SaleOrderDto>> getSaleOrderPage(@Validated BasePageReqParams pageParams,
@Validated(Get.class) SaleOrderDto searchParams) {
var wrapper = new LambdaQueryWrapper<Document>();
if (searchParams != null) {
if (StringUtils.hasText(searchParams.customerName())) {
wrapper.like(Document::getCustomerId, searchParams.customerId());
}
if (searchParams != null) {
if (StringUtils.hasText(searchParams.customerName())) {
wrapper.like(Document::getStoreName, searchParams.customerName());
}
}
return BaseResult.successWithData(saleOrderService.getSaleOrderPage(pageParams, wrapper));
}
@ApiLog(type = OperationType.ADD, remark = "新增一条SaleOrder记录")
@Operation(summary = "新增SaleOrder", operationId = "addSaleOrder")
@ApiLog(type = OperationType.ADD, remark = "新增一条销售订单记录")
@Operation(summary = "新增销售订单", operationId = "addSaleOrder")
@PostMapping("/addSaleOrder")
@PreAuthorize("hasAuthority('sale_order:add')")
public BaseResult<?> addSaleOrder(@Validated(Add.class) @RequestBody SaleOrderAddDto dto) {
@@ -59,62 +61,60 @@ public class SaleOrderController {
return BaseResult.success();
}
@ApiLog(type = OperationType.UPDATE, remark = "更新一条SaleOrder记录")
@Operation(summary = "更新SaleOrder", operationId = "updateSaleOrder")
@ApiLog(type = OperationType.UPDATE, remark = "更新一条销售订单记录")
@Operation(summary = "更新销售订单", operationId = "updateSaleOrder")
@PostMapping("/updateSaleOrder")
@PreAuthorize("hasAuthority('sale_order:update')")
public BaseResult<?> updateSaleOrder(@Validated(Update.class) @RequestBody SaleOrderDto dto) {
@PreAuthorize("hasAuthority('sale_order:edit')")
public BaseResult<?> updateSaleOrder(@Validated(Update.class) @RequestBody SaleOrderUpdateDto dto) {
saleOrderService.updateSaleOrder(dto);
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "删除一条SaleOrder记录")
@Operation(summary = "删除SaleOrder", operationId = "deleteSaleOrder")
@ApiLog(type = OperationType.DELETE, remark = "删除一条销售订单记录")
@Operation(summary = "删除销售订单", operationId = "deleteSaleOrder")
@PostMapping("/deleteSaleOrder")
@PreAuthorize("hasAuthority('sale_order:delete')")
@PreAuthorize("hasAuthority('sale_order:remove')")
public BaseResult<?> deleteSaleOrder(@Validated(DeleteOne.class) @RequestBody BaseDeleteBody req) {
saleOrderService.deleteSaleOrder(req.id());
return BaseResult.success();
}
@ApiLog(type = OperationType.DELETE, remark = "批量删除SaleOrder记录")
@Operation(summary = "批量删除SaleOrder", operationId = "deleteSaleOrderBatch")
@ApiLog(type = OperationType.DELETE, remark = "批量删除销售订单记录")
@Operation(summary = "批量删除销售订单", operationId = "deleteSaleOrderBatch")
@PostMapping("/deleteSaleOrderBatch")
@PreAuthorize("hasAuthority('sale_order:deleteBatch')")
@PreAuthorize("hasAuthority('sale_order:remove')")
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")
@ApiLog(type = OperationType.UPDATE, remark = "审核销售订单")
@Operation(summary = "审核销售订单", operationId = "approveSaleOrder")
@PostMapping("/approveSaleOrder")
@PreAuthorize("hasAuthority('sale_order:approve')")
public BaseResult<?> approveSaleOrder(
@Parameter(description = "销售订单ID") @RequestParam Long id) {
saleOrderService.approve(id);
public BaseResult<?> approveSaleOrder(@Validated @RequestBody BaseApproveAndRejectDto dto) {
saleOrderService.approve(dto.id());
return BaseResult.success();
}
@ApiLog(type = OperationType.UPDATE, remark = "反审核SaleOrder")
@Operation(summary = "反审核SaleOrder", operationId = "unapproveSaleOrder")
@ApiLog(type = OperationType.UPDATE, remark = "反审核销售订单")
@Operation(summary = "反审核销售订单", operationId = "unapproveSaleOrder")
@PostMapping("/unapproveSaleOrder")
@PreAuthorize("hasAuthority('sale_order:unapprove')")
public BaseResult<?> unapproveSaleOrder(
@Parameter(description = "销售订单ID") @RequestParam Long id) {
saleOrderService.unapprove(id);
@PreAuthorize("hasAuthority('sale_order:reject')")
public BaseResult<?> unapproveSaleOrder(@Validated @RequestBody BaseApproveAndRejectDto dto) {
saleOrderService.unapprove(dto.id());
return BaseResult.success();
}
@Operation(summary = "获取SaleOrder明细", operationId = "getSaleOrderDetail")
@GetMapping("/getSaleOrderDetail")
@PreAuthorize("hasAuthority('saleorder:index')")
public BaseResult<List<SaleOrderItemDto>> getSaleOrderDetail(
@Parameter(description = "销售订单ID") @RequestParam Long id) {
return BaseResult.successWithData(saleOrderService.getDetail(id));
@Operation(summary = "获取销售订单明细列表", operationId = "getSaleOrderItemList")
@GetMapping("/getSaleOrderItemList")
@PreAuthorize("hasAuthority('sale_order:showItem')")
public BaseResult<List<SaleOrderItemDto>> getSaleOrderItemList(
@Parameter(description = "销售订单ID") @RequestParam("saleOrderId") Long saleOrderId) {
return BaseResult.successWithData(saleOrderService.getDetail(saleOrderId));
}
@Operation(summary = "导入SaleOrder明细", operationId = "importSaleOrderItems")
@Operation(summary = "导入销售订单明细", operationId = "importSaleOrderItems")
@PostMapping("/importSaleOrderItems")
@PreAuthorize("hasAuthority('sale_order:import')")
public BaseResult<List<SaleOrderItemDto>> importSaleOrderItems(

View File

@@ -4,27 +4,9 @@ import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
@Schema(description = "设备DTO")
@Schema(description = "SN溯源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 = "产品类型")
@Schema(description = "产品类型/型号")
String productType,
@Schema(description = "产品SN")
String productSn,
@@ -34,36 +16,10 @@ public record DeviceDto(
String serialNum,
@Schema(description = "软件版本")
String softVersion,
@Schema(description = "AL版本")
@Schema(description = "算法版本")
String alVersion,
@Schema(description = "AL编号")
String alNum,
@Schema(description = "AL状态")
Boolean alStatus,
@Schema(description = "维修状态")
Integer repairStatus,
@Schema(description = "备注")
String mark,
@Schema(description = "客户ID")
Integer customerId,
@Schema(description = "出库状态")
Boolean outStatus,
@Schema(description = "出库日期")
@Schema(description = "出库日期/出货日期")
LocalDateTime outProductDate,
@Schema(description = "维修备注")
String repairMark,
@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
@Schema(description = "返修记录拼接字符串")
String repairRecords
) {}

View File

@@ -4,60 +4,26 @@ import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
@Schema(description = "返修记录DTO")
@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 = "产品类型")
@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 = "出库日期")
@Schema(description = "序列号")
String serialNum,
@Schema(description = "软件版本")
String softVersion,
@Schema(description = "算法版本")
String alVersion,
@Schema(description = "出库日期/出货日期")
LocalDateTime outProductDate,
@Schema(description = "维修日期")
LocalDateTime repairDate,
@Schema(description = "维修备注")
String repairMark,
@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
@Schema(description = "创建时间")
LocalDateTime createDate,
@Schema(description = "维修次数")
Integer repairCount,
@Schema(description = "返修记录")
String repairMark
) {}

View File

@@ -1,30 +1,21 @@
package com.niuan.erp.module.sale.controller.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.time.LocalDateTime;
/**
* 销售订单返回 DTO - 前端展示用
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public record SaleOrderDto(
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 formStatus,
Integer customerId,
String customerName) {}
String customerName,
Double totalValue
) {}

View File

@@ -1,24 +1,18 @@
package com.niuan.erp.module.sale.controller.dto;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* 销售订单明细返回 DTO - 前端展示用
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public record SaleOrderItemDto(
Long id,
Integer status,
LocalDateTime createDate,
Long createUserId,
String createUserName,
LocalDateTime updateDate,
Long updateUserId,
String updateUserName,
Integer documentNo,
String documentCode,
String projectName,
String partNumber,
String productSpecs,
Integer saleCount,
Double price,
Double totalPrice,
Integer sendCount,
String saleMark,
Integer reserve1,
String reserve2) {}
String saleMark
) {}

View File

@@ -1,26 +1,39 @@
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.Mapping;
import org.mapstruct.ReportingPolicy;
import java.util.List;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface SaleOrderConverter {
Document toEntity(SaleOrderDto dto);
Document toEntity(SaleOrderAddDto dto);
@Mapping(source = "id", target = "id")
@Mapping(source = "createDate", target = "createDate")
@Mapping(source = "formCode", target = "formCode")
@Mapping(source = "formName", target = "formName")
@Mapping(source = "formMark", target = "formMark")
@Mapping(source = "formStatus.value", target = "formStatus")
@Mapping(source = "customerId", target = "customerId")
@Mapping(source = "storeName", target = "customerName")
@Mapping(source = "totalValue", target = "totalValue")
SaleOrderDto toDto(Document entity);
List<SaleOrderDto> toDtoList(List<Document> entities);
SaleOrderItem toEntity(SaleOrderItemAddDto dto);
List<SaleOrderItem> toEntityList(List<SaleOrderItemAddDto> dtoList);
@Mapping(source = "id", target = "id")
@Mapping(source = "partNumber", target = "partNumber")
@Mapping(source = "saleCount", target = "saleCount")
@Mapping(source = "price", target = "price")
@Mapping(source = "totalPrice", target = "totalPrice")
@Mapping(source = "sendCount", target = "sendCount")
@Mapping(source = "saleMark", target = "saleMark")
SaleOrderItemDto toDto(SaleOrderItem entity);
List<SaleOrderItemDto> toSaleOrderItemDtoList(List<SaleOrderItem> entities);
}
}

View File

@@ -1,9 +1,20 @@
package com.niuan.erp.module.sale.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.niuan.erp.module.sale.controller.dto.DeviceResultDto;
import com.niuan.erp.module.sale.entity.Device;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface DeviceMapper extends BaseMapper<Device> {
/**
* SN溯源分页查询
*/
IPage<DeviceResultDto> selectDeviceSnPage(Page<DeviceResultDto> page,
@Param("searchCode") String searchCode,
@Param("keyAccountId") Integer keyAccountId);
}

View File

@@ -1,7 +1,11 @@
package com.niuan.erp.module.sale.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.niuan.erp.module.sale.controller.dto.RepairRecordResultDto;
import com.niuan.erp.module.sale.entity.RepairRecord;
import org.apache.ibatis.annotations.Param;
/**
* <p>
@@ -13,4 +17,10 @@ import com.niuan.erp.module.sale.entity.RepairRecord;
*/
public interface RepairRecordMapper extends BaseMapper<RepairRecord> {
/**
* 返修报表分页查询
*/
IPage<RepairRecordResultDto> selectRepairReportPage(Page<RepairRecordResultDto> page,
@Param("searchCode") String searchCode,
@Param("keyAccountIdSearch") Integer keyAccountIdSearch);
}

View File

@@ -1,25 +1,11 @@
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;
import java.util.List;
import com.niuan.erp.module.sale.controller.dto.DeviceQueryDto;
import com.niuan.erp.module.sale.controller.dto.DeviceResultDto;
public interface DeviceService {
IPage<DeviceDto> getDevicePage(BasePageReqParams pageParams, LambdaQueryWrapper<Device> wrapper);
void addDevice(DeviceDto dto);
void updateDevice(DeviceDto dto);
void deleteDevice(long id);
void deleteBatch(List<Long> ids);
List<BaseSelectDto> getKeyAccount();
IPage<DeviceResultDto> getDeviceSnPage(BasePageReqParams pageParams, DeviceQueryDto searchParams);
}

View File

@@ -1,25 +1,14 @@
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;
import java.util.List;
import com.niuan.erp.module.sale.controller.dto.RepairRecordQueryDto;
import com.niuan.erp.module.sale.controller.dto.RepairRecordResultDto;
public interface RepairRecordService {
IPage<RepairRecordDto> getRepairRecordPage(BasePageReqParams pageParams, LambdaQueryWrapper<RepairRecord> wrapper);
IPage<RepairRecordResultDto> getRepairReportPage(BasePageReqParams pageParams, RepairRecordQueryDto searchParams);
void addRepairRecord(RepairRecordDto dto);
void updateRepairRecord(RepairRecordDto dto);
void deleteRepairRecord(long id);
void deleteBatch(List<Long> ids);
List<BaseSelectDto> getKeyAccount();
}

View File

@@ -7,6 +7,7 @@ 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.controller.dto.SaleOrderUpdateDto;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@@ -17,7 +18,7 @@ public interface SaleOrderService {
void addSaleOrder(SaleOrderAddDto dto);
void updateSaleOrder(SaleOrderDto dto);
void updateSaleOrder(SaleOrderUpdateDto dto);
void deleteSaleOrder(long id);

View File

@@ -1,24 +1,18 @@
package com.niuan.erp.module.sale.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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;
import com.niuan.erp.common.exception.BusinessException;
import com.niuan.erp.module.sale.controller.dto.DeviceQueryDto;
import com.niuan.erp.module.sale.controller.dto.DeviceResultDto;
import com.niuan.erp.module.sale.entity.Device;
import com.niuan.erp.module.sale.mapper.DeviceMapper;
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;
@Service
@@ -26,53 +20,16 @@ import java.util.List;
@RequiredArgsConstructor
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
private final DeviceConverter deviceConverter;
@Override
public IPage<DeviceDto> getDevicePage(BasePageReqParams pageParams, LambdaQueryWrapper<Device> wrapper) {
IPage<Device> result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper);
return result.convert(deviceConverter::toDto);
}
@Override
public void addDevice(DeviceDto dto) {
Device entity = deviceConverter.toEntity(dto);
entity.setCreateUserId(SecurityUtils.getUserId());
entity.setCreateUserName(SecurityUtils.getUserName());
entity.setCreateDate(LocalDateTime.now());
entity.setStatus(0);
this.baseMapper.insert(entity);
}
@Override
public void updateDevice(DeviceDto dto) {
Device entity = deviceConverter.toEntity(dto);
entity.setUpdateUserId(SecurityUtils.getUserId());
entity.setUpdateUserName(SecurityUtils.getUserName());
entity.setUpdateDate(LocalDateTime.now());
this.baseMapper.updateById(entity);
}
@Override
public void deleteDevice(long id) {
this.baseMapper.deleteById(id);
}
@Override
public void deleteBatch(List<Long> ids) {
this.baseMapper.deleteBatchIds(ids);
}
@Override
public List<BaseSelectDto> getKeyAccount() {
LambdaQueryWrapper<Device> wrapper = new LambdaQueryWrapper<>();
wrapper.select(Device::getKeyAccountId);
wrapper.isNotNull(Device::getKeyAccountId);
wrapper.groupBy(Device::getKeyAccountId);
List<Device> devices = this.baseMapper.selectList(wrapper);
return devices.stream()
.map(device -> new BaseSelectDto(device.getKeyAccountId().toString(), device.getKeyAccountId().toString()))
.toList();
public IPage<DeviceResultDto> getDeviceSnPage(BasePageReqParams pageParams, DeviceQueryDto searchParams) {
if (pageParams == null) {
throw new BusinessException("common.validate.page_params.not_null");
}
String searchCode = searchParams != null ? searchParams.searchCode() : null;
Integer keyAccountId = searchParams != null ? searchParams.keyAccountId() : null;
return this.baseMapper.selectDeviceSnPage(
new Page<>(pageParams.page(), pageParams.pageSize()),
searchCode,
keyAccountId);
}
}

View File

@@ -5,19 +5,22 @@ 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.exception.BusinessException;
import com.niuan.erp.common.utils.SecurityUtils;
import com.niuan.erp.module.sale.controller.dto.RepairRecordDto;
import com.niuan.erp.module.sale.converter.RepairRecordConverter;
import com.niuan.erp.module.sale.controller.dto.RepairRecordQueryDto;
import com.niuan.erp.module.sale.controller.dto.RepairRecordResultDto;
import com.niuan.erp.module.sale.entity.Device;
import com.niuan.erp.module.sale.entity.RepairRecord;
import com.niuan.erp.module.sale.mapper.DeviceMapper;
import com.niuan.erp.module.sale.mapper.RepairRecordMapper;
import com.niuan.erp.module.sale.service.RepairRecordService;
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;
@Service
@@ -25,53 +28,101 @@ import java.util.List;
@RequiredArgsConstructor
public class RepairRecordServiceImpl extends ServiceImpl<RepairRecordMapper, RepairRecord> implements RepairRecordService {
private final RepairRecordConverter repairRecordConverter;
private final DeviceMapper deviceMapper;
@Override
public IPage<RepairRecordDto> getRepairRecordPage(BasePageReqParams pageParams, LambdaQueryWrapper<RepairRecord> wrapper) {
IPage<RepairRecord> result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper);
return result.convert(repairRecordConverter::toDto);
public IPage<RepairRecordResultDto> getRepairReportPage(BasePageReqParams pageParams, RepairRecordQueryDto searchParams) {
if (pageParams == null) {
throw new BusinessException("common.validate.page_params.not_null");
}
String searchCode = searchParams != null ? searchParams.searchCode() : null;
Integer keyAccountIdSearch = searchParams != null ? searchParams.keyAccountIdSearch() : null;
return this.baseMapper.selectRepairReportPage(
new Page<>(pageParams.page(), pageParams.pageSize()),
searchCode,
keyAccountIdSearch);
}
@Override
public void addRepairRecord(RepairRecordDto dto) {
RepairRecord entity = repairRecordConverter.toEntity(dto);
// 从 Device 查询补充信息
Device device = findDevice(dto);
// 构建 RepairRecord 实体
RepairRecord entity = new RepairRecord();
entity.setProductSn(StringUtils.hasText(dto.productSn()) ? dto.productSn() : (device != null ? device.getProductSn() : null));
entity.setMac(StringUtils.hasText(dto.mac()) ? dto.mac() : (device != null ? device.getMac() : null));
entity.setProductType(StringUtils.hasText(dto.productType()) ? dto.productType() : (device != null ? device.getProductType() : null));
entity.setOutProductDate(dto.outProductDate() != null ? dto.outProductDate() : (device != null ? device.getOutProductDate() : null));
entity.setRepairMark(dto.repairMark());
entity.setCreateUserId(SecurityUtils.getUserId());
entity.setCreateUserName(SecurityUtils.getUserName());
entity.setCreateDate(LocalDateTime.now());
entity.setStatus(0);
// 设置客户ID从 Device 获取)
if (device != null && device.getKeyAccountId() != null) {
entity.setKeyAccountId(device.getKeyAccountId());
}
this.baseMapper.insert(entity);
// 更新 Device 信息(前端有值则覆盖)
if (device != null) {
updateDeviceInfo(dto, device);
}
}
@Override
public void updateRepairRecord(RepairRecordDto dto) {
RepairRecord entity = repairRecordConverter.toEntity(dto);
entity.setUpdateUserId(SecurityUtils.getUserId());
entity.setUpdateUserName(SecurityUtils.getUserName());
entity.setUpdateDate(LocalDateTime.now());
this.baseMapper.updateById(entity);
private Device findDevice(RepairRecordDto dto) {
LambdaQueryWrapper<Device> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.hasText(dto.productSn())) {
wrapper.eq(Device::getProductSn, dto.productSn());
} else if (StringUtils.hasText(dto.mac())) {
wrapper.eq(Device::getMac, dto.mac());
} else if (StringUtils.hasText(dto.serialNum())) {
wrapper.eq(Device::getSerialNum, dto.serialNum());
} else {
return null;
}
// MAC可能重复取第一条
wrapper.orderByAsc(Device::getId);
wrapper.last("LIMIT 1");
return deviceMapper.selectOne(wrapper);
}
@Override
public void deleteRepairRecord(long id) {
this.baseMapper.deleteById(id);
}
private void updateDeviceInfo(RepairRecordDto dto, Device device) {
boolean updated = false;
@Override
public void deleteBatch(List<Long> ids) {
this.baseMapper.deleteBatchIds(ids);
}
if (StringUtils.hasText(dto.productType())) {
device.setProductType(dto.productType());
updated = true;
}
if (StringUtils.hasText(dto.mac())) {
device.setMac(dto.mac());
updated = true;
}
if (StringUtils.hasText(dto.serialNum())) {
device.setSerialNum(dto.serialNum());
updated = true;
}
if (StringUtils.hasText(dto.softVersion())) {
device.setSoftVersion(dto.softVersion());
updated = true;
}
if (StringUtils.hasText(dto.alVersion())) {
device.setAlVersion(dto.alVersion());
updated = true;
}
if (dto.outProductDate() != null) {
device.setOutProductDate(dto.outProductDate());
updated = true;
}
@Override
public List<BaseSelectDto> getKeyAccount() {
LambdaQueryWrapper<RepairRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.select(RepairRecord::getKeyAccountId);
wrapper.isNotNull(RepairRecord::getKeyAccountId);
wrapper.groupBy(RepairRecord::getKeyAccountId);
List<RepairRecord> repairRecords = this.baseMapper.selectList(wrapper);
return repairRecords.stream()
.map(repairRecord -> new BaseSelectDto(repairRecord.getKeyAccountId().toString(), repairRecord.getKeyAccountId().toString()))
.toList();
if (updated) {
device.setUpdateUserId(SecurityUtils.getUserId());
device.setUpdateUserName(SecurityUtils.getUserName());
device.setUpdateDate(LocalDateTime.now());
deviceMapper.updateById(device);
}
}
}

View File

@@ -16,10 +16,16 @@ 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.controller.dto.SaleOrderItemUpdateDto;
import com.niuan.erp.module.sale.controller.dto.SaleOrderUpdateDto;
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 com.niuan.erp.module.warehouse.entity.Stock;
import com.niuan.erp.module.warehouse.entity.WarehouseItem;
import com.niuan.erp.module.warehouse.mapper.StockMapper;
import com.niuan.erp.module.warehouse.mapper.WarehouseItemMapper;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Service;
@@ -29,7 +35,10 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -43,10 +52,17 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
private final SaleOrderItemMapper saleOrderItemMapper;
private final StockMapper stockMapper;
private final WarehouseItemMapper warehouseItemMapper;
@Override
public IPage<SaleOrderDto> getSaleOrderPage(BasePageReqParams pageParams, LambdaQueryWrapper<Document> wrapper) {
wrapper.eq(Document::getFormType, DocumentType.SALES_ORDER);
IPage<Document> result = this.baseMapper.selectPage(new Page<>(pageParams.page(), pageParams.pageSize()), wrapper);
IPage<Document> result = this.baseMapper.selectPage(
new Page<>(pageParams.page(), pageParams.pageSize()),
wrapper
);
return result.convert(saleOrderConverter::toDto);
}
@@ -54,37 +70,52 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
public void addSaleOrder(SaleOrderAddDto dto) {
List<SaleOrderItemAddDto> saleOrderItems = dto.saleOrderItems();
if (saleOrderItems == null || saleOrderItems.isEmpty()) {
throw new BusinessException("sale.sale_order.exception.no_sale_order_items");
}
// 计算订单总额
double totalValue = saleOrderItems.stream()
.mapToDouble(item -> item.saleCount() * item.price())
.sum();
Document entity = saleOrderConverter.toEntity(dto);
// 创建主单据
Document entity = new Document();
entity.setFormCode(dto.formCode());
entity.setFormName(dto.formName());
entity.setFormMark(dto.formMark());
entity.setCustomerId(dto.customerId());
entity.setStoreName(dto.customerName());
entity.setTotalValue(totalValue);
entity.setFormType(DocumentType.SALES_ORDER);
entity.setFormStatus(FormStatus.NO_APPROVE);
entity.setStatus(0);
entity.setCreateUserId(SecurityUtils.getUserId());
entity.setCreateUserName(SecurityUtils.getUserName());
entity.setCreateDate(LocalDateTime.now());
entity.setStatus(0);
entity.setFormType(DocumentType.SALES_ORDER);
entity.setFormStatus(FormStatus.NO_APPROVE);
entity.setCustomerId(SecurityUtils.getCustomerId());
this.baseMapper.insert(entity);
List<SaleOrderItem> 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);
// 创建明细
List<SaleOrderItem> itemEntities = new ArrayList<>();
for (SaleOrderItemAddDto itemDto : saleOrderItems) {
SaleOrderItem item = new SaleOrderItem();
item.setDocumentNo(entity.getId().intValue());
item.setDocumentCode(entity.getFormCode());
item.setPartNumber(itemDto.partNumber());
item.setSaleCount(itemDto.saleCount());
item.setPrice(itemDto.price());
item.setTotalPrice(itemDto.saleCount() * itemDto.price());
item.setSaleMark(itemDto.saleMark());
item.setSendCount(0);
item.setStatus(0);
item.setCreateUserId(SecurityUtils.getUserId());
item.setCreateUserName(SecurityUtils.getUserName());
item.setCreateDate(LocalDateTime.now());
itemEntities.add(item);
}
saleOrderItemMapper.insert(saleOrderItemEntities);
saleOrderItemMapper.insert(itemEntities);
}
@Override
public void updateSaleOrder(SaleOrderDto dto) {
public void updateSaleOrder(SaleOrderUpdateDto dto) {
Document existingEntity = this.baseMapper.selectById(dto.id());
if (existingEntity == null) {
throw new BusinessException("sale.sale_order.exception.not_found");
@@ -93,11 +124,48 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
throw new BusinessException("sale.sale_order.exception.cannot_update_approved");
}
Document entity = saleOrderConverter.toEntity(dto);
entity.setUpdateUserId(SecurityUtils.getUserId());
entity.setUpdateUserName(SecurityUtils.getUserName());
entity.setUpdateDate(LocalDateTime.now());
this.baseMapper.updateById(entity);
// 计算新的订单总额
double totalValue = dto.saleOrderItems().stream()
.mapToDouble(item -> item.saleCount() * item.price())
.sum();
// 更新主单据
existingEntity.setFormName(dto.formName());
existingEntity.setFormMark(dto.formMark());
existingEntity.setCustomerId(dto.customerId());
existingEntity.setStoreName(dto.customerName());
existingEntity.setTotalValue(totalValue);
existingEntity.setUpdateUserId(SecurityUtils.getUserId());
existingEntity.setUpdateUserName(SecurityUtils.getUserName());
existingEntity.setUpdateDate(LocalDateTime.now());
this.baseMapper.updateById(existingEntity);
// 删除旧明细
LambdaQueryWrapper<SaleOrderItem> deleteWrapper = new LambdaQueryWrapper<>();
deleteWrapper.eq(SaleOrderItem::getDocumentNo, dto.id());
saleOrderItemMapper.delete(deleteWrapper);
// 插入新明细
List<SaleOrderItem> itemEntities = new ArrayList<>();
for (SaleOrderItemUpdateDto itemDto : dto.saleOrderItems()) {
SaleOrderItem item = new SaleOrderItem();
item.setDocumentNo(existingEntity.getId().intValue());
item.setDocumentCode(existingEntity.getFormCode());
item.setPartNumber(itemDto.partNumber());
item.setSaleCount(itemDto.saleCount());
item.setPrice(itemDto.price());
item.setTotalPrice(itemDto.saleCount() * itemDto.price());
item.setSaleMark(itemDto.saleMark());
item.setSendCount(0);
item.setStatus(0);
item.setCreateUserId(SecurityUtils.getUserId());
item.setCreateUserName(SecurityUtils.getUserName());
item.setCreateDate(LocalDateTime.now());
itemEntities.add(item);
}
saleOrderItemMapper.insert(itemEntities);
}
@Override
@@ -155,6 +223,50 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
throw new BusinessException("sale.sale_order.exception.no_sale_order_items");
}
// 验证并扣除库存
for (SaleOrderItem item : items) {
LambdaQueryWrapper<Stock> stockWrapper = new LambdaQueryWrapper<>();
stockWrapper.eq(Stock::getPartNumber, item.getPartNumber());
// 如果指定了仓库,则按仓库查询
if (entity.getStoreNo() != null) {
stockWrapper.eq(Stock::getStoreNo, entity.getStoreNo());
}
Stock stock;
if (entity.getStoreNo() != null) {
// 指定了仓库,使用 selectOne
stock = stockMapper.selectOne(stockWrapper);
} else {
// 未指定仓库,查询所有仓库库存并汇总
List<Stock> stockList = stockMapper.selectList(stockWrapper);
if (stockList == null || stockList.isEmpty()) {
stock = null;
} else if (stockList.size() == 1) {
stock = stockList.get(0);
} else {
// 多条记录,取第一条并汇总数量
stock = stockList.get(0);
int totalCount = stockList.stream().mapToInt(Stock::getProductCount).sum();
stock.setProductCount(totalCount);
}
}
if (stock == null) {
throw new BusinessException("warehouse.stock_transfer_order.exception.stock_not_found");
}
if (stock.getProductCount() < item.getSaleCount()) {
throw new BusinessException("warehouse.stock_transfer_order.exception.insufficient_stock");
}
// 扣除库存 - 如果有多条记录,只更新第一条
stock.setProductCount(stock.getProductCount() - item.getSaleCount());
stock.setUpdateDate(LocalDateTime.now());
stock.setUpdateUserId(SecurityUtils.getUserId());
stock.setUpdateUserName(SecurityUtils.getUserName());
stockMapper.updateById(stock);
}
entity.setFormStatus(FormStatus.APPROVE);
entity.setUpdateDate(LocalDateTime.now());
entity.setUpdateUserId(SecurityUtils.getUserId());
@@ -183,6 +295,7 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
itemWrapper.eq(SaleOrderItem::getDocumentNo, id);
List<SaleOrderItem> items = saleOrderItemMapper.selectList(itemWrapper);
// 检查是否有已出货数量
Long shippedCount = items.stream()
.filter(item -> item.getSendCount() != null && item.getSendCount() > 0)
.count();
@@ -191,6 +304,42 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
throw new BusinessException("sale.sale_order.exception.cannot_unapprove_with_shipped");
}
// 恢复库存
for (SaleOrderItem item : items) {
LambdaQueryWrapper<Stock> stockWrapper = new LambdaQueryWrapper<>();
stockWrapper.eq(Stock::getPartNumber, item.getPartNumber());
// 如果指定了仓库,则按仓库查询
if (entity.getStoreNo() != null) {
stockWrapper.eq(Stock::getStoreNo, entity.getStoreNo());
}
Stock stock;
if (entity.getStoreNo() != null) {
// 指定了仓库,使用 selectOne
stock = stockMapper.selectOne(stockWrapper);
} else {
// 未指定仓库,查询所有仓库库存
List<Stock> stockList = stockMapper.selectList(stockWrapper);
if (stockList == null || stockList.isEmpty()) {
stock = null;
} else {
// 取第一条记录
stock = stockList.get(0);
}
}
if (stock == null) {
throw new BusinessException("warehouse.stock_transfer_order.exception.stock_not_found");
}
// 恢复库存 - 只更新第一条记录
stock.setProductCount(stock.getProductCount() + item.getSaleCount());
stock.setUpdateDate(LocalDateTime.now());
stock.setUpdateUserId(SecurityUtils.getUserId());
stock.setUpdateUserName(SecurityUtils.getUserName());
stockMapper.updateById(stock);
}
entity.setFormStatus(FormStatus.NO_APPROVE);
entity.setUpdateDate(LocalDateTime.now());
entity.setUpdateUserId(SecurityUtils.getUserId());
@@ -210,7 +359,40 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
LambdaQueryWrapper<SaleOrderItem> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SaleOrderItem::getDocumentNo, id);
List<SaleOrderItem> items = saleOrderItemMapper.selectList(wrapper);
return saleOrderConverter.toSaleOrderItemDtoList(items);
// 获取所有物料编号
List<String> partNumbers = items.stream()
.map(SaleOrderItem::getPartNumber)
.filter(StringUtils::hasText)
.distinct()
.toList();
// 查询物料型号信息
Map<String, String> productSpecsMap = new HashMap<>();
if (!partNumbers.isEmpty()) {
LambdaQueryWrapper<WarehouseItem> warehouseWrapper = new LambdaQueryWrapper<>();
warehouseWrapper.in(WarehouseItem::getPartNumber, partNumbers);
List<WarehouseItem> warehouseItems = warehouseItemMapper.selectList(warehouseWrapper);
productSpecsMap = warehouseItems.stream()
.collect(Collectors.toMap(
WarehouseItem::getPartNumber,
WarehouseItem::getProductSpecs,
(a, b) -> a
));
}
// 转换为 DTO 并设置 productSpecs
Map<String, String> finalProductSpecsMap = productSpecsMap;
return items.stream().map(item -> new SaleOrderItemDto(
item.getId(),
item.getPartNumber(),
finalProductSpecsMap.getOrDefault(item.getPartNumber(), ""),
item.getSaleCount(),
item.getPrice(),
item.getTotalPrice(),
item.getSendCount(),
item.getSaleMark()
)).toList();
}
@Override
@@ -228,19 +410,19 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
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));
String productSpecs = getCellValueAsString(row.getCell(1));
String saleCountStr = getCellValueAsString(row.getCell(2));
String priceStr = getCellValueAsString(row.getCell(3));
String saleMark = getCellValueAsString(row.getCell(4));
if (!StringUtils.hasText(partNumber)) continue;
Integer saleCount = StringUtils.hasText(saleCountStr) ? Integer.parseInt(saleCountStr) : 0;
Double price = StringUtils.hasText(priceStr) ? Double.parseDouble(priceStr) : 0.0;
Double totalPrice = saleCount * price;
SaleOrderItemDto item = new SaleOrderItemDto(
null, null, null, null, null, null, null, null,
null, null, null, partNumber, saleCount, price,
saleCount * price, 0, saleMark, null, null
null, partNumber, productSpecs, saleCount, price, totalPrice, 0, saleMark
);
items.add(item);
}
@@ -262,4 +444,4 @@ public class SaleOrderServiceImpl extends ServiceImpl<DocumentMapper, Document>
default -> "";
};
}
}
}

View File

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
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.BaseTree;
import com.niuan.erp.common.base.CommonValidateGroup.DeleteBatch;
import com.niuan.erp.common.base.CommonValidateGroup.DeleteOne;
import com.niuan.erp.common.base.BaseSelectDto;
@@ -45,6 +46,13 @@ public class KeyAccountController {
return BaseResult.successWithData(keyAccountService.getKeyAccountSelectList());
}
@Operation(summary = "查询客户树结构", operationId = "getKeyAccountTree")
@GetMapping("/getKeyAccountTree")
@PreAuthorize("hasAnyAuthority('device_sn:index', 'repair_record:index')")
public BaseResult<List<BaseTree>> getKeyAccountTree() {
return BaseResult.successWithData(keyAccountService.getKeyAccountTree());
}
@PostMapping("/addKeyAccount")
public BaseResult<?> addKeyAccount(@Valid @RequestBody KeyAccountDto dto) {
keyAccountService.addKeyAccount(dto);

View File

@@ -4,6 +4,7 @@ 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.common.base.BaseTree;
import com.niuan.erp.module.sys.controller.dto.KeyAccountDto;
import com.niuan.erp.module.sys.entity.KeyAccount;
@@ -15,6 +16,8 @@ public interface KeyAccountService {
List<BaseSelectDto> getKeyAccountSelectList();
List<BaseTree> getKeyAccountTree();
void addKeyAccount(KeyAccountDto dto);
void updateKeyAccount(KeyAccountDto dto);

View File

@@ -6,6 +6,7 @@ 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.base.BaseTree;
import com.niuan.erp.common.utils.SecurityUtils;
import com.niuan.erp.module.sys.controller.dto.KeyAccountDto;
import com.niuan.erp.module.sys.converter.KeyAccountConverter;
@@ -17,7 +18,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@@ -39,6 +43,33 @@ public class KeyAccountServiceImpl extends ServiceImpl<KeyAccountMapper, KeyAcco
return this.baseMapper.getKeyAccountSelectList();
}
@Override
public List<BaseTree> getKeyAccountTree() {
LambdaQueryWrapper<KeyAccount> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(KeyAccount::getStatus, 1);
wrapper.orderByAsc(KeyAccount::getLevelPath);
List<KeyAccount> keyAccounts = this.baseMapper.selectList(wrapper);
Map<Long, List<KeyAccount>> parentMap = keyAccounts.stream()
.collect(Collectors.groupingBy(ka -> ka.getParentId() != null ? ka.getParentId() : 0L));
return buildTree(parentMap, 0L);
}
private List<BaseTree> buildTree(Map<Long, List<KeyAccount>> parentMap, Long parentId) {
List<KeyAccount> children = parentMap.get(parentId);
if (children == null || children.isEmpty()) {
return new ArrayList<>();
}
List<BaseTree> treeList = new ArrayList<>();
for (KeyAccount ka : children) {
List<BaseTree> childTrees = buildTree(parentMap, ka.getId());
treeList.add(new BaseTree(ka.getId(), ka.getKeyAccountName(), childTrees));
}
return treeList;
}
@Override
public void addKeyAccount(KeyAccountDto dto) {
KeyAccount entity = keyAccountConverter.toEntity(dto);