using System.Collections.Generic; using System.Data.Common; using System.Threading.Tasks; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.WorkFlow; using static GB5Shared.DTO.WorkFlow.ContractsDTO; namespace GB5Shared.WorkFlow.WorkFlowEngine { public interface IWorkFlowEngine { /// /// Facts key injected by EventHandler when login.WipApprovalId > 0. /// When this key is true, CheckWorkFlowApplicability returns WORKFLOW.NONE, /// allowing the entity to be saved directly without triggering WIP again. /// const string WipApprovalFactKey = "__WipApproval"; // ── Applicability check (primary — uses MWORKFLOWCONFIG) ────────────── /// /// Checks MWORKFLOWCONFIG to determine whether a workflow applies /// for the given context, and returns the resolved workflow id. /// /// Steps: /// 1. Query MWORKFLOWCONFIG filtered by EntityId / ClientId / OUId / /// BizTransactionClassId / BizTransactionId (wildcards via -1). /// 2. For each candidate row (most-specific first) evaluate EVALCONDITION /// against . /// 3. Return the first match. /// 4. If Facts["__WipApproval"] == true, returns WORKFLOW.NONE immediately /// (WIP approval callback — skip all workflow checks). /// Task CheckWorkFlowApplicability( WorkflowCheckContext context, LoginDTO login, DbTransaction tx); // ── Start workflow ──────────────────────────────────────────────────── /// /// For REGULAR: creates a workflow instance and tasks for the first approval level. /// For WIP (IsFormBasedApproval=true): inserts TWORKFLOWWIP, then creates the /// instance with ObjectId=WipId, then enters the first approval level. /// Returns a WorkflowStartResult describing what was started. /// Task StartWorkflowAsync( WorkflowStartRequest request, LoginDTO login, DbTransaction tx); // ── Action handling ─────────────────────────────────────────────────── /// /// Processes one or more approver actions (Approve / Reject / Return / Escalate). /// When all tasks at the current approval level are approved the engine /// automatically advances to the next level and creates tasks there. /// When no further levels exist the workflow is completed. /// For WIP instances at final approval: updates TWORKFLOWWIP.STATUS=Approved and /// returns WipDispatchInfo in WorkflowActionResult.PendingDispatches. /// Caller MUST dispatch after committing the transaction. /// Task HandleActionsAsync( IEnumerable actions, LoginDTO login, DbTransaction tx); // ── Cancel on delete / cancel on resubmit ───────────────────────────── /// /// Cancels any real pending (WORKFLOWSTATUS=0) workflow instance for the given /// entity+object — used when the underlying record is deleted, or is about to be /// resubmitted after an edit while its prior approval is still pending. Cancels the /// instance's TWORKFLOWTASK rows, the instance itself, its linked TWORKFLOWWIP row /// (if any), and writes one TWORKFLOWHISTORY entry (Action=Cancel) per cancelled /// instance. Safe no-op (returns 0) when nothing is pending, e.g. for a new record. /// Task CancelPendingInstanceAsync( int entityId, int objectId, string reason, LoginDTO login, DbTransaction tx); // ── WIP post-dispatch update ───────────────────────────────────────── /// /// Called by EventHandler after the WIP approval callback saves the entity. /// Writes the real saved entity ID (e.g. TaskId) into TWORKFLOWWIP.OBJECTID /// while still inside the same transaction — so the update is atomic with the /// entity save and rolls back together if the BLL transaction later fails. /// Task UpdateWipObjectIdAsync( int wipId, int objectId, LoginDTO login, DbTransaction tx); // ── Auto-approve ───────────────────────────────────────────────────── /// /// Returns all pending tasks whose DUEON has passed and whose step /// has ISAUTOAPPROVE = 0 (0=Yes). Called by the background auto-approve job. /// Task> GetOverdueAutoApproveTasksAsync( LoginDTO login, DbTransaction tx); // ── Delegation ──────────────────────────────────────────────────────── /// /// Resolves the effective approver: if the principal has an active delegation /// record the delegate user-id is returned; otherwise the principal id is used. /// Task ResolveEffectiveUserAsync( long userId, System.DateTime at, LoginDTO login, DbTransaction tx); } public interface IUserDelegationResolver { Task> GetAsync( long userId, LoginDTO login, DbTransaction tx); } }