Fix processes triggers (On card update, on subprocesses done) (#9841)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-09-12 15:12:05 +07:00
committed by GitHub
parent 6fcf58574e
commit fb95ad6ce7
12 changed files with 91 additions and 44 deletions
+1
View File
@@ -1118,6 +1118,7 @@ export function createModel (builder: Builder): void {
{
label: process.string.OnSubProcessesDone,
icon: process.icon.WaitSubprocesses,
checkFunction: process.triggerCheck.SubProcessesDoneCheck,
requiredParams: [],
init: false,
auto: true
+5 -3
View File
@@ -16,7 +16,7 @@ import core, { type Doc } from '@hcengineering/core'
import { Mixin, type Builder } from '@hcengineering/model'
import { TMethod, TProcessFunction, TTrigger } from '@hcengineering/model-process'
import type { Resource } from '@hcengineering/platform'
import process, { ExecutionStatus, type CheckFunc } from '@hcengineering/process'
import process, { ExecutionStatus } from '@hcengineering/process'
import serverCore from '@hcengineering/server-core'
import serverProcess, {
type RollbackFunc,
@@ -24,7 +24,8 @@ import serverProcess, {
type FuncImpl,
type MethodImpl,
type TransformFunc,
type TriggerImpl
type TriggerImpl,
type CheckFunc
} from '@hcengineering/server-process'
export { serverProcessId } from '@hcengineering/server-process'
@@ -68,7 +69,8 @@ export function createModel (builder: Builder): void {
})
builder.mixin(process.trigger.OnSubProcessesDone, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
preventRollback: true
preventRollback: true,
serverCheckFunc: serverProcess.func.CheckSubProcessesDone
})
builder.mixin(process.method.RunSubProcess, process.class.Method, serverProcess.mixin.MethodImpl, {
@@ -78,6 +78,10 @@
items={triggersItems}
bind:selected={selectedTrigger}
label={plugin.string.Trigger}
on:change={() => {
params = {}
result = null
}}
justify={'left'}
width={'100%'}
kind={'no-border'}
+3 -1
View File
@@ -80,7 +80,8 @@ import {
showDoneQuery,
timeTransitionCheck,
todoTranstionCheck,
updateCardTranstionCheck
updateCardTranstionCheck,
subProcessesDoneCheck
} from './utils'
export * from './query'
@@ -160,6 +161,7 @@ export default async (): Promise<Resources> => ({
},
triggerCheck: {
UpdateCheck: updateCardTranstionCheck,
SubProcessesDoneCheck: subProcessesDoneCheck,
ToDo: todoTranstionCheck,
Time: timeTransitionCheck
},
+2 -14
View File
@@ -88,13 +88,7 @@ export class ProcessMiddleware extends BasePresentationMiddleware implements Pre
from: execution.currentState,
trigger: process.trigger.OnCardUpdate
})
const transition = await pickTransition(
this.client.getModel(),
this.client.getHierarchy(),
execution,
transitions,
updated
)
const transition = await pickTransition(this.client, execution, transitions, updated)
if (transition === undefined) return
const context = await getNextStateUserInput(execution, transition, execution.context)
const txop = new TxOperations(this.client, getCurrentAccount().primarySocialId)
@@ -158,13 +152,7 @@ export class ProcessMiddleware extends BasePresentationMiddleware implements Pre
from: execution.currentState,
trigger: process.trigger.OnToDoClose
})
const transition = await pickTransition(
this.client.getModel(),
this.client.getHierarchy(),
execution,
transitions,
todo
)
const transition = await pickTransition(this.client, execution, transitions, todo)
if (transition === undefined) return
const context = await getNextStateUserInput(execution, transition, execution.context)
const txop = new TxOperations(this.client, getCurrentAccount().primarySocialId)
+3 -3
View File
@@ -149,7 +149,7 @@ export function parseValue (modes: Mode[], value: any): [any, Mode] {
if (mode.parse !== undefined) {
return [mode.parse(obj2[key2]), mode]
}
return [obj2[key2], mode]
return [mode.withoutEditor === true ? undefined : obj2[key2], mode]
}
obj1 = obj1[key1]
obj2 = obj2[key2]
@@ -168,10 +168,10 @@ export function buildResult (mode: Mode, value: any): any {
const key = Object.keys(q)[0]
const v: any = (q as any)[key]
if (typeof v !== 'object') {
if (typeof value === 'string') {
if (typeof value === 'string' && typeof v === 'string') {
res[key] = v.replace('$val', value)
} else {
res[key] = value
res[key] = mode.withoutEditor === true ? v : value ?? v
}
return result
}
+35 -10
View File
@@ -21,9 +21,7 @@ import core, {
type Doc,
type DocumentQuery,
generateId,
type Hierarchy,
matchQuery,
type ModelDb,
type Ref,
type RefTo,
type Space,
@@ -110,20 +108,19 @@ export function getContextMasterTag (
}
export async function pickTransition (
model: ModelDb,
hierarchy: Hierarchy,
client: Client,
execution: Execution,
transitions: Transition[],
doc: Doc
): Promise<Transition | undefined> {
for (const tr of transitions) {
const trigger = model.findObject(tr.trigger)
const trigger = client.getModel().findObject(tr.trigger)
if (trigger === undefined) continue
if (trigger.checkFunction === undefined) return tr
const filled = fillParams(tr.triggerParams, execution)
const checkFunc = await getResource(trigger.checkFunction)
if (checkFunc === undefined) continue
const res = await checkFunc(filled, doc, hierarchy)
const res = await checkFunc(client, execution, filled, doc)
if (res) return tr
}
}
@@ -596,21 +593,49 @@ export async function requestResult (
})
}
export function todoTranstionCheck (params: Record<string, any>, doc: Doc): boolean {
export function todoTranstionCheck (
client: Client,
execution: Execution,
params: Record<string, any>,
doc: Doc
): boolean {
if (params._id === undefined) return false
return doc._id === params._id
}
export function timeTransitionCheck (params: Record<string, any>): boolean {
export function timeTransitionCheck (
client: Client,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>
): boolean {
if (params.value === undefined) return false
return params.value <= Date.now()
}
export function updateCardTranstionCheck (params: Record<string, any>, doc: Doc, hierarchy: Hierarchy): boolean {
const res = matchQuery([doc], params, doc._class, hierarchy, true)
export function updateCardTranstionCheck (
client: Client,
execution: Execution,
params: Record<string, any>,
doc: Doc
): boolean {
const res = matchQuery([doc], params, doc._class, client.getHierarchy(), true)
return res.length > 0
}
export async function subProcessesDoneCheck (
client: Client,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>
): Promise<boolean> {
const res = client.findOne(process.class.Execution, {
parentId: execution._id,
status: ExecutionStatus.Active
})
return res === undefined
}
export function getCirteriaEditor (of: Ref<Class<Doc>>, category: AttributeCategory): AnyComponent | undefined {
const client = getClient()
if (category !== 'attribute') {
+5 -3
View File
@@ -12,7 +12,7 @@
// limitations under the License.
import { Card, MasterTag, Tag } from '@hcengineering/card'
import { Association, Class, Doc, DocumentUpdate, Hierarchy, ObjQueryType, Ref, Tx, Type } from '@hcengineering/core'
import { Association, Class, Client, Doc, DocumentUpdate, ObjQueryType, Ref, Tx, Type } from '@hcengineering/core'
import { Asset, IntlString, Plugin, plugin, Resource } from '@hcengineering/platform'
import { ToDo } from '@hcengineering/time'
import { AnyComponent } from '@hcengineering/ui'
@@ -84,9 +84,10 @@ export enum ExecutionLogAction {
}
export type CheckFunc = (
client: Client,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>,
hierarchy: Hierarchy
context: Record<string, any>
) => Promise<boolean>
export enum ExecutionStatus {
@@ -216,6 +217,7 @@ export default plugin(processId, {
triggerCheck: {
ToDo: '' as Resource<CheckFunc>,
UpdateCheck: '' as Resource<CheckFunc>,
SubProcessesDoneCheck: '' as Resource<CheckFunc>,
Time: '' as Resource<CheckFunc>
},
string: {
@@ -14,7 +14,7 @@
//
import cardPlugin, { Card, MasterTag, Tag } from '@hcengineering/card'
import core, { Association, Data, Doc, generateId, Hierarchy, matchQuery, Ref, Relation, Tx } from '@hcengineering/core'
import core, { Association, Data, Doc, generateId, matchQuery, Ref, Relation, Tx } from '@hcengineering/core'
import process, {
Execution,
ExecutionContext,
@@ -27,23 +27,37 @@ import process, {
import { ExecuteResult, ProcessControl } from '@hcengineering/server-process'
import time, { ToDoPriority } from '@hcengineering/time'
export function CheckToDo (params: Record<string, any>, context: Record<string, any>): boolean {
export function CheckToDo (
control: ProcessControl,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>
): boolean {
if (params._id === undefined) return false
if (context.todo === undefined) return false
return context.todo._id === params._id
}
export async function CheckSubProcessesDone (control: ProcessControl, execution: Execution): Promise<boolean> {
const res = control.client.findOne(process.class.Execution, {
parentId: execution._id,
status: ExecutionStatus.Active
})
return res === undefined
}
export function OnCardUpdateCheck (
control: ProcessControl,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>,
hierarchy: Hierarchy
context: Record<string, any>
): boolean {
if (context.card === undefined) return false
const res = matchQuery([context.card], params, context.card._class, hierarchy, true)
const res = matchQuery([context.card], params, context.card._class, control.client.getHierarchy(), true)
return res.length > 0
}
export function CheckTime (params: Record<string, any>): boolean {
export function CheckTime (control: ProcessControl, execution: Execution, params: Record<string, any>): boolean {
if (params.value === undefined) return false
return params.value <= Date.now()
}
@@ -80,6 +80,7 @@ import {
AddTag,
CheckToDo,
OnCardUpdateCheck,
CheckSubProcessesDone,
CheckTime
} from './functions'
import { ToDoCancellRollback, ToDoCloseRollback } from './rollback'
@@ -356,6 +357,7 @@ export default async () => ({
AddTag,
CheckToDo,
OnCardUpdateCheck,
CheckSubProcessesDone,
CheckTime
},
transform: {
+10 -2
View File
@@ -1,9 +1,9 @@
import { Doc, Mixin, Ref } from '@hcengineering/core'
import type { Plugin, Resource } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import { CheckFunc, Method, ProcessFunction, Trigger } from '@hcengineering/process'
import { Execution, Method, ProcessFunction, Trigger } from '@hcengineering/process'
import { TriggerFunc } from '@hcengineering/server-core'
import { ExecuteFunc, RollbackFunc, TransformFunc } from './types'
import { ExecuteFunc, ProcessControl, RollbackFunc, TransformFunc } from './types'
export * from './types'
@@ -12,6 +12,13 @@ export * from './types'
*/
export const serverProcessId = 'server-process' as Plugin
export type CheckFunc = (
control: ProcessControl,
execution: Execution,
params: Record<string, any>,
context: Record<string, any>
) => Promise<boolean>
export interface TriggerImpl extends Trigger {
serverCheckFunc?: Resource<CheckFunc>
rollbackFunc?: Resource<RollbackFunc>
@@ -49,6 +56,7 @@ export default plugin(serverProcessId, {
AddTag: '' as Resource<ExecuteFunc>,
CheckToDo: '' as Resource<CheckFunc>,
OnCardUpdateCheck: '' as Resource<CheckFunc>,
CheckSubProcessesDone: '' as Resource<CheckFunc>,
CheckTime: '' as Resource<CheckFunc>
},
transform: {
+1 -2
View File
@@ -529,13 +529,12 @@ export async function pickTransition (
for (const tr of transitions) {
const trigger = control.client.getModel().findObject(tr.trigger)
if (trigger === undefined) continue
if (trigger.checkFunction === undefined) return tr
const impl = control.client.getHierarchy().as(trigger, serverProcess.mixin.TriggerImpl)
if (impl?.serverCheckFunc === undefined) return tr
const filled = await fillParams(tr.triggerParams, execution, control)
const checkFunc = await getResource(impl.serverCheckFunc)
if (checkFunc === undefined) continue
const res = await checkFunc(filled, context, control.client.getHierarchy())
const res = await checkFunc(control, execution, filled, context)
if (res) return tr
}
}