mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-10 17:30:51 +00:00
Add auto-start functionality and related triggers for processes (#8478)
This commit is contained in:
parent
d2bdf88249
commit
507078d7ac
@ -11,7 +11,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 contact, { type Employee } from '@hcengineering/contact'
|
||||||
import core, {
|
import core, {
|
||||||
AccountRole,
|
AccountRole,
|
||||||
@ -68,11 +68,14 @@ export class TProcess extends TDoc implements Process {
|
|||||||
@Prop(TypeString(), core.string.Description)
|
@Prop(TypeString(), core.string.Description)
|
||||||
description!: string
|
description!: string
|
||||||
|
|
||||||
@Prop(TypeRef(card.class.MasterTag), core.string.Name)
|
@Prop(TypeRef(card.class.MasterTag), card.string.MasterTag)
|
||||||
masterTag!: Ref<MasterTag>
|
masterTag!: Ref<MasterTag | Tag>
|
||||||
|
|
||||||
@Prop(ArrOf(TypeRef(process.class.State)), process.string.States)
|
@Prop(ArrOf(TypeRef(process.class.State)), process.string.States)
|
||||||
states!: Ref<State>[]
|
states!: Ref<State>[]
|
||||||
|
|
||||||
|
@Prop(TypeBoolean(), process.string.StartAutomatically)
|
||||||
|
autoStart: boolean | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(process.class.Execution, core.class.Doc, DOMAIN_PROCESS)
|
@Model(process.class.Execution, core.class.Doc, DOMAIN_PROCESS)
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"@hcengineering/model": "^0.6.11",
|
"@hcengineering/model": "^0.6.11",
|
||||||
"@hcengineering/model-process": "^0.6.0",
|
"@hcengineering/model-process": "^0.6.0",
|
||||||
"@hcengineering/process": "^0.6.0",
|
"@hcengineering/process": "^0.6.0",
|
||||||
|
"@hcengineering/card": "^0.6.0",
|
||||||
"@hcengineering/server-process": "^0.6.0",
|
"@hcengineering/server-process": "^0.6.0",
|
||||||
"@hcengineering/server-core": "^0.6.1"
|
"@hcengineering/server-core": "^0.6.1"
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import card from '@hcengineering/card'
|
||||||
import core, { type Doc } from '@hcengineering/core'
|
import core, { type Doc } from '@hcengineering/core'
|
||||||
import { Mixin, type Builder } from '@hcengineering/model'
|
import { Mixin, type Builder } from '@hcengineering/model'
|
||||||
import { TMethod, TProcessFunction } from '@hcengineering/model-process'
|
import { TMethod, TProcessFunction } from '@hcengineering/model-process'
|
||||||
@ -19,8 +20,8 @@ import process from '@hcengineering/process'
|
|||||||
import serverCore from '@hcengineering/server-core'
|
import serverCore from '@hcengineering/server-core'
|
||||||
import serverProcess, {
|
import serverProcess, {
|
||||||
type ExecuteFunc,
|
type ExecuteFunc,
|
||||||
type MethodImpl,
|
|
||||||
type FuncImpl,
|
type FuncImpl,
|
||||||
|
type MethodImpl,
|
||||||
type TransformFunc
|
type TransformFunc
|
||||||
} from '@hcengineering/server-process'
|
} from '@hcengineering/server-process'
|
||||||
|
|
||||||
@ -110,6 +111,24 @@ export function createModel (builder: Builder): void {
|
|||||||
isAsync: true
|
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, {
|
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
||||||
trigger: serverProcess.trigger.OnExecutionCreate,
|
trigger: serverProcess.trigger.OnExecutionCreate,
|
||||||
txMatch: {
|
txMatch: {
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "První pracovní den po",
|
"FirstWorkingDayAfter": "První pracovní den po",
|
||||||
"FallbackValueError": "Zobrazit chybu, pokud není k dispozici",
|
"FallbackValueError": "Zobrazit chybu, pokud není k dispozici",
|
||||||
"Required": "Povinné",
|
"Required": "Povinné",
|
||||||
|
"StartAutomatically": "Spustit automaticky",
|
||||||
"Error": "Chyba",
|
"Error": "Chyba",
|
||||||
"Continue": "Pokračovat"
|
"Continue": "Pokračovat"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "Erste Arbeitstag nach",
|
"FirstWorkingDayAfter": "Erste Arbeitstag nach",
|
||||||
"FallbackValueError": "Fehler anzeigen, wenn leer",
|
"FallbackValueError": "Fehler anzeigen, wenn leer",
|
||||||
"Required": "Erforderlich",
|
"Required": "Erforderlich",
|
||||||
|
"StartAutomatically": "Automatisch starten",
|
||||||
"Error": "Fehler",
|
"Error": "Fehler",
|
||||||
"Continue": "Fortsetzen"
|
"Continue": "Fortsetzen"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "First working day after",
|
"FirstWorkingDayAfter": "First working day after",
|
||||||
"FallbackValueError": "Show error if empty",
|
"FallbackValueError": "Show error if empty",
|
||||||
"Required": "Required",
|
"Required": "Required",
|
||||||
|
"StartAutomatically": "Start automatically",
|
||||||
"Error": "Error",
|
"Error": "Error",
|
||||||
"Continue": "Continue"
|
"Continue": "Continue"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "Primer día de trabajo después",
|
"FirstWorkingDayAfter": "Primer día de trabajo después",
|
||||||
"FallbackValueError": "Mostrar error si está vacío",
|
"FallbackValueError": "Mostrar error si está vacío",
|
||||||
"Required": "Requerido",
|
"Required": "Requerido",
|
||||||
|
"StartAutomatically": "Iniciar automáticamente",
|
||||||
"Error": "Error",
|
"Error": "Error",
|
||||||
"Continue": "Continuar"
|
"Continue": "Continuar"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "Premier jour de travail après",
|
"FirstWorkingDayAfter": "Premier jour de travail après",
|
||||||
"FallbackValueError": "Afficher l'erreur si vide",
|
"FallbackValueError": "Afficher l'erreur si vide",
|
||||||
"Required": "Requis",
|
"Required": "Requis",
|
||||||
|
"StartAutomatically": "Démarrer automatiquement",
|
||||||
"Error": "Erreur",
|
"Error": "Erreur",
|
||||||
"Continue": "Continuer"
|
"Continue": "Continuer"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "Primo giorno lavorativo dopo",
|
"FirstWorkingDayAfter": "Primo giorno lavorativo dopo",
|
||||||
"FallbackValueError": "Mostra errore se vuoto",
|
"FallbackValueError": "Mostra errore se vuoto",
|
||||||
"Required": "Obbligatorio",
|
"Required": "Obbligatorio",
|
||||||
|
"StartAutomatically": "Avvia automaticamente",
|
||||||
"Error": "Errore",
|
"Error": "Errore",
|
||||||
"Continue": "Continua"
|
"Continue": "Continua"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "Primeiro Dia de Trabalho Após",
|
"FirstWorkingDayAfter": "Primeiro Dia de Trabalho Após",
|
||||||
"FallbackValueError": "Mostrar erro se vazio",
|
"FallbackValueError": "Mostrar erro se vazio",
|
||||||
"Required": "Obrigatório",
|
"Required": "Obrigatório",
|
||||||
|
"StartAutomatically": "Iniciar automaticamente",
|
||||||
"Error": "Erro",
|
"Error": "Erro",
|
||||||
"Continue": "Continuar"
|
"Continue": "Continuar"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "Первый рабочий день после",
|
"FirstWorkingDayAfter": "Первый рабочий день после",
|
||||||
"FallbackValueError": "Показывать ошибку, если недоступно",
|
"FallbackValueError": "Показывать ошибку, если недоступно",
|
||||||
"Required": "Обязательно",
|
"Required": "Обязательно",
|
||||||
|
"StartAutomatically": "Запускать автоматически",
|
||||||
"Error": "Ошибка",
|
"Error": "Ошибка",
|
||||||
"Continue": "Продолжить"
|
"Continue": "Продолжить"
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"FirstWorkingDayAfter": "在之后的第一个工作日",
|
"FirstWorkingDayAfter": "在之后的第一个工作日",
|
||||||
"FallbackValueError": "显示错误,如果为空",
|
"FallbackValueError": "显示错误,如果为空",
|
||||||
"Required": "必需",
|
"Required": "必需",
|
||||||
|
"StartAutomatically": "自动启动",
|
||||||
"Error": "错误",
|
"Error": "错误",
|
||||||
"Continue": "继续"
|
"Continue": "继续"
|
||||||
},
|
},
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
IconDescription,
|
IconDescription,
|
||||||
navigate,
|
navigate,
|
||||||
NavItem,
|
NavItem,
|
||||||
|
ToggleWithLabel,
|
||||||
Scroller,
|
Scroller,
|
||||||
secondNavSeparators,
|
secondNavSeparators,
|
||||||
Separator,
|
Separator,
|
||||||
@ -77,6 +78,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function saveAutoStart (e: CustomEvent<boolean>): Promise<void> {
|
||||||
|
if (value !== undefined) {
|
||||||
|
await client.update(value, { autoStart: e.detail })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function addState (): Promise<void> {
|
async function addState (): Promise<void> {
|
||||||
if (value === undefined) return
|
if (value === undefined) return
|
||||||
const prevState = states[states.length - 1]
|
const prevState = states[states.length - 1]
|
||||||
@ -160,6 +167,13 @@
|
|||||||
<EditBox bind:value={value.name} on:change={saveName} placeholder={process.string.Untitled} />
|
<EditBox bind:value={value.name} on:change={saveName} placeholder={process.string.Untitled} />
|
||||||
<ButtonIcon icon={IconDelete} size="small" kind="secondary" on:click={handleDelete} />
|
<ButtonIcon icon={IconDelete} size="small" kind="secondary" on:click={handleDelete} />
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<ToggleWithLabel
|
||||||
|
on={value.autoStart ?? false}
|
||||||
|
on:change={saveAutoStart}
|
||||||
|
label={process.string.StartAutomatically}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="hulyComponent-content flex-col-center">
|
<div class="hulyComponent-content flex-col-center">
|
||||||
<div class="flex-col-center">
|
<div class="flex-col-center">
|
||||||
{#each sortedStates as state (state._id)}
|
{#each sortedStates as state (state._id)}
|
||||||
|
@ -89,6 +89,7 @@ export default mergeIds(processId, process, {
|
|||||||
FirstWorkingDayAfter: '' as IntlString,
|
FirstWorkingDayAfter: '' as IntlString,
|
||||||
FallbackValueError: '' as IntlString,
|
FallbackValueError: '' as IntlString,
|
||||||
Required: '' as IntlString,
|
Required: '' as IntlString,
|
||||||
|
StartAutomatically: '' as IntlString,
|
||||||
Continue: '' as IntlString
|
Continue: '' as IntlString
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Card, MasterTag } from '@hcengineering/card'
|
import { Card, MasterTag, Tag } from '@hcengineering/card'
|
||||||
import { Employee } from '@hcengineering/contact'
|
import { Employee } from '@hcengineering/contact'
|
||||||
import { Class, Doc, DocumentUpdate, ObjQueryType, Ref, Tx } from '@hcengineering/core'
|
import { Class, Doc, DocumentUpdate, ObjQueryType, Ref, Tx } from '@hcengineering/core'
|
||||||
import { Asset, IntlString, Plugin, plugin } from '@hcengineering/platform'
|
import { Asset, IntlString, Plugin, plugin } from '@hcengineering/platform'
|
||||||
@ -25,10 +25,11 @@ import { AttributeCategory } from '@hcengineering/view'
|
|||||||
export const processId = 'process' as Plugin
|
export const processId = 'process' as Plugin
|
||||||
|
|
||||||
export interface Process extends Doc {
|
export interface Process extends Doc {
|
||||||
masterTag: Ref<MasterTag>
|
masterTag: Ref<MasterTag | Tag>
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
states: Ref<State>[]
|
states: Ref<State>[]
|
||||||
|
autoStart?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Execution extends Doc {
|
export interface Execution extends Doc {
|
||||||
|
@ -25,6 +25,7 @@ import core, {
|
|||||||
Timestamp,
|
Timestamp,
|
||||||
Tx,
|
Tx,
|
||||||
TxCreateDoc,
|
TxCreateDoc,
|
||||||
|
TxMixin,
|
||||||
TxProcessor,
|
TxProcessor,
|
||||||
TxRemoveDoc,
|
TxRemoveDoc,
|
||||||
TxUpdateDoc
|
TxUpdateDoc
|
||||||
@ -635,6 +636,63 @@ export function FirstWorkingDayAfter (val: Timestamp): Timestamp {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function OnCardCreate (txes: Tx[], control: TriggerControl): Promise<Tx[]> {
|
||||||
|
const res: Tx[] = []
|
||||||
|
for (const tx of txes) {
|
||||||
|
if (tx._class !== core.class.TxCreateDoc) continue
|
||||||
|
const createTx = tx as TxCreateDoc<Card>
|
||||||
|
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<Tx[]> {
|
||||||
|
const res: Tx[] = []
|
||||||
|
for (const tx of txes) {
|
||||||
|
if (tx._class !== core.class.TxMixin) continue
|
||||||
|
const mixinTx = tx as TxMixin<Card, Card>
|
||||||
|
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<Tx[]> {
|
export async function OnExecutionContinue (txes: Tx[], control: TriggerControl): Promise<Tx[]> {
|
||||||
const res: Tx[] = []
|
const res: Tx[] = []
|
||||||
for (const tx of txes) {
|
for (const tx of txes) {
|
||||||
@ -679,6 +737,8 @@ export default async () => ({
|
|||||||
FirstWorkingDayAfter
|
FirstWorkingDayAfter
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
|
OnCardCreate,
|
||||||
|
OnTagAdd,
|
||||||
OnExecutionCreate,
|
OnExecutionCreate,
|
||||||
OnStateRemove,
|
OnStateRemove,
|
||||||
OnProcessRemove,
|
OnProcessRemove,
|
||||||
|
@ -46,6 +46,8 @@ export default plugin(serverProcessId, {
|
|||||||
FirstWorkingDayAfter: '' as Resource<TransformFunc>
|
FirstWorkingDayAfter: '' as Resource<TransformFunc>
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
|
OnCardCreate: '' as Resource<TriggerFunc>,
|
||||||
|
OnTagAdd: '' as Resource<TriggerFunc>,
|
||||||
OnExecutionCreate: '' as Resource<TriggerFunc>,
|
OnExecutionCreate: '' as Resource<TriggerFunc>,
|
||||||
OnStateRemove: '' as Resource<TriggerFunc>,
|
OnStateRemove: '' as Resource<TriggerFunc>,
|
||||||
OnProcessRemove: '' as Resource<TriggerFunc>,
|
OnProcessRemove: '' as Resource<TriggerFunc>,
|
||||||
|
Loading…
Reference in New Issue
Block a user