diff --git a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java index 7c04c19f1..958e25898 100644 --- a/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java +++ b/yudao-module-erp/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java @@ -19,6 +19,11 @@ public interface ErrorCodeConstants { // ========== ERP 销售订单(1-030-201-000) ========== ErrorCode SALE_ORDER_NOT_EXISTS = new ErrorCode(1_020_201_000, "销售订单不存在"); + ErrorCode SALE_ORDER_DELETE_FAIL_APPROVE = new ErrorCode(1_020_201_001, "销售订单({})已审核,无法删除"); + ErrorCode SALE_ORDER_PROCESS_FAIL = new ErrorCode(1_020_201_002, "反审核失败,只有已审核的销售订单才能反审核"); + ErrorCode SALE_ORDER_APPROVE_FAIL = new ErrorCode(1_020_201_003, "审核失败,只有未审核的销售订单才能审核"); + ErrorCode SALE_ORDER_NO_EXISTS = new ErrorCode(1_020_201_004, "生成销售单号失败,请重新提交"); + ErrorCode SALE_ORDER_UPDATE_FAIL_APPROVE = new ErrorCode(1_020_201_005, "销售订单({})已审核,无法修改"); // ========== ERP 仓库 1-030-400-000 ========== ErrorCode WAREHOUSE_NOT_EXISTS = new ErrorCode(1_030_400_000, "仓库不存在"); @@ -78,4 +83,8 @@ public interface ErrorCodeConstants { ErrorCode PRODUCT_UNIT_NAME_DUPLICATE = new ErrorCode(1_030_502_001, "已存在该名字的产品单位"); ErrorCode PRODUCT_UNIT_EXITS_PRODUCT = new ErrorCode(1_030_502_002, "存在产品使用该单位,无法删除"); + // ========== ERP 结算账户 1-030-600-000 ========== + ErrorCode ACCOUNT_NOT_EXISTS = new ErrorCode(1_030_600_000, "结算账户不存在"); + ErrorCode ACCOUNT_NOT_ENABLE = new ErrorCode(1_030_600_001, "结算账户({})未启用"); + } diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java new file mode 100644 index 000000000..a66d28bdf --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/ErpAccountController.java @@ -0,0 +1,106 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountRespVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; + +@Tag(name = "管理后台 - ERP 结算账户") +@RestController +@RequestMapping("/erp/account") +@Validated +public class ErpAccountController { + + @Resource + private ErpAccountService accountService; + + @PostMapping("/create") + @Operation(summary = "创建结算账户") + @PreAuthorize("@ss.hasPermission('erp:account:create')") + public CommonResult createAccount(@Valid @RequestBody ErpAccountSaveReqVO createReqVO) { + return success(accountService.createAccount(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新结算账户") + @PreAuthorize("@ss.hasPermission('erp:account:update')") + public CommonResult updateAccount(@Valid @RequestBody ErpAccountSaveReqVO updateReqVO) { + accountService.updateAccount(updateReqVO); + return success(true); + } + + @PutMapping("/update-default-status") + @Operation(summary = "更新结算账户默认状态") + @Parameters({ + @Parameter(name = "id", description = "编号", required = true), + @Parameter(name = "status", description = "状态", required = true) + }) + public CommonResult updateAccountDefaultStatus(@RequestParam("id") Long id, + @RequestParam("defaultStatus") Boolean defaultStatus) { + accountService.updateAccountDefaultStatus(id, defaultStatus); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除结算账户") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('erp:account:delete')") + public CommonResult deleteAccount(@RequestParam("id") Long id) { + accountService.deleteAccount(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得结算账户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('erp:account:query')") + public CommonResult getAccount(@RequestParam("id") Long id) { + ErpAccountDO account = accountService.getAccount(id); + return success(BeanUtils.toBean(account, ErpAccountRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得结算账户分页") + @PreAuthorize("@ss.hasPermission('erp:account:query')") + public CommonResult> getAccountPage(@Valid ErpAccountPageReqVO pageReqVO) { + PageResult pageResult = accountService.getAccountPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, ErpAccountRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出结算账户 Excel") + @PreAuthorize("@ss.hasPermission('erp:account:export')") + @OperateLog(type = EXPORT) + public void exportAccountExcel(@Valid ErpAccountPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = accountService.getAccountPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "结算账户.xls", "数据", ErpAccountRespVO.class, + BeanUtils.toBean(list, ErpAccountRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountPageReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountPageReqVO.java new file mode 100644 index 000000000..682689e5e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountPageReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - ERP 结算账户分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ErpAccountPageReqVO extends PageParam { + + @Schema(description = "账户编码", example = "A88") + private String no; + + @Schema(description = "账户名称", example = "张三") + private String name; + + @Schema(description = "备注", example = "随便") + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountRespVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountRespVO.java new file mode 100644 index 000000000..57e6c10b5 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountRespVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - ERP 结算账户 Response VO") +@Data +@ExcelIgnoreUnannotated +public class ErpAccountRespVO { + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28684") + @ExcelProperty("结算账户编号") + private Long id; + + @Schema(description = "账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @ExcelProperty("账户名称") + private String name; + + @Schema(description = "账户编码", example = "A88") + @ExcelProperty("账户编码") + private String no; + + @Schema(description = "备注", example = "随便") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("开启状态") + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("排序") + private Integer sort; + + @Schema(description = "是否默认", example = "1") + @ExcelProperty("是否默认") + private Boolean defaultStatus; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountSaveReqVO.java new file mode 100644 index 000000000..1f96946a7 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/finance/vo/ErpAccountSaveReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.erp.controller.admin.finance.vo; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - ERP 结算账户新增/修改 Request VO") +@Data +public class ErpAccountSaveReqVO { + + @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28684") + private Long id; + + @Schema(description = "账户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotEmpty(message = "账户名称不能为空") + private String name; + + @Schema(description = "账户编码", example = "A88") + private String no; + + @Schema(description = "备注", example = "随便") + private String remark; + + @Schema(description = "开启状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "开启状态不能为空") + @InEnum(value = CommonStatusEnum.class) + private Integer status; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "排序不能为空") + private Integer sort; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java index 1706915aa..e23a1fab3 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -16,10 +15,6 @@ public class ErpSaleOrderSaveReqVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") private Long id; - @Schema(description = "销售单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "XS001") - @NotEmpty(message = "销售单编号不能为空") - private String no; - @Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1724") @NotNull(message = "客户编号不能为空") private Long customerId; @@ -31,15 +26,13 @@ public class ErpSaleOrderSaveReqVO { @Schema(description = "销售员编号", example = "1888") private Long saleUserId; - @Schema(description = "结算账户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31189") - @NotNull(message = "结算账户编号不能为空") + @Schema(description = "结算账户编号", example = "31189") private Long accountId; @Schema(description = "优惠率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "99.88") private BigDecimal discountPercent; - @Schema(description = "定金金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127") - @NotNull(message = "定金金额,单位:元不能为空") + @Schema(description = "定金金额,单位:元", example = "7127") private BigDecimal depositPrice; @Schema(description = "附件地址", example = "https://www.iocoder.cn") diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java new file mode 100644 index 000000000..fe01cc228 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/finance/ErpAccountDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.erp.dal.dataobject.finance; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * ERP 结算账户 DO + * + * @author 芋道源码 + */ +@TableName("erp_account") +@KeySequence("erp_account_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ErpAccountDO extends BaseDO { + + /** + * 结算账户编号 + */ + @TableId + private Long id; + /** + * 账户名称 + */ + private String name; + /** + * 账户编码 + */ + private String no; + /** + * 备注 + */ + private String remark; + /** + * 开启状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 排序 + */ + private Integer sort; + /** + * 是否默认 + */ + private Boolean defaultStatus; + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java new file mode 100644 index 000000000..d879a933e --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpAccountMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.erp.dal.mysql.finance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * ERP 结算账户 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface ErpAccountMapper extends BaseMapperX { + + default PageResult selectPage(ErpAccountPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(ErpAccountDO::getName, reqVO.getName()) + .eqIfPresent(ErpAccountDO::getNo, reqVO.getNo()) + .eqIfPresent(ErpAccountDO::getRemark, reqVO.getRemark()) + .orderByDesc(ErpAccountDO::getId)); + } + + default ErpAccountDO selectByDefaultStatus() { + return selectOne(ErpAccountDO::getDefaultStatus, true); + } + + default List selectListByStatus(Integer status) { + return selectList(ErpAccountDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java index bd2612db1..0e48e97e1 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java @@ -38,6 +38,11 @@ public class ErpNoRedisDAO { */ public static final String STOCK_CHECK_NO_PREFIX = "QCPD"; + /** + * 销售订单 {@link cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO} + */ + public static final String SALE_ORDER_NO_PREFIX = "XSDD"; + @Resource private StringRedisTemplate stringRedisTemplate; diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java new file mode 100644 index 000000000..8e23f0dca --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountService.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import jakarta.validation.Valid; + +/** + * ERP 结算账户 Service 接口 + * + * @author 芋道源码 + */ +public interface ErpAccountService { + + /** + * 创建结算账户 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createAccount(@Valid ErpAccountSaveReqVO createReqVO); + + /** + * 更新ERP 结算账户 + * + * @param updateReqVO 更新信息 + */ + void updateAccount(@Valid ErpAccountSaveReqVO updateReqVO); + + /** + * 更新结算账户默认状态 + * + * @param id 编号 + * @param defaultStatus 默认状态 + */ + void updateAccountDefaultStatus(Long id, Boolean defaultStatus); + + /** + * 删除结算账户 + * + * @param id 编号 + */ + void deleteAccount(Long id); + + /** + * 获得结算账户 + * + * @param id 编号 + * @return 结算账户 + */ + ErpAccountDO getAccount(Long id); + + /** + * 校验结算账户 + * + * @param id 编号 + * @return 结算账户 + */ + ErpAccountDO validateAccount(Long id); + + /** + * 获得结算账户分页 + * + * @param pageReqVO 分页查询 + * @return 结算账户分页 + */ + PageResult getAccountPage(ErpAccountPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java new file mode 100644 index 000000000..2ff33de1a --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImpl.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountPageReqVO; +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.ErpAccountSaveReqVO; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpAccountMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; + +/** + * ERP 结算账户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class ErpAccountServiceImpl implements ErpAccountService { + + @Resource + private ErpAccountMapper accountMapper; + + @Override + public Long createAccount(ErpAccountSaveReqVO createReqVO) { + // 插入 + ErpAccountDO account = BeanUtils.toBean(createReqVO, ErpAccountDO.class); + accountMapper.insert(account); + // 返回 + return account.getId(); + } + + @Override + public void updateAccount(ErpAccountSaveReqVO updateReqVO) { + // 校验存在 + validateAccountExists(updateReqVO.getId()); + // 更新 + ErpAccountDO updateObj = BeanUtils.toBean(updateReqVO, ErpAccountDO.class); + accountMapper.updateById(updateObj); + } + + @Override + public void updateAccountDefaultStatus(Long id, Boolean defaultStatus) { + // 1. 校验存在 + validateAccountExists(id); + + // 2.1 如果开启,则需要关闭所有其它的默认 + if (defaultStatus) { + ErpAccountDO account = accountMapper.selectByDefaultStatus(); + if (account != null) { + accountMapper.updateById(new ErpAccountDO().setId(account.getId()).setDefaultStatus(false)); + } + } + // 2.2 更新对应的默认状态 + accountMapper.updateById(new ErpAccountDO().setId(id).setDefaultStatus(defaultStatus)); + } + + @Override + public void deleteAccount(Long id) { + // 校验存在 + validateAccountExists(id); + // 删除 + accountMapper.deleteById(id); + } + + private void validateAccountExists(Long id) { + if (accountMapper.selectById(id) == null) { + throw exception(ACCOUNT_NOT_EXISTS); + } + } + + @Override + public ErpAccountDO getAccount(Long id) { + return accountMapper.selectById(id); + } + + @Override + public ErpAccountDO validateAccount(Long id) { + ErpAccountDO account = accountMapper.selectById(id); + if (account == null) { + throw exception(ACCOUNT_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(account.getStatus())) { + throw exception(ACCOUNT_NOT_ENABLE, account.getName()); + } + return account; + } + + @Override + public PageResult getAccountPage(ErpAccountPageReqVO pageReqVO) { + return accountMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java index 3e5d7d11b..662eb1189 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper; import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper; import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; +import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; import cn.iocoder.yudao.module.erp.service.product.ErpProductService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -52,6 +53,8 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { private ErpProductService productService; @Resource private ErpCustomerService customerService; + @Resource + private ErpAccountService accountService; @Override @Transactional(rollbackFor = Exception.class) @@ -60,17 +63,20 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { List saleOrderItems = validateSaleOrderItems(createReqVO.getItems()); // 1.2 校验客户 customerService.validateCustomer(createReqVO.getCustomerId()); - // 1.3 生成调拨单号,并校验唯一性 - String no = noRedisDAO.generate(ErpNoRedisDAO.STOCK_MOVE_NO_PREFIX); + // 1.3 校验结算账户 + if (createReqVO.getAccountId() != null) { + accountService.validateAccount(createReqVO.getAccountId()); + } + // 1.4 生成调拨单号,并校验唯一性 + String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX); if (saleOrderMapper.selectByNo(no) != null) { - throw exception(STOCK_MOVE_NO_EXISTS); + throw exception(SALE_ORDER_NO_EXISTS); } // 2.1 插入订单 ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in - .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()) - .setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add)) - .setTotalPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO))); + .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())); + calculateTotalPrice(saleOrder, saleOrderItems); saleOrderMapper.insert(saleOrder); // 2.2 插入订单项 saleOrderItems.forEach(o -> o.setOrderId(saleOrder.getId())); @@ -84,22 +90,38 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { // 1.1 校验存在 ErpSaleOrderDO saleOrder = validateSaleOrderExists(updateReqVO.getId()); if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) { - throw exception(STOCK_MOVE_UPDATE_FAIL_APPROVE, saleOrder.getNo()); + throw exception(SALE_ORDER_UPDATE_FAIL_APPROVE, saleOrder.getNo()); } // 1.2 校验客户 customerService.validateCustomer(updateReqVO.getCustomerId()); - // 1.3 校验订单项的有效性 + // 1.3 校验结算账户 + if (updateReqVO.getAccountId() != null) { + accountService.validateAccount(updateReqVO.getAccountId()); + } + // 1.4 校验订单项的有效性 List saleOrderItems = validateSaleOrderItems(updateReqVO.getItems()); // 2.1 更新订单 - ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class, in -> in - .setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add)) - .setTotalPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add))); + ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class); + calculateTotalPrice(updateObj, saleOrderItems); saleOrderMapper.updateById(updateObj); // 2.2 更新订单项 updateSaleOrderItemList(updateReqVO.getId(), saleOrderItems); } + private void calculateTotalPrice(ErpSaleOrderDO saleOrder, List saleOrderItems) { + saleOrder.setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add)); + saleOrder.setTotalProductPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); + saleOrder.setTotalTaxPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); + saleOrder.setTotalPrice(saleOrder.getTotalProductPrice().add(saleOrder.getTotalTaxPrice())); + // 计算优惠价格 + if (saleOrder.getDiscountPercent() == null) { + saleOrder.setDiscountPercent(BigDecimal.ZERO); + } + saleOrder.setDiscountPrice(MoneyUtils.priceMultiply(saleOrder.getTotalPrice(), saleOrder.getDiscountPercent())); + saleOrder.setTotalPrice(saleOrder.getTotalPrice().subtract(saleOrder.getDiscountPrice())); + } + @Override @Transactional(rollbackFor = Exception.class) public void updateSaleOrderStatus(Long id, Integer status) { @@ -108,7 +130,7 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { ErpSaleOrderDO saleOrder = validateSaleOrderExists(id); // 1.2 校验状态 if (saleOrder.getStatus().equals(status)) { - throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL); + throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL); } // TODO @芋艿:需要校验是不是有入库、有退货 @@ -116,7 +138,7 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(), new ErpSaleOrderDO().setStatus(status)); if (updateCount == 0) { - throw exception(approve ? STOCK_MOVE_APPROVE_FAIL : STOCK_MOVE_PROCESS_FAIL); + throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL); } } @@ -126,9 +148,15 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { convertSet(list, ErpSaleOrderSaveReqVO.Item::getProductId)); Map productMap = convertMap(productList, ErpProductDO::getId); // 2. 转化为 ErpSaleOrderItemDO 列表 - return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> item - .setProductUnitId(productMap.get(item.getProductId()).getUnitId()) - .setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())))); + return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> { + item.setProductUnitId(productMap.get(item.getProductId()).getUnitId()); + item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); + if (item.getTotalPrice() == null) { + return; + } + item.setTaxPrice(MoneyUtils.priceMultiply(item.getTotalPrice(), item.getTaxPercent())); + item.setTotalPrice(item.getTotalPrice().add(item.getTaxPrice())); + })); } private void updateSaleOrderItemList(Long id, List newList) { @@ -160,7 +188,7 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { } saleOrders.forEach(saleOrder -> { if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) { - throw exception(STOCK_MOVE_DELETE_FAIL_APPROVE, saleOrder.getNo()); + throw exception(SALE_ORDER_DELETE_FAIL_APPROVE, saleOrder.getNo()); } }); @@ -176,7 +204,7 @@ public class ErpSaleOrderServiceImpl implements ErpSaleOrderService { private ErpSaleOrderDO validateSaleOrderExists(Long id) { ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(id); if (saleOrder == null) { - throw exception(STOCK_MOVE_NOT_EXISTS); + throw exception(SALE_ORDER_NOT_EXISTS); } return saleOrder; } diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/finance/ErpAccountMapper.xml b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/finance/ErpAccountMapper.xml new file mode 100644 index 000000000..a3cf6877b --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/resources/mapper/finance/ErpAccountMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/test/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImplTest.java b/yudao-module-erp/yudao-module-erp-biz/src/test/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImplTest.java new file mode 100644 index 000000000..19fc9f313 --- /dev/null +++ b/yudao-module-erp/yudao-module-erp-biz/src/test/java/cn/iocoder/yudao/module/erp/service/finance/ErpAccountServiceImplTest.java @@ -0,0 +1,142 @@ +package cn.iocoder.yudao.module.erp.service.finance; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import jakarta.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.*; +import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; +import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpAccountMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link ErpAccountServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(ErpAccountServiceImpl.class) +public class ErpAccountServiceImplTest extends BaseDbUnitTest { + + @Resource + private ErpAccountServiceImpl accountService; + + @Resource + private ErpAccountMapper accountMapper; + + @Test + public void testCreateAccount_success() { + // 准备参数 + ErpAccountSaveReqVO createReqVO = randomPojo(ErpAccountSaveReqVO.class).setId(null); + + // 调用 + Long accountId = accountService.createAccount(createReqVO); + // 断言 + assertNotNull(accountId); + // 校验记录的属性是否正确 + ErpAccountDO account = accountMapper.selectById(accountId); + assertPojoEquals(createReqVO, account, "id"); + } + + @Test + public void testUpdateAccount_success() { + // mock 数据 + ErpAccountDO dbAccount = randomPojo(ErpAccountDO.class); + accountMapper.insert(dbAccount);// @Sql: 先插入出一条存在的数据 + // 准备参数 + ErpAccountSaveReqVO updateReqVO = randomPojo(ErpAccountSaveReqVO.class, o -> { + o.setId(dbAccount.getId()); // 设置更新的 ID + }); + + // 调用 + accountService.updateAccount(updateReqVO); + // 校验是否更新正确 + ErpAccountDO account = accountMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, account); + } + + @Test + public void testUpdateAccount_notExists() { + // 准备参数 + ErpAccountSaveReqVO updateReqVO = randomPojo(ErpAccountSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> accountService.updateAccount(updateReqVO), ACCOUNT_NOT_EXISTS); + } + + @Test + public void testDeleteAccount_success() { + // mock 数据 + ErpAccountDO dbAccount = randomPojo(ErpAccountDO.class); + accountMapper.insert(dbAccount);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbAccount.getId(); + + // 调用 + accountService.deleteAccount(id); + // 校验数据不存在了 + assertNull(accountMapper.selectById(id)); + } + + @Test + public void testDeleteAccount_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> accountService.deleteAccount(id), ACCOUNT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetAccountPage() { + // mock 数据 + ErpAccountDO dbAccount = randomPojo(ErpAccountDO.class, o -> { // 等会查询到 + o.setNo(null); + o.setRemark(null); + o.setStatus(null); + o.setName(null); + }); + accountMapper.insert(dbAccount); + // 测试 no 不匹配 + accountMapper.insert(cloneIgnoreId(dbAccount, o -> o.setNo(null))); + // 测试 remark 不匹配 + accountMapper.insert(cloneIgnoreId(dbAccount, o -> o.setRemark(null))); + // 测试 status 不匹配 + accountMapper.insert(cloneIgnoreId(dbAccount, o -> o.setStatus(null))); + // 测试 name 不匹配 + accountMapper.insert(cloneIgnoreId(dbAccount, o -> o.setName(null))); + // 准备参数 + ErpAccountPageReqVO reqVO = new ErpAccountPageReqVO(); + reqVO.setNo(null); + reqVO.setRemark(null); + reqVO.setStatus(null); + reqVO.setName(null); + + // 调用 + PageResult pageResult = accountService.getAccountPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbAccount, pageResult.getList().get(0)); + } + +} \ No newline at end of file