mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-11 01:40:32 +00:00
Add parallel execution restriction feature to processes (#8477)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
b9c6d337d8
commit
e2a2011ad8
@ -74,6 +74,9 @@ export class TProcess extends TDoc implements Process {
|
||||
@Prop(ArrOf(TypeRef(process.class.State)), process.string.States)
|
||||
states!: Ref<State>[]
|
||||
|
||||
@Prop(TypeBoolean(), process.string.ParallelExecutionForbidden)
|
||||
parallelExecutionForbidden?: boolean
|
||||
|
||||
@Prop(TypeBoolean(), process.string.StartAutomatically)
|
||||
autoStart: boolean | undefined
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
export let readonly: boolean = false
|
||||
export let label: IntlString = card.string.Card
|
||||
export let _class: Ref<Class<Card>>
|
||||
export let ignoreObjects: Ref<Card>[] | undefined = undefined
|
||||
|
||||
export let focusIndex: number | undefined = undefined
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
@ -53,7 +54,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
showPopup(CardsPopup, { selected: value, _class }, eventToHTMLElement(event), change)
|
||||
showPopup(CardsPopup, { selected: value, _class, ignoreObjects }, eventToHTMLElement(event), change)
|
||||
}
|
||||
|
||||
const change = (val: Card | undefined): void => {
|
||||
|
@ -23,6 +23,7 @@
|
||||
export let _class: Ref<Class<Card>>
|
||||
export let selected: Ref<Card> | undefined
|
||||
export let selectedObjects: Ref<Card>[] | undefined
|
||||
export let ignoreObjects: Ref<Card>[] | undefined = undefined
|
||||
export let multiSelect: boolean = false
|
||||
export let allowDeselect: boolean = true
|
||||
export let titleDeselect: IntlString | undefined = undefined
|
||||
@ -41,6 +42,7 @@
|
||||
{multiSelect}
|
||||
{allowDeselect}
|
||||
{titleDeselect}
|
||||
{ignoreObjects}
|
||||
type={'object'}
|
||||
groupBy={'_class'}
|
||||
on:update
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "První pracovní den po",
|
||||
"FallbackValueError": "Zobrazit chybu, pokud není k dispozici",
|
||||
"Required": "Povinné",
|
||||
"ParallelExecutionForbidden": "Nedovoluj paralelní spuštění",
|
||||
"StartAutomatically": "Spustit automaticky",
|
||||
"Error": "Chyba",
|
||||
"Continue": "Pokračovat"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "Erste Arbeitstag nach",
|
||||
"FallbackValueError": "Fehler anzeigen, wenn leer",
|
||||
"Required": "Erforderlich",
|
||||
"ParallelExecutionForbidden": "Parallele Ausführung verboten",
|
||||
"StartAutomatically": "Automatisch starten",
|
||||
"Error": "Fehler",
|
||||
"Continue": "Fortsetzen"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "First working day after",
|
||||
"FallbackValueError": "Show error if empty",
|
||||
"Required": "Required",
|
||||
"ParallelExecutionForbidden": "Parallel execution forbidden",
|
||||
"StartAutomatically": "Start automatically",
|
||||
"Error": "Error",
|
||||
"Continue": "Continue"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "Primer día de trabajo después",
|
||||
"FallbackValueError": "Mostrar error si está vacío",
|
||||
"Required": "Requerido",
|
||||
"ParallelExecutionForbidden": "Ejecución paralela prohibida",
|
||||
"StartAutomatically": "Iniciar automáticamente",
|
||||
"Error": "Error",
|
||||
"Continue": "Continuar"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "Premier jour de travail après",
|
||||
"FallbackValueError": "Afficher l'erreur si vide",
|
||||
"Required": "Requis",
|
||||
"ParallelExecutionForbidden": "Exécution parallèle interdite",
|
||||
"StartAutomatically": "Démarrer automatiquement",
|
||||
"Error": "Erreur",
|
||||
"Continue": "Continuer"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "Primo giorno lavorativo dopo",
|
||||
"FallbackValueError": "Mostra errore se vuoto",
|
||||
"Required": "Obbligatorio",
|
||||
"ParallelExecutionForbidden": "Esecuzione parallela vietata",
|
||||
"StartAutomatically": "Avvia automaticamente",
|
||||
"Error": "Errore",
|
||||
"Continue": "Continua"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "Primeiro Dia de Trabalho Após",
|
||||
"FallbackValueError": "Mostrar erro se vazio",
|
||||
"Required": "Obrigatório",
|
||||
"ParallelExecutionForbidden": "Execução Paralela Proibida",
|
||||
"StartAutomatically": "Iniciar automaticamente",
|
||||
"Error": "Erro",
|
||||
"Continue": "Continuar"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "Первый рабочий день после",
|
||||
"FallbackValueError": "Показывать ошибку, если недоступно",
|
||||
"Required": "Обязательно",
|
||||
"ParallelExecutionForbidden": "Одновременное выполнение запрещено",
|
||||
"StartAutomatically": "Запускать автоматически",
|
||||
"Error": "Ошибка",
|
||||
"Continue": "Продолжить"
|
||||
|
@ -43,6 +43,7 @@
|
||||
"FirstWorkingDayAfter": "在之后的第一个工作日",
|
||||
"FallbackValueError": "显示错误,如果为空",
|
||||
"Required": "必需",
|
||||
"ParallelExecutionForbidden": "禁止并行执行",
|
||||
"StartAutomatically": "自动启动",
|
||||
"Error": "错误",
|
||||
"Continue": "继续"
|
||||
|
@ -78,6 +78,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function saveRestriction (e: CustomEvent<boolean>): Promise<void> {
|
||||
if (value !== undefined) {
|
||||
await client.update(value, { parallelExecutionForbidden: e.detail })
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAutoStart (e: CustomEvent<boolean>): Promise<void> {
|
||||
if (value !== undefined) {
|
||||
await client.update(value, { autoStart: e.detail })
|
||||
@ -167,6 +173,13 @@
|
||||
<EditBox bind:value={value.name} on:change={saveName} placeholder={process.string.Untitled} />
|
||||
<ButtonIcon icon={IconDelete} size="small" kind="secondary" on:click={handleDelete} />
|
||||
</div>
|
||||
<div>
|
||||
<ToggleWithLabel
|
||||
on={value.parallelExecutionForbidden ?? false}
|
||||
on:change={saveRestriction}
|
||||
label={process.string.ParallelExecutionForbidden}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<ToggleWithLabel
|
||||
on={value.autoStart ?? false}
|
||||
|
@ -50,6 +50,30 @@
|
||||
}
|
||||
|
||||
$: selectedProcess = processes.find((p) => p._id === process)
|
||||
|
||||
let ignoreObjects: Ref<Card>[] = []
|
||||
|
||||
$: void filter(process)
|
||||
|
||||
async function filter (process: Ref<Process> | undefined): Promise<void> {
|
||||
if (process === undefined) {
|
||||
ignoreObjects = []
|
||||
return
|
||||
}
|
||||
const pr = client.getModel().findObject(process)
|
||||
if (pr === undefined) {
|
||||
ignoreObjects = []
|
||||
return
|
||||
}
|
||||
if (pr.parallelExecutionForbidden !== true) {
|
||||
ignoreObjects = []
|
||||
return
|
||||
}
|
||||
|
||||
const executions = await client.findAll(plugin.class.Execution, { process, done: false }, {})
|
||||
const cards = new Set(executions.map((it) => it.card))
|
||||
ignoreObjects = [...cards]
|
||||
}
|
||||
</script>
|
||||
|
||||
<CardPopup
|
||||
@ -74,6 +98,12 @@
|
||||
/>
|
||||
</div>
|
||||
{#if selectedProcess !== undefined}
|
||||
<CardSelector kind={'regular'} size={'medium'} bind:value={card} _class={selectedProcess.masterTag} />
|
||||
<CardSelector
|
||||
kind={'regular'}
|
||||
size={'medium'}
|
||||
bind:value={card}
|
||||
{ignoreObjects}
|
||||
_class={selectedProcess.masterTag}
|
||||
/>
|
||||
{/if}
|
||||
</CardPopup>
|
||||
|
@ -33,6 +33,31 @@
|
||||
const res = client.getModel().findAllSync(process.class.Process, {})
|
||||
const processes = res.filter((it) => resClasses.includes(it.masterTag))
|
||||
|
||||
async function filterProcesses (processes: Process[]): Promise<Process[]> {
|
||||
const res: Process[] = []
|
||||
const shouldCheck: Process[] = []
|
||||
for (const val of processes) {
|
||||
if (val.parallelExecutionForbidden === true) {
|
||||
shouldCheck.push(val)
|
||||
} else {
|
||||
res.push(val)
|
||||
}
|
||||
}
|
||||
if (shouldCheck.length === 0) return res
|
||||
|
||||
const executions = await client.findAll(process.class.Execution, {
|
||||
process: { $in: shouldCheck.map((it) => it._id) },
|
||||
done: false
|
||||
})
|
||||
const notAllowed = new Set(executions.map((it) => it.process))
|
||||
for (const val of shouldCheck) {
|
||||
if (!notAllowed.has(val._id)) {
|
||||
res.push(val)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function runProcess (_id: Ref<Process>): Promise<void> {
|
||||
@ -57,16 +82,18 @@
|
||||
<Label label={process.string.NoProcesses} />
|
||||
</div>
|
||||
{:else}
|
||||
{#each processes as process}
|
||||
<button
|
||||
class="ap-menuItem flex-row-center withIcon w-full"
|
||||
on:click|preventDefault|stopPropagation={async () => {
|
||||
await runProcess(process._id)
|
||||
}}
|
||||
>
|
||||
{process.name}
|
||||
</button>
|
||||
{/each}
|
||||
{#await filterProcesses(processes) then processes}
|
||||
{#each processes as process}
|
||||
<button
|
||||
class="ap-menuItem flex-row-center withIcon w-full"
|
||||
on:click|preventDefault|stopPropagation={async () => {
|
||||
await runProcess(process._id)
|
||||
}}
|
||||
>
|
||||
{process.name}
|
||||
</button>
|
||||
{/each}
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="ap-space x2" />
|
||||
|
@ -89,6 +89,7 @@ export default mergeIds(processId, process, {
|
||||
FirstWorkingDayAfter: '' as IntlString,
|
||||
FallbackValueError: '' as IntlString,
|
||||
Required: '' as IntlString,
|
||||
ParallelExecutionForbidden: '' as IntlString,
|
||||
StartAutomatically: '' as IntlString,
|
||||
Continue: '' as IntlString
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ export interface Process extends Doc {
|
||||
name: string
|
||||
description: string
|
||||
states: Ref<State>[]
|
||||
parallelExecutionForbidden?: boolean
|
||||
autoStart?: boolean
|
||||
}
|
||||
|
||||
|
@ -528,10 +528,24 @@ export async function RunSubProcess (
|
||||
control: TriggerControl
|
||||
): Promise<ExecuteResult | undefined> {
|
||||
if (params._id === undefined) return
|
||||
const processId = params._id as Ref<Process>
|
||||
const target = control.modelDb.findObject(processId)
|
||||
if (target === undefined) return
|
||||
if (target.parallelExecutionForbidden === true) {
|
||||
const currentExecution = await control.findAll(control.ctx, process.class.Execution, {
|
||||
process: target._id,
|
||||
card: execution.card,
|
||||
done: false
|
||||
})
|
||||
if (currentExecution.length > 0) {
|
||||
// todo, show erro after merge another pr
|
||||
return
|
||||
}
|
||||
}
|
||||
const res: Tx[] = []
|
||||
res.push(
|
||||
control.txFactory.createTxCreateDoc(process.class.Execution, core.space.Workspace, {
|
||||
process: params._id as Ref<Process>,
|
||||
process: processId,
|
||||
currentState: null,
|
||||
currentToDo: null,
|
||||
card: execution.card,
|
||||
|
Loading…
Reference in New Issue
Block a user