From 507078d7acb08b89feabc2f2b2d4b4d996a80e36 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Mon, 7 Apr 2025 09:06:58 +0500 Subject: [PATCH] Add auto-start functionality and related triggers for processes (#8478) --- models/process/src/index.ts | 9 ++- models/server-process/package.json | 1 + models/server-process/src/index.ts | 21 ++++++- plugins/process-assets/lang/cs.json | 1 + plugins/process-assets/lang/de.json | 1 + plugins/process-assets/lang/en.json | 1 + plugins/process-assets/lang/es.json | 1 + plugins/process-assets/lang/fr.json | 1 + plugins/process-assets/lang/it.json | 1 + plugins/process-assets/lang/pt.json | 1 + plugins/process-assets/lang/ru.json | 1 + plugins/process-assets/lang/zh.json | 1 + .../src/components/ProcessEditor.svelte | 14 +++++ plugins/process-resources/src/plugin.ts | 1 + plugins/process/src/index.ts | 5 +- server-plugins/process-resources/src/index.ts | 60 +++++++++++++++++++ server-plugins/process/src/index.ts | 2 + 17 files changed, 116 insertions(+), 6 deletions(-) diff --git a/models/process/src/index.ts b/models/process/src/index.ts index f9466bc5e8..65264b8626 100644 --- a/models/process/src/index.ts +++ b/models/process/src/index.ts @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import card, { type Card, type MasterTag } from '@hcengineering/card' +import card, { type Tag, type Card, type MasterTag } from '@hcengineering/card' import contact, { type Employee } from '@hcengineering/contact' import core, { AccountRole, @@ -68,11 +68,14 @@ export class TProcess extends TDoc implements Process { @Prop(TypeString(), core.string.Description) description!: string - @Prop(TypeRef(card.class.MasterTag), core.string.Name) - masterTag!: Ref + @Prop(TypeRef(card.class.MasterTag), card.string.MasterTag) + masterTag!: Ref @Prop(ArrOf(TypeRef(process.class.State)), process.string.States) states!: Ref[] + + @Prop(TypeBoolean(), process.string.StartAutomatically) + autoStart: boolean | undefined } @Model(process.class.Execution, core.class.Doc, DOMAIN_PROCESS) diff --git a/models/server-process/package.json b/models/server-process/package.json index ac7c58ebab..22309a79cb 100644 --- a/models/server-process/package.json +++ b/models/server-process/package.json @@ -33,6 +33,7 @@ "@hcengineering/model": "^0.6.11", "@hcengineering/model-process": "^0.6.0", "@hcengineering/process": "^0.6.0", + "@hcengineering/card": "^0.6.0", "@hcengineering/server-process": "^0.6.0", "@hcengineering/server-core": "^0.6.1" } diff --git a/models/server-process/src/index.ts b/models/server-process/src/index.ts index 785a52bac1..45a2b0bc50 100644 --- a/models/server-process/src/index.ts +++ b/models/server-process/src/index.ts @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import card from '@hcengineering/card' import core, { type Doc } from '@hcengineering/core' import { Mixin, type Builder } from '@hcengineering/model' import { TMethod, TProcessFunction } from '@hcengineering/model-process' @@ -19,8 +20,8 @@ import process from '@hcengineering/process' import serverCore from '@hcengineering/server-core' import serverProcess, { type ExecuteFunc, - type MethodImpl, type FuncImpl, + type MethodImpl, type TransformFunc } from '@hcengineering/server-process' @@ -110,6 +111,24 @@ export function createModel (builder: Builder): void { isAsync: true }) + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverProcess.trigger.OnCardCreate, + txMatch: { + _class: core.class.TxCreateDoc, + objectClass: card.class.Card + }, + isAsync: true + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverProcess.trigger.OnTagAdd, + txMatch: { + _class: core.class.TxMixin, + objectClass: card.class.Card + }, + isAsync: true + }) + builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverProcess.trigger.OnExecutionCreate, txMatch: { diff --git a/plugins/process-assets/lang/cs.json b/plugins/process-assets/lang/cs.json index 02140dd469..c7b239ce68 100644 --- a/plugins/process-assets/lang/cs.json +++ b/plugins/process-assets/lang/cs.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "První pracovní den po", "FallbackValueError": "Zobrazit chybu, pokud není k dispozici", "Required": "Povinné", + "StartAutomatically": "Spustit automaticky", "Error": "Chyba", "Continue": "Pokračovat" }, diff --git a/plugins/process-assets/lang/de.json b/plugins/process-assets/lang/de.json index a6899a05f9..bb43c22cb6 100644 --- a/plugins/process-assets/lang/de.json +++ b/plugins/process-assets/lang/de.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "Erste Arbeitstag nach", "FallbackValueError": "Fehler anzeigen, wenn leer", "Required": "Erforderlich", + "StartAutomatically": "Automatisch starten", "Error": "Fehler", "Continue": "Fortsetzen" }, diff --git a/plugins/process-assets/lang/en.json b/plugins/process-assets/lang/en.json index 719748e947..9dd8ca0b38 100644 --- a/plugins/process-assets/lang/en.json +++ b/plugins/process-assets/lang/en.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "First working day after", "FallbackValueError": "Show error if empty", "Required": "Required", + "StartAutomatically": "Start automatically", "Error": "Error", "Continue": "Continue" }, diff --git a/plugins/process-assets/lang/es.json b/plugins/process-assets/lang/es.json index 1b89e7c041..df7cc3c3c7 100644 --- a/plugins/process-assets/lang/es.json +++ b/plugins/process-assets/lang/es.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "Primer día de trabajo después", "FallbackValueError": "Mostrar error si está vacío", "Required": "Requerido", + "StartAutomatically": "Iniciar automáticamente", "Error": "Error", "Continue": "Continuar" }, diff --git a/plugins/process-assets/lang/fr.json b/plugins/process-assets/lang/fr.json index bef7828326..80f5c1ff8f 100644 --- a/plugins/process-assets/lang/fr.json +++ b/plugins/process-assets/lang/fr.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "Premier jour de travail après", "FallbackValueError": "Afficher l'erreur si vide", "Required": "Requis", + "StartAutomatically": "Démarrer automatiquement", "Error": "Erreur", "Continue": "Continuer" }, diff --git a/plugins/process-assets/lang/it.json b/plugins/process-assets/lang/it.json index 088e354630..c3bdd25318 100644 --- a/plugins/process-assets/lang/it.json +++ b/plugins/process-assets/lang/it.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "Primo giorno lavorativo dopo", "FallbackValueError": "Mostra errore se vuoto", "Required": "Obbligatorio", + "StartAutomatically": "Avvia automaticamente", "Error": "Errore", "Continue": "Continua" }, diff --git a/plugins/process-assets/lang/pt.json b/plugins/process-assets/lang/pt.json index 49660a3896..41d0680fe2 100644 --- a/plugins/process-assets/lang/pt.json +++ b/plugins/process-assets/lang/pt.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "Primeiro Dia de Trabalho Após", "FallbackValueError": "Mostrar erro se vazio", "Required": "Obrigatório", + "StartAutomatically": "Iniciar automaticamente", "Error": "Erro", "Continue": "Continuar" }, diff --git a/plugins/process-assets/lang/ru.json b/plugins/process-assets/lang/ru.json index 1f38ab60fe..724f11484b 100644 --- a/plugins/process-assets/lang/ru.json +++ b/plugins/process-assets/lang/ru.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "Первый рабочий день после", "FallbackValueError": "Показывать ошибку, если недоступно", "Required": "Обязательно", + "StartAutomatically": "Запускать автоматически", "Error": "Ошибка", "Continue": "Продолжить" }, diff --git a/plugins/process-assets/lang/zh.json b/plugins/process-assets/lang/zh.json index b657f8e124..751512ddd6 100644 --- a/plugins/process-assets/lang/zh.json +++ b/plugins/process-assets/lang/zh.json @@ -43,6 +43,7 @@ "FirstWorkingDayAfter": "在之后的第一个工作日", "FallbackValueError": "显示错误,如果为空", "Required": "必需", + "StartAutomatically": "自动启动", "Error": "错误", "Continue": "继续" }, diff --git a/plugins/process-resources/src/components/ProcessEditor.svelte b/plugins/process-resources/src/components/ProcessEditor.svelte index eb247d43f4..ddaeeda058 100644 --- a/plugins/process-resources/src/components/ProcessEditor.svelte +++ b/plugins/process-resources/src/components/ProcessEditor.svelte @@ -29,6 +29,7 @@ IconDescription, navigate, NavItem, + ToggleWithLabel, Scroller, secondNavSeparators, Separator, @@ -77,6 +78,12 @@ } } + async function saveAutoStart (e: CustomEvent): Promise { + if (value !== undefined) { + await client.update(value, { autoStart: e.detail }) + } + } + async function addState (): Promise { if (value === undefined) return const prevState = states[states.length - 1] @@ -160,6 +167,13 @@ +
+ +
{#each sortedStates as state (state._id)} diff --git a/plugins/process-resources/src/plugin.ts b/plugins/process-resources/src/plugin.ts index de09f252c4..a11d307385 100644 --- a/plugins/process-resources/src/plugin.ts +++ b/plugins/process-resources/src/plugin.ts @@ -89,6 +89,7 @@ export default mergeIds(processId, process, { FirstWorkingDayAfter: '' as IntlString, FallbackValueError: '' as IntlString, Required: '' as IntlString, + StartAutomatically: '' as IntlString, Continue: '' as IntlString } }) diff --git a/plugins/process/src/index.ts b/plugins/process/src/index.ts index 7dca55fd00..1128ab02f8 100644 --- a/plugins/process/src/index.ts +++ b/plugins/process/src/index.ts @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Card, MasterTag } from '@hcengineering/card' +import { Card, MasterTag, Tag } from '@hcengineering/card' import { Employee } from '@hcengineering/contact' import { Class, Doc, DocumentUpdate, ObjQueryType, Ref, Tx } from '@hcengineering/core' import { Asset, IntlString, Plugin, plugin } from '@hcengineering/platform' @@ -25,10 +25,11 @@ import { AttributeCategory } from '@hcengineering/view' export const processId = 'process' as Plugin export interface Process extends Doc { - masterTag: Ref + masterTag: Ref name: string description: string states: Ref[] + autoStart?: boolean } export interface Execution extends Doc { diff --git a/server-plugins/process-resources/src/index.ts b/server-plugins/process-resources/src/index.ts index 9dea5c6a58..9a9a55c341 100644 --- a/server-plugins/process-resources/src/index.ts +++ b/server-plugins/process-resources/src/index.ts @@ -25,6 +25,7 @@ import core, { Timestamp, Tx, TxCreateDoc, + TxMixin, TxProcessor, TxRemoveDoc, TxUpdateDoc @@ -635,6 +636,63 @@ export function FirstWorkingDayAfter (val: Timestamp): Timestamp { return val } +export async function OnCardCreate (txes: Tx[], control: TriggerControl): Promise { + const res: Tx[] = [] + for (const tx of txes) { + if (tx._class !== core.class.TxCreateDoc) continue + const createTx = tx as TxCreateDoc + if (!control.hierarchy.isDerived(createTx.objectClass, card.class.Card)) continue + const ancestors = control.hierarchy + .getAncestors(createTx.objectClass) + .filter((p) => control.hierarchy.isDerived(p, card.class.Card)) + + const processes = control.modelDb.findAllSync(process.class.Process, { + masterTag: { $in: ancestors }, + autoStart: true + }) + for (const proc of processes) { + res.push( + control.txFactory.createTxCreateDoc(process.class.Execution, core.space.Workspace, { + process: proc._id, + currentState: null, + card: createTx.objectId, + done: false, + rollback: {}, + currentToDo: null, + assignee: null + }) + ) + } + } + return res +} + +export async function OnTagAdd (txes: Tx[], control: TriggerControl): Promise { + const res: Tx[] = [] + for (const tx of txes) { + if (tx._class !== core.class.TxMixin) continue + const mixinTx = tx as TxMixin + if (!control.hierarchy.isDerived(mixinTx.objectClass, card.class.Card)) continue + if (Object.keys(mixinTx.attributes).length !== 0) continue + + const processes = control.modelDb.findAllSync(process.class.Process, { masterTag: mixinTx.mixin, autoStart: true }) + for (const proc of processes) { + res.push( + control.txFactory.createTxCreateDoc(process.class.Execution, core.space.Workspace, { + process: proc._id, + currentState: null, + card: mixinTx.objectId, + done: false, + rollback: {}, + currentToDo: null, + assignee: null + }) + ) + } + } + return res +} + export async function OnExecutionContinue (txes: Tx[], control: TriggerControl): Promise { const res: Tx[] = [] for (const tx of txes) { @@ -679,6 +737,8 @@ export default async () => ({ FirstWorkingDayAfter }, trigger: { + OnCardCreate, + OnTagAdd, OnExecutionCreate, OnStateRemove, OnProcessRemove, diff --git a/server-plugins/process/src/index.ts b/server-plugins/process/src/index.ts index 1bbe3cf813..69ba740a0b 100644 --- a/server-plugins/process/src/index.ts +++ b/server-plugins/process/src/index.ts @@ -46,6 +46,8 @@ export default plugin(serverProcessId, { FirstWorkingDayAfter: '' as Resource }, trigger: { + OnCardCreate: '' as Resource, + OnTagAdd: '' as Resource, OnExecutionCreate: '' as Resource, OnStateRemove: '' as Resource, OnProcessRemove: '' as Resource,