[小工具]UOF2 BPM 強制取回(退簽) + 送出重跑流程 | EIP |一等一科技

by | 5 月 29, 2026 | 一等一UOF系統, 程式 | 0 comments

Views: 0

官方沒這個工具,我反映過了,很多時候BPM只有作為ERP上的合約簽核使用而已,ERP修改合約資料後,BPM就需要退回+更新資料+從申請者重送,一等一官方本身有退回的程序,但改完資料後沒有「重送」的API,這篇內容是我土炮一個相關功能

已知問題

  1. 文章標題要自己再更新一次,系統不會更新
  2. 退回後如果是自由流程(我其實搞不清楚這啥用途),基本上會送出失敗
  3. 我目前只有測試v27跟v28 其他版本能不能用就自求多福…

程式碼

廢話不多…說直接給大家程式碼

自己移植到需要的部分

我是放在CDS/Tools/BPM_ReturnSite.aspx

前端 BPM_ReturnSite.aspx

<%@ Page Title="" Language="C#" MasterPageFile="~/Master/DefaultMasterPage.master" AutoEventWireup="true" CodeFile="BPM_ReturnSite.aspx.cs" Inherits="CDS_Tools_BPM_ReturnSite" %>

<%@ Register TagPrefix="Fast" Namespace="Ede.Uof.Utility.Component" Assembly="Ede.Uof.Utility.Component.Grid" %>
<%@ Import Namespace="Ede.Uof.EIP.SystemInfo" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
    <asp:UpdatePanel runat="server" ID="Udp1">
        <ContentTemplate>
            <asp:HiddenField runat="server" ID="hfLastSiteId" />
            <table  class="PopTable">
                <tr>
                    <td>Current.UserGUID 目前登入者GUID</td>
                    <td><%= Current.UserGUID %></td>
                    
                </tr>
                <tr>
                    <td>Current.User.Name 目前登入者名稱</td>
                    <td><%= Current.User.Name %></td>
                </tr>
                <tr>
                    <td>
                        DOC_NBR
                    </td>
                    <td>
                        <asp:TextBox runat="server" ID="txtDocNbr"></asp:TextBox> 
                        <asp:Button runat="server" ID="btnFindTaskId" Text="Doc Nbr 查詢Task ID" OnClick="btnFindTaskId_OnClick"/>
                        <asp:Label runat="server" ID="lblMsgFindTaskID" Text="" ForeColor="blue"></asp:Label>
                    </td>
                </tr>
                
                <tr>
                    <td>TASK ID</td>
                    <td>
                        <asp:TextBox runat="server" ID="txtTaskID"></asp:TextBox></td>
                </tr>
                <tr>
                    <td>Comment 退回申請者要傳送的訊息
                    </td>
                    <td>
                        <asp:TextBox runat="server" ID="txtComment" Rows="3" TextMode="MultiLine"></asp:TextBox></td>
                </tr>
                <tr>
                    <td></td>
                    <td>
                        <asp:Button runat="server" ID="btnSetReturn" OnClick="btnSetReturn_OnClick" Text="退回申請者" /></td>
                        <asp:Label runat="server" ID="lbMessage" ForeColor="Red"></asp:Label>
                </tr>
                <tr>
                    <tr>
                        <td>Comment-重送</td>
                        <td>
                            <asp:TextBox runat="server" ID="txtCommentReApply" Rows="3" TextMode="MultiLine"></asp:TextBox></td>
                    </tr>
                    <td></td>
                    <td>
                        <asp:Button runat="server" ID="btnReApply" Text="重送" OnClick="btnReApply_OnClick"/>
                        <asp:Label runat="server" ID="lbMessageReApply" Text="" ForeColor="Green" />
                    </td>
                </tr>
            </table>


        </ContentTemplate>
    </asp:UpdatePanel>
</asp:Content>

後端 BPM_ReturnSite.aspx.cs

using Ede.Uof.EIP.Organization.Util;
using Ede.Uof.EIP.SystemInfo;
using Ede.Uof.WKF.Data;
using Ede.Uof.WKF.Engine;
using Ede.Uof.WKF.Exceptions;
using Ede.Uof.WKF.PersonalBox;
using Ede.Uof.WKF.Utility;
using System;
using System.Data;


