feat(sip): 新增采购订单相关功能及优化审批流程

- 在application.yml中新增采购订单在线审批配置
- 为采购订单模块添加查询接口及权限控制
- 增加根据采购单号查询订单详情的方法
- 优化采购订单撤回逻辑,支持更多状态下的撤回操作
- 更新采购订单页面权限标识和状态判断条件
- 修复采购订单详情页付款方式字段显示问题
- 关联仓库信息到采购订单并展示仓库名称
- 启用待办任务中的业务信息处理逻辑
master
chenhao 2025-12-02 17:44:45 +08:00
parent 43e2225c3d
commit bb79ce79bd
10 changed files with 214 additions and 174 deletions

View File

@ -89,7 +89,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="付款方式" prop="payMethod"> <el-form-item label="付款方式" prop="payMethod">
<span>{{ selectedVendor.payType==='1'?'出库付款':'入库付款' }}</span> <span>{{ selectedVendor.payMethod==='1'?'出库付款':'入库付款' }}</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -411,10 +411,12 @@ export default {
this.selectedVendor = this.vendorOptions.find(item => item.vendorId === vendorId) || {}; this.selectedVendor = this.vendorOptions.find(item => item.vendorId === vendorId) || {};
this.form.warehouseId = this.selectedVendor.warehouseId this.form.warehouseId = this.selectedVendor.warehouseId
this.currentVendorCode=this.selectedVendor.vendorCode; this.currentVendorCode=this.selectedVendor.vendorCode;
this.form.payMethod=this.selectedVendor.payMethod;
} else { } else {
this.selectedVendor = {}; this.selectedVendor = {};
this.form.warehouseId = null; this.form.warehouseId = null;
this.currentVendorCode = null; this.currentVendorCode = null;
this.form.payMethod = null;
} }
}, },
/** 处理采购员选择 */ /** 处理采购员选择 */

View File

@ -163,11 +163,11 @@
type="text" type="text"
icon="el-icon-s-promotion" icon="el-icon-s-promotion"
@click="handleInitiateVendorConfirmation(scope.row)" @click="handleInitiateVendorConfirmation(scope.row)"
v-hasPermi="['sip:purchaseorder:edit']" v-hasPermi="['sip:purchaseorder:confirm']"
>发起供应商确认 >发起供应商确认
</el-button> </el-button>
<el-button <el-button
v-if="scope.row.approveStatus === '2' && (!scope.row.confirmStatus || scope.row.confirmStatus === '')" v-if="scope.row.approveStatus === '2' && (!scope.row.confirmStatus || scope.row.confirmStatus === ''|| scope.row.confirmStatus === '2')"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-refresh-left" icon="el-icon-refresh-left"

View File

@ -1,166 +1,169 @@
# 项目相关配置 # 项目相关配置
ruoyi: ruoyi:
# 名称 # 名称
name: RuoYi name: RuoYi
# 版本 # 版本
version: 4.8.0 version: 4.8.0
# 版权年份 # 版权年份
copyrightYear: 2025 copyrightYear: 2025
# 实例演示开关 # 实例演示开关
demoEnabled: true demoEnabled: true
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath # 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: D:\ruoyi\uploadPath\oms profile: D:\ruoyi\uploadPath\oms
# 获取ip地址开关 # 获取ip地址开关
addressEnabled: false addressEnabled: false
# 开发环境配置 # 开发环境配置
server: server:
# 服务器的HTTP端口默认为80 # 服务器的HTTP端口默认为80
port: 28080 port: 28080
servlet: servlet:
# 应用的访问路径 # 应用的访问路径
context-path: / context-path: /
tomcat: tomcat:
# tomcat的URI编码 # tomcat的URI编码
uri-encoding: UTF-8 uri-encoding: UTF-8
# 连接数满后的排队数默认为100 # 连接数满后的排队数默认为100
accept-count: 1000 accept-count: 1000
threads: threads:
# tomcat最大线程数默认为200 # tomcat最大线程数默认为200
max: 800 max: 800
# Tomcat启动初始化的线程数默认值10 # Tomcat启动初始化的线程数默认值10
min-spare: 100 min-spare: 100
# 日志配置 # 日志配置
logging: logging:
level: level:
com.ruoyi: debug com.ruoyi: debug
org.springframework: warn org.springframework: warn
# 用户配置 # 用户配置
user: user:
password: password:
# 密码错误{maxRetryCount}次锁定10分钟 # 密码错误{maxRetryCount}次锁定10分钟
maxRetryCount: 5 maxRetryCount: 5
# Spring配置 # Spring配置
spring: spring:
# 模板引擎 # 模板引擎
thymeleaf: thymeleaf:
mode: HTML mode: HTML
encoding: utf-8 encoding: utf-8
# 禁用缓存 # 禁用缓存
cache: false cache: false
# 资源信息 # 资源信息
messages: messages:
# 国际化资源文件路径 # 国际化资源文件路径
basename: static/i18n/messages basename: static/i18n/messages
jackson: jackson:
time-zone: GMT+8 time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss
profiles: profiles:
active: dev active: dev
# 文件上传 # 文件上传
servlet: servlet:
multipart: multipart:
# 单个文件大小 # 单个文件大小
max-file-size: 10MB max-file-size: 10MB
# 设置总上传的文件大小 # 设置总上传的文件大小
max-request-size: 20MB max-request-size: 20MB
# 服务模块 # 服务模块
devtools: devtools:
restart: restart:
# 热部署开关 # 热部署开关
enabled: false enabled: false
# MyBatis # MyBatis
mybatis: mybatis:
# 搜索指定包别名 # 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件 # 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件 # 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml configLocation: classpath:mybatis/mybatis-config.xml
# PageHelper分页插件 # PageHelper分页插件
pagehelper: pagehelper:
helperDialect: mysql helperDialect: mysql
supportMethodsArguments: true supportMethodsArguments: true
params: count=countSql params: count=countSql
# Shiro # Shiro
shiro: shiro:
user: user:
# 登录地址 # 登录地址
loginUrl: /login loginUrl: /login
# 权限认证失败地址 # 权限认证失败地址
unauthorizedUrl: /unauth unauthorizedUrl: /unauth
# 首页地址 # 首页地址
indexUrl: /index indexUrl: /index
# 验证码开关 # 验证码开关
captchaEnabled: false captchaEnabled: false
# 验证码类型 math 数字计算 char 字符验证 # 验证码类型 math 数字计算 char 字符验证
captchaType: math captchaType: math
cookie: cookie:
# 设置Cookie的域名 默认空,即当前访问的域名 # 设置Cookie的域名 默认空,即当前访问的域名
domain: domain:
# 设置cookie的有效访问路径 # 设置cookie的有效访问路径
path: / path: /
# 设置HttpOnly属性 # 设置HttpOnly属性
httpOnly: true httpOnly: true
# 设置Cookie的过期时间天为单位 # 设置Cookie的过期时间天为单位
maxAge: 30 maxAge: 30
# 设置密钥务必保持唯一性生成方式直接拷贝到main运行即可Base64.encodeToString(CipherUtils.generateNewKey(128, "AES").getEncoded()) 默认启动生成随机秘钥随机秘钥会导致之前客户端RememberMe Cookie无效如设置固定秘钥RememberMe Cookie则有效 # 设置密钥务必保持唯一性生成方式直接拷贝到main运行即可Base64.encodeToString(CipherUtils.generateNewKey(128, "AES").getEncoded()) 默认启动生成随机秘钥随机秘钥会导致之前客户端RememberMe Cookie无效如设置固定秘钥RememberMe Cookie则有效
cipherKey: p8D/J77AfqR1i4tQYorziA== cipherKey: p8D/J77AfqR1i4tQYorziA==
session: session:
# Session超时时间-1代表永不过期默认30分钟 # Session超时时间-1代表永不过期默认30分钟
expireTime: 120 expireTime: 120
# 同步session到数据库的周期默认1分钟 # 同步session到数据库的周期默认1分钟
dbSyncPeriod: 1 dbSyncPeriod: 1
# 相隔多久检查一次session的有效性默认就是10分钟 # 相隔多久检查一次session的有效性默认就是10分钟
validationInterval: 10 validationInterval: 10
# 同一个用户最大会话数比如2的意思是同一个账号允许最多同时两个人登录默认-1不限制 # 同一个用户最大会话数比如2的意思是同一个账号允许最多同时两个人登录默认-1不限制
maxSession: -1 maxSession: -1
# 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户 # 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户
kickoutAfter: false kickoutAfter: false
rememberMe: rememberMe:
# 是否开启记住我 # 是否开启记住我
enabled: true enabled: true
# 防止XSS攻击 # 防止XSS攻击
xss: xss:
# 过滤开关 # 过滤开关
enabled: true enabled: true
# 排除链接(多个用逗号分隔) # 排除链接(多个用逗号分隔)
excludes: /system/notice/* excludes: /system/notice/*
# 匹配链接 # 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/* urlPatterns: /system/*,/monitor/*,/tool/*
# Swagger配置 # Swagger配置
swagger: swagger:
# 是否开启swagger # 是否开启swagger
enabled: true enabled: true
process: process:
#流程定义名称 #流程定义名称
definition: definition:
orderApproveOnline: order_approve_online orderApproveOnline: order_approve_online
orderApproveOffline: order_approve_offline orderApproveOffline: order_approve_offline
purchaseOrderApprove: purchase_order_online
#业务执行实例bean name ,可以按审批节点配置 业务审批回调方法处理业务逻辑. key 为流程节点主键ID value 要执行的业务方法名称,不配置则默认调用TodoCommonTemplate.todoApproveCallback
instance: #业务执行实例bean name ,可以按审批节点配置 业务审批回调方法处理业务逻辑. key 为流程节点主键ID value 要执行的业务方法名称,不配置则默认调用TodoCommonTemplate.todoApproveCallback
orderApproveOnline: instance:
beanName: projectOrderInfoServiceImpl orderApproveOnline:
orderApproveOffline: beanName: projectOrderInfoServiceImpl
beanName: projectOrderInfoServiceImpl orderApproveOffline:
unis: beanName: projectOrderInfoServiceImpl
inventory: purchaseOrderOnline:
allAuthRole: 103,101 beanName: omsPurchaseOrderServiceImpl
order: unis:
# 执行单截止时间 inventory:
endHour: 96 allAuthRole: 103,101
mail: order:
enabled: true # 执行单截止时间
# 商务角色id endHour: 96
businessRoleId: 103 mail:
enabled: true
flowable: # 商务角色id
businessRoleId: 103
flowable:
database-schema-update: false database-schema-update: false

View File

@ -124,6 +124,12 @@ public class OmsPurchaseOrderController extends BaseController
{ {
return success(omsPurchaseOrderService.selectOmsPurchaseOrderById(id)); return success(omsPurchaseOrderService.selectOmsPurchaseOrderById(id));
} }
@RequiresPermissions("sip:purchaseorder:query")
@GetMapping(value = "/code/{purchaseNo}")
public AjaxResult getInfo(@PathVariable("purchaseNo") String purchaseNo)
{
return success(omsPurchaseOrderService.selectOmsPurchaseOrderByNo(purchaseNo));
}
/** /**
* *
@ -150,7 +156,7 @@ public class OmsPurchaseOrderController extends BaseController
/** /**
* *
*/ */
@RequiresPermissions("sip:purchaseorder:edit") @RequiresPermissions("sip:purchaseorder:confirm")
@Log(title = "采购单主表", businessType = BusinessType.UPDATE) @Log(title = "采购单主表", businessType = BusinessType.UPDATE)
@PutMapping("/vendorConfirmStatus") @PutMapping("/vendorConfirmStatus")
public AjaxResult vendorConfirmStatus(@RequestBody OmsPurchaseOrder omsPurchaseOrder) public AjaxResult vendorConfirmStatus(@RequestBody OmsPurchaseOrder omsPurchaseOrder)

View File

@ -59,6 +59,7 @@ public class OmsPurchaseOrder extends BaseEntity
/** 入库仓库ID */ /** 入库仓库ID */
private Long warehouseId; private Long warehouseId;
private String warehouseName;
/** 付款方式 */ /** 付款方式 */
private String payMethod; private String payMethod;

View File

@ -166,14 +166,14 @@ public class TodoServiceImpl implements TodoService {
todoDto.setRoleName(userRoleMap.get(Long.parseLong(todoDto.getApproveUser()))); todoDto.setRoleName(userRoleMap.get(Long.parseLong(todoDto.getApproveUser())));
} }
//关联业务信息 //关联业务信息
// dealBusinessInfo(todo, todoCompletedList); dealBusinessInfo(todo, todoCompletedList);
return todoCompletedList; return todoCompletedList;
} }
private void dealBusinessInfo(Todo todo, List<Todo> todoCompletedList) { private void dealBusinessInfo(Todo todo, List<Todo> todoCompletedList) {
if (CollUtil.isEmpty(todoCompletedList)) { if (CollUtil.isEmpty(todoCompletedList) || CollUtil.isEmpty(todo.getProcessKeyList())) {
return; return;
} }
TodoCommonTemplate todoExecuteInstance = null; TodoCommonTemplate todoExecuteInstance = null;

View File

@ -111,4 +111,6 @@ public interface OmsPurchaseOrderMapper
List<OmsPurchaseOrderItem> listItemByCodeList(List<String> collect); List<OmsPurchaseOrderItem> listItemByCodeList(List<String> collect);
int recallPurchaseOrder(OmsPurchaseOrder omsPurchaseOrder);
} }

View File

@ -95,4 +95,6 @@ public interface IOmsPurchaseOrderService
void innerWarehouse(Long itemId, Long quantity); void innerWarehouse(Long itemId, Long quantity);
void cancelInnerItem(List<OmsPurchaseOrderItem> omsPurchaseOrderItems); void cancelInnerItem(List<OmsPurchaseOrderItem> omsPurchaseOrderItems);
OmsPurchaseOrder selectOmsPurchaseOrderByNo(String purchaseNo);
} }

View File

@ -349,6 +349,14 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
} }
} }
@Override
public OmsPurchaseOrder selectOmsPurchaseOrderByNo(String purchaseNo) {
OmsPurchaseOrder omsPurchaseOrder = omsPurchaseOrderMapper.selectByNo(purchaseNo);
List<OmsPurchaseOrderItem> omsPurchaseOrderItems = omsPurchaseOrderMapper.listItemByPurchaseId(omsPurchaseOrder.getId());
omsPurchaseOrder.setOmsPurchaseOrderItemList(omsPurchaseOrderItems);
return omsPurchaseOrder;
}
/** /**
* *
@ -403,15 +411,16 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
} }
// 只有审批状态为“已通过”(2) 且 供应商确认状态为空或 null 时才能撤回 // 只有审批状态为“已通过”(2) 且 供应商确认状态为空或 null 时才能撤回
if (ApproveStatusEnum.APPROVE_COMPLETE.getCode().equals(omsPurchaseOrder.getApproveStatus()) && if (ApproveStatusEnum.APPROVE_COMPLETE.getCode().equals(omsPurchaseOrder.getApproveStatus()) &&
(StringUtils.isEmpty(omsPurchaseOrder.getConfirmStatus()) || "".equals(omsPurchaseOrder.getConfirmStatus()))) { (StringUtils.isEmpty(omsPurchaseOrder.getConfirmStatus()) || "".equals(omsPurchaseOrder.getConfirmStatus())|| OmsPurchaseOrder.ConfirmStatusEnum.REJECT.getCode().equals(omsPurchaseOrder.getConfirmStatus()))) {
// 保存历史记录 // 保存历史记录
saveOrderHistory(omsPurchaseOrder); saveOrderHistory(omsPurchaseOrder);
omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.WAIT_COMMIT.getCode()); // 设置为待审批(草稿) omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.WAIT_COMMIT.getCode()); // 设置为待审批(草稿)
omsPurchaseOrder.setApproveTime(null); // 清空审批时间 omsPurchaseOrder.setApproveTime(null); // 清空审批时间
omsPurchaseOrder.setConfirmStatus(null); // 清空审批时间
omsPurchaseOrder.setUpdateTime(DateUtils.getNowDate()); omsPurchaseOrder.setUpdateTime(DateUtils.getNowDate());
omsPurchaseOrder.setVersion(omsPurchaseOrder.getVersion() + 1); // 版本号 +1 omsPurchaseOrder.setVersion(omsPurchaseOrder.getVersion() + 1); // 版本号 +1
return omsPurchaseOrderMapper.updateOmsPurchaseOrder(omsPurchaseOrder); return omsPurchaseOrderMapper.recallPurchaseOrder(omsPurchaseOrder);
} else { } else {
throw new ServiceException("当前订单状态不允许撤回"); throw new ServiceException("当前订单状态不允许撤回");
} }
@ -466,6 +475,11 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
if (approveBtn.equals(0)) { if (approveBtn.equals(0)) {
handleRejectOrder( businessKey); handleRejectOrder( businessKey);
} }
// else {
// if ("公司领导".equals(taskName) && approveBtn == 1) {
// handleCompanyLeaderApproval(businessKey);
// }
// }
return TodoCommonTemplate.super.todoApproveCallback(todo); return TodoCommonTemplate.super.todoApproveCallback(todo);
} }

View File

@ -69,9 +69,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select t1.id, t1.purchase_no, t1.buyer_name, t1.buyer_address, t1.vendor_id, t1.currency, t1.purchaser_id, t1.purchaser_name select t1.id, t1.purchase_no, t1.buyer_name, t1.buyer_address, t1.vendor_id, t1.currency, t1.purchaser_id, t1.purchaser_name
, t1.purchaser_mobile, t1.purchaser_email, t1.warehouse_id, t1.pay_method, t1.owner_id, t1.owner_name, t1.remark, t1.total_amount,t1.flow_type , t1.purchaser_mobile, t1.purchaser_email, t1.warehouse_id, t1.pay_method, t1.owner_id, t1.owner_name, t1.remark, t1.total_amount,t1.flow_type
, t1.status, t1.approve_status, t1.approve_time, t1.approve_node, t1.confirm_status, t1.create_time, t1.update_time, t1.del_flag,t1.version , t1.status, t1.approve_status, t1.approve_time, t1.approve_node, t1.confirm_status, t1.create_time, t1.update_time, t1.del_flag,t1.version
,t2.vendor_name,t2.vendor_user,t2.vendor_phone ,t2.vendor_name,t2.vendor_user,t2.vendor_phone,t3.warehouse_name
from oms_purchase_order t1 from oms_purchase_order t1
left join oms_vendor_info t2 on t1.vendor_id = t2.vendor_id left join oms_vendor_info t2 on t1.vendor_id = t2.vendor_id
left join oms_warehouse_info t3 on t1.warehouse_id = t3.id
</sql> </sql>
<select id="selectOmsPurchaseOrderList" parameterType="OmsPurchaseOrder" resultMap="OmsPurchaseOrderResult"> <select id="selectOmsPurchaseOrderList" parameterType="OmsPurchaseOrder" resultMap="OmsPurchaseOrderResult">
@ -410,6 +411,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where t1.product_code = #{item.productCode} and t2.purchase_no=#{item.purchaseNo} where t1.product_code = #{item.productCode} and t2.purchase_no=#{item.purchaseNo}
</foreach> </foreach>
</update> </update>
<update id="recallPurchaseOrder">
update oms_purchase_order
set approve_status = #{approveStatus},
approve_time = null,
confirm_status = null,
update_time = #{updateTime},
version = version + 1
where purchase_no = #{purchaseNo}
</update>
<insert id="batchOmsPurchaseOrderItem"> <insert id="batchOmsPurchaseOrderItem">
insert into oms_purchase_order_item( purchase_id, product_code, inner_quantity, quantity, price, tax_rate, tax_total, amount_total,delivery_date) values insert into oms_purchase_order_item( purchase_id, product_code, inner_quantity, quantity, price, tax_rate, tax_total, amount_total,delivery_date) values