mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Add subprocess match trigger (#10101)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -94,6 +94,22 @@ export function defineTriggers (builder: Builder): void {
|
||||
process.trigger.OnSubProcessesDone
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.WhenSubProcessMatches,
|
||||
icon: process.icon.WaitSubprocesses,
|
||||
editor: process.component.SubProcessMatchEditor,
|
||||
presenter: process.component.SubProcessMatchPresenter,
|
||||
checkFunction: process.triggerCheck.SubProcessMatchCheck,
|
||||
requiredParams: ['process'],
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.OnSubProcessMatch
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
|
||||
@@ -77,6 +77,11 @@ export function createModel (builder: Builder): void {
|
||||
serverCheckFunc: serverProcess.func.CheckSubProcessesDone
|
||||
})
|
||||
|
||||
builder.mixin(process.trigger.OnSubProcessMatch, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
|
||||
preventRollback: true,
|
||||
serverCheckFunc: serverProcess.func.CheckSubProcessMatch
|
||||
})
|
||||
|
||||
builder.mixin(process.method.RunSubProcess, process.class.Method, serverProcess.mixin.MethodImpl, {
|
||||
func: serverProcess.func.RunSubProcess
|
||||
})
|
||||
|
||||
@@ -742,6 +742,10 @@ export function findAttributeEditorByAttribute (client: Client, attribute: AnyAt
|
||||
const hierarchy = client.getHierarchy()
|
||||
if (attribute === undefined) return
|
||||
|
||||
if (attribute.editor != null) {
|
||||
return attribute.editor
|
||||
}
|
||||
|
||||
if (attribute.type._class === core.class.TypeAny) {
|
||||
const _type: TypeAny = attribute.type as TypeAny<AnyComponent>
|
||||
return _type.editor ?? _type.presenter
|
||||
@@ -769,9 +773,6 @@ export function findAttributeEditorByAttribute (client: Client, attribute: AnyAt
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.editor != null) {
|
||||
return attribute.editor
|
||||
}
|
||||
const editorMixin = hierarchy.classHierarchyMixin(presenterClass.attrClass, mixin)
|
||||
|
||||
if (editorMixin?.inlineEditor === undefined) {
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"EnterValue": "Zadat hodnotu",
|
||||
"OnToDoDone": "Při dokončení akční položky",
|
||||
"OnSubProcessesDone": "Při dokončení podprocesů",
|
||||
"WhenSubProcessMatches": "Když podproces odpovídá podmínce",
|
||||
"Result": "Výsledek",
|
||||
"NoResultRequired": "Není vyžadován žádný výsledek",
|
||||
"RequestResult": "Požádat o výsledek",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"EnterValue": "Wert eingeben",
|
||||
"OnToDoDone": "Beim Erledigen von Action Item",
|
||||
"OnSubProcessesDone": "Beim Abschluss von Unterprozessen",
|
||||
"WhenSubProcessMatches": "Wenn Unterprozess übereinstimmt",
|
||||
"Result": "Ergebnis",
|
||||
"NoResultRequired": "Kein Ergebnis erforderlich",
|
||||
"RequestResult": "Ergebnis anfordern",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"EnterValue": "Enter value",
|
||||
"OnToDoDone": "When action item done",
|
||||
"OnSubProcessesDone": "On sub processes done",
|
||||
"WhenSubProcessMatches": "When subprocess matches",
|
||||
"Result": "Result",
|
||||
"RequestResult": "Request result",
|
||||
"NoResultRequired": "No result required",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "Ingrese un valor",
|
||||
"OnToDoDone": "Al completar la acción",
|
||||
"OnSubProcessesDone": "Al finalizar los subprocesos",
|
||||
"WhenSubProcessMatches": "Cuando el subproceso coincide",
|
||||
"Result": "Resultado",
|
||||
"RequestResult": "Solicitar resultado",
|
||||
"NoResultRequired": "No se requiere resultado",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "Saisir une valeur",
|
||||
"OnToDoDone": "Lors de l'achèvement de l'action",
|
||||
"OnSubProcessesDone": "Au fin des sous-processus",
|
||||
"WhenSubProcessMatches": "Lorsque le sous-processus correspond",
|
||||
"Result": "Résultat",
|
||||
"RequestResult": "Demander le résultat",
|
||||
"NoResultRequired": "Aucun résultat requis",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "Inserisci valore",
|
||||
"OnToDoDone": "Quando l'azione è completata",
|
||||
"OnSubProcessesDone": "Alla fine dei sottoprocessi",
|
||||
"WhenSubProcessMatches": "Quando il sottoprocesso corrisponde",
|
||||
"Result": "Risultato",
|
||||
"RequestResult": "Richiedi risultato",
|
||||
"NoResultRequired": "Nessun risultato richiesto",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "値を入力",
|
||||
"OnToDoDone": "アクション完了時",
|
||||
"OnSubProcessesDone": "サブプロセス完了時",
|
||||
"WhenSubProcessMatches": "サブプロセスが条件に一致したとき",
|
||||
"RequestResult": "結果をリクエスト",
|
||||
"NoResultRequired": "結果は不要",
|
||||
"From": "から",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "Insira valor",
|
||||
"OnToDoDone": "Ao completar a ação",
|
||||
"OnSubProcessesDone": "Ao concluir processos sub",
|
||||
"WhenSubProcessMatches": "Quando o subprocesso corresponde",
|
||||
"Result": "Resultado",
|
||||
"RequestResult": "Solicitar resultado",
|
||||
"NoResultRequired": "Nenhum resultado requerido",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "Введите значение",
|
||||
"OnToDoDone": "При завершении задачи",
|
||||
"OnSubProcessesDone": "При завершении подпроцессов",
|
||||
"WhenSubProcessMatches": "Когда подпроцесс соответствует условию",
|
||||
"Result": "Результат",
|
||||
"NoResultRequired": "Результат не требуется",
|
||||
"RequestResult": "Запросить результат",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"EnterValue": "Değer gir",
|
||||
"OnToDoDone": "Eylem öğesi tamamlandığında",
|
||||
"OnSubProcessesDone": "Alt süreçler tamamlandığında",
|
||||
"WhenSubProcessMatches": "Alt süreç eşleştiğinde",
|
||||
"Result": "Sonuç",
|
||||
"RequestResult": "Sonuç talep et",
|
||||
"NoResultRequired": "Sonuç gerekmez",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"EnterValue": "输入值",
|
||||
"OnToDoDone": "任务完成时",
|
||||
"OnSubProcessesDone": "子进程完成时",
|
||||
"WhenSubProcessMatches": "当子进程匹配条件时",
|
||||
"Result": "结果",
|
||||
"RequestResult": "请求结果",
|
||||
"NoResultRequired": "无需结果",
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
<script lang="ts">
|
||||
import { AnyAttribute } from '@hcengineering/core'
|
||||
import { Context, Process } from '@hcengineering/process'
|
||||
import { Component, DropdownLabelsIntl } from '@hcengineering/ui'
|
||||
import { Component } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { buildResult, Mode, ModeId, Modes, parseValue } from '../../query'
|
||||
import BaseCriteriaEditor from './BaseCriteriaEditor.svelte'
|
||||
import ModeSelector from './ModeSelector.svelte'
|
||||
|
||||
export let readonly: boolean
|
||||
export let value: any
|
||||
@@ -32,7 +33,6 @@
|
||||
|
||||
let [val, selectedMode] = parseValue(modesValues, value)
|
||||
|
||||
$: mode = selectedMode.id
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: [val, selectedMode] = parseValue(modesValues, value)
|
||||
@@ -42,27 +42,18 @@
|
||||
value = result
|
||||
dispatch('change', result)
|
||||
}
|
||||
|
||||
function changeMode (e: CustomEvent): void {
|
||||
const editorChanged = e.detail
|
||||
if (editorChanged) {
|
||||
val = undefined
|
||||
}
|
||||
changeResult(val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center flex-gap-4">
|
||||
<DropdownLabelsIntl
|
||||
items={modesValues}
|
||||
selected={mode}
|
||||
disabled={readonly}
|
||||
minW0={false}
|
||||
kind={'no-border'}
|
||||
width={'100%'}
|
||||
on:selected={(e) => {
|
||||
mode = e.detail
|
||||
const prevEditor = (selectedMode.editor ?? selectedMode.withoutEditor) ? null : undefined
|
||||
selectedMode = modesValues.find((m) => m.id === mode) ?? modesValues[0]
|
||||
const newEditor = (selectedMode.editor ?? selectedMode.withoutEditor) ? null : undefined
|
||||
if (prevEditor !== newEditor) {
|
||||
val = undefined
|
||||
}
|
||||
changeResult(val)
|
||||
}}
|
||||
/>
|
||||
<ModeSelector bind:selectedMode modes={modesValues} {readonly} on:change={changeMode} />
|
||||
{#if selectedMode.editor}
|
||||
<div class="w-full">
|
||||
<Component
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { DropdownLabelsIntl } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Mode } from '../../query'
|
||||
|
||||
export let selectedMode: Mode
|
||||
export let readonly: boolean
|
||||
export let modes: Mode[] = []
|
||||
|
||||
$: mode = selectedMode.id
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<DropdownLabelsIntl
|
||||
items={modes}
|
||||
selected={mode}
|
||||
disabled={readonly}
|
||||
minW0={false}
|
||||
kind={'no-border'}
|
||||
width={'100%'}
|
||||
on:selected={(e) => {
|
||||
mode = e.detail
|
||||
const prevEditor = (selectedMode.editor ?? selectedMode.withoutEditor) ? null : undefined
|
||||
selectedMode = modes.find((m) => m.id === mode) ?? modes[0]
|
||||
const newEditor = (selectedMode.editor ?? selectedMode.withoutEditor) ? null : undefined
|
||||
dispatch('change', prevEditor !== newEditor)
|
||||
}}
|
||||
/>
|
||||
@@ -0,0 +1,73 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Process, State } from '@hcengineering/process'
|
||||
import { Dropdown, DropdownLabels } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import plugin from '../../plugin'
|
||||
import { buildResult, Modes, parseValue } from '../../query'
|
||||
import ModeSelector from './ModeSelector.svelte'
|
||||
import { ObjectBox } from '@hcengineering/view-resources'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
|
||||
export let readonly: boolean
|
||||
export let target: Ref<Process> | undefined
|
||||
export let value: any
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const modes = [Modes.ArrayAll, Modes.ArrayAny, Modes.ArrayNotIncludes]
|
||||
|
||||
let [val, selectedMode] = parseValue(modes, value)
|
||||
$: [val, selectedMode] = parseValue(modes, value)
|
||||
|
||||
function changeMode (): void {
|
||||
value = buildResult(selectedMode, val)
|
||||
dispatch('change', value)
|
||||
}
|
||||
|
||||
function change (e: CustomEvent): void {
|
||||
val = e.detail ?? []
|
||||
value = buildResult(selectedMode, val)
|
||||
dispatch('change', value)
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
let states: State[] = []
|
||||
$: query.query(
|
||||
plugin.class.State,
|
||||
{ process: target },
|
||||
(items) => {
|
||||
states = items
|
||||
},
|
||||
{ sort: { rank: SortingOrder.Ascending } }
|
||||
)
|
||||
|
||||
$: selected = states.filter((state) => val?.includes(state._id))?.map((p) => p._id)
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center flex-gap-4">
|
||||
<ModeSelector bind:selectedMode {modes} {readonly} on:change={changeMode} />
|
||||
<DropdownLabels
|
||||
useFlexGrow
|
||||
width={'100%'}
|
||||
multiselect={true}
|
||||
items={states.map((state) => ({ id: state._id, label: state.title }))}
|
||||
{selected}
|
||||
on:selected={change}
|
||||
/>
|
||||
</div>
|
||||
@@ -0,0 +1,106 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Process, State } from '@hcengineering/process'
|
||||
import { DropdownLabels, Label } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import plugin from '../../plugin'
|
||||
import { ModeId, Modes } from '../../query'
|
||||
import { getContext } from '../../utils'
|
||||
import BaseCriteria from '../criterias/BaseCriteria.svelte'
|
||||
import SubProcessStateCriteria from '../criterias/SubProcessStateCriteria.svelte'
|
||||
|
||||
export let readonly: boolean
|
||||
export let process: Process
|
||||
export let params: Record<string, any>
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
$: possibleProcesses = getPossibleProcesses(process)
|
||||
|
||||
function getPossibleProcesses (proc: Process): Ref<Process>[] {
|
||||
const res = new Set<Ref<Process>>()
|
||||
const transitions = client.getModel().findAllSync(plugin.class.Transition, { process: proc._id })
|
||||
for (const transition of transitions) {
|
||||
for (const action of transition.actions ?? []) {
|
||||
if (action.methodId === plugin.method.RunSubProcess) {
|
||||
if (action.params._id) {
|
||||
res.add(action.params._id as Ref<Process>)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...res]
|
||||
}
|
||||
|
||||
$: processes = client.getModel().findAllSync(plugin.class.Process, { _id: { $in: possibleProcesses } })
|
||||
|
||||
$: items = processes.map((it) => ({
|
||||
id: it._id,
|
||||
label: it.name
|
||||
}))
|
||||
|
||||
let selectedProcessId: Ref<Process> | undefined = params.process
|
||||
$: selectedProcessId = params.process
|
||||
|
||||
$: selected = selectedProcessId !== undefined ? items.find((it) => it.id === selectedProcessId)?.id : undefined
|
||||
|
||||
function changeProcess (e: CustomEvent<any>): void {
|
||||
if (readonly || e.detail == null) return
|
||||
selectedProcessId = e.detail
|
||||
params = {
|
||||
process: selectedProcessId
|
||||
}
|
||||
dispatch('change', { params })
|
||||
}
|
||||
|
||||
function changeStateCriteria (e: CustomEvent<any>): void {
|
||||
if (readonly || e.detail == null) return
|
||||
params = {
|
||||
...params,
|
||||
currentState: e.detail
|
||||
}
|
||||
dispatch('change', { params })
|
||||
}
|
||||
|
||||
const modes: ModeId[] = ['ArrayAll', 'ArrayAny', 'ArrayNotIncludes']
|
||||
</script>
|
||||
|
||||
<div class="editor-grid">
|
||||
<Label label={plugin.string.Process} />
|
||||
<DropdownLabels
|
||||
disabled={readonly}
|
||||
autoSelect={false}
|
||||
enableSearch={false}
|
||||
width={'100%'}
|
||||
{items}
|
||||
{selected}
|
||||
placeholder={plugin.string.Process}
|
||||
on:selected={changeProcess}
|
||||
/>
|
||||
|
||||
{#if selectedProcessId !== undefined}
|
||||
<Label label={plugin.string.Step} />
|
||||
<SubProcessStateCriteria
|
||||
{readonly}
|
||||
target={selectedProcessId}
|
||||
value={params.currentState}
|
||||
on:change={changeStateCriteria}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Process, State } from '@hcengineering/process'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
import plugin from '../../plugin'
|
||||
|
||||
export let params: Record<string, any>
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let selectedProcess: Process | undefined
|
||||
$: if (params.process) {
|
||||
selectedProcess = client.getModel().findAllSync(plugin.class.Process, { _id: params.process })[0]
|
||||
} else {
|
||||
selectedProcess = undefined
|
||||
}
|
||||
|
||||
let states: State[] = []
|
||||
$: if (params.process) {
|
||||
states = client.getModel().findAllSync(plugin.class.State, { process: params.process })
|
||||
} else {
|
||||
states = []
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if selectedProcess}
|
||||
: <Label label={plugin.string.Process} />
|
||||
<span class="overflow-label">{selectedProcess.name}</span>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.overflow-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -67,6 +67,8 @@ import TimeEditor from './components/settings/TimeEditor.svelte'
|
||||
import TimePresenter from './components/settings/TimePresenter.svelte'
|
||||
import ToDoSettingPresenter from './components/settings/ToDoPresenter.svelte'
|
||||
import TransitionRefPresenter from './components/settings/TransitionRefPresenter.svelte'
|
||||
import SubProcessMatchEditor from './components/settings/SubProcessMatchEditor.svelte'
|
||||
import SubProcessMatchPresenter from './components/settings/SubProcessMatchPresenter.svelte'
|
||||
import AppendEditor from './components/transformEditors/AppendEditor.svelte'
|
||||
import CutEditor from './components/transformEditors/CutEditor.svelte'
|
||||
import ReplaceEditor from './components/transformEditors/ReplaceEditor.svelte'
|
||||
@@ -81,7 +83,8 @@ import {
|
||||
todoTranstionCheck,
|
||||
matchCardCheck,
|
||||
subProcessesDoneCheck,
|
||||
fieldChangesCheck
|
||||
fieldChangesCheck,
|
||||
subProcessMatchCheck
|
||||
} from './utils'
|
||||
import FieldChangesEditor from './components/settings/FieldChangesEditor.svelte'
|
||||
|
||||
@@ -138,7 +141,9 @@ export default async (): Promise<Resources> => ({
|
||||
AddTagPresenter,
|
||||
ExecutionMyToDos,
|
||||
FieldChangesEditor,
|
||||
FunctionSubmenu
|
||||
FunctionSubmenu,
|
||||
SubProcessMatchEditor,
|
||||
SubProcessMatchPresenter
|
||||
},
|
||||
criteriaEditor: {
|
||||
BaseCriteria,
|
||||
@@ -164,6 +169,7 @@ export default async (): Promise<Resources> => ({
|
||||
MatchCheck: matchCardCheck,
|
||||
FieldChangedCheck: fieldChangesCheck,
|
||||
SubProcessesDoneCheck: subProcessesDoneCheck,
|
||||
SubProcessMatchCheck: subProcessMatchCheck,
|
||||
ToDo: todoTranstionCheck,
|
||||
Time: timeTransitionCheck
|
||||
},
|
||||
|
||||
@@ -71,6 +71,8 @@ export default mergeIds(processId, process, {
|
||||
TimePresenter: '' as AnyComponent,
|
||||
AddTagEditor: '' as AnyComponent,
|
||||
AddTagPresenter: '' as AnyComponent,
|
||||
SubProcessMatchEditor: '' as AnyComponent,
|
||||
SubProcessMatchPresenter: '' as AnyComponent,
|
||||
FunctionSubmenu: '' as AnyComponent
|
||||
},
|
||||
criteriaEditor: {
|
||||
@@ -166,6 +168,7 @@ export default mergeIds(processId, process, {
|
||||
EnterValue: '' as IntlString,
|
||||
OnToDoDone: '' as IntlString,
|
||||
OnSubProcessesDone: '' as IntlString,
|
||||
WhenSubProcessMatches: '' as IntlString,
|
||||
WaitUntil: '' as IntlString,
|
||||
WhenCardMatches: '' as IntlString,
|
||||
WhenFieldChanges: '' as IntlString,
|
||||
|
||||
@@ -165,19 +165,23 @@ function hasSameKeys (target: any, value: any): boolean {
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return typeof target !== 'object' && typeof value !== 'object'
|
||||
return typeof target !== 'object' && isNotObject(value)
|
||||
}
|
||||
}
|
||||
|
||||
function isNotObject (value: any): boolean {
|
||||
return typeof value !== 'object' || value === null || Array.isArray(value)
|
||||
}
|
||||
|
||||
function isModeMatch (mode: Mode, value: any): boolean {
|
||||
if (typeof mode.query !== 'object') return typeof value !== 'object' || Array.isArray(value)
|
||||
if (typeof mode.query === 'object' && (typeof value !== 'object' || Array.isArray(value))) return false
|
||||
if (typeof mode.query !== 'object') return isNotObject(value)
|
||||
if (typeof mode.query === 'object' && isNotObject(value)) return false
|
||||
return hasSameKeys(mode.query, value)
|
||||
}
|
||||
|
||||
function getValue (_value: any): any {
|
||||
let value = _value
|
||||
while (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 1) {
|
||||
while (!isNotObject(value) && Object.keys(value).length === 1) {
|
||||
value = value[Object.keys(value)[0]]
|
||||
}
|
||||
return value
|
||||
|
||||
@@ -656,6 +656,42 @@ export async function subProcessesDoneCheck (
|
||||
return res === undefined
|
||||
}
|
||||
|
||||
export async function subProcessMatchCheck (
|
||||
client: Client,
|
||||
execution: Execution,
|
||||
params: Record<string, any>,
|
||||
context: Record<string, any>
|
||||
): Promise<boolean> {
|
||||
const { process: _process, ...otherCritera } = params
|
||||
if (_process === undefined) return false
|
||||
if (Object.keys(otherCritera).length === 0) return true
|
||||
|
||||
const subProcesses = await client.findAll(process.class.Execution, {
|
||||
parentId: execution._id,
|
||||
process: params.process
|
||||
})
|
||||
|
||||
if (subProcesses.length === 0) return false
|
||||
|
||||
const [predicate, value] = Object.entries(otherCritera)[0]
|
||||
|
||||
const res = matchQuery(
|
||||
subProcesses,
|
||||
{ currentState: { $in: value } },
|
||||
process.class.Execution,
|
||||
client.getHierarchy(),
|
||||
true
|
||||
)
|
||||
if (predicate === '$all') {
|
||||
return res.length === subProcesses.length
|
||||
} else if (predicate === '$any') {
|
||||
return res.length > 0
|
||||
} else if (predicate === '$nin') {
|
||||
return res.length === 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function getCriteriaEditor (
|
||||
of: Ref<Class<Doc>>,
|
||||
category: AttributeCategory
|
||||
|
||||
@@ -221,6 +221,7 @@ export default plugin(processId, {
|
||||
OnCardUpdate: '' as Ref<Trigger>, // in fact WhenCardMatches, should migrate in future
|
||||
WhenFieldChanges: '' as Ref<Trigger>,
|
||||
OnSubProcessesDone: '' as Ref<Trigger>,
|
||||
OnSubProcessMatch: '' as Ref<Trigger>,
|
||||
OnToDoClose: '' as Ref<Trigger>,
|
||||
OnToDoRemove: '' as Ref<Trigger>,
|
||||
OnExecutionStart: '' as Ref<Trigger>,
|
||||
@@ -232,6 +233,7 @@ export default plugin(processId, {
|
||||
MatchCheck: '' as Resource<CheckFunc>,
|
||||
FieldChangedCheck: '' as Resource<CheckFunc>,
|
||||
SubProcessesDoneCheck: '' as Resource<CheckFunc>,
|
||||
SubProcessMatchCheck: '' as Resource<CheckFunc>,
|
||||
Time: '' as Resource<CheckFunc>
|
||||
},
|
||||
string: {
|
||||
|
||||
@@ -94,6 +94,42 @@ export async function CheckSubProcessesDone (control: ProcessControl, execution:
|
||||
return res === undefined
|
||||
}
|
||||
|
||||
export async function CheckSubProcessMatch (
|
||||
control: ProcessControl,
|
||||
execution: Execution,
|
||||
params: Record<string, any>,
|
||||
context: Record<string, any>
|
||||
): Promise<boolean> {
|
||||
const { process: targetProcess, currentState } = params
|
||||
|
||||
if (targetProcess === undefined || currentState === undefined) return false
|
||||
|
||||
const subExecutions = await control.client.findAll(process.class.Execution, {
|
||||
parentId: execution._id,
|
||||
process: targetProcess
|
||||
})
|
||||
|
||||
if (subExecutions.length === 0) return false
|
||||
|
||||
const [predicate, value] = Object.entries(currentState)[0]
|
||||
|
||||
const res = matchQuery(
|
||||
subExecutions,
|
||||
{ currentState: { $in: value as any } },
|
||||
process.class.Execution,
|
||||
control.client.getHierarchy(),
|
||||
true
|
||||
)
|
||||
if (predicate === '$all') {
|
||||
return res.length === subExecutions.length
|
||||
} else if (predicate === '$any') {
|
||||
return res.length > 0
|
||||
} else if (predicate === '$nin') {
|
||||
return res.length === 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function MatchCardCheck (
|
||||
control: ProcessControl,
|
||||
execution: Execution,
|
||||
|
||||
@@ -93,6 +93,7 @@ import {
|
||||
CheckToDoCancelled,
|
||||
MatchCardCheck,
|
||||
CheckSubProcessesDone,
|
||||
CheckSubProcessMatch,
|
||||
CheckTime,
|
||||
FieldChangedCheck
|
||||
} from './functions'
|
||||
@@ -398,6 +399,7 @@ export default async () => ({
|
||||
FieldChangedCheck,
|
||||
MatchCardCheck,
|
||||
CheckSubProcessesDone,
|
||||
CheckSubProcessMatch,
|
||||
CheckTime
|
||||
},
|
||||
transform: {
|
||||
|
||||
@@ -59,6 +59,7 @@ export default plugin(serverProcessId, {
|
||||
MatchCardCheck: '' as Resource<CheckFunc>,
|
||||
FieldChangedCheck: '' as Resource<CheckFunc>,
|
||||
CheckSubProcessesDone: '' as Resource<CheckFunc>,
|
||||
CheckSubProcessMatch: '' as Resource<CheckFunc>,
|
||||
CheckTime: '' as Resource<CheckFunc>
|
||||
},
|
||||
transform: {
|
||||
|
||||
@@ -700,7 +700,10 @@ async function checkParent (execution: Execution, control: ProcessControl): Prom
|
||||
{
|
||||
from: parent.currentState,
|
||||
process: parent.process,
|
||||
trigger: process.trigger.OnSubProcessesDone
|
||||
trigger:
|
||||
filtered.length === 0
|
||||
? { $in: [process.trigger.OnSubProcessesDone, process.trigger.OnSubProcessMatch] }
|
||||
: process.trigger.OnSubProcessMatch
|
||||
},
|
||||
{
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
|
||||
Reference in New Issue
Block a user