feat: add OnExecutionDone trigger and related functionality for proce… (#10944)

* feat: add OnExecutionDone trigger and related functionality for process management

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>

* Fix

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>

---------

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2026-07-02 04:14:58 +05:00
committed by GitHub
parent 65cb5987bf
commit ae3b7f6216
5 changed files with 67 additions and 7 deletions
+11
View File
@@ -419,6 +419,17 @@ export function createModel (builder: Builder): void {
},
isAsync: true
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverProcess.trigger.OnExecutionDone,
txMatch: {
_class: core.class.TxUpdateDoc,
objectClass: process.class.Execution,
'operations.status': ExecutionStatus.Done
},
isAsync: true
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverProcess.trigger.OnExecutionCreate,
txMatch: {
@@ -17,6 +17,9 @@
import { Card } from '@hcengineering/card'
import { Attachments } from '@hcengineering/attachment-resources'
import { createEventDispatcher, onMount } from 'svelte'
import { getClient } from '@hcengineering/presentation'
import { canChangeDoc } from '@hcengineering/view-resources'
import { permissionsStore } from '@hcengineering/contact-resources'
export let readonly: boolean = false
export let doc: Card
@@ -29,6 +32,11 @@
dispatch('loaded')
}
})
const client = getClient()
const h = client.getHierarchy()
$: updatePermissionForbidden = doc && !canChangeDoc(doc?._class, doc?.space, $permissionsStore)
</script>
{#if !hidden}
@@ -38,7 +46,9 @@
_class={doc._class}
space={doc.space}
attachments={doc.attachments ?? 0}
{readonly}
readonly={readonly ||
updatePermissionForbidden ||
h.getAncestors(doc._class).some((p) => doc.readonlySections?.includes(p))}
on:attachments={() => dispatch('loaded')}
/>
</div>
@@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Card } from '@hcengineering/card'
import { Card, MasterTag, Tag } from '@hcengineering/card'
import { Class, Doc, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Process } from '@hcengineering/process'
@@ -34,13 +34,21 @@
const res = client.getModel().findAllSync(process.class.Process, {})
const processes = res.filter((it) => resClasses.includes(it.masterTag) && it.automationOnly !== true)
function getCardPossibleClasses (card: Card): Ref<Class<Doc>>[] {
const asc = h.getAncestors(card._class)
const mixins = h.getDescendants(card._class).filter((p) => h.hasMixin(card, p))
return [...asc, ...mixins]
type PossibleProcessClass = Ref<MasterTag | Tag>
function getCardPossibleClasses (card: Card): PossibleProcessClass[] {
const ancestors = h.getAncestors(card._class) as PossibleProcessClass[]
const res = new Set<PossibleProcessClass>(ancestors)
const mixins = h.getAllPossibleMixins(card._class).filter((mixin) => h.hasMixin(card, mixin))
for (const mixin of mixins) {
res.add(mixin)
}
return [...res]
}
function getPossibleClasses (): Ref<Class<Doc>>[] {
function getPossibleClasses (): PossibleProcessClass[] {
if (!value) {
return []
}
@@ -52,6 +52,7 @@ import process, {
} from '@hcengineering/process'
import { QueueTopic, TriggerControl } from '@hcengineering/server-core'
import { ProcessMessage } from '@hcengineering/server-process'
import time from '@hcengineering/time'
import {
AddRelation,
AddTag,
@@ -304,6 +305,34 @@ export async function OnExecutionContinue (txes: Tx[], control: TriggerControl):
return []
}
export async function OnExecutionDone (txes: Tx[], control: TriggerControl): Promise<Tx[]> {
const res: Tx[] = []
for (const tx of txes) {
if (tx._class !== core.class.TxUpdateDoc) continue
if (tx.space !== core.space.Tx) continue
const updateTx = tx as TxUpdateDoc<Execution>
if (!control.hierarchy.isDerived(updateTx.objectClass, process.class.Execution)) continue
if (updateTx.operations.status !== ExecutionStatus.Done) continue
const todos = await control.findAll(control.ctx, process.class.ProcessToDo, {
execution: updateTx.objectId,
doneOn: null
})
if (todos.length === 0) continue
const workslots = await control.findAll(control.ctx, time.class.WorkSlot, {
attachedTo: { $in: todos.map((todo) => todo._id) }
})
const todosWithWorkslots = new Set(workslots.map((workslot) => workslot.attachedTo as string))
for (const todo of todos) {
if (todosWithWorkslots.has(todo._id as string)) continue
res.push(control.txFactory.createTxRemoveDoc(todo._class, todo.space, todo._id))
}
}
return res
}
export async function OnProcessRemove (txes: Tx[], control: TriggerControl): Promise<Tx[]> {
const res: Tx[] = []
for (const tx of txes) {
@@ -825,6 +854,7 @@ export default async () => ({
OnProcessToDoClose,
OnProcessToDoRemove,
OnExecutionContinue,
OnExecutionDone,
OnCustomEvent,
OnExecutionRemove,
OnCardCreate,
+1
View File
@@ -147,6 +147,7 @@ export default plugin(serverProcessId, {
OnProcessToDoRemove: '' as Resource<TriggerFunc>,
OnExecutionContinue: '' as Resource<TriggerFunc>,
OnCustomEvent: '' as Resource<TriggerFunc>,
OnExecutionDone: '' as Resource<TriggerFunc>,
OnExecutionRemove: '' as Resource<TriggerFunc>
}
})