mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
WhenRequiredFieldsFilled (#10937)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# Project Instructions
|
||||
|
||||
- Do not run build commands automatically for verification.
|
||||
- When finishing work, state that the task is complete and ask the user to verify it manually.
|
||||
@@ -124,6 +124,21 @@ export function defineTriggers (builder: Builder): void {
|
||||
process.trigger.WhenFieldChanges
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
process.class.Trigger,
|
||||
core.space.Model,
|
||||
{
|
||||
label: process.string.WhenRequiredFieldsFilled,
|
||||
icon: process.icon.WhenCardMatches,
|
||||
presenter: process.component.CardUpdatePresenter,
|
||||
requiredParams: [],
|
||||
checkFunction: process.triggerCheck.RequiredFieldsFilledCheck,
|
||||
init: false,
|
||||
auto: true
|
||||
},
|
||||
process.trigger.WhenRequiredFieldsFilled
|
||||
)
|
||||
|
||||
// builder.createDoc(
|
||||
// process.class.Trigger,
|
||||
// core.space.Model,
|
||||
|
||||
@@ -70,6 +70,11 @@ export function createModel (builder: Builder): void {
|
||||
serverCheckFunc: serverProcess.func.FieldChangedCheck
|
||||
})
|
||||
|
||||
builder.mixin(process.trigger.WhenRequiredFieldsFilled, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
|
||||
preventRollback: true,
|
||||
serverCheckFunc: serverProcess.func.RequiredFieldsFilledCheck
|
||||
})
|
||||
|
||||
builder.mixin(process.trigger.OnApproveRequestApproved, process.class.Trigger, serverProcess.mixin.TriggerImpl, {
|
||||
preventRollback: true,
|
||||
serverCheckFunc: serverProcess.func.ApproveRequestApproved
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"CreateCard": "Vytvořit kartu",
|
||||
"WhenCardMatches": "Když karta odpovídá",
|
||||
"WhenFieldChanges": "Když se pole změní",
|
||||
"WhenRequiredFieldsFilled": "Když jsou vyplněna všechna povinná pole",
|
||||
"Insert": "Vložit",
|
||||
"Remove": "Odstranit",
|
||||
"RemoveFirst": "Odstranit první",
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"CreateCard": "Karte erstellen",
|
||||
"WhenCardMatches": "Wenn Karte übereinstimmt",
|
||||
"WhenFieldChanges": "Wenn sich das Feld ändert",
|
||||
"WhenRequiredFieldsFilled": "Wenn erforderliche Felder ausgefüllt sind",
|
||||
"Insert": "Einfügen",
|
||||
"Remove": "Entfernen",
|
||||
"RemoveFirst": "Erstes entfernen",
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"CreateCard": "Create card",
|
||||
"WhenCardMatches": "When card matches",
|
||||
"WhenFieldChanges": "When field changes",
|
||||
"WhenRequiredFieldsFilled": "When required fields are filled",
|
||||
"ArraySizeEquals": "Size equals",
|
||||
"ArraySizeGt": "Size greater than",
|
||||
"ArraySizeGte": "Size greater than or equal",
|
||||
@@ -201,4 +202,4 @@
|
||||
"TooDeepTransitionRecursion": "Too deep transition recursion",
|
||||
"ToDoAlreadyCompleted": "Action item already completed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "Crear tarjeta",
|
||||
"WhenCardMatches": "Cuando la tarjeta coincide",
|
||||
"WhenFieldChanges": "Cuando el campo cambia",
|
||||
"WhenRequiredFieldsFilled": "Cuando los campos obligatorios estén completos",
|
||||
"ArraySizeEquals": "Tamaño igual a",
|
||||
"ArraySizeGt": "Tamaño mayor que",
|
||||
"ArraySizeGte": "Tamaño mayor o igual que",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "Créer une carte",
|
||||
"WhenCardMatches": "Lorsque la carte correspond",
|
||||
"WhenFieldChanges": "Lorsque le champ change",
|
||||
"WhenRequiredFieldsFilled": "Lorsque les champs requis sont remplis",
|
||||
"ArraySizeEquals": "Taille égale à",
|
||||
"ArraySizeGt": "Taille supérieure à",
|
||||
"ArraySizeGte": "Taille supérieure ou égale à",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "Crea scheda",
|
||||
"WhenCardMatches": "Quando la scheda corrisponde",
|
||||
"WhenFieldChanges": "Quando il campo cambia",
|
||||
"WhenRequiredFieldsFilled": "Quando i campi richiesti sono compilati",
|
||||
"ArraySizeEquals": "Dimensione uguale a",
|
||||
"ArraySizeGt": "Dimensione maggiore di",
|
||||
"ArraySizeGte": "Dimensione maggiore o uguale a",
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
"CreateCard": "カードを作成",
|
||||
"WhenCardMatches": "カードが一致したとき",
|
||||
"WhenFieldChanges": "フィールドが変更されたとき",
|
||||
"WhenRequiredFieldsFilled": "必須フィールドが入力されたとき",
|
||||
"ArraySizeEquals": "サイズが等しい",
|
||||
"ArraySizeGt": "サイズがより大きい",
|
||||
"ArraySizeGte": "サイズが以上",
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"CreateCard": "카드 생성",
|
||||
"WhenCardMatches": "카드 일치 시",
|
||||
"WhenFieldChanges": "필드 변경 시",
|
||||
"WhenRequiredFieldsFilled": "필수 필드가 채워졌을 때",
|
||||
"ArraySizeEquals": "크기 같음",
|
||||
"ArraySizeGt": "크기가 더 큼",
|
||||
"ArraySizeGte": "크기가 이상",
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"CreateCard": "Utwórz kartę",
|
||||
"WhenCardMatches": "Kiedy karta spełnia warunek",
|
||||
"WhenFieldChanges": "Kiedy pole jest zmieniane",
|
||||
"WhenRequiredFieldsFilled": "Kiedy wymagane pola są wypełnione",
|
||||
"ArraySizeEquals": "Rozmiar równy",
|
||||
"ArraySizeGt": "Rozmiar większy niż",
|
||||
"ArraySizeGte": "Rozmiar większy lub równy",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "Criar Cartão",
|
||||
"WhenCardMatches": "Quando o cartão corresponde",
|
||||
"WhenFieldChanges": "Quando o campo muda",
|
||||
"WhenRequiredFieldsFilled": "Quando os campos obrigatórios forem preenchidos",
|
||||
"ArraySizeEquals": "Tamanho igual a",
|
||||
"ArraySizeGt": "Tamanho maior que",
|
||||
"ArraySizeGte": "Tamanho maior ou igual a",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "Criar Cartão",
|
||||
"WhenCardMatches": "Quando o cartão corresponde",
|
||||
"WhenFieldChanges": "Quando o campo muda",
|
||||
"WhenRequiredFieldsFilled": "Quando os campos obrigatórios forem preenchidos",
|
||||
"ArraySizeEquals": "Tamanho igual a",
|
||||
"ArraySizeGt": "Tamanho maior que",
|
||||
"ArraySizeGte": "Tamanho maior ou igual a",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "Создать карточку",
|
||||
"WhenCardMatches": "Когда карточка соответствует",
|
||||
"WhenFieldChanges": "Когда поле изменено",
|
||||
"WhenRequiredFieldsFilled": "Когда обязательные поля заполнены",
|
||||
"ArraySizeEquals": "Количество равно",
|
||||
"ArraySizeGt": "Количество больше",
|
||||
"ArraySizeGte": "Количество больше или равно",
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"CreateCard": "Kart oluştur",
|
||||
"WhenCardMatches": "Kart eşleştiğinde",
|
||||
"WhenFieldChanges": "Alan değiştiğinde",
|
||||
"WhenRequiredFieldsFilled": "Gerekli alanlar doldurulduğunda",
|
||||
"ArraySizeEquals": "Boyut eşittir",
|
||||
"ArraySizeGt": "Boyut büyüktür",
|
||||
"ArraySizeGte": "Boyut büyük veya eşittir",
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"CreateCard": "创建卡片",
|
||||
"WhenCardMatches": "当卡片匹配时",
|
||||
"WhenFieldChanges": "当字段更改时",
|
||||
"WhenRequiredFieldsFilled": "当必需字段被填充时",
|
||||
"ArraySizeEquals": "大小等于",
|
||||
"ArraySizeGt": "大小大于",
|
||||
"ArraySizeGte": "大小大于等于",
|
||||
|
||||
@@ -106,6 +106,7 @@ import {
|
||||
eventCheck,
|
||||
fieldChangesCheck,
|
||||
matchCardCheck,
|
||||
requiredFieldsFilledCheck,
|
||||
showDoneQuery,
|
||||
subProcessesDoneCheck,
|
||||
subProcessMatchCheck,
|
||||
@@ -212,6 +213,7 @@ export default async (): Promise<Resources> => ({
|
||||
triggerCheck: {
|
||||
MatchCheck: matchCardCheck,
|
||||
FieldChangedCheck: fieldChangesCheck,
|
||||
RequiredFieldsFilledCheck: requiredFieldsFilledCheck,
|
||||
SubProcessesDoneCheck: subProcessesDoneCheck,
|
||||
SubProcessMatchCheck: subProcessMatchCheck,
|
||||
ToDo: todoTranstionCheck,
|
||||
|
||||
@@ -200,6 +200,7 @@ export default mergeIds(processId, process, {
|
||||
WaitUntil: '' as IntlString,
|
||||
WhenCardMatches: '' as IntlString,
|
||||
WhenFieldChanges: '' as IntlString,
|
||||
WhenRequiredFieldsFilled: '' as IntlString,
|
||||
OnEvent: '' as IntlString,
|
||||
Result: '' as IntlString,
|
||||
RequestResult: '' as IntlString,
|
||||
|
||||
@@ -23,6 +23,7 @@ import core, {
|
||||
type DocumentUpdate,
|
||||
findProperty,
|
||||
generateId,
|
||||
getObjectValue,
|
||||
matchQuery,
|
||||
type Ref,
|
||||
type RefTo,
|
||||
@@ -962,6 +963,36 @@ export function fieldChangesCheck (
|
||||
return res.length > 0
|
||||
}
|
||||
|
||||
function isRequiredValueFilled (value: any, attr: AnyAttribute): boolean {
|
||||
if (attr.type?._class === core.class.TypeMarkup) return !isEmptyMarkup(value)
|
||||
if (Array.isArray(value)) return value.length > 0
|
||||
return value !== undefined && value !== null && value !== ''
|
||||
}
|
||||
|
||||
export function requiredFieldsFilledCheck (
|
||||
client: Client,
|
||||
execution: Execution,
|
||||
params: Record<string, any>,
|
||||
context: Record<string, any>
|
||||
): boolean {
|
||||
let doc = context.card
|
||||
if (doc === undefined) return false
|
||||
const _process = client.getModel().findObject(execution.process)
|
||||
if (_process === undefined) return false
|
||||
const hierarchy = client.getHierarchy()
|
||||
const attributes = Array.from(
|
||||
hierarchy.isMixin(_process.masterTag)
|
||||
? hierarchy.getOwnAttributes(_process.masterTag).entries()
|
||||
: hierarchy.getAllAttributes(_process.masterTag, core.class.Doc).entries()
|
||||
).filter(([, attr]) => attr.required === true && attr.hidden !== true)
|
||||
|
||||
if (hierarchy.isMixin(_process.masterTag)) {
|
||||
doc = hierarchy.as(doc, _process.masterTag)
|
||||
}
|
||||
|
||||
return attributes.every(([key, attr]) => isRequiredValueFilled(getObjectValue(key, doc), attr))
|
||||
}
|
||||
|
||||
export async function subProcessesDoneCheck (
|
||||
client: Client,
|
||||
execution: Execution,
|
||||
|
||||
@@ -279,6 +279,7 @@ export default plugin(processId, {
|
||||
trigger: {
|
||||
OnCardUpdate: '' as Ref<Trigger>, // in fact WhenCardMatches, should migrate in future
|
||||
WhenFieldChanges: '' as Ref<Trigger>,
|
||||
WhenRequiredFieldsFilled: '' as Ref<Trigger>,
|
||||
OnSubProcessesDone: '' as Ref<Trigger>,
|
||||
OnSubProcessMatch: '' as Ref<Trigger>,
|
||||
OnToDoClose: '' as Ref<Trigger>,
|
||||
@@ -294,6 +295,7 @@ export default plugin(processId, {
|
||||
ToDo: '' as Resource<CheckFunc>,
|
||||
MatchCheck: '' as Resource<CheckFunc>,
|
||||
FieldChangedCheck: '' as Resource<CheckFunc>,
|
||||
RequiredFieldsFilledCheck: '' as Resource<CheckFunc>,
|
||||
SubProcessesDoneCheck: '' as Resource<CheckFunc>,
|
||||
SubProcessMatchCheck: '' as Resource<CheckFunc>,
|
||||
Time: '' as Resource<CheckFunc>,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { AnySvelteComponent, Icon, IconMoreV2, IconOpenedArrow, Label, tooltip } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import setting from '../plugin'
|
||||
|
||||
export let attribute: AnyAttribute
|
||||
export let attributeType: IntlString | undefined = undefined
|
||||
@@ -64,6 +65,9 @@
|
||||
{/if}
|
||||
<div class="hulyTableAttr-content__row-label font-regular-14 grow" class:accent={!attribute.hidden}>
|
||||
<Label label={attribute.label} />
|
||||
{#if attribute.required === true}
|
||||
<span class="required-marker font-medium-12" use:tooltip={{ label: setting.string.Required }}>*</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if attributeMapper}
|
||||
<svelte:component this={attributeMapper.component} {...attributeMapper.props} {attribute} />
|
||||
@@ -91,3 +95,18 @@
|
||||
<IconOpenedArrow size={'small'} />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
.hulyTableAttr-content__row-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
gap: var(--spacing-0_5);
|
||||
}
|
||||
|
||||
.required-marker {
|
||||
flex-shrink: 0;
|
||||
color: var(--global-error-TextColor);
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import cardPlugin, { Card, MasterTag, Tag } from '@hcengineering/card'
|
||||
import core, {
|
||||
Association,
|
||||
AnyAttribute,
|
||||
checkMixinKey,
|
||||
Class,
|
||||
Data,
|
||||
@@ -220,6 +221,36 @@ export function FieldChangedCheck (
|
||||
return res.length > 0
|
||||
}
|
||||
|
||||
function isRequiredValueFilled (value: any, attr: AnyAttribute): boolean {
|
||||
if (attr.type?._class === core.class.TypeMarkup) return !isEmptyMarkup(value)
|
||||
if (Array.isArray(value)) return value.length > 0
|
||||
return value !== undefined && value !== null && value !== ''
|
||||
}
|
||||
|
||||
export function RequiredFieldsFilledCheck (
|
||||
control: ProcessControl,
|
||||
execution: Execution,
|
||||
params: Record<string, any>,
|
||||
context: Record<string, any>
|
||||
): boolean {
|
||||
let card = context.card
|
||||
if (card === undefined) return false
|
||||
const _process = control.client.getModel().findObject(execution.process)
|
||||
if (_process === undefined) return false
|
||||
const hierarchy = control.client.getHierarchy()
|
||||
const attributes = Array.from(
|
||||
hierarchy.isMixin(_process.masterTag)
|
||||
? hierarchy.getOwnAttributes(_process.masterTag).entries()
|
||||
: hierarchy.getAllAttributes(_process.masterTag, core.class.Doc).entries()
|
||||
).filter(([, attr]) => attr.required === true && attr.hidden !== true)
|
||||
|
||||
if (hierarchy.isMixin(_process.masterTag)) {
|
||||
card = hierarchy.as(card, _process.masterTag)
|
||||
}
|
||||
|
||||
return attributes.every(([key, attr]) => isRequiredValueFilled(getObjectValue(key, card), attr))
|
||||
}
|
||||
|
||||
export function CheckTime (control: ProcessControl, execution: Execution, params: Record<string, any>): boolean {
|
||||
if (params.value === undefined) return false
|
||||
return params.value <= Date.now()
|
||||
|
||||
@@ -72,6 +72,7 @@ import {
|
||||
LockField,
|
||||
LockSection,
|
||||
MatchCardCheck,
|
||||
RequiredFieldsFilledCheck,
|
||||
RequestApproval,
|
||||
RunSubProcess,
|
||||
UnlockCard,
|
||||
@@ -588,7 +589,11 @@ export async function OnCardUpdate (txes: Tx[], control: TriggerControl): Promis
|
||||
const ops = isUpdateTx(cudTx) ? cudTx.operations : cudTx.attributes
|
||||
await putEventToQueue(
|
||||
{
|
||||
event: [process.trigger.OnCardUpdate, process.trigger.WhenFieldChanges],
|
||||
event: [
|
||||
process.trigger.OnCardUpdate,
|
||||
process.trigger.WhenFieldChanges,
|
||||
process.trigger.WhenRequiredFieldsFilled
|
||||
],
|
||||
card: cudTx.objectId,
|
||||
createdOn: tx.modifiedOn,
|
||||
_id: tx._id,
|
||||
@@ -731,6 +736,7 @@ export default async () => ({
|
||||
CheckToDoCancelled,
|
||||
FieldChangedCheck,
|
||||
MatchCardCheck,
|
||||
RequiredFieldsFilledCheck,
|
||||
CheckSubProcessesDone,
|
||||
CheckSubProcessMatch,
|
||||
CheckTime,
|
||||
|
||||
@@ -67,6 +67,7 @@ export default plugin(serverProcessId, {
|
||||
CheckToDoCancelled: '' as Resource<CheckFunc>,
|
||||
MatchCardCheck: '' as Resource<CheckFunc>,
|
||||
FieldChangedCheck: '' as Resource<CheckFunc>,
|
||||
RequiredFieldsFilledCheck: '' as Resource<CheckFunc>,
|
||||
CheckSubProcessesDone: '' as Resource<CheckFunc>,
|
||||
CheckSubProcessMatch: '' as Resource<CheckFunc>,
|
||||
CheckTime: '' as Resource<CheckFunc>,
|
||||
|
||||
@@ -164,7 +164,14 @@ async function processCardExecutions (control: ProcessControl, record: ProcessMe
|
||||
}
|
||||
|
||||
// we should update timers, probably context value changed
|
||||
if (record.event.some((p) => p === process.trigger.OnCardUpdate || p === process.trigger.WhenFieldChanges)) {
|
||||
if (
|
||||
record.event.some(
|
||||
(p) =>
|
||||
p === process.trigger.OnCardUpdate ||
|
||||
p === process.trigger.WhenFieldChanges ||
|
||||
p === process.trigger.WhenRequiredFieldsFilled
|
||||
)
|
||||
) {
|
||||
await updateTimers(control, record)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user