public partial class CDS_Tools_BPM_ReturnSite :Ede.Uof.Utility.Page.BasePage
{

    private string AddWKFSignerInfoList(UserSet userSet)
    {
        string userSetXml = string.Empty;

        if (userSet != null)
        {
            for (int i = 0; i <= userSet.Items.Count - 1; i++)
            {
                UserSetUser userSetUser = (UserSetUser)userSet.Items[i];

                if (userSetUser.WKFSignerInfoList.Count <= 0)
                {
                    var ebUser = userSetUser.EBUsers[0];
                    WKFSignerInfo wkfSignerInfo = new WKFSignerInfo(
                        ebUser.GroupID,
                        ebUser.GroupName,
                        string.Empty,
                        string.Empty,
                        string.Empty,
                        string.Empty
                    );
                    userSetUser.WKFSignerInfoList.Add(wkfSignerInfo);
                }
            }

            userSetXml = userSet.GetXML();
        }

        return userSetXml;
    }

    protected void Page_Load(object sender, EventArgs e)
    {

    }

    private readonly string _connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["connectionstring"].ConnectionString;

    /// <summary>
    /// 退回申請者
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnSetReturn_OnClick(object sender, EventArgs e)
    {

        lbMessage.Text = "";
        var result = GetSiteIDAndNodeSeq(txtTaskID.Text);


        //如果沒有資料就不執行
        if (result.SiteID == "無資料" || result.NodeSeq == "無資料")
        {
            lbMessage.Text = "查無資料,請確認TaskID是否正確";
            return;
        }

        //退回申請者
        try
        {
            //一定要有備註才能退回申請者
            UpdateComment(txtComment.Text, txtTaskID.Text);
            var rUCO = new ReturnSignUCO();
            rUCO.ReturnToApplicant(txtTaskID.Text, result.SiteID, Convert.ToInt32(result.NodeSeq), Current.UserGUID, Convert.ToBoolean(false), Source.PCBatch.ToString());
            lbMessage.Text = "退簽完成";
        }
        catch (Exception exception)
        {
            lbMessage.Text = $"退簽失敗 {exception}";
        }

    }

    private void UpdateComment(string message , string TASKID)
    {
        message = (message ?? "").Replace("\r\n", "\n").Replace("\n", "\r\n");
        string sql = @"UPDATE [TB_WKF_TASK_NODE]  SET COMMENT = @COMMENT WHERE TASK_ID = @TASKID
            AND FINISH_TIME IS NULL";
        //sql
        using (var conn = new System.Data.SqlClient.SqlConnection(this._connectionString))
        {
            conn.Open();
            using (var cmd = new System.Data.SqlClient.SqlCommand(sql, conn))
            {
                cmd.Parameters.AddWithValue("@COMMENT", message);
                cmd.Parameters.AddWithValue("@TASKID", TASKID);
                cmd.ExecuteNonQuery();
            }
        }
    }
    
    private (string SiteID, string NodeSeq) GetSiteIDAndNodeSeq(string taskID)
    {
        string sql = @"
            SELECT SITE_ID, NODE_SEQ
            FROM TB_WKF_TASK_NODE
             WHERE TASK_ID = @TASKID
            AND FINISH_TIME IS NULL
        ";

        DataTable dt = new DataTable();
        using (var conn = new System.Data.SqlClient.SqlConnection(this._connectionString))
        using (var cmd = new System.Data.SqlClient.SqlCommand(sql, conn))
        {
            cmd.Parameters.AddWithValue("@TASKID", taskID);
            using (var da = new System.Data.SqlClient.SqlDataAdapter(cmd))
            {
                da.Fill(dt);
            }
        }

        if (dt.Rows.Count > 0)
        {
            return (dt.Rows[0]["SITE_ID"].ToString(), dt.Rows[0]["NODE_SEQ"].ToString());
        }
        return ("無資料", "無資料");
    }

    protected void btnFindTaskId_OnClick(object sender, EventArgs e)
    {
        lblMsgFindTaskID.Text = "";
        txtTaskID.Text = "";

        //根據單號找TaskID
        string sql = @"
            SELECT TASK_ID
            FROM TB_WKF_TASK
             WHERE DOC_NBR = @DOC_NBR";
        DataTable dt = new DataTable();
        using (var conn = new System.Data.SqlClient.SqlConnection(this._connectionString))
            using (var cmd = new System.Data.SqlClient.SqlCommand(sql, conn))
        {
            cmd.Parameters.AddWithValue("@DOC_NBR", txtDocNbr.Text);
            using (var da = new System.Data.SqlClient.SqlDataAdapter(cmd))
            {
                da.Fill(dt);
            }
            if (dt.Rows.Count > 0)
            {
                txtTaskID.Text = dt.Rows[0]["TASK_ID"].ToString();
                lblMsgFindTaskID.Text = "查詢成功";
            }
            else
            {
                lblMsgFindTaskID.Text = "查無資料,請確認單號是否正確";
            }
        }
    }

    /// <summary>
    /// 檢查是否已經退回使用者
    /// </summary>
    /// <returns></returns>
    public bool IsReturnUser(string Task_ID)
    {
        string sql = @"
SELECT TOP (1) [TASK_ID]
      ,[TASK_STATUS]
  FROM [TB_WKF_TASK]
  where TASK_ID = @TASK_ID;
";
        using (var conn = new System.Data.SqlClient.SqlConnection(this._connectionString))
        using (var cmd = new System.Data.SqlClient.SqlCommand(sql, conn))
        {
            cmd.Parameters.AddWithValue("@TASK_ID", Task_ID);
            conn.Open();
            using (var reader = cmd.ExecuteReader())
            {
                if (reader.Read())
                {
                    //這個旗標要4才是退回使用者,其他狀態都不是
                    return reader["TASK_STATUS"].ToString() == "4";
                }
            }
        }
        return false;
    }

    /// <summary>
    /// 進行退簽動作
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void btnReApply_OnClick(object sender, EventArgs e)
    {
        lbMessageReApply.Text = "";

        try
        {
            string taskId = txtTaskID.Text.Trim();

            if (string.IsNullOrWhiteSpace(taskId))
            {
                lbMessageReApply.Text = "請輸入 TaskID";
                return;
            }

            UpdateComment(txtCommentReApply.Text, taskId);
            string newTaskId = ReApplyReturnedTaskInternal(taskId, Current.UserGUID);

            lbMessageReApply.Text = $"重送成功,TaskID={newTaskId}";
        }
        catch (NoCondBranchSiteException ex)
        {
            lbMessageReApply.Text = $"重送失敗:找不到條件分支站點。{ex.Message}";
        }
        catch (NoJobTitleException ex)
        {
            lbMessageReApply.Text = $"重送失敗:申請者無職級。{ex.Message}";
        }
        catch (MustSetCustSiteException ex)
        {
            lbMessageReApply.Text = $"重送失敗:需指定自訂流程站點。{ex.Message}";
        }
        catch (MustSetGroupSiteException ex)
        {
            lbMessageReApply.Text = $"重送失敗:需指定自選流程站點。{ex.Message}";
        }
        catch (CallExternalDllFlowException ex)
        {
            lbMessageReApply.Text = $"重送失敗:外部流程 DLL 例外。{ex.Message}";
        }
        catch (AccountNoFoundException ex)
        {
            lbMessageReApply.Text = $"重送失敗:找不到帳號。{ex.Message}";
        }
        catch (ExternalDllFormatErrorException ex)
        {
            lbMessageReApply.Text = $"重送失敗:外部 DLL 格式錯誤。{ex.Message}";
        }
        catch (Exception ex)
        {
            lbMessageReApply.Text = $"重送失敗:{ex}";
        }
    }

    /// <summary>
    /// 重送已退簽使用者的TaskID
    /// </summary>
    /// <param name="taskId">退簽的TaskID</param>
    /// <param name="operatorUserGuid">重送者</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private string ReApplyReturnedTaskInternal(string taskId, string operatorUserGuid)
    {
        if (!IsReturnUser(taskId))
        {
            throw new Exception("此申請單尚未退回使用者,無法重送");
        }

        Task task = new Task(taskId);

        if (task == null)
            throw new Exception("找不到 Task");

        if (task.Applicant == null)
            throw new Exception("找不到 Applicant");

        if (task.CurrentSite == null)
            throw new Exception("找不到 CurrentSite");

        // ★ 關鍵:先把目前 Task 轉成重送用暫存 Script
        var returnUco = new ReturnSignUCO();
        returnUco.TransferTaskToTempScript(taskId);

        // ★ 可選:確認 script 真的存在
        ScriptUCO scriptUCO = new ScriptUCO();
        DataTable dtFormVersion = scriptUCO.GetFormVersionInfo(taskId);
        if (dtFormVersion == null || dtFormVersion.Rows.Count == 0)
        {
            throw new Exception("TransferTaskToTempScript 後仍找不到 Script/FormVersion 資料");
        }

        DefinedTask defTask = DefinedTask.GetVirtualDefinedTask(
            taskId,
            task.Applicant.GroupId,
            task.Applicant.JobTitleId
        );

        if (defTask == null)
        {
            throw new Exception("GetVirtualDefinedTask 回傳 null(已做 TransferTaskToTempScript)");
        }

        if (defTask.CurrentDocument != null &&
            defTask.CurrentDocument.AllowSkipped &&
            defTask.BeSkippedSite != null &&
            defTask.BeSkippedSite.Count > 0)
        {
            throw new Exception("此單含略過站點,暫不支援");
        }

        if (defTask.DefinedTaskSiteList != null && defTask.DefinedTaskSiteList.Count > 0)
        {
            throw new Exception("此單為平行站點流程,暫不支援");
        }

        if (defTask.NextDefinedTaskSite == null)
        {
            throw new Exception("找不到下一站點,可能已到流程末端");
        }

        DefinedTaskSite nextSite = defTask.NextDefinedTaskSite;

        if (nextSite.IsCustSite)
            throw new Exception("下一站為自訂流程站點,暫不支援");

        if (nextSite.IsExternalSite)
            throw new Exception("下一站為外部站點,暫不支援");

        if (nextSite.SiteKind == DefinedTaskSiteKind.AssignAbleSite)
            throw new Exception("下一站為指定式站點,暫不支援");

        if (nextSite.SiteKind == DefinedTaskSiteKind.FreeSite)
            throw new Exception("下一站為自由流程站點,暫不支援");

        TempDefinedSiteDataSet tempDs = defTask.TempDefSiteInfoDs;

        if (tempDs == null ||
            tempDs.TB_WKF_TASK_SITE_TEMP == null ||
            tempDs.TB_WKF_TASK_SITE_TEMP.Rows.Count == 0)
        {
            throw new Exception("TempDefinedSiteDataSet 為空");
        }

        var row = (TempDefinedSiteDataSet.TB_WKF_TASK_SITE_TEMPRow)tempDs.TB_WKF_TASK_SITE_TEMP.Rows[0];

        row.IS_PREADDITIONAL_SITE = nextSite.IsPreAdditionalSite;
        row.PREADDITIONAL_FLOW = nextSite.PreAdditionalFlow ?? "";
        row.CUST_TYPE = 0;
        row.IS_MASTER_SITE = row.IsSRC_PARENT_SITE_IDNull();
        row.IS_END_BY_APPLICANT = false;

        row.IS_SAVE_SCRIPT = false;
        row.TIMEOUT_COUNT = nextSite.TimeOutCont;

        int cycleHours = 0;
        int.TryParse(new TaskUtilityUCO().GetCycleAlertHours(task.FormVersionId, nextSite), out cycleHours);
        row.CYCLE_HOURS = cycleHours;

        row.TIMEOUT_SEND_COUNT = nextSite.TimeOutSendCount;
        row.SIGN_TYPE = (int)nextSite.SignType;
        row.SIGNER_USER_SET = AddWKFSignerInfoList(nextSite.AllSignerUserSet);
        row.ALERTER_USER_SET = nextSite.AllAlerterUserSet == null
            ? new UserSet().GetXML()
            : nextSite.AllAlerterUserSet.GetXML();
        row.ADDTIONAL_FLOW_ID = defTask.AddtionalFlowId ?? "";
        row.SOURCE = Source.PC.ToString();

        DefinedTask actualTask = new DefinedTask(taskId, task.CurrentSite.SiteId, 0);
        actualTask.ReApply(tempDs, Source.PC, operatorUserGuid);
        EBUser ub;
        

        return actualTask.TaskId;
    }

}

0 Comments

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *