mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-10-08 02:06:16 -05:00
Add negation for other things, universal query builder
This commit is contained in:
@@ -172,32 +172,34 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) {
|
||||
<div class="col-md-6">
|
||||
<div class="trigger-tag-conditions mb-3">
|
||||
<div class="col">
|
||||
<div class="trigger-conditions mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="form-label mb-0" i18n>Tag conditions</label>
|
||||
<label class="form-label mb-0" i18n>Conditions</label>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-primary ms-auto"
|
||||
(click)="addTagCondition(i)"
|
||||
[disabled]="!canAddTagCondition(formGroup)"
|
||||
(click)="addCondition(formGroup)"
|
||||
[disabled]="!canAddCondition(formGroup)"
|
||||
>
|
||||
<i-bs name="plus-circle"></i-bs> <span i18n>Add condition</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2" formArrayName="tagConditions">
|
||||
@if (getTagConditionsFormArray(formGroup).length === 0) {
|
||||
<p class="text-muted small" i18n>No tag conditions added. Add one to refine tag-based matching.</p>
|
||||
<div class="mt-2" formArrayName="conditions">
|
||||
@if (getConditionsFormArray(formGroup).length === 0) {
|
||||
<p class="text-muted small" i18n>No conditions added. Add one to define document filters.</p>
|
||||
}
|
||||
@for (condition of getTagConditionsFormArray(formGroup).controls; track condition; let conditionIndex = $index) {
|
||||
@for (condition of getConditionsFormArray(formGroup).controls; track condition; let conditionIndex = $index) {
|
||||
<div [formGroupName]="conditionIndex" class="border rounded p-3 mb-2">
|
||||
<div class="d-flex align-items-start gap-2 mb-2">
|
||||
<div class="flex-grow-1">
|
||||
<pngx-input-select
|
||||
i18n-title
|
||||
title="Condition type"
|
||||
[items]="getTagConditionSelectItems(formGroup, conditionIndex)"
|
||||
[items]="getConditionTypeOptions(formGroup, conditionIndex)"
|
||||
formControlName="type"
|
||||
[allowNull]="false"
|
||||
></pngx-input-select>
|
||||
@@ -205,25 +207,33 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-link text-danger p-0 ms-1"
|
||||
(click)="removeTagCondition(i, conditionIndex)"
|
||||
(click)="removeCondition(formGroup, conditionIndex)"
|
||||
>
|
||||
<i-bs name="trash"></i-bs>
|
||||
<span class="visually-hidden" i18n>Remove condition</span>
|
||||
</button>
|
||||
</div>
|
||||
<pngx-input-tags
|
||||
[allowCreate]="false"
|
||||
[title]="getTagConditionLabel(condition.get('type').value)"
|
||||
[hint]="getTagConditionHint(formGroup, conditionIndex)"
|
||||
formControlName="tags"
|
||||
></pngx-input-tags>
|
||||
@if (isTagsCondition(condition.get('type').value)) {
|
||||
<pngx-input-tags
|
||||
[allowCreate]="false"
|
||||
[title]="getConditionValueLabel(condition.get('type').value)"
|
||||
[hint]="getConditionHint(formGroup, conditionIndex)"
|
||||
formControlName="values"
|
||||
></pngx-input-tags>
|
||||
} @else {
|
||||
<pngx-input-select
|
||||
[title]="getConditionValueLabel(condition.get('type').value)"
|
||||
[items]="getConditionSelectItems(condition.get('type').value)"
|
||||
[hint]="getConditionHint(formGroup, conditionIndex)"
|
||||
[allowNull]="true"
|
||||
[multiple]="isSelectMultiple(condition.get('type').value)"
|
||||
formControlName="values"
|
||||
></pngx-input-select>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<pngx-input-select i18n-title title="Has correspondent" [items]="correspondents" [allowNull]="true" formControlName="filter_has_correspondent"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Has document type" [items]="documentTypes" [allowNull]="true" formControlName="filter_has_document_type"></pngx-input-select>
|
||||
<pngx-input-select i18n-title title="Has storage path" [items]="storagePaths" [allowNull]="true" formControlName="filter_has_storage_path"></pngx-input-select>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@@ -43,6 +43,7 @@ import { EditDialogMode } from '../edit-dialog.component'
|
||||
import {
|
||||
DOCUMENT_SOURCE_OPTIONS,
|
||||
SCHEDULE_DATE_FIELD_OPTIONS,
|
||||
TriggerConditionType,
|
||||
WORKFLOW_ACTION_OPTIONS,
|
||||
WORKFLOW_TYPE_OPTIONS,
|
||||
WorkflowEditDialogComponent,
|
||||
@@ -375,29 +376,73 @@ describe('WorkflowEditDialogComponent', () => {
|
||||
expect(component.objectForm.get('actions').value[0].webhook).toBeNull()
|
||||
})
|
||||
|
||||
it('should map tag condition builder values into trigger filters on save', () => {
|
||||
it('should map condition builder values into trigger filters on save', () => {
|
||||
component.object = undefined
|
||||
component.addTrigger()
|
||||
const triggerGroup = component.triggerFields.at(0)
|
||||
component.addTagCondition(0)
|
||||
component.addTagCondition(0)
|
||||
component.addTagCondition(0)
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
|
||||
const tagConditions = component.getTagConditionsFormArray(
|
||||
const conditions = component.getConditionsFormArray(
|
||||
triggerGroup as FormGroup
|
||||
)
|
||||
expect(tagConditions.length).toBe(3)
|
||||
expect(conditions.length).toBe(3)
|
||||
|
||||
tagConditions.at(0).get('tags').setValue([1])
|
||||
tagConditions.at(1).get('tags').setValue([2, 3])
|
||||
tagConditions.at(2).get('tags').setValue([4])
|
||||
conditions.at(0).get('values').setValue([1])
|
||||
conditions.at(1).get('values').setValue([2, 3])
|
||||
conditions.at(2).get('values').setValue([4])
|
||||
|
||||
const addConditionOfType = (type: TriggerConditionType) => {
|
||||
component.addCondition(triggerGroup as FormGroup)
|
||||
const conditionArray = component.getConditionsFormArray(
|
||||
triggerGroup as FormGroup
|
||||
)
|
||||
const newCondition = conditionArray.at(conditionArray.length - 1)
|
||||
newCondition.get('type').setValue(type)
|
||||
return newCondition
|
||||
}
|
||||
|
||||
const correspondentIs = addConditionOfType(
|
||||
TriggerConditionType.CorrespondentIs
|
||||
)
|
||||
correspondentIs.get('values').setValue(1)
|
||||
|
||||
const correspondentNot = addConditionOfType(
|
||||
TriggerConditionType.CorrespondentNot
|
||||
)
|
||||
correspondentNot.get('values').setValue([1])
|
||||
|
||||
const documentTypeIs = addConditionOfType(
|
||||
TriggerConditionType.DocumentTypeIs
|
||||
)
|
||||
documentTypeIs.get('values').setValue(1)
|
||||
|
||||
const documentTypeNot = addConditionOfType(
|
||||
TriggerConditionType.DocumentTypeNot
|
||||
)
|
||||
documentTypeNot.get('values').setValue([1])
|
||||
|
||||
const storagePathIs = addConditionOfType(TriggerConditionType.StoragePathIs)
|
||||
storagePathIs.get('values').setValue(1)
|
||||
|
||||
const storagePathNot = addConditionOfType(
|
||||
TriggerConditionType.StoragePathNot
|
||||
)
|
||||
storagePathNot.get('values').setValue([1])
|
||||
|
||||
const formValues = component['getFormValues']()
|
||||
|
||||
expect(formValues.triggers[0].filter_has_tags).toEqual([1])
|
||||
expect(formValues.triggers[0].filter_has_all_tags).toEqual([2, 3])
|
||||
expect(formValues.triggers[0].filter_has_not_tags).toEqual([4])
|
||||
expect(formValues.triggers[0].tagConditions).toBeUndefined()
|
||||
expect(formValues.triggers[0].filter_has_correspondent).toEqual(1)
|
||||
expect(formValues.triggers[0].filter_has_not_correspondents).toEqual([1])
|
||||
expect(formValues.triggers[0].filter_has_document_type).toEqual(1)
|
||||
expect(formValues.triggers[0].filter_has_not_document_types).toEqual([1])
|
||||
expect(formValues.triggers[0].filter_has_storage_path).toEqual(1)
|
||||
expect(formValues.triggers[0].filter_has_not_storage_paths).toEqual([1])
|
||||
expect(formValues.triggers[0].conditions).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should remove selected custom field from the form group', () => {
|
||||
|
@@ -135,27 +135,116 @@ export const WORKFLOW_ACTION_OPTIONS = [
|
||||
},
|
||||
]
|
||||
|
||||
export enum TagConditionType {
|
||||
Any = 'any',
|
||||
All = 'all',
|
||||
None = 'none',
|
||||
export enum TriggerConditionType {
|
||||
TagsAny = 'tags_any',
|
||||
TagsAll = 'tags_all',
|
||||
TagsNone = 'tags_none',
|
||||
CorrespondentIs = 'correspondent_is',
|
||||
CorrespondentNot = 'correspondent_not',
|
||||
DocumentTypeIs = 'document_type_is',
|
||||
DocumentTypeNot = 'document_type_not',
|
||||
StoragePathIs = 'storage_path_is',
|
||||
StoragePathNot = 'storage_path_not',
|
||||
}
|
||||
|
||||
const TAG_CONDITION_OPTIONS = [
|
||||
interface TriggerConditionDefinition {
|
||||
id: TriggerConditionType
|
||||
name: string
|
||||
hint?: string
|
||||
valueLabel: string
|
||||
inputType: 'tags' | 'select'
|
||||
allowMultipleEntries: boolean
|
||||
allowMultipleValues: boolean
|
||||
selectItems?: 'correspondents' | 'documentTypes' | 'storagePaths'
|
||||
}
|
||||
|
||||
const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [
|
||||
{
|
||||
id: TagConditionType.Any,
|
||||
id: TriggerConditionType.TagsAny,
|
||||
name: $localize`Has any of these tags`,
|
||||
hint: $localize`Trigger matches when the document has at least one of the selected tags.`,
|
||||
valueLabel: $localize`Tags`,
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
},
|
||||
{
|
||||
id: TagConditionType.All,
|
||||
id: TriggerConditionType.TagsAll,
|
||||
name: $localize`Has all of these tags`,
|
||||
hint: $localize`Trigger matches when the document has every tag in the selection.`,
|
||||
hint: $localize`Trigger matches only when every selected tag is present.`,
|
||||
valueLabel: $localize`Tags`,
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
},
|
||||
{
|
||||
id: TagConditionType.None,
|
||||
id: TriggerConditionType.TagsNone,
|
||||
name: $localize`Does not have these tags`,
|
||||
hint: $localize`Trigger matches when the document has none of the selected tags.`,
|
||||
hint: $localize`Trigger matches only when none of the selected tags are present.`,
|
||||
valueLabel: $localize`Tags`,
|
||||
inputType: 'tags',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentIs,
|
||||
name: $localize`Has correspondent`,
|
||||
hint: $localize`Trigger matches when the document has the selected correspondent.`,
|
||||
valueLabel: $localize`Correspondent`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: false,
|
||||
selectItems: 'correspondents',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.CorrespondentNot,
|
||||
name: $localize`Does not have correspondents`,
|
||||
hint: $localize`Trigger matches when the document does not have any of the selected correspondents.`,
|
||||
valueLabel: $localize`Correspondents`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
selectItems: 'correspondents',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.DocumentTypeIs,
|
||||
name: $localize`Has document type`,
|
||||
hint: $localize`Trigger matches when the document has the selected document type.`,
|
||||
valueLabel: $localize`Document type`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: false,
|
||||
selectItems: 'documentTypes',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.DocumentTypeNot,
|
||||
name: $localize`Does not have document types`,
|
||||
hint: $localize`Trigger matches when the document does not have any of the selected document types.`,
|
||||
valueLabel: $localize`Document types`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
selectItems: 'documentTypes',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.StoragePathIs,
|
||||
name: $localize`Has storage path`,
|
||||
hint: $localize`Trigger matches when the document has the selected storage path.`,
|
||||
valueLabel: $localize`Storage path`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: false,
|
||||
selectItems: 'storagePaths',
|
||||
},
|
||||
{
|
||||
id: TriggerConditionType.StoragePathNot,
|
||||
name: $localize`Does not have storage paths`,
|
||||
hint: $localize`Trigger matches when the document does not have any of the selected storage paths.`,
|
||||
valueLabel: $localize`Storage paths`,
|
||||
inputType: 'select',
|
||||
allowMultipleEntries: false,
|
||||
allowMultipleValues: true,
|
||||
selectItems: 'storagePaths',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -194,8 +283,8 @@ export class WorkflowEditDialogComponent
|
||||
{
|
||||
public WorkflowTriggerType = WorkflowTriggerType
|
||||
public WorkflowActionType = WorkflowActionType
|
||||
public TagConditionType = TagConditionType
|
||||
public tagConditionOptions = TAG_CONDITION_OPTIONS
|
||||
public TriggerConditionType = TriggerConditionType
|
||||
public conditionDefinitions = TRIGGER_CONDITION_DEFINITIONS
|
||||
|
||||
private correspondentService: CorrespondentService
|
||||
private documentTypeService: DocumentTypeService
|
||||
@@ -423,29 +512,86 @@ export class WorkflowEditDialogComponent
|
||||
formValues.triggers = formValues.triggers.map(
|
||||
(trigger: any, index: number) => {
|
||||
const triggerFormGroup = this.triggerFields.at(index) as FormGroup
|
||||
const conditions = this.getTagConditionsFormArray(triggerFormGroup)
|
||||
const conditions = this.getConditionsFormArray(triggerFormGroup)
|
||||
|
||||
const tagBuckets: Record<TagConditionType, number[]> = {
|
||||
[TagConditionType.Any]: [],
|
||||
[TagConditionType.All]: [],
|
||||
[TagConditionType.None]: [],
|
||||
const aggregate = {
|
||||
filter_has_tags: [] as number[],
|
||||
filter_has_all_tags: [] as number[],
|
||||
filter_has_not_tags: [] as number[],
|
||||
filter_has_not_correspondents: [] as number[],
|
||||
filter_has_not_document_types: [] as number[],
|
||||
filter_has_not_storage_paths: [] as number[],
|
||||
filter_has_correspondent: null as number | null,
|
||||
filter_has_document_type: null as number | null,
|
||||
filter_has_storage_path: null as number | null,
|
||||
}
|
||||
|
||||
conditions.controls.forEach((control) => {
|
||||
const type = control.get('type').value as TagConditionType
|
||||
const tags = control.get('tags').value as number[]
|
||||
if (tags?.length) {
|
||||
tagBuckets[type] = [...tags]
|
||||
} else {
|
||||
tagBuckets[type] = []
|
||||
const type = control.get('type').value as TriggerConditionType
|
||||
const values = control.get('values').value
|
||||
|
||||
if (values === null || values === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(values) && values.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TriggerConditionType.TagsAny:
|
||||
aggregate.filter_has_tags = [...values]
|
||||
break
|
||||
case TriggerConditionType.TagsAll:
|
||||
aggregate.filter_has_all_tags = [...values]
|
||||
break
|
||||
case TriggerConditionType.TagsNone:
|
||||
aggregate.filter_has_not_tags = [...values]
|
||||
break
|
||||
case TriggerConditionType.CorrespondentIs:
|
||||
aggregate.filter_has_correspondent = Array.isArray(values)
|
||||
? values[0]
|
||||
: values
|
||||
break
|
||||
case TriggerConditionType.CorrespondentNot:
|
||||
aggregate.filter_has_not_correspondents = [...values]
|
||||
break
|
||||
case TriggerConditionType.DocumentTypeIs:
|
||||
aggregate.filter_has_document_type = Array.isArray(values)
|
||||
? values[0]
|
||||
: values
|
||||
break
|
||||
case TriggerConditionType.DocumentTypeNot:
|
||||
aggregate.filter_has_not_document_types = [...values]
|
||||
break
|
||||
case TriggerConditionType.StoragePathIs:
|
||||
aggregate.filter_has_storage_path = Array.isArray(values)
|
||||
? values[0]
|
||||
: values
|
||||
break
|
||||
case TriggerConditionType.StoragePathNot:
|
||||
aggregate.filter_has_not_storage_paths = [...values]
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
trigger.filter_has_tags = tagBuckets[TagConditionType.Any]
|
||||
trigger.filter_has_all_tags = tagBuckets[TagConditionType.All]
|
||||
trigger.filter_has_not_tags = tagBuckets[TagConditionType.None]
|
||||
trigger.filter_has_tags = aggregate.filter_has_tags
|
||||
trigger.filter_has_all_tags = aggregate.filter_has_all_tags
|
||||
trigger.filter_has_not_tags = aggregate.filter_has_not_tags
|
||||
trigger.filter_has_not_correspondents =
|
||||
aggregate.filter_has_not_correspondents
|
||||
trigger.filter_has_not_document_types =
|
||||
aggregate.filter_has_not_document_types
|
||||
trigger.filter_has_not_storage_paths =
|
||||
aggregate.filter_has_not_storage_paths
|
||||
trigger.filter_has_correspondent =
|
||||
aggregate.filter_has_correspondent ?? null
|
||||
trigger.filter_has_document_type =
|
||||
aggregate.filter_has_document_type ?? null
|
||||
trigger.filter_has_storage_path =
|
||||
aggregate.filter_has_storage_path ?? null
|
||||
|
||||
delete trigger.tagConditions
|
||||
delete trigger.conditions
|
||||
|
||||
return trigger
|
||||
}
|
||||
@@ -455,110 +601,281 @@ export class WorkflowEditDialogComponent
|
||||
return formValues
|
||||
}
|
||||
|
||||
private createTagConditionFormGroup(
|
||||
type: TagConditionType,
|
||||
tags: number[] = []
|
||||
private createConditionFormGroup(
|
||||
type: TriggerConditionType,
|
||||
initialValue?: number | number[]
|
||||
): FormGroup {
|
||||
return new FormGroup({
|
||||
const group = new FormGroup({
|
||||
type: new FormControl(type),
|
||||
tags: new FormControl(tags ?? []),
|
||||
values: new FormControl(this.normalizeConditionValue(type, initialValue)),
|
||||
})
|
||||
|
||||
group
|
||||
.get('type')
|
||||
.valueChanges.subscribe((newType: TriggerConditionType) => {
|
||||
group.get('values').setValue(this.getDefaultConditionValue(newType), {
|
||||
emitEvent: false,
|
||||
})
|
||||
})
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
private buildTagConditionsFormArray(trigger: WorkflowTrigger): FormArray {
|
||||
private buildConditionFormArray(trigger: WorkflowTrigger): FormArray {
|
||||
const conditions = new FormArray([])
|
||||
|
||||
if (trigger.filter_has_tags && trigger.filter_has_tags.length > 0) {
|
||||
conditions.push(
|
||||
this.createTagConditionFormGroup(TagConditionType.Any, [
|
||||
...trigger.filter_has_tags,
|
||||
])
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.TagsAny,
|
||||
trigger.filter_has_tags
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (trigger.filter_has_all_tags && trigger.filter_has_all_tags.length > 0) {
|
||||
conditions.push(
|
||||
this.createTagConditionFormGroup(TagConditionType.All, [
|
||||
...trigger.filter_has_all_tags,
|
||||
])
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.TagsAll,
|
||||
trigger.filter_has_all_tags
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (trigger.filter_has_not_tags && trigger.filter_has_not_tags.length > 0) {
|
||||
conditions.push(
|
||||
this.createTagConditionFormGroup(TagConditionType.None, [
|
||||
...trigger.filter_has_not_tags,
|
||||
])
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.TagsNone,
|
||||
trigger.filter_has_not_tags
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (trigger.filter_has_correspondent) {
|
||||
conditions.push(
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.CorrespondentIs,
|
||||
trigger.filter_has_correspondent
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
trigger.filter_has_not_correspondents &&
|
||||
trigger.filter_has_not_correspondents.length > 0
|
||||
) {
|
||||
conditions.push(
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.CorrespondentNot,
|
||||
trigger.filter_has_not_correspondents
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (trigger.filter_has_document_type) {
|
||||
conditions.push(
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.DocumentTypeIs,
|
||||
trigger.filter_has_document_type
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
trigger.filter_has_not_document_types &&
|
||||
trigger.filter_has_not_document_types.length > 0
|
||||
) {
|
||||
conditions.push(
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.DocumentTypeNot,
|
||||
trigger.filter_has_not_document_types
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (trigger.filter_has_storage_path) {
|
||||
conditions.push(
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.StoragePathIs,
|
||||
trigger.filter_has_storage_path
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
trigger.filter_has_not_storage_paths &&
|
||||
trigger.filter_has_not_storage_paths.length > 0
|
||||
) {
|
||||
conditions.push(
|
||||
this.createConditionFormGroup(
|
||||
TriggerConditionType.StoragePathNot,
|
||||
trigger.filter_has_not_storage_paths
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return conditions
|
||||
}
|
||||
|
||||
getTagConditionsFormArray(formGroup: FormGroup): FormArray {
|
||||
return formGroup.get('tagConditions') as FormArray
|
||||
getConditionsFormArray(formGroup: FormGroup): FormArray {
|
||||
return formGroup.get('conditions') as FormArray
|
||||
}
|
||||
|
||||
getTagConditionLabel(type: TagConditionType): string {
|
||||
return (
|
||||
this.tagConditionOptions.find((option) => option.id === type)?.name ?? ''
|
||||
)
|
||||
}
|
||||
getConditionTypeOptions(formGroup: FormGroup, conditionIndex: number) {
|
||||
const conditions = this.getConditionsFormArray(formGroup)
|
||||
|
||||
getTagConditionHint(formGroup: FormGroup, conditionIndex: number): string {
|
||||
const conditions = this.getTagConditionsFormArray(formGroup)
|
||||
const type = conditions.at(conditionIndex).get('type')
|
||||
.value as TagConditionType
|
||||
return (
|
||||
this.tagConditionOptions.find((option) => option.id === type)?.hint ?? ''
|
||||
)
|
||||
}
|
||||
|
||||
getTagConditionSelectItems(formGroup: FormGroup, conditionIndex: number) {
|
||||
const conditions = this.getTagConditionsFormArray(formGroup)
|
||||
return this.tagConditionOptions.map((option) => ({
|
||||
...option,
|
||||
disabled: conditions.controls.some((control, idx) => {
|
||||
if (idx === conditionIndex) {
|
||||
return false
|
||||
}
|
||||
return control.get('type').value === option.id
|
||||
}),
|
||||
return this.conditionDefinitions.map((definition) => ({
|
||||
id: definition.id,
|
||||
name: definition.name,
|
||||
disabled:
|
||||
!definition.allowMultipleEntries &&
|
||||
conditions.controls.some((control, idx) => {
|
||||
if (idx === conditionIndex) {
|
||||
return false
|
||||
}
|
||||
return control.get('type').value === definition.id
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
canAddTagCondition(formGroup: FormGroup): boolean {
|
||||
const conditions = this.getTagConditionsFormArray(formGroup)
|
||||
return conditions.length < this.tagConditionOptions.length
|
||||
canAddCondition(formGroup: FormGroup): boolean {
|
||||
const conditions = this.getConditionsFormArray(formGroup)
|
||||
const usedTypes = conditions.controls.map(
|
||||
(control) => control.get('type').value as TriggerConditionType
|
||||
)
|
||||
|
||||
return this.conditionDefinitions.some((definition) => {
|
||||
if (definition.allowMultipleEntries) {
|
||||
return true
|
||||
}
|
||||
return !usedTypes.includes(definition.id)
|
||||
})
|
||||
}
|
||||
|
||||
addTagCondition(triggerIndex: number) {
|
||||
const triggerFormGroup = this.triggerFields.at(triggerIndex) as FormGroup
|
||||
const conditions = this.getTagConditionsFormArray(triggerFormGroup)
|
||||
const availableTypes = this.tagConditionOptions
|
||||
.map((option) => option.id)
|
||||
.filter(
|
||||
(type) =>
|
||||
!conditions.controls.some(
|
||||
(control) => control.get('type').value === type
|
||||
)
|
||||
)
|
||||
if (availableTypes.length === 0) {
|
||||
addCondition(triggerFormGroup: FormGroup) {
|
||||
const triggerIndex = this.triggerFields.controls.indexOf(triggerFormGroup)
|
||||
if (triggerIndex === -1) {
|
||||
return
|
||||
}
|
||||
conditions.push(this.createTagConditionFormGroup(availableTypes[0]))
|
||||
|
||||
const conditions = this.getConditionsFormArray(triggerFormGroup)
|
||||
|
||||
const availableDefinition = this.conditionDefinitions.find((definition) => {
|
||||
if (definition.allowMultipleEntries) {
|
||||
return true
|
||||
}
|
||||
return !conditions.controls.some(
|
||||
(control) => control.get('type').value === definition.id
|
||||
)
|
||||
})
|
||||
|
||||
if (!availableDefinition) {
|
||||
return
|
||||
}
|
||||
|
||||
conditions.push(this.createConditionFormGroup(availableDefinition.id))
|
||||
triggerFormGroup.markAsDirty()
|
||||
triggerFormGroup.markAsTouched()
|
||||
}
|
||||
|
||||
removeTagCondition(triggerIndex: number, conditionIndex: number) {
|
||||
const triggerFormGroup = this.triggerFields.at(triggerIndex) as FormGroup
|
||||
const conditions = this.getTagConditionsFormArray(triggerFormGroup)
|
||||
removeCondition(triggerFormGroup: FormGroup, conditionIndex: number) {
|
||||
const triggerIndex = this.triggerFields.controls.indexOf(triggerFormGroup)
|
||||
if (triggerIndex === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
const conditions = this.getConditionsFormArray(triggerFormGroup)
|
||||
conditions.removeAt(conditionIndex)
|
||||
triggerFormGroup.markAsDirty()
|
||||
triggerFormGroup.markAsTouched()
|
||||
}
|
||||
|
||||
getConditionDefinition(
|
||||
type: TriggerConditionType
|
||||
): TriggerConditionDefinition | undefined {
|
||||
return this.conditionDefinitions.find(
|
||||
(definition) => definition.id === type
|
||||
)
|
||||
}
|
||||
|
||||
getConditionName(type: TriggerConditionType): string {
|
||||
return this.getConditionDefinition(type)?.name ?? ''
|
||||
}
|
||||
|
||||
getConditionHint(formGroup: FormGroup, conditionIndex: number): string {
|
||||
const conditions = this.getConditionsFormArray(formGroup)
|
||||
const type = conditions.at(conditionIndex).get('type')
|
||||
.value as TriggerConditionType
|
||||
return this.getConditionDefinition(type)?.hint ?? ''
|
||||
}
|
||||
|
||||
getConditionValueLabel(type: TriggerConditionType): string {
|
||||
return this.getConditionDefinition(type)?.valueLabel ?? ''
|
||||
}
|
||||
|
||||
isTagsCondition(type: TriggerConditionType): boolean {
|
||||
return this.getConditionDefinition(type)?.inputType === 'tags'
|
||||
}
|
||||
|
||||
isMultiValueCondition(type: TriggerConditionType): boolean {
|
||||
switch (type) {
|
||||
case TriggerConditionType.TagsAny:
|
||||
case TriggerConditionType.TagsAll:
|
||||
case TriggerConditionType.TagsNone:
|
||||
case TriggerConditionType.CorrespondentNot:
|
||||
case TriggerConditionType.DocumentTypeNot:
|
||||
case TriggerConditionType.StoragePathNot:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
isSelectMultiple(type: TriggerConditionType): boolean {
|
||||
return !this.isTagsCondition(type) && this.isMultiValueCondition(type)
|
||||
}
|
||||
|
||||
getConditionSelectItems(type: TriggerConditionType) {
|
||||
const definition = this.getConditionDefinition(type)
|
||||
if (!definition || definition.inputType !== 'select') {
|
||||
return []
|
||||
}
|
||||
|
||||
switch (definition.selectItems) {
|
||||
case 'correspondents':
|
||||
return this.correspondents
|
||||
case 'documentTypes':
|
||||
return this.documentTypes
|
||||
case 'storagePaths':
|
||||
return this.storagePaths
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private getDefaultConditionValue(type: TriggerConditionType) {
|
||||
return this.isMultiValueCondition(type) ? [] : null
|
||||
}
|
||||
|
||||
private normalizeConditionValue(
|
||||
type: TriggerConditionType,
|
||||
value?: number | number[]
|
||||
) {
|
||||
if (value === undefined || value === null) {
|
||||
return this.getDefaultConditionValue(type)
|
||||
}
|
||||
|
||||
if (this.isMultiValueCondition(type)) {
|
||||
return Array.isArray(value) ? [...value] : [value]
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.length > 0 ? value[0] : null
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
private createTriggerField(
|
||||
trigger: WorkflowTrigger,
|
||||
emitEvent: boolean = false
|
||||
@@ -583,7 +900,7 @@ export class WorkflowEditDialogComponent
|
||||
filter_has_storage_path: new FormControl(
|
||||
trigger.filter_has_storage_path
|
||||
),
|
||||
tagConditions: this.buildTagConditionsFormArray(trigger),
|
||||
conditions: this.buildConditionFormArray(trigger),
|
||||
schedule_offset_days: new FormControl(trigger.schedule_offset_days),
|
||||
schedule_is_recurring: new FormControl(trigger.schedule_is_recurring),
|
||||
schedule_recurring_interval_days: new FormControl(
|
||||
@@ -708,6 +1025,9 @@ export class WorkflowEditDialogComponent
|
||||
filter_has_tags: [],
|
||||
filter_has_all_tags: [],
|
||||
filter_has_not_tags: [],
|
||||
filter_has_not_correspondents: [],
|
||||
filter_has_not_document_types: [],
|
||||
filter_has_not_storage_paths: [],
|
||||
filter_has_correspondent: null,
|
||||
filter_has_document_type: null,
|
||||
filter_has_storage_path: null,
|
||||
|
@@ -44,6 +44,12 @@ export interface WorkflowTrigger extends ObjectWithId {
|
||||
|
||||
filter_has_not_tags?: number[] // Tag.id[]
|
||||
|
||||
filter_has_not_correspondents?: number[] // Correspondent.id[]
|
||||
|
||||
filter_has_not_document_types?: number[] // DocumentType.id[]
|
||||
|
||||
filter_has_not_storage_paths?: number[] // StoragePath.id[]
|
||||
|
||||
filter_has_correspondent?: number // Correspondent.id
|
||||
|
||||
filter_has_document_type?: number // DocumentType.id
|
||||
|
Reference in New Issue
Block a user