ERP审批流程的制作

1. 业务场景:

公司为开发一套适用性强的审批系统,并且灵活性可以高一点,灵活性高指的是:我们审批流通常是xxx人发起审批单,A审批=>B审批=》C审批通过,这个审批单才算结束,但防中间有一个审批人拒绝,这审批单也结束,我们想制作成,可以自定义的选择审批顺序,已达到灵活的选择审批人的目的。
例如:项目场景:项目中有用户表oa_user,底下关联着角色表oa_role,另有客户表,我们登录的用户这边指的是业务员,也有可能登录的这个用户是客户,区分以这个用户的一个字段customer_type如果是2的话他就是客户,等于1的话就是业务员。

具体的表关系如下:
在这里插入图片描述
在这里插入图片描述


2. 数据库设计:

1. 流程主表

在这里插入图片描述
2. 流程节点表
在这里插入图片描述
3. 流程线表
在这里插入图片描述
4. 流程节点角色表
在这里插入图片描述
5. 审批单

在这里插入图片描述
5. 审批单明细
在这里插入图片描述


3. 开发流程分析:

3.1 模块1:Flow的设计

在这里插入图片描述

  1. Flow的新增

Flow表的新增涉及到三张表同步新增,分别是flow,flow_node,flow_line。

我们新增一条数据:
flow表:flow_no应该做限制不能重复
在这里插入图片描述
flow_node表:这里的数据是flow的审核人数据
在这里插入图片描述
flow_line表:这里的数据是审核人具体的审核顺序,第一个审核人的pre_node_id设置为0,最后审核人的next_node_id设置成-1
在这里插入图片描述
VO的设计原则:尽可能让更多的地方能够共用同一个VO
考虑到我们单个Flow的详情需要展示:flow_node,flow_node_role,而插入只需要插入flow_node和flow,于是VO可以设计成FlowInfoVO ,其中FlowVO-Flow,FlowNodeVO-FlowNode,FlowNodeRoleVO-FlowNodeRole
以Bean一一对应

//同时包含flow,flow_node,flow_node_role信息
public class FlowInfoVO extends FlowVO{
	List<FlowInfoVO> flowInfoVO ;
}
FlowInfoVO extends FowVO{
//包含flow信息
	List<FlowNodeInfoVO> flowNodeInfoVO ;
}
FlowNodeInfoVO extend FlowNodeVO{
//包含角色信息
	List<FlowNodeRoleVO> flowNodeRoleVO;
}

插入的细节:

public void insertFlowNodeInfoVO(FlowInfoVO flowInfoVO) {
        //flow的插入
        Flow flow = new Flow();
        BeanUtils.copyProperties(flowInfoVO,flow);
        this.save(flow);
        //flow_node的擦插入
        List<FlowNode> flowNodeList = new ArrayList<>();
        List<FlowNodeInfoVO> flowNodeInfoVOList = flowInfoVO.getFlowNodeInfoVOList();
        for (FlowNodeInfoVO flowNodeInfoVO : flowNodeInfoVOList) {
            FlowNode flowNode = new FlowNode();
            BeanUtils.copyProperties(flowNodeInfoVO,flowNode);
            flowNode.setFlowNo(flow.getFlowNo());
            flowNodeList.add(flowNode);
        }
        flowNodeService.saveBatch(flowNodeList);
        //flow_line的插入
        flowNodeList = flowNodeService.list(new QueryWrapper<FlowNode>().eq("flow_no",flowInfoVO.getFlowNo()));
        List<FlowLine> flowLineList = new ArrayList<>();
        for (int i = 0; i < flowNodeList.size(); i++) {
            FlowLine flowLine = new FlowLine();
            flowLine.setFlowNo(flow.getFlowNo());
            flowLine.setFlowNodeId(flowNodeList.get(i).getFlowNodeId());
            if(i == 0){
                flowLine.setPrevNodeId(0);
            }else{
                flowLine.setPrevNodeId(flowNodeList.get(i-1).getFlowNodeId());
            }
            if( i == flowNodeList.size() - 1){
                flowLine.setNextNodeId(-1);
            }else{
                flowLine.setNextNodeId(flowNodeList.get(i + 1).getFlowNodeId());
            }
            flowLineList.add(flowLine);
        }
        flowLineService.saveBatch(flowLineList);
    }
  1. Flow的分页查询

分页查询这边再多查询一些内容,开始只是查询flow的数据,这边多展示一下flow下面的flow_node,当然是根据flow_line排序好了,为了是为了到时候修改的时候不需要重新请求接口插叙这些节点的顺序。

    public IPage<FlowInfoVO> selectPageFlow(int pageNum, int pageSize,String flowNode,String flowName) {
        //获取流程表
        IPage<Flow> page = this.page(new Page<>(pageNum, pageSize));
        List<Flow> records = page.getRecords();
        if(records.size() == 0){
            return new Page<FlowInfoVO>();
        }
        //取出流程代码集合
        List<String> collect = records.stream().map(a -> a.getFlowNo()).collect(Collectors.toList());

        //查询带有顺序的流程节点
        List<FlowNodeInfoVO> flowNodeVOS = flowNodeMapper.selectFlowNodeVOList(collect);
        Map<String, List<FlowNodeInfoVO>> map = flowNodeVOS.stream().collect(Collectors.groupingBy(FlowNodeInfoVO::getFlowNo));
        IPage<FlowInfoVO> iPage = new Page<>(pageNum, pageSize);
        iPage.setTotal(page.getTotal());
        iPage.setCurrent(page.getCurrent());

        //流式编程
        List<FlowInfoVO> flowInfoVOList = records.stream().map(i -> {
            FlowInfoVO flowInfoVO = new FlowInfoVO();
            BeanUtils.copyProperties(i, flowInfoVO);
            flowInfoVO.setFlowNodeInfoVOList(map.get(flowInfoVO.getFlowNo()));
            return flowInfoVO;
        }).collect(Collectors.toList());
        iPage.setRecords(flowInfoVOList);
        return iPage;
    }
  1. 单个Flow的顺序修改

修改这边更新的是flow_line,所以我们就必须传递flow_node_id,分页查询也必须查出来,内部逻辑是flow_line对应flow_no对应的全部清掉,重新插入新的flow_line数据

 public void updateFlowLineVO(List<FlowLineVO> flowLineVOList) {
        List<FlowLine> flowLineList = new ArrayList<>();
        //如果只有一个,那就没修改的必要的了
        if(flowLineVOList.size() == 1) return;
        //将修改后的节点的前驱和后继节点进行更新
        for (int i = 0; i < flowLineVOList.size(); i++) {
            FlowLine flowLine = new FlowLine();
            if(i == 0){
                flowLineVOList.get(i).setPrevNodeId(0);
                flowLineVOList.get(i).setNextNodeId(flowLineVOList.get(i + 1).getFlowNodeId());
            }else if(i == flowLineVOList.size()-1){
                flowLineVOList.get(i).setNextNodeId(-1);
                flowLineVOList.get(i).setPrevNodeId(flowLineVOList.get(i - 1).getFlowNodeId());
            }else{
                flowLineVOList.get(i).setNextNodeId(flowLineVOList.get(i + 1).getFlowNodeId());
                flowLineVOList.get(i).setPrevNodeId(flowLineVOList.get(i - 1).getFlowNodeId());
            }
            BeanUtils.copyProperties(flowLineVOList.get(i),flowLine);
            flowLineList.add(flowLine);
        }
        List<Integer> collectIds = flowLineVOList.stream().map(a -> a.getFlowLineId()).collect(Collectors.toList());
        //删除原来表中的顺序
        flowLineService.removeByIds(collectIds);
        //批量保存,修改后的流程顺序
        flowLineService.saveBatch(flowLineList);
    }
  1. 单个Flow的详情

这边的查询嵌套就比较多了,条件我们应该是flowId,查询步骤:
先查询当前flowId对应的节点(有顺序的),再根据这些节点id查询对应的角色,最后分组保存!

    public FlowInfoVO selectFlowInfoVO(Integer flowId) {
        /*step1:查询流程详情*/
        FlowInfoVO flowInfoVO = new FlowInfoVO();
        Flow flow = baseMapper.selectById(flowId);
        flowInfoVO.setFlowNodeInfoVOList( baseMapper.selectFlowInfoVOById(flowId));
        //获取流程节点id
        List<Integer> collect = flowInfoVO.getFlowNodeInfoVOList().stream().map(a -> a.getFlowNodeId()).collect(Collectors.toList());
        /*step2:查询节点id对应的节点角色信息*/
        //取出个节点对应的角色
        List<FlowNodeRoleVO> flowNodeRoles = flowNodeRoleMapper.selectFlowNodeRoleVO(collect);
        /*step3:查询节点id对应的节点角色信息*/
        //分组节点id分组角色
        Map<Integer,List<FlowNodeRoleVO>> map =flowNodeRoles.stream().collect(Collectors.groupingBy(FlowNodeRoleVO::getFlowNodeId));
        //各节点角色数据填充
        flowInfoVO.getFlowNodeInfoVOList().stream().forEach(i->{
            flowInfoVO.setFlowNo(flow.getFlowNo());
            flowInfoVO.setFlowName(flow.getFlowName());
            flowInfoVO.setRemark(flow.getRemark());
            flowInfoVO.setFlowId(flow.getFlowId());
            i.setFlowNodeRoleVOList(map.get(i.getFlowNodeId()));
        });
        return flowInfoVO;
    }
  1. 节点角色的插入

这里要做的是插入每个节点对应的角色,操作的是flow_node_role表,看似插入,其实重置该节点对应的角色,也就是说先删除,再插入。

    public void addRoles(Integer flowNodeId, List<Integer> roleIds) {
        List<FlowNodeRole> flowNodeRoles = new ArrayList<>();
        for (Integer roleId : roleIds) {
            FlowNodeRole flowNodeRole1 = new FlowNodeRole();
            flowNodeRole1.setFlowNodeId(flowNodeId);
            flowNodeRole1.setRoleId(roleId);
            flowNodeRoles.add(flowNodeRole1);
        }
        flowNodeRoleService.remove(new QueryWrapper<FlowNodeRole>().eq("flow_node_id",flowNodeId));
        flowNodeRoleService.saveOrUpdateBatch(flowNodeRoles);
    }

注意:如果对于主键关联性比较强的,应尽量避免先删除,后插入!!

3.2 模块2:审批单的接入

在这里插入图片描述

  1. 审批单提交

审批单提交对于客户的话,只需要往审批单表中插入一条数据即可,但是对应业务员申请的话,需要走审批流程,就需要同时去更新审批单,审批单明细这两张表的数据,而这这两张表的一些数据是来自于Flow流程中的节点数据。

     /*****************************************************************
     * 函数名: insertUrgentOrder
     * 功能  : {toDo:插入到急件记录表中}
     * 作者  :lzy 2020/10/23
     * 参数表 :
     *   @param projectCardId :工程卡id
     *   @param customerDataId : 客户id
     *   @param type : 1客户  2业务员
     *   @param salesmanId : 业务员id
     * 返回值:
     *   @return : void
     *
     * 修改记录:
     *          日期                 修改人                 修改说明
     ******************************************************************/
    public void insertUrgentOder(Integer projectCardId, Integer customerDataId, Integer salesmanId,Integer type) {
        //获取该审批的第一个节点
        FlowNodeVO urgentFlow = flowNodeMapper.selectFirstOrLast("urgent_flow", 0);
        ProjectCardUrgent projectCardUrgent = new ProjectCardUrgent();
        projectCardUrgent.setProjectCardId(projectCardId);
        projectCardUrgent.setCustomerDataId(customerDataId);
        projectCardUrgent.setStatus(1);
        projectCardUrgent.setCreateId(ShiroUtils.getUserInfo().getUserId());
        projectCardUrgent.setCreateTime(new Date());
        //设置客户或业务员申请急件
        if (type == 1) {
            projectCardUrgent.setApplyMan(customerDataMapper.selectById(customerDataId).getCustomerFullName()); //设置为客户为自己申请
        } else {
            projectCardUrgent.setFlowNo("urgent_flow");
            projectCardUrgent.setCurrentNode(urgentFlow.getFlowNodeId());
            projectCardUrgent.setDocumentStatus(0); //未审核
            projectCardUrgent.setApplyMan(oaUserMapper.selectById(salesmanId).getUserName()); //设置为业务员申请
        }
        projectCardUrgent.setType(type);
        //插入急件
        projectCardUrgentMapper.insert(projectCardUrgent);
        //如果是业务员审批,则需要插入审批单的明细记录
        if(type == 2){
            ProjectUrgentReviewRecord projectUrgentReviewRecord = new ProjectUrgentReviewRecord();
            projectUrgentReviewRecord.setProjectCardUrgentId(projectCardUrgent.getProjectCardUrgentId());
            projectUrgentReviewRecord.setApprovalStatus(urgentFlow.getFlowNodeId());
            projectUrgentReviewRecord.setApprovalText(urgentFlow.getFlowNodeName());
            projectUrgentReviewRecord.setCreateId(ShiroUtils.getUserInfo().getUserId());
            projectUrgentReviewRecord.setCreateTime(new Date());
            projectUrgentReviewRecord.setProjectCardUrgentId(projectCardUrgent.getProjectCardUrgentId());
            projectUrgentReviewRecord.setType(1);
            projectUrgentReviewRecordMapper.insert(projectUrgentReviewRecord);
        }

    }
  1. 审核记录单查询

这边审核记录单的查询,展示的是当前用户角色是否和节点的角色有包含关系,换句话说,当前用户具备的角色是否有审核单子的权限。

    /*****************************************************************
     * 函数名: toExamineList
     * 功能  : {toDo: 审核列表}
     * 作者  :cx139 2020/10/29
     * 参数表 :
     *   @param pageNum :
     *   @param pageSize :
     *   @param projectCardNumber :
     *   @param customerData :
     * 返回值:
     *   @return : com.baomidou.mybatisplus.core.metadata.IPage<com.hgfzp.textile.erp.vo.ProjectCardUrgentVOS>
     *
     * 修改记录:
     *          日期                 修改人                 修改说明
     ******************************************************************/
    public IPage<ProjectCardUrgentVO> toExamineList(Integer pageNum, Integer pageSize, String projectCardNumber, String customerData) {
        //获取当前登录的用户的角色信息
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id", ShiroUtils.getUserInfo().getUserId());
        queryWrapper.eq("status", 1);
        List<OaUserRole> oaUserRoles = oaUserRoleMapper.selectList(queryWrapper);
        List<Integer> integers = oaUserRoles.stream().map(a -> a.getRoleId()).collect(Collectors.toList());

        QueryWrapper<ProjectCardUrgent> qw = new QueryWrapper();
        qw.eq("t.type",2);
        Page page = new Page(pageNum, pageSize);
        IPage<ProjectCardUrgentVO> iPage= baseMapper.toExamineList(page, customerData, projectCardNumber, qw,integers);
        return iPage;
    }

Mapper层的xml:

<!--审核列表-->
    <select id="toExamineList" resultType="com.hgfzp.textile.erp.vo.ProjectCardUrgentVO">
        SELECT
        t.*,
        d.customer_code,
        d.customer_abbreviation,
        d.customer_full_name,
        d.customer_abbreviation,
        e.cargo_name,
        a.project_card_number,
        f.customer_color_number,
        f.colour_number_name,
        g.user_name oaUser,
        fn.flow_node_name,

        fn2.flow_node_name nextFlowNodeName,fn2.flow_node_id nextFlowNodeId
        FROM
        project_card_urgent t
        LEFT JOIN project_card a ON a.project_card_id = t.project_card_id
        LEFT JOIN dyeing_notice_serial_number b ON a.dyeing_notice_serial_number_id = b.dyeing_notice_serial_number_id
        LEFT JOIN embryo_cloth_warehousing c ON b.embryo_cloth_warehousing_id = c.embryo_cloth_warehousing_id
        LEFT JOIN customer_data d ON t.customer_data_id = d.customer_data_id
        LEFT JOIN cargo_name_information e ON c.cargo_name_information_id = e.cargo_name_information_id
        LEFT JOIN colour_number f ON b.colour_number_id = f.colour_number_id
        LEFT JOIN oa_user g ON g.user_id = d.salesman_id
        LEFT JOIN flow_node fn ON fn.flow_node_id = t.current_node
        LEFT JOIN flow_line fl ON fl.flow_node_id = t.current_node
        LEFT JOIN flow_node_role fnr ON fl.next_node_id = fnr.flow_node_id
        LEFT JOIN flow_node fn2 ON fn2.flow_node_id = fl.next_node_id
        <where>
            <if test="projectCardNumber != null">
                and a.project_card_number like concat(#{projectCardNumber},'%')
            </if>
            <if test="customerData != null">
                and d.customer_full_name like concat(#{customerData},'%')
                or d.customer_abbreviation like concat(#{customerData},'%')
                or d.customer_code like concat(#{customerData},'%')
            </if>
            <if test="ew != null">
                <if test="ew.SqlSegment != null">
                    AND ${ew.SqlSegment}
                </if>
            </if>
            AND fnr.role_id IN
            <foreach collection="roleIds" item="roleId" index="index" open="(" close=")" separator=",">
                #{roleId}
            </foreach>

        </where>
        order by t.project_card_urgent_id desc
    </select>
  1. 审批单的审核

审核需要传递上一个审核通过的人节点currentNode ,和即将要来审核节点nextFlowNodeId ,一定要做一个判断的是:按照正常的流程来说,我们是必不需要判断currentNode != projectCardUrgent.getCurrentNode() 因为它们是一定相等的,但是也有可能出现不等的情况下,如果传入一个已经审批过的节点,此时就会出现审批重复的局面!!

每提交一次审核都会向审批单明细插入一条数据
在这里插入图片描述
审批单的current_node也会跟着改变
当全部的流程节点都完成审批,则会去更新主表的result_status

    /*****************************************************************
     * 函数名: toExamine
     * 功能  : {toDo: 审核}
     * 作者  :cx139 2020/10/29
     * 参数表 :
     *   @param projectCardUrgentId : 工程卡急件id
     *   @param type : 通过1 拒绝2
     *   @param currentNode : 审批单最近的审核人节点
     *   @param nextFlowNodeId : 下一个审核人节点
     * 返回值:
     *   @return : void
     *
     * 修改记录:
     *          日期                 修改人                 修改说明
     ******************************************************************/
public Result toExamine(Integer projectCardUrgentId, Integer type,Integer currentNode,Integer nextFlowNodeId) throws Exception {
        //查询单据信息
        ProjectCardUrgent projectCardUrgent = projectCardUrgentService.getById(projectCardUrgentId);
        //单据节点与传入节是否一致   projectCardUrgent.getCurrentNode()已审批过的节点
        if(currentNode != projectCardUrgent.getCurrentNode()){
            return ResultGenerator.genFailResult("当前已审批");
        }
        /*step1:查询用户角色并查看权限*/
        QueryWrapper qw = new QueryWrapper();
        qw.eq("user_id", ShiroUtils.getUserInfo().getUserId());
        qw.eq("status", 1);
        List<OaUserRole> oaUserRoles = oaUserRoleMapper.selectList(qw);
        //取出当前登录用户的角色id
        List<Integer> integers = oaUserRoles.stream().map(a -> a.getRoleId()).collect(Collectors.toList());
        //取出当前审批人的角色
        List<FlowNodeRoleVO> flowNodeRoleVOS = flowNodeRoleMapper.selectFlowNodeRoleByFlowNodeId(nextFlowNodeId);
        List<Integer> flowRoleIds = flowNodeRoleVOS.stream().map(i->i.getRoleId()).collect(Collectors.toList());
        boolean flag = false;
        //判断当前审批人是否有做当前审批动作的角色的权限
        for (Integer integer : integers) {
            if(flowRoleIds.contains(integer)){
                flag = true;
                break;
            }
        }
        if(!flag){
            return ResultGenerator.genFailResult("用户没有审批权限");
        }
        /*step2:更新审核记录表*/
        //下一个节点信息
        FlowNodeVO flowNodeVO = flowNodeMapper.selectFlowNodeVOById(nextFlowNodeId);
        //审核记录表
        ProjectUrgentReviewRecord projectUrgentReviewRecord = new ProjectUrgentReviewRecord();
        projectUrgentReviewRecord.setProjectCardUrgentId(projectCardUrgentId);
        projectUrgentReviewRecord.setCreateId(ShiroUtils.getUserInfo().getUserId());
        projectUrgentReviewRecord.setCreateTime(new Date());
        projectUrgentReviewRecord.setType(type);
        projectUrgentReviewRecord.setApprovalStatus(nextFlowNodeId);
        projectUrgentReviewRecord.setApprovalText(flowNodeVO.getFlowNodeName());
        this.save(projectUrgentReviewRecord);

        /*step3:审核单更新*/
        //主表更新
        projectCardUrgent.setProjectCardUrgentId(projectCardUrgentId);
        projectCardUrgent.setCurrentNode(nextFlowNodeId);
        //如果是最后一个节点或者拒绝
        if(flowNodeVO.getNextNodeId() == -1 || type == 2){
            //单据结束
            projectCardUrgent.setDocumentStatus(2);
        }else{
            //否则在审批中
            projectCardUrgent.setDocumentStatus(1);
        }
        //设置单据结果状态
        projectCardUrgent.setResultStatus(type);
        projectCardUrgentMapper.updateById(projectCardUrgent);
        /*step4:急件审核单通过,调用排程接口*/
        //如果是审批结束,调用排程接口
        if(flowNodeVO.getNextNodeId() == -1 && type == 1){
            //更新工程卡急件状态
            projectCardService.update(new UpdateWrapper<ProjectCard>()
                    .eq("project_card_id",projectCardUrgent.getProjectCardId()).set("urgent_type",1));
            projectCardScheduleCommon.projectCardChangeUrgent(projectCardUrgent.getProjectCardId(),projectCardUrgent.getCustomerDataId());
        }
        return ResultGenerator.genSuccessResult();
    }
  1. 审批单提交记录查询

记录查询展示的是该单子的审核进度和单子的基本信息

    /*****************************************************************
     * 函数名: selectProjectCardUrgentPage
     * 功能  : {toDo:工程卡急件分页查询+条件查询}
     * 作者  : liuzeyu 2020/8/29
     * 参数表 :
     *   @param pageNum :页数
     *   @param pageSize :页大小
     * 返回值:
     *   @return : com.hgfzp.textile.common.utils.result.Result<com.baomidou.mybatisplus.core.metadata.IPage<com.hgfzp.textile.erp.vo.ProjectCardUrgentVOS>>
     *
     * 修改记录:
     *          日期                 修改人                 修改说明
     ******************************************************************/
    @Override
    public Result<IPage<ProjectCardUrgentVO>> selectProjectCardUrgentPages(Integer pageNum, Integer pageSize,
                                                                            String projectCardNumber, String customerData,Integer isAll) {
        List<ProjectCardUrgent> projectCardUrgents = projectCardUrgentMapper.selectList(null);
        List<Integer> projectCardIds = projectCardUrgents.stream().map(group -> group.getProjectCardId()).collect(Collectors.toList());
        IPage<ProjectCardUrgentVO> projectCardUrgentVOSIPage = null;
        QueryWrapper queryWrapper = new QueryWrapper();
        if (projectCardNumber != null) {
            queryWrapper.likeRight("a.project_card_number", projectCardNumber);
        }
        if (customerData != null) {
            queryWrapper.likeRight("d.customer_full_name", customerData);
        }
        //默认值为1全查,但是如果等于0的时候就查询业务员的
        if(isAll == 0){
            queryWrapper.eq("t.create_id",ShiroUtils.getUserInfo().getUserId());
        }

        projectCardUrgentVOSIPage = projectCardUrgentMapper.selectProjectCardUrgentPages(new Page(pageNum, pageSize), projectCardIds, queryWrapper);
        return ResultGenerator.genSuccessResult(projectCardUrgentVOSIPage);
    }

对应的xml文件

<!--    工程卡急件分页记录查询-->
    <select id="selectProjectCardUrgentPages" resultType="com.hgfzp.textile.erp.vo.ProjectCardUrgentVO">
        SELECT
            t.*,
            d.customer_code,
            d.customer_abbreviation,
            d.customer_full_name,
            e.cargo_name,
            a.project_card_number,
            f.customer_color_number,
            f.colour_number_name,
            g.user_name oaUser,
            fn.flow_node_name,
            fn2.flow_node_name nextFlowNodeName,fn2.flow_node_id nextFlowNodeId
        FROM
            project_card_urgent t
            LEFT JOIN project_card a ON a.project_card_id = t.project_card_id
            LEFT JOIN dyeing_notice_serial_number b ON a.dyeing_notice_serial_number_id = b.dyeing_notice_serial_number_id
            LEFT JOIN embryo_cloth_warehousing c ON b.embryo_cloth_warehousing_id = c.embryo_cloth_warehousing_id
            LEFT JOIN customer_data d ON t.customer_data_id = d.customer_data_id
            LEFT JOIN cargo_name_information e ON c.cargo_name_information_id = e.cargo_name_information_id
            LEFT JOIN colour_number f ON b.colour_number_id = f.colour_number_id
            LEFT JOIN oa_user g ON g.user_id = d.salesman_id
            LEFT JOIN flow_node fn ON fn.flow_node_id = t.current_node
            LEFT JOIN flow_line fl ON fl.flow_node_id = t.current_node
            LEFT JOIN flow_node_role fnr ON fl.next_node_id = fnr.flow_node_id
            LEFT JOIN flow_node fn2 ON fn2.flow_node_id = fl.next_node_id
        <where>
        <if test="projectCardIds.size() > 0">
            a.project_card_id in
            <foreach collection="projectCardIds" item="projectCardId" index="index" open="(" close=")" separator=",">
                #{projectCardId}
            </foreach>
        </if>
            <if test="ew != null">
                <if test="ew.SqlSegment != null">
                    AND ${ew.SqlSegment}
                </if>
            </if>
        </where>
        group by a.project_card_number
        order by t.project_card_urgent_id desc
#先分组后排序
    </select>

4. 小结

审批系统的内部实现逻辑比较复杂,考虑的问题也会比较多,难点在于审批单的接入。系统也存在很多需要改善的地方,例如:该系统没有修改节点的功能,实则是因为如果存在单子正在审批过程,一修改节点就会导致审批错乱,难以维护。正常的审批系统应该还会有单子驳回的功能,随着学习的深入会继续完善该系统!!

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
朋科PECU-ERP系统是专门针对小型企业,个体经营者或个人开发的超级ERP应用管理系统。它通过一系列的电子单据、表格、报表把企业和个人的所有资源包括商品、用品(设备、工具)、客户、雇员、资金、信息进行整合全面一体化集成管理,对企业和个人的所有业务流程实现自动化无纸化管理。企业属下的分支机构之间均能通过互联网实现资源共享、协同作业。能最大限度地充分利用企业和个人的现有资源,并能极大地提高企业和个人的各项业务效率。系统核心功能主要包括:办公管理、生产管理、采购管理、销售(POS)管理、仓库管理、商品管理、用品管理、财务管理、雇员管理、运输管理、系统管理。通用性、人性化设计,能适应多种行业的业务需要。 系统功能特点: 1. 首创以资源为核心进行集成化高效管理,摒弃以业务流程为核心的传统模式。 传统ERP系统大致分为三种解决方案:一是根据客户业务流程进行定制开发,且不论不同企业的业务流程有天渊之别,即使同一企业也会因不同发展阶段的业务流程会发生不可预测的变化。再强大的软件企业也不可能长期安排工程师对企业进行二次开发,就算企业有这样的能力,客户也无法负担昂贵的实施费用!二是开发复杂大而全的成品系统,希望能解决各种客户不同业务流程的需求,同样,固定的系统永远无法适应业务流程的变化。更会导致客户为不需要的功能支付额外的费用;三是只保留部分业务流程的精简版半成品系统,客户必须要购买多种系统才能完成所有业务。各种系统之间无法很好协同运作。为了解决ERP的发展瓶颈,PECU-ERP系统首创以企业和个人的资源(人、财、物)为核心,对各项业务流程进行高度集成化,集企业OA、客户CRM、进销存、财务、人力资源、生产计划MRP以及个人用品、信息、行动计划的精华于一体,并使各种资源、信息互相关联起来,从而彻底打破不同企业之间的业务壁垒,使企业和个人都能轻松摆脱繁杂无序低效的人工管理,使经济生活变得有序高效,早日享受IT技术创新带来的幸福新生活。 2. 独有时光倒流功能,无需开账即可使用系统,并可重现任一时间段的财务数据。 传统ERP系统使用前需进行繁杂的开账设置,把企业所有的商品结存、资金账户余额、基础资料等初始数据输入系统后才能开始使用系统,往往在未完成初始数据输入前,由于发生了新业务,数据又发生了变化,大大影响了数据的准确性和时效性。PECU-ERP系统运用独有算法,能使用户彻底摆脱财务开账或结账日期的限制,无需开账即可使用系统,并可为用户呈现任意时段的财务数据,极大方便用户查账对账。当发现财务处理错漏时,即使结账日期之前的数据也能在用户授权权限范围内进行修改、甚至删除处理。真正做到我的数据我做主。 3.用户界面简洁友好,极易操作,即装即用,零培训费用。 系统摒弃了华丽的导航式操作界面,一切操作均模拟企业的实际业务流程,直达企业的核心需求,至简、高效、实用,能充分适应企业的发展需求,企业经营者无需重新学习ERP管理理念,无需建立完善会计制度,更不需要重新调整业务流程。系统已经针对一般加工贸易行业,预置了城市城区、商品类型、收支类型、客户类型等常用基础资料,免却用户前期基础资料输入的麻烦,更无需开账后才使用系统,可以在使用过程中随时输入初始数据,真正实现即装即用。 4.技术成熟、性能稳定、兼容性好、速度非常快。 系统技术平台基于当今流行的ACCESS数据库管理平台,技术成熟,界面简洁,容易上手,费用低廉,运行稳定,是微软OFFICE套件的必备工具,兼容性非常好,对电脑性能要求非常低,普通的家用电脑足可应付。可节省大量硬件投资费用。操作庞大的ERP数据就像操作办公文档一样,轻点鼠标,只需几秒,精彩数据即刻为您呈现。 5. 首创跨界超级ERP系统、费用低、易安装、速度快、不断线。 既有C/S系统速度快,也有B/S系统易安装易维护的优点,同时也彻底克服传统C/S系统需要安装专用服务器,安装维护难度大,费用高的缺点,也避免了B/S系统因网络塞车的频频掉线,失去响应的漫长等待。在局域网环境内只需几分钟即可把任意一台电脑主机设置为ERP服务器,实现ERP数据实时共享,使员工协同高效办公成为可能。客户端速度非常快。而且不需安装任何服务(SERVER)操作系统,只需普通的XP家庭版操作系统即可,成本非常低廉。在互联网环境上,通过借助远程接入技术可实现外网高效、安全访问内网的ERP数据,使您无论身处何处,仍能随时随地操作您的ERP系统。 6. 首创数据分布式存储,使用户彻底摆脱数据丢失的烦恼。 支持不同主机的对等数据共享,系统可自动生成加密的超小型更新数据包,只需通过QQ等工具传送接收后导入到本地ERP系统后即可实现数据的远程共享。正是由于数据是对等分布存储在不同主机上,任何一台主机发生故障,故障修复后都可通过网络从其它主机获取完整用户数据,从而快速恢复系统运行,使用户业务能不间断正常运作。 7. 独有单据网络数字签名功能,网络远程审单轻松快捷。 提供网络数字签名功能,允许用户授权给指定的操作员在业务单据中进行数字签名并可以通过网络发送单据。不但节省大量传真费用,又方便企业经营者通过网络随时随地进行单据审批工作。真正实现移动高效办公。 8.独有十二级用户授权控制机制,数据安全无忧。 从董事长到普工共分12个级别精确控制操作员对系统数据的访问和修改权限,甚至可精确控制每个用户对每个数据的操作权限。非常方便用户管理不同操作员的操作权限。不用担心宝贵数据会被非法访问。 9.强大图片处理功能,数据形象生动。 拥有强大的图片处理功能,商品、用品、客户、雇员均可显示jpg格式的图片,只要存放在指定的文件夹中,并按系统格式命名,系统便可为数据自动链接相关的图片,在数据录入、查询、统计、报表时均可同时显示相关图片、更形象、更生动地帮助用户识别各种数据。甚至可以运用系统为您快速制作一本产品画册。 10. 人性化设计,业务适应能力强。 商品和用品均有货位管理功能,方便用户对商品和用品进行实物管理。支持各种读条码机,能自动识别12位国际条码和9位PECU商品编码,实现类似超市POS机功能。有标签和图片自动打印功能,能打印出各种规格,各种用途的标签和图片,甚至可以打印洗水唛和产品彩图宣传单张。能统计分析财务收支,商品销售,客户销售等重要资料,并能自动生成各种报表。生产系统能对产品的生产工序,工具设备配料进行管理,并能自动生成领料进仓单对产品进行成本核算以及自动计算工人加工费。 试用操作员:1888,密码1888
FlowPortal BPM 流程管理 FlowPortal采用微软.net技术,能进行可视化免编程的业务流程管理(BPM)平台,经上海易正信息技术有限公司经过10年研发而成。 现该系统已广泛应用于政府、制造、零售、服务、地产等行业领域。 一、能自实施的BPM系统平台 借助内置的微软asp. net规格的表单设计器XForm Designer及其附带的丰富的表单控件元素,IT人员无需编程就可以快速实现表单的电子化迁移,并且制作出来的电子表单使用友好,功能丰富具有专业水准。 一体化完整的BPM解决方案,彻底的免编程设计,从拖拉式流程设计器、表单设计器、报表设计器均符合免编程设计原则,并且,所有产品包括组织结构管理,电子表单、流程设计、报表设计全部符合微软产品用户已有的使用习惯。 基于为广大IT人员熟悉的通用标准,流程描述语言使用微软C#标准,表单使用微软asp. net标准。 二、充分整合现有资源 FlowPortal. net开放的体系架构允许将企业现有IT系统中的组织架构、用户信息,整合到BPM系统使用,不管他们是位于AD、HR还是位于特有的IT系统内。 业务流转时,可以集合存储在不同系统中的数据建立复合业务实体,比如:存储在ERP中的客户、产品信息,存储在HR系统中的人事信息等等 自动化不同系统中业务数据的处理,比如:采购审批通过时,自动在ERP中生成PR单,人事入职流程中,自动在各个系统中建立用户信息。 借助可插拔的体系架构,通过第三方开发,实现对现有信息和系统的利用,比如利用企业特有业务系统存储在InfoSys数据库内的信息。 通过可嵌入的组件,将BPM集成到SharePoint、企业现有IT系统的框架内。 三、实现无限可能 流程定义时,赋予企业用户使用微软C#扩展流程功能的能力,比如用C#表达逻辑实现一个会签表决规则,又如:流程提交时使用HR系统中的数据验证申请合法性。 表单设计上,借助微软asp. net技术,扩展表单功能,由于表单设计器生成的是标准的asp. net表单,使得企业可以借助微软asp. net的强大功能实现复杂的需求。 可插拔的体系架构,允许企业将BPM体统和企业现有业务系统整合到一起,比如:利用企业现有的弹出式消息系统发送BPM通知消息。 四、有效保障流程管理工作持续、深入开展 可靠、稳定、高效的系统使得BPM系统深入人心 快速实施能力、良好的最终用户使用体验让IT部门轻松,使用者满意,会促使更多的流程需求被建议并实施优异的可扩展能力,为确保IT部门始终有能力满足最终用户的各种需求提供保障。 FlowPortal. net的客户都在持续深入得使用BPM系统,新的流程需求被持续提出并实施上线,不断拓展到新的工厂、事业部门、甚至拓展到集团内其它国家和地区的工厂、企业。 五、随时随地获得所需信息 企业可以使用内置的报表工具按需定制报表,实时查看企业关键业务数据。 FlowPortal. net的报表可以执行数据的钻取,渐入式分析,查询,图形化展示。 FlowPortal. net的报表可以跟据流程的权限定义,使得每个部门的领导只看到各自管辖部门内员工所发起业务的统计数据。
还在为ERP软件实施难,价格贵而苦恼吗,我们将以最低的价格为您提供优质的ERP软件,RX ERP生产管理系统,信息集成度高,操作简洁易学,实施上线快,性价比最高,强大的生产管理,适合大众化需求,满足个性化要求,超强内核设计,大型ERP架构。选择RX ERP,无论您是软件销售商,还是企业用户都将为您带来更多的利润.联系人:陈生(13688922084) QQ:453462216 功能模块: 生产管理:BOM组合生产、简易生产、工序生产、MRP计划生产、全能生管、生产质检、委外托工 物流管理:采购管理、销售管理、库存管理、物流成本、物流质检、海关贸易 财务管理:总帐管理、收款管理、付款管理、票据银行、固定资产 行政管理: 人事考勤、薪资管理、审核流程、预警管理、自定义 功能简介: 系统设置: 提供用户自定义的各种基础数据编码,比如客户/厂商信息、会计科目设置、货品资料信息、审核流程设置、操作者及权限信息等。 采购管理:软件从请购单到询价单、采购订单、收货质检、验收入库等过程进行管理,帮助企业管理合同、定金,从价格、质量、交货及时率等角度详细评价和比较供应商的历史信用,以及处理异常业务的退回等。 销售管理:软件有针对性的从报价到受订单/合同、定金、折扣处理、信用检查与控制、准备货物、发货出库(退货处理)等过程进行追踪管理。提供针对业务员、产品、时间等多维角度的统计分析报表。 库存管理:软件从库存物品的出入库操作、库存调整调拨业务、盘点业务及盈亏差异处理等过程进行管理,支持多仓库、货位、批次、包装单位等管理方式,支持固定、变动成本核算方式和多种计算方法。 收款管理:软件从通过特有的凭证模版自定义技术,帮助企业实现收款业务(应收、预收、冲消、票据等)与总帐和销售之间的信息动态同步和共享。所有销售业务引起的出入库物流业务细节,都可以自动及时地反映在财务帐上,软件还提供多角度收款考核及应收帐款帐龄分析。 付款管理:帮助企业实现付款业务(应付、预付、冲消、票据等)与总帐和采购之间的信息动态同步和共享。所有采购业务引起的出入库物流业务细节,都可以自动及时地反映在财务帐上,软件提供应付帐款帐龄分析。 票据银行:软件从企业经营活动中遇到的支票、本票、期票、信用证等票据业务进行管理,包括冲消、托收、兑现、转付、贴现、退票、注销、还票、质押等业务,提供票据的帐龄分析。同时管理银行帐户的收支情况、资金预估、余额调节表、内部结算、代付款等银行相关业务。 总帐报表:帮助企业管理日常帐务及财务报表工作(凭证、帐簿、报表等)。所有不是从物流业务自动结转记帐的凭证(如费用报销等),都可以在总帐模块中录入并汇总记帐,同时,各种财务报表和管理报表都可以由用户自己定义。 固定资产:软件通过帮助企业管理固定资产及其变动明细、固定资产每月折旧的自动记帐和报表制作等工作,从而准确及时、节省企业财务人员的工作量。 BOM组合生产:系统严格按BOM表用量进行组合,成品缴库的同时冲减原材料的库存数量,能有效控制原材料的使用数量,准确计算出成品成本。 简易生产:企业接到加工订单后,可依据BOM表进行材料需求分析并产生原材料采购计划,指导企业进行物料采购,同时生产车间依据加工单进行原材料的领退作业,生产完工后进行缴库,并通过人工及制费分摊作业核算成本。清晰、有效地管理企业的采购业务,从而达到有效管理企业生产领料业务和有效控制生产成本的目的。 工序生产:企业接到加工订单后根据制成品工艺路线产生各工序的生产加工单,各生产车间凭工序加工单进行原材料的领退,工序生产完工后,进行各工序的生产日报作业,完工后进行成品缴库,并通过人工及制费分摊作业核算成本。企业可通过生产动态表对工序生产情况进行有效控制。 MRP计划生产:系统依据订单要求的时间、数量进行生产需求分析,并自动产生原材料采购计划和制成品生产计划(含委外加工计划),指导企业进行物料采购,同时生产车间依据加工单进行原材料的领退作业,生产完工后进行缴库,并通过人工及制费分摊作业核算成本。帮助企业控制货品库存,实现动态库存最小化,对企业自制生产业务和委外加工业务进行有效管理。 全能生管:系统依据订单要求的时间、数量进行主生产计划运算,并依据主生产计划的结果进行材料需求计划运算,产生缺料表,指导企业原材料采购,同时依据主生产计划的结果进行请制单维护作业,把所需生产的成品自动转成加工单,再依据产品工艺路线的设置产生工序生产通知单,对产品生产工序进行管理,还可以结合产能分析,计算出每道工序生产机台的生产能力和生产负荷,使企业可以有效把握各工序生产机台的生产能力与生产负荷是否平衡,保证生产计划有效执行。 质检管理:系统提供对采购货品、托工缴回货品、工序生产货品、制成品等货品的品质
UniGUI ERP 是一种基于 UniGUI 框架开发的企业资源规划(ERP)系统的源代码。 UniGUI 是一种用于开发 Web 应用程序的强大工具,它能够将 Delphi(Object Pascal)语言与 Ext JS 框架结合起来,实现跨平台、响应式的应用程序开发。使用 UniGUI,开发人员可以快速创建功能强大、外观精美的 Web 应用程序,而不需要掌握前端开发的复杂知识。 UniGUI ERP 源代码提供了一个完整的企业资源规划系统的基础架构,包括主要模块如客户关系管理、供应链管理、销售管理、采购管理、财务管理以及库存管理等。开发人员可以根据实际需求对这些模块进行定制和扩展,以满足企业的特定需求。 UniGUI ERP 的源代码还包含了一套简洁、优雅的用户界面设计,使得用户能够轻松地操作系统,提高工作效率。此外,源代码还包含了丰富的后台数据处理与计算功能,可以帮助企业更好地管理其资源、提高生产效率。 使用 UniGUI ERP 源代码,开发人员可以根据企业的业务需求进行自定义开发,添加新增模块或功能,以满足企业的特殊需求。同时,由于 UniGUI 是一个跨平台的开发框架,UniGUI ERP 可以在不同的操作系统和设备上运行,包括 Windows、Linux、Mac 等。 总之,UniGUI ERP 源代码是一种灵活、可定制的企业资源规划系统,它提供了一个基于 UniGUI 框架的开发基础,开发人员可以通过对源代码进行定制和扩展,创建适合企业需求的现代化、高效的 ERP 系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值