mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Refactor workflow trigger conditions to filters
This commit is contained in:
		| @@ -176,58 +176,58 @@ | ||||
|     @if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) { | ||||
|       <div class="row mt-3"> | ||||
|         <div class="col"> | ||||
|           <div class="trigger-conditions mb-3"> | ||||
|           <div class="trigger-filters mb-3"> | ||||
|             <div class="d-flex align-items-center"> | ||||
|               <label class="form-label mb-0" i18n>Conditions</label> | ||||
|               <label class="form-label mb-0" i18n>Advanced Filters</label> | ||||
|               <button | ||||
|                 type="button" | ||||
|                 class="btn btn-sm btn-outline-primary ms-auto" | ||||
|                 (click)="addCondition(formGroup)" | ||||
|                 [disabled]="!canAddCondition(formGroup)" | ||||
|                 (click)="addFilter(formGroup)" | ||||
|                 [disabled]="!canAddFilter(formGroup)" | ||||
|               > | ||||
|                 <i-bs name="plus-circle"></i-bs> <span i18n>Add condition</span> | ||||
|                 <i-bs name="plus-circle"></i-bs> <span i18n>Add filter</span> | ||||
|               </button> | ||||
|             </div> | ||||
|             <ul class="mt-2 list-group conditions" formArrayName="conditions"> | ||||
|               @if (getConditionsFormArray(formGroup).length === 0) { | ||||
|                 <p class="text-muted small" i18n>No conditions added. Add one to define document filters.</p> | ||||
|             <ul class="mt-2 list-group filters" formArrayName="filters"> | ||||
|               @if (getFiltersFormArray(formGroup).length === 0) { | ||||
|                 <p class="text-muted small" i18n>No advanced workflow filters defined.</p> | ||||
|               } | ||||
|               @for (condition of getConditionsFormArray(formGroup).controls; track condition; let conditionIndex = $index) { | ||||
|                 <li [formGroupName]="conditionIndex" class="list-group-item"> | ||||
|               @for (filter of getFiltersFormArray(formGroup).controls; track filter; let filterIndex = $index) { | ||||
|                 <li [formGroupName]="filterIndex" class="list-group-item"> | ||||
|                   <div class="d-flex align-items-center gap-2"> | ||||
|                     <div class="w-25"> | ||||
|                       <pngx-input-select | ||||
|                         i18n-title | ||||
|                         [items]="getConditionTypeOptions(formGroup, conditionIndex)" | ||||
|                         [items]="getFilterTypeOptions(formGroup, filterIndex)" | ||||
|                         formControlName="type" | ||||
|                         [allowNull]="false" | ||||
|                       ></pngx-input-select> | ||||
|                     </div> | ||||
|                     <div class="flex-grow-1"> | ||||
|                       @if (isTagsCondition(condition.get('type').value)) { | ||||
|                       @if (isTagsFilter(filter.get('type').value)) { | ||||
|                         <pngx-input-tags | ||||
|                           [allowCreate]="false" | ||||
|                           [title]="null" | ||||
|                           formControlName="values" | ||||
|                         ></pngx-input-tags> | ||||
|                       } @else if ( | ||||
|                         isCustomFieldQueryCondition(condition.get('type').value) | ||||
|                         isCustomFieldQueryFilter(filter.get('type').value) | ||||
|                       ) { | ||||
|                         <pngx-custom-fields-query-dropdown | ||||
|                           [selectionModel]="getCustomFieldQueryModel(condition)" | ||||
|                           (selectionModelChange)="onCustomFieldQuerySelectionChange(condition, $event)" | ||||
|                           [selectionModel]="getCustomFieldQueryModel(filter)" | ||||
|                           (selectionModelChange)="onCustomFieldQuerySelectionChange(filter, $event)" | ||||
|                           [useDropdown]="false" | ||||
|                         ></pngx-custom-fields-query-dropdown> | ||||
|                         @if (!isCustomFieldQueryValid(condition)) { | ||||
|                         @if (!isCustomFieldQueryValid(filter)) { | ||||
|                           <div class="text-danger small" i18n> | ||||
|                             Complete the custom field query configuration. | ||||
|                           </div> | ||||
|                         } | ||||
|                       } @else { | ||||
|                         <pngx-input-select | ||||
|                           [items]="getConditionSelectItems(condition.get('type').value)" | ||||
|                           [items]="getFilterSelectItems(filter.get('type').value)" | ||||
|                           [allowNull]="true" | ||||
|                           [multiple]="isSelectMultiple(condition.get('type').value)" | ||||
|                           [multiple]="isSelectMultiple(filter.get('type').value)" | ||||
|                           formControlName="values" | ||||
|                         ></pngx-input-select> | ||||
|                       } | ||||
| @@ -235,7 +235,7 @@ | ||||
|                     <button | ||||
|                       type="button" | ||||
|                       class="btn btn-link text-danger p-0" | ||||
|                       (click)="removeCondition(formGroup, conditionIndex)" | ||||
|                       (click)="removeFilter(formGroup, filterIndex)" | ||||
|                     > | ||||
|                       <i-bs name="trash"></i-bs><span class="ms-1" i18n>Delete</span> | ||||
|                     </button> | ||||
|   | ||||
| @@ -8,6 +8,6 @@ | ||||
|     font-size: 1rem; | ||||
| } | ||||
|  | ||||
| :host ::ng-deep .conditions .paperless-input-select.mb-3 { | ||||
| :host ::ng-deep .filters .paperless-input-select.mb-3 { | ||||
|     margin-bottom: 0 !important; | ||||
| } | ||||
|   | ||||
| @@ -50,7 +50,7 @@ import { EditDialogMode } from '../edit-dialog.component' | ||||
| import { | ||||
|   DOCUMENT_SOURCE_OPTIONS, | ||||
|   SCHEDULE_DATE_FIELD_OPTIONS, | ||||
|   TriggerConditionType, | ||||
|   TriggerFilterType, | ||||
|   WORKFLOW_ACTION_OPTIONS, | ||||
|   WORKFLOW_TYPE_OPTIONS, | ||||
|   WorkflowEditDialogComponent, | ||||
| @@ -395,62 +395,50 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     expect(component.matchingPatternRequired(triggerGroup)).toBe(false) | ||||
|   }) | ||||
|  | ||||
|   it('should map condition builder values into trigger filters on save', () => { | ||||
|   it('should map filter builder values into trigger filters on save', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) | ||||
|     component.addCondition(triggerGroup as FormGroup) | ||||
|     component.addCondition(triggerGroup as FormGroup) | ||||
|     component.addCondition(triggerGroup as FormGroup) | ||||
|     component.addFilter(triggerGroup as FormGroup) | ||||
|     component.addFilter(triggerGroup as FormGroup) | ||||
|     component.addFilter(triggerGroup as FormGroup) | ||||
|  | ||||
|     const conditions = component.getConditionsFormArray( | ||||
|       triggerGroup as FormGroup | ||||
|     ) | ||||
|     expect(conditions.length).toBe(3) | ||||
|     const filters = component.getFiltersFormArray(triggerGroup as FormGroup) | ||||
|     expect(filters.length).toBe(3) | ||||
|  | ||||
|     conditions.at(0).get('values').setValue([1]) | ||||
|     conditions.at(1).get('values').setValue([2, 3]) | ||||
|     conditions.at(2).get('values').setValue([4]) | ||||
|     filters.at(0).get('values').setValue([1]) | ||||
|     filters.at(1).get('values').setValue([2, 3]) | ||||
|     filters.at(2).get('values').setValue([4]) | ||||
|  | ||||
|     const addConditionOfType = (type: TriggerConditionType) => { | ||||
|       const newCondition = component.addCondition(triggerGroup as FormGroup) | ||||
|       newCondition.get('type').setValue(type) | ||||
|       return newCondition | ||||
|     const addFilterOfType = (type: TriggerFilterType) => { | ||||
|       const newFilter = component.addFilter(triggerGroup as FormGroup) | ||||
|       newFilter.get('type').setValue(type) | ||||
|       return newFilter | ||||
|     } | ||||
|  | ||||
|     const correspondentIs = addConditionOfType( | ||||
|       TriggerConditionType.CorrespondentIs | ||||
|     ) | ||||
|     const correspondentIs = addFilterOfType(TriggerFilterType.CorrespondentIs) | ||||
|     correspondentIs.get('values').setValue(1) | ||||
|  | ||||
|     const correspondentNot = addConditionOfType( | ||||
|       TriggerConditionType.CorrespondentNot | ||||
|     ) | ||||
|     const correspondentNot = addFilterOfType(TriggerFilterType.CorrespondentNot) | ||||
|     correspondentNot.get('values').setValue([1]) | ||||
|  | ||||
|     const documentTypeIs = addConditionOfType( | ||||
|       TriggerConditionType.DocumentTypeIs | ||||
|     ) | ||||
|     const documentTypeIs = addFilterOfType(TriggerFilterType.DocumentTypeIs) | ||||
|     documentTypeIs.get('values').setValue(1) | ||||
|  | ||||
|     const documentTypeNot = addConditionOfType( | ||||
|       TriggerConditionType.DocumentTypeNot | ||||
|     ) | ||||
|     const documentTypeNot = addFilterOfType(TriggerFilterType.DocumentTypeNot) | ||||
|     documentTypeNot.get('values').setValue([1]) | ||||
|  | ||||
|     const storagePathIs = addConditionOfType(TriggerConditionType.StoragePathIs) | ||||
|     const storagePathIs = addFilterOfType(TriggerFilterType.StoragePathIs) | ||||
|     storagePathIs.get('values').setValue(1) | ||||
|  | ||||
|     const storagePathNot = addConditionOfType( | ||||
|       TriggerConditionType.StoragePathNot | ||||
|     ) | ||||
|     const storagePathNot = addFilterOfType(TriggerFilterType.StoragePathNot) | ||||
|     storagePathNot.get('values').setValue([1]) | ||||
|  | ||||
|     const customFieldCondition = addConditionOfType( | ||||
|       TriggerConditionType.CustomFieldQuery | ||||
|     const customFieldFilter = addFilterOfType( | ||||
|       TriggerFilterType.CustomFieldQuery | ||||
|     ) | ||||
|     const customFieldQuery = JSON.stringify(['AND', [[1, 'exact', 'test']]]) | ||||
|     customFieldCondition.get('values').setValue(customFieldQuery) | ||||
|     customFieldFilter.get('values').setValue(customFieldQuery) | ||||
|  | ||||
|     const formValues = component['getFormValues']() | ||||
|  | ||||
| @@ -466,23 +454,21 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     expect(formValues.triggers[0].filter_custom_field_query).toEqual( | ||||
|       customFieldQuery | ||||
|     ) | ||||
|     expect(formValues.triggers[0].conditions).toBeUndefined() | ||||
|     expect(formValues.triggers[0].filters).toBeUndefined() | ||||
|   }) | ||||
|  | ||||
|   it('should ignore empty and null condition values when mapping filters', () => { | ||||
|   it('should ignore empty and null filter values when mapping filters', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     const tagsCondition = component.addCondition(triggerGroup) | ||||
|     tagsCondition.get('type').setValue(TriggerConditionType.TagsAny) | ||||
|     tagsCondition.get('values').setValue([]) | ||||
|     const tagsFilter = component.addFilter(triggerGroup) | ||||
|     tagsFilter.get('type').setValue(TriggerFilterType.TagsAny) | ||||
|     tagsFilter.get('values').setValue([]) | ||||
|  | ||||
|     const correspondentCondition = component.addCondition(triggerGroup) | ||||
|     correspondentCondition | ||||
|       .get('type') | ||||
|       .setValue(TriggerConditionType.CorrespondentIs) | ||||
|     correspondentCondition.get('values').setValue(null) | ||||
|     const correspondentFilter = component.addFilter(triggerGroup) | ||||
|     correspondentFilter.get('type').setValue(TriggerFilterType.CorrespondentIs) | ||||
|     correspondentFilter.get('values').setValue(null) | ||||
|  | ||||
|     const formValues = component['getFormValues']() | ||||
|  | ||||
| @@ -495,15 +481,15 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     const addConditionOfType = (type: TriggerConditionType, value: any) => { | ||||
|       const condition = component.addCondition(triggerGroup) | ||||
|       condition.get('type').setValue(type) | ||||
|       condition.get('values').setValue(value) | ||||
|     const addFilterOfType = (type: TriggerFilterType, value: any) => { | ||||
|       const filter = component.addFilter(triggerGroup) | ||||
|       filter.get('type').setValue(type) | ||||
|       filter.get('values').setValue(value) | ||||
|     } | ||||
|  | ||||
|     addConditionOfType(TriggerConditionType.CorrespondentIs, [5]) | ||||
|     addConditionOfType(TriggerConditionType.DocumentTypeIs, [6]) | ||||
|     addConditionOfType(TriggerConditionType.StoragePathIs, [7]) | ||||
|     addFilterOfType(TriggerFilterType.CorrespondentIs, [5]) | ||||
|     addFilterOfType(TriggerFilterType.DocumentTypeIs, [6]) | ||||
|     addFilterOfType(TriggerFilterType.StoragePathIs, [7]) | ||||
|  | ||||
|     const formValues = component['getFormValues']() | ||||
|  | ||||
| @@ -512,22 +498,22 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     expect(formValues.triggers[0].filter_has_storage_path).toEqual(7) | ||||
|   }) | ||||
|  | ||||
|   it('should convert multi-value condition values when aggregating filters', () => { | ||||
|   it('should convert multi-value filter values when aggregating filters', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     const setCondition = (type: TriggerConditionType, value: number): void => { | ||||
|       const condition = component.addCondition(triggerGroup) as FormGroup | ||||
|       condition.get('type').setValue(type) | ||||
|       condition.get('values').setValue(value) | ||||
|     const setFilter = (type: TriggerFilterType, value: number): void => { | ||||
|       const filter = component.addFilter(triggerGroup) as FormGroup | ||||
|       filter.get('type').setValue(type) | ||||
|       filter.get('values').setValue(value) | ||||
|     } | ||||
|  | ||||
|     setCondition(TriggerConditionType.TagsAll, 11) | ||||
|     setCondition(TriggerConditionType.TagsNone, 12) | ||||
|     setCondition(TriggerConditionType.CorrespondentNot, 13) | ||||
|     setCondition(TriggerConditionType.DocumentTypeNot, 14) | ||||
|     setCondition(TriggerConditionType.StoragePathNot, 15) | ||||
|     setFilter(TriggerFilterType.TagsAll, 11) | ||||
|     setFilter(TriggerFilterType.TagsNone, 12) | ||||
|     setFilter(TriggerFilterType.CorrespondentNot, 13) | ||||
|     setFilter(TriggerFilterType.DocumentTypeNot, 14) | ||||
|     setFilter(TriggerFilterType.StoragePathNot, 15) | ||||
|  | ||||
|     const formValues = component['getFormValues']() | ||||
|  | ||||
| @@ -538,55 +524,55 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     expect(formValues.triggers[0].filter_has_not_storage_paths).toEqual([15]) | ||||
|   }) | ||||
|  | ||||
|   it('should reuse condition type options and update disabled state', () => { | ||||
|   it('should reuse filter type options and update disabled state', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|     component.addCondition(triggerGroup) | ||||
|     component.addFilter(triggerGroup) | ||||
|  | ||||
|     const optionsFirst = component.getConditionTypeOptions(triggerGroup, 0) | ||||
|     const optionsSecond = component.getConditionTypeOptions(triggerGroup, 0) | ||||
|     const optionsFirst = component.getFilterTypeOptions(triggerGroup, 0) | ||||
|     const optionsSecond = component.getFilterTypeOptions(triggerGroup, 0) | ||||
|     expect(optionsFirst).toBe(optionsSecond) | ||||
|  | ||||
|     // to force disabled flag | ||||
|     component.addCondition(triggerGroup) | ||||
|     const conditionArray = component.getConditionsFormArray(triggerGroup) | ||||
|     const firstCondition = conditionArray.at(0) | ||||
|     firstCondition.get('type').setValue(TriggerConditionType.CorrespondentIs) | ||||
|     component.addFilter(triggerGroup) | ||||
|     const filterArray = component.getFiltersFormArray(triggerGroup) | ||||
|     const firstFilter = filterArray.at(0) | ||||
|     firstFilter.get('type').setValue(TriggerFilterType.CorrespondentIs) | ||||
|  | ||||
|     component.addCondition(triggerGroup) | ||||
|     const updatedConditions = component.getConditionsFormArray(triggerGroup) | ||||
|     const secondCondition = updatedConditions.at(1) | ||||
|     const options = component.getConditionTypeOptions(triggerGroup, 1) | ||||
|     component.addFilter(triggerGroup) | ||||
|     const updatedFilters = component.getFiltersFormArray(triggerGroup) | ||||
|     const secondFilter = updatedFilters.at(1) | ||||
|     const options = component.getFilterTypeOptions(triggerGroup, 1) | ||||
|     const correspondentIsOption = options.find( | ||||
|       (option) => option.id === TriggerConditionType.CorrespondentIs | ||||
|       (option) => option.id === TriggerFilterType.CorrespondentIs | ||||
|     ) | ||||
|     expect(correspondentIsOption.disabled).toBe(true) | ||||
|  | ||||
|     firstCondition.get('type').setValue(TriggerConditionType.DocumentTypeNot) | ||||
|     secondCondition.get('type').setValue(TriggerConditionType.TagsAll) | ||||
|     const postChangeOptions = component.getConditionTypeOptions(triggerGroup, 1) | ||||
|     firstFilter.get('type').setValue(TriggerFilterType.DocumentTypeNot) | ||||
|     secondFilter.get('type').setValue(TriggerFilterType.TagsAll) | ||||
|     const postChangeOptions = component.getFilterTypeOptions(triggerGroup, 1) | ||||
|     const correspondentOptionAfter = postChangeOptions.find( | ||||
|       (option) => option.id === TriggerConditionType.CorrespondentIs | ||||
|       (option) => option.id === TriggerFilterType.CorrespondentIs | ||||
|     ) | ||||
|     expect(correspondentOptionAfter.disabled).toBe(false) | ||||
|   }) | ||||
|  | ||||
|   it('should keep multi-entry condition options enabled and allow duplicates', () => { | ||||
|   it('should keep multi-entry filter options enabled and allow duplicates', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     component.conditionDefinitions = [ | ||||
|     component.filterDefinitions = [ | ||||
|       { | ||||
|         id: TriggerConditionType.TagsAny, | ||||
|         id: TriggerFilterType.TagsAny, | ||||
|         name: 'Any tags', | ||||
|         inputType: 'tags', | ||||
|         allowMultipleEntries: true, | ||||
|         allowMultipleValues: true, | ||||
|       } as any, | ||||
|       { | ||||
|         id: TriggerConditionType.CorrespondentIs, | ||||
|         id: TriggerFilterType.CorrespondentIs, | ||||
|         name: 'Correspondent is', | ||||
|         inputType: 'select', | ||||
|         allowMultipleEntries: false, | ||||
| @@ -595,36 +581,36 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|       } as any, | ||||
|     ] | ||||
|  | ||||
|     const firstCondition = component.addCondition(triggerGroup) | ||||
|     firstCondition.get('type').setValue(TriggerConditionType.TagsAny) | ||||
|     const firstFilter = component.addFilter(triggerGroup) | ||||
|     firstFilter.get('type').setValue(TriggerFilterType.TagsAny) | ||||
|  | ||||
|     const secondCondition = component.addCondition(triggerGroup) | ||||
|     expect(secondCondition).not.toBeNull() | ||||
|     const secondFilter = component.addFilter(triggerGroup) | ||||
|     expect(secondFilter).not.toBeNull() | ||||
|  | ||||
|     const options = component.getConditionTypeOptions(triggerGroup, 1) | ||||
|     const options = component.getFilterTypeOptions(triggerGroup, 1) | ||||
|     const multiEntryOption = options.find( | ||||
|       (option) => option.id === TriggerConditionType.TagsAny | ||||
|       (option) => option.id === TriggerFilterType.TagsAny | ||||
|     ) | ||||
|  | ||||
|     expect(multiEntryOption.disabled).toBe(false) | ||||
|     expect(component.canAddCondition(triggerGroup)).toBe(true) | ||||
|     expect(component.canAddFilter(triggerGroup)).toBe(true) | ||||
|   }) | ||||
|  | ||||
|   it('should return null when no condition definitions remain available', () => { | ||||
|   it('should return null when no filter definitions remain available', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     component.conditionDefinitions = [ | ||||
|     component.filterDefinitions = [ | ||||
|       { | ||||
|         id: TriggerConditionType.TagsAny, | ||||
|         id: TriggerFilterType.TagsAny, | ||||
|         name: 'Any tags', | ||||
|         inputType: 'tags', | ||||
|         allowMultipleEntries: false, | ||||
|         allowMultipleValues: true, | ||||
|       } as any, | ||||
|       { | ||||
|         id: TriggerConditionType.CorrespondentIs, | ||||
|         id: TriggerFilterType.CorrespondentIs, | ||||
|         name: 'Correspondent is', | ||||
|         inputType: 'select', | ||||
|         allowMultipleEntries: false, | ||||
| @@ -633,18 +619,18 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|       } as any, | ||||
|     ] | ||||
|  | ||||
|     const firstCondition = component.addCondition(triggerGroup) | ||||
|     firstCondition.get('type').setValue(TriggerConditionType.TagsAny) | ||||
|     const secondCondition = component.addCondition(triggerGroup) | ||||
|     secondCondition.get('type').setValue(TriggerConditionType.CorrespondentIs) | ||||
|     const firstFilter = component.addFilter(triggerGroup) | ||||
|     firstFilter.get('type').setValue(TriggerFilterType.TagsAny) | ||||
|     const secondFilter = component.addFilter(triggerGroup) | ||||
|     secondFilter.get('type').setValue(TriggerFilterType.CorrespondentIs) | ||||
|  | ||||
|     expect(component.canAddCondition(triggerGroup)).toBe(false) | ||||
|     expect(component.addCondition(triggerGroup)).toBeNull() | ||||
|     expect(component.canAddFilter(triggerGroup)).toBe(false) | ||||
|     expect(component.addFilter(triggerGroup)).toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should skip condition definitions without handlers when building form array', () => { | ||||
|     const originalDefinitions = component.conditionDefinitions | ||||
|     component.conditionDefinitions = [ | ||||
|   it('should skip filter definitions without handlers when building form array', () => { | ||||
|     const originalDefinitions = component.filterDefinitions | ||||
|     component.filterDefinitions = [ | ||||
|       { | ||||
|         id: 999, | ||||
|         name: 'Unsupported', | ||||
| @@ -667,52 +653,52 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|       filter_custom_field_query: null, | ||||
|     } as any | ||||
|  | ||||
|     const conditions = component['buildConditionFormArray'](trigger) | ||||
|     expect(conditions.length).toBe(0) | ||||
|     const filters = component['buildFiltersFormArray'](trigger) | ||||
|     expect(filters.length).toBe(0) | ||||
|  | ||||
|     component.conditionDefinitions = originalDefinitions | ||||
|     component.filterDefinitions = originalDefinitions | ||||
|   }) | ||||
|  | ||||
|   it('should return null when adding condition for unknown trigger form group', () => { | ||||
|     expect(component.addCondition(new FormGroup({}) as any)).toBeNull() | ||||
|   it('should return null when adding filter for unknown trigger form group', () => { | ||||
|     expect(component.addFilter(new FormGroup({}) as any)).toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should ignore remove condition calls for unknown trigger form group', () => { | ||||
|   it('should ignore remove filter calls for unknown trigger form group', () => { | ||||
|     expect(() => | ||||
|       component.removeCondition(new FormGroup({}) as any, 0) | ||||
|       component.removeFilter(new FormGroup({}) as any, 0) | ||||
|     ).not.toThrow() | ||||
|   }) | ||||
|  | ||||
|   it('should teardown custom field query model when removing a custom field condition', () => { | ||||
|   it('should teardown custom field query model when removing a custom field filter', () => { | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     component.addCondition(triggerGroup) | ||||
|     const conditions = component.getConditionsFormArray(triggerGroup) | ||||
|     const conditionGroup = conditions.at(0) as FormGroup | ||||
|     conditionGroup.get('type').setValue(TriggerConditionType.CustomFieldQuery) | ||||
|     component.addFilter(triggerGroup) | ||||
|     const filters = component.getFiltersFormArray(triggerGroup) | ||||
|     const filterGroup = filters.at(0) as FormGroup | ||||
|     filterGroup.get('type').setValue(TriggerFilterType.CustomFieldQuery) | ||||
|  | ||||
|     const model = component.getCustomFieldQueryModel(conditionGroup) | ||||
|     const model = component.getCustomFieldQueryModel(filterGroup) | ||||
|     expect(model).toBeDefined() | ||||
|     expect( | ||||
|       component['getStoredCustomFieldQueryModel'](conditionGroup as any) | ||||
|       component['getStoredCustomFieldQueryModel'](filterGroup as any) | ||||
|     ).toBe(model) | ||||
|  | ||||
|     component.removeCondition(triggerGroup, 0) | ||||
|     component.removeFilter(triggerGroup, 0) | ||||
|     expect( | ||||
|       component['getStoredCustomFieldQueryModel'](conditionGroup as any) | ||||
|       component['getStoredCustomFieldQueryModel'](filterGroup as any) | ||||
|     ).toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should return readable condition names', () => { | ||||
|     expect(component.getConditionName(TriggerConditionType.TagsAny)).toBe( | ||||
|   it('should return readable filter names', () => { | ||||
|     expect(component.getFilterName(TriggerFilterType.TagsAny)).toBe( | ||||
|       'Has any of these tags' | ||||
|     ) | ||||
|     expect(component.getConditionName(999 as any)).toBe('') | ||||
|     expect(component.getFilterName(999 as any)).toBe('') | ||||
|   }) | ||||
|  | ||||
|   it('should build condition form array from existing trigger filters', () => { | ||||
|   it('should build filter form array from existing trigger filters', () => { | ||||
|     const trigger = workflow.triggers[0] | ||||
|     trigger.filter_has_tags = [1] | ||||
|     trigger.filter_has_all_tags = [2, 3] | ||||
| @@ -731,64 +717,62 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     component.object = workflow | ||||
|     component.ngOnInit() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|     const conditions = component.getConditionsFormArray(triggerGroup) | ||||
|     expect(conditions.length).toBe(10) | ||||
|     const customFieldCondition = conditions.at(9) as FormGroup | ||||
|     expect(customFieldCondition.get('type').value).toBe( | ||||
|       TriggerConditionType.CustomFieldQuery | ||||
|     const filters = component.getFiltersFormArray(triggerGroup) | ||||
|     expect(filters.length).toBe(10) | ||||
|     const customFieldFilter = filters.at(9) as FormGroup | ||||
|     expect(customFieldFilter.get('type').value).toBe( | ||||
|       TriggerFilterType.CustomFieldQuery | ||||
|     ) | ||||
|     const model = component.getCustomFieldQueryModel(customFieldCondition) | ||||
|     const model = component.getCustomFieldQueryModel(customFieldFilter) | ||||
|     expect(model.isValid()).toBe(true) | ||||
|   }) | ||||
|  | ||||
|   it('should expose select metadata helpers', () => { | ||||
|     expect( | ||||
|       component.isSelectMultiple(TriggerConditionType.CorrespondentNot) | ||||
|     ).toBe(true) | ||||
|     expect( | ||||
|       component.isSelectMultiple(TriggerConditionType.CorrespondentIs) | ||||
|     ).toBe(false) | ||||
|     expect(component.isSelectMultiple(TriggerFilterType.CorrespondentNot)).toBe( | ||||
|       true | ||||
|     ) | ||||
|     expect(component.isSelectMultiple(TriggerFilterType.CorrespondentIs)).toBe( | ||||
|       false | ||||
|     ) | ||||
|  | ||||
|     component.correspondents = [{ id: 1, name: 'C1' } as any] | ||||
|     component.documentTypes = [{ id: 2, name: 'DT' } as any] | ||||
|     component.storagePaths = [{ id: 3, name: 'SP' } as any] | ||||
|  | ||||
|     expect( | ||||
|       component.getConditionSelectItems(TriggerConditionType.CorrespondentIs) | ||||
|       component.getFilterSelectItems(TriggerFilterType.CorrespondentIs) | ||||
|     ).toEqual(component.correspondents) | ||||
|     expect( | ||||
|       component.getConditionSelectItems(TriggerConditionType.DocumentTypeIs) | ||||
|       component.getFilterSelectItems(TriggerFilterType.DocumentTypeIs) | ||||
|     ).toEqual(component.documentTypes) | ||||
|     expect( | ||||
|       component.getConditionSelectItems(TriggerConditionType.StoragePathIs) | ||||
|       component.getFilterSelectItems(TriggerFilterType.StoragePathIs) | ||||
|     ).toEqual(component.storagePaths) | ||||
|     expect( | ||||
|       component.getConditionSelectItems(TriggerConditionType.TagsAll) | ||||
|     ).toEqual([]) | ||||
|     expect(component.getFilterSelectItems(TriggerFilterType.TagsAll)).toEqual( | ||||
|       [] | ||||
|     ) | ||||
|  | ||||
|     expect( | ||||
|       component.isCustomFieldQueryCondition( | ||||
|         TriggerConditionType.CustomFieldQuery | ||||
|       ) | ||||
|       component.isCustomFieldQueryFilter(TriggerFilterType.CustomFieldQuery) | ||||
|     ).toBe(true) | ||||
|   }) | ||||
|  | ||||
|   it('should return empty select items when definition is missing', () => { | ||||
|     const originalDefinitions = component.conditionDefinitions | ||||
|     component.conditionDefinitions = [] | ||||
|     const originalDefinitions = component.filterDefinitions | ||||
|     component.filterDefinitions = [] | ||||
|  | ||||
|     expect( | ||||
|       component.getConditionSelectItems(TriggerConditionType.CorrespondentIs) | ||||
|       component.getFilterSelectItems(TriggerFilterType.CorrespondentIs) | ||||
|     ).toEqual([]) | ||||
|  | ||||
|     component.conditionDefinitions = originalDefinitions | ||||
|     component.filterDefinitions = originalDefinitions | ||||
|   }) | ||||
|  | ||||
|   it('should return empty select items when definition has unknown source', () => { | ||||
|     const originalDefinitions = component.conditionDefinitions | ||||
|     component.conditionDefinitions = [ | ||||
|     const originalDefinitions = component.filterDefinitions | ||||
|     component.filterDefinitions = [ | ||||
|       { | ||||
|         id: TriggerConditionType.CorrespondentIs, | ||||
|         id: TriggerFilterType.CorrespondentIs, | ||||
|         name: 'Correspondent is', | ||||
|         inputType: 'select', | ||||
|         allowMultipleEntries: false, | ||||
| @@ -798,10 +782,10 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     ] | ||||
|  | ||||
|     expect( | ||||
|       component.getConditionSelectItems(TriggerConditionType.CorrespondentIs) | ||||
|       component.getFilterSelectItems(TriggerFilterType.CorrespondentIs) | ||||
|     ).toEqual([]) | ||||
|  | ||||
|     component.conditionDefinitions = originalDefinitions | ||||
|     component.filterDefinitions = originalDefinitions | ||||
|   }) | ||||
|  | ||||
|   it('should handle custom field query selection change and validation states', () => { | ||||
| @@ -837,19 +821,19 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|   }) | ||||
|  | ||||
|   it('should recover from invalid custom field query json and update control on changes', () => { | ||||
|     const conditionGroup = new FormGroup({ | ||||
|     const filterGroup = new FormGroup({ | ||||
|       values: new FormControl('not-json'), | ||||
|     }) | ||||
|  | ||||
|     component['ensureCustomFieldQueryModel'](conditionGroup, 'not-json') | ||||
|     component['ensureCustomFieldQueryModel'](filterGroup, 'not-json') | ||||
|  | ||||
|     const model = component['getStoredCustomFieldQueryModel']( | ||||
|       conditionGroup as any | ||||
|       filterGroup as any | ||||
|     ) | ||||
|     expect(model).toBeDefined() | ||||
|     expect(model.queries.length).toBeGreaterThan(0) | ||||
|  | ||||
|     const valuesControl = conditionGroup.get('values') | ||||
|     const valuesControl = filterGroup.get('values') | ||||
|     expect(valuesControl.value).toBeNull() | ||||
|  | ||||
|     const expression = new CustomFieldQueryExpression([ | ||||
| @@ -865,7 +849,7 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|  | ||||
|     expect(valuesControl.value).toEqual(JSON.stringify(expression.serialize())) | ||||
|  | ||||
|     component['clearCustomFieldQueryModel'](conditionGroup as any) | ||||
|     component['clearCustomFieldQueryModel'](filterGroup as any) | ||||
|   }) | ||||
|  | ||||
|   it('should handle custom field query model change edge cases', () => { | ||||
| @@ -898,71 +882,61 @@ describe('WorkflowEditDialogComponent', () => { | ||||
|     expect(groupWithControl.get('values').value).toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should normalize condition values for single and multi selects', () => { | ||||
|   it('should normalize filter values for single and multi selects', () => { | ||||
|     expect( | ||||
|       component['normalizeConditionValue'](TriggerConditionType.TagsAny) | ||||
|       component['normalizeFilterValue'](TriggerFilterType.TagsAny) | ||||
|     ).toEqual([]) | ||||
|     expect( | ||||
|       component['normalizeConditionValue'](TriggerConditionType.TagsAny, 5) | ||||
|       component['normalizeFilterValue'](TriggerFilterType.TagsAny, 5) | ||||
|     ).toEqual([5]) | ||||
|     expect( | ||||
|       component['normalizeConditionValue'](TriggerConditionType.TagsAny, [5, 6]) | ||||
|       component['normalizeFilterValue'](TriggerFilterType.TagsAny, [5, 6]) | ||||
|     ).toEqual([5, 6]) | ||||
|     expect( | ||||
|       component['normalizeConditionValue']( | ||||
|         TriggerConditionType.CorrespondentIs, | ||||
|         [7] | ||||
|       ) | ||||
|       component['normalizeFilterValue'](TriggerFilterType.CorrespondentIs, [7]) | ||||
|     ).toEqual(7) | ||||
|     expect( | ||||
|       component['normalizeConditionValue']( | ||||
|         TriggerConditionType.CorrespondentIs, | ||||
|         8 | ||||
|       ) | ||||
|       component['normalizeFilterValue'](TriggerFilterType.CorrespondentIs, 8) | ||||
|     ).toEqual(8) | ||||
|     const customFieldJson = JSON.stringify(['AND', [[1, 'exact', 'test']]]) | ||||
|     expect( | ||||
|       component['normalizeConditionValue']( | ||||
|         TriggerConditionType.CustomFieldQuery, | ||||
|       component['normalizeFilterValue']( | ||||
|         TriggerFilterType.CustomFieldQuery, | ||||
|         customFieldJson | ||||
|       ) | ||||
|     ).toEqual(customFieldJson) | ||||
|  | ||||
|     const customFieldObject = ['AND', [[1, 'exact', 'other']]] | ||||
|     expect( | ||||
|       component['normalizeConditionValue']( | ||||
|         TriggerConditionType.CustomFieldQuery, | ||||
|       component['normalizeFilterValue']( | ||||
|         TriggerFilterType.CustomFieldQuery, | ||||
|         customFieldObject | ||||
|       ) | ||||
|     ).toEqual(JSON.stringify(customFieldObject)) | ||||
|  | ||||
|     expect( | ||||
|       component['normalizeConditionValue']( | ||||
|         TriggerConditionType.CustomFieldQuery, | ||||
|       component['normalizeFilterValue']( | ||||
|         TriggerFilterType.CustomFieldQuery, | ||||
|         false | ||||
|       ) | ||||
|     ).toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should add and remove condition form groups', () => { | ||||
|   it('should add and remove filter form groups', () => { | ||||
|     component['changeDetector'] = { detectChanges: jest.fn() } as any | ||||
|     component.object = undefined | ||||
|     component.addTrigger() | ||||
|     const triggerGroup = component.triggerFields.at(0) as FormGroup | ||||
|  | ||||
|     component.addCondition(triggerGroup) | ||||
|     component.addFilter(triggerGroup) | ||||
|  | ||||
|     component.removeCondition(triggerGroup, 0) | ||||
|     expect(component.getConditionsFormArray(triggerGroup).length).toBe(0) | ||||
|     component.removeFilter(triggerGroup, 0) | ||||
|     expect(component.getFiltersFormArray(triggerGroup).length).toBe(0) | ||||
|  | ||||
|     component.addCondition(triggerGroup) | ||||
|     const conditionArrayAfterAdd = | ||||
|       component.getConditionsFormArray(triggerGroup) | ||||
|     conditionArrayAfterAdd | ||||
|       .at(0) | ||||
|       .get('type') | ||||
|       .setValue(TriggerConditionType.TagsAll) | ||||
|     expect(component.getConditionsFormArray(triggerGroup).length).toBe(1) | ||||
|     component.addFilter(triggerGroup) | ||||
|     const filterArrayAfterAdd = component.getFiltersFormArray(triggerGroup) | ||||
|     filterArrayAfterAdd.at(0).get('type').setValue(TriggerFilterType.TagsAll) | ||||
|     expect(component.getFiltersFormArray(triggerGroup).length).toBe(1) | ||||
|   }) | ||||
|  | ||||
|   it('should remove selected custom field from the form group', () => { | ||||
|   | ||||
| @@ -141,7 +141,7 @@ export const WORKFLOW_ACTION_OPTIONS = [ | ||||
|   }, | ||||
| ] | ||||
|  | ||||
| export enum TriggerConditionType { | ||||
| export enum TriggerFilterType { | ||||
|   TagsAny = 'tags_any', | ||||
|   TagsAll = 'tags_all', | ||||
|   TagsNone = 'tags_none', | ||||
| @@ -154,8 +154,8 @@ export enum TriggerConditionType { | ||||
|   CustomFieldQuery = 'custom_field_query', | ||||
| } | ||||
|  | ||||
| interface TriggerConditionDefinition { | ||||
|   id: TriggerConditionType | ||||
| interface TriggerFilterDefinition { | ||||
|   id: TriggerFilterType | ||||
|   name: string | ||||
|   inputType: 'tags' | 'select' | 'customFieldQuery' | ||||
|   allowMultipleEntries: boolean | ||||
| @@ -164,7 +164,7 @@ interface TriggerConditionDefinition { | ||||
|   disabled?: boolean | ||||
| } | ||||
|  | ||||
| type TriggerConditionOption = TriggerConditionDefinition & { | ||||
| type TriggerFilterOption = TriggerFilterDefinition & { | ||||
|   disabled?: boolean | ||||
| } | ||||
|  | ||||
| @@ -181,7 +181,7 @@ type TriggerFilterAggregate = { | ||||
|   filter_custom_field_query: string | null | ||||
| } | ||||
|  | ||||
| interface ConditionFilterHandler { | ||||
| interface FilterHandler { | ||||
|   apply: (aggregate: TriggerFilterAggregate, values: any) => void | ||||
|   extract: (trigger: WorkflowTrigger) => any | ||||
|   hasValue: (value: any) => boolean | ||||
| @@ -192,35 +192,35 @@ const CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY = Symbol( | ||||
|   'customFieldQuerySubscription' | ||||
| ) | ||||
|  | ||||
| type CustomFieldConditionGroup = FormGroup & { | ||||
| type CustomFieldFilterGroup = FormGroup & { | ||||
|   [CUSTOM_FIELD_QUERY_MODEL_KEY]?: CustomFieldQueriesModel | ||||
|   [CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?: Subscription | ||||
| } | ||||
|  | ||||
| const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
| const TRIGGER_FILTER_DEFINITIONS: TriggerFilterDefinition[] = [ | ||||
|   { | ||||
|     id: TriggerConditionType.TagsAny, | ||||
|     id: TriggerFilterType.TagsAny, | ||||
|     name: $localize`Has any of these tags`, | ||||
|     inputType: 'tags', | ||||
|     allowMultipleEntries: false, | ||||
|     allowMultipleValues: true, | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.TagsAll, | ||||
|     id: TriggerFilterType.TagsAll, | ||||
|     name: $localize`Has all of these tags`, | ||||
|     inputType: 'tags', | ||||
|     allowMultipleEntries: false, | ||||
|     allowMultipleValues: true, | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.TagsNone, | ||||
|     id: TriggerFilterType.TagsNone, | ||||
|     name: $localize`Does not have these tags`, | ||||
|     inputType: 'tags', | ||||
|     allowMultipleEntries: false, | ||||
|     allowMultipleValues: true, | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.CorrespondentIs, | ||||
|     id: TriggerFilterType.CorrespondentIs, | ||||
|     name: $localize`Has correspondent`, | ||||
|     inputType: 'select', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -228,7 +228,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
|     selectItems: 'correspondents', | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.CorrespondentNot, | ||||
|     id: TriggerFilterType.CorrespondentNot, | ||||
|     name: $localize`Does not have correspondents`, | ||||
|     inputType: 'select', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -236,7 +236,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
|     selectItems: 'correspondents', | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.DocumentTypeIs, | ||||
|     id: TriggerFilterType.DocumentTypeIs, | ||||
|     name: $localize`Has document type`, | ||||
|     inputType: 'select', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -244,7 +244,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
|     selectItems: 'documentTypes', | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.DocumentTypeNot, | ||||
|     id: TriggerFilterType.DocumentTypeNot, | ||||
|     name: $localize`Does not have document types`, | ||||
|     inputType: 'select', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -252,7 +252,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
|     selectItems: 'documentTypes', | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.StoragePathIs, | ||||
|     id: TriggerFilterType.StoragePathIs, | ||||
|     name: $localize`Has storage path`, | ||||
|     inputType: 'select', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -260,7 +260,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
|     selectItems: 'storagePaths', | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.StoragePathNot, | ||||
|     id: TriggerFilterType.StoragePathNot, | ||||
|     name: $localize`Does not have storage paths`, | ||||
|     inputType: 'select', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -268,7 +268,7 @@ const TRIGGER_CONDITION_DEFINITIONS: TriggerConditionDefinition[] = [ | ||||
|     selectItems: 'storagePaths', | ||||
|   }, | ||||
|   { | ||||
|     id: TriggerConditionType.CustomFieldQuery, | ||||
|     id: TriggerFilterType.CustomFieldQuery, | ||||
|     name: $localize`Matches custom field query`, | ||||
|     inputType: 'customFieldQuery', | ||||
|     allowMultipleEntries: false, | ||||
| @@ -280,18 +280,15 @@ const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter( | ||||
|   (a) => a.id !== MATCH_AUTO | ||||
| ) | ||||
|  | ||||
| const CONDITION_FILTER_HANDLERS: Record< | ||||
|   TriggerConditionType, | ||||
|   ConditionFilterHandler | ||||
| > = { | ||||
|   [TriggerConditionType.TagsAny]: { | ||||
| const FILTER_HANDLERS: Record<TriggerFilterType, FilterHandler> = { | ||||
|   [TriggerFilterType.TagsAny]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_tags = Array.isArray(values) ? [...values] : [values] | ||||
|     }, | ||||
|     extract: (trigger) => trigger.filter_has_tags, | ||||
|     hasValue: (value) => Array.isArray(value) && value.length > 0, | ||||
|   }, | ||||
|   [TriggerConditionType.TagsAll]: { | ||||
|   [TriggerFilterType.TagsAll]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_all_tags = Array.isArray(values) | ||||
|         ? [...values] | ||||
| @@ -300,7 +297,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_all_tags, | ||||
|     hasValue: (value) => Array.isArray(value) && value.length > 0, | ||||
|   }, | ||||
|   [TriggerConditionType.TagsNone]: { | ||||
|   [TriggerFilterType.TagsNone]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_not_tags = Array.isArray(values) | ||||
|         ? [...values] | ||||
| @@ -309,7 +306,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_not_tags, | ||||
|     hasValue: (value) => Array.isArray(value) && value.length > 0, | ||||
|   }, | ||||
|   [TriggerConditionType.CorrespondentIs]: { | ||||
|   [TriggerFilterType.CorrespondentIs]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_correspondent = Array.isArray(values) | ||||
|         ? (values[0] ?? null) | ||||
| @@ -318,7 +315,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_correspondent, | ||||
|     hasValue: (value) => value !== null && value !== undefined, | ||||
|   }, | ||||
|   [TriggerConditionType.CorrespondentNot]: { | ||||
|   [TriggerFilterType.CorrespondentNot]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_not_correspondents = Array.isArray(values) | ||||
|         ? [...values] | ||||
| @@ -327,7 +324,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_not_correspondents, | ||||
|     hasValue: (value) => Array.isArray(value) && value.length > 0, | ||||
|   }, | ||||
|   [TriggerConditionType.DocumentTypeIs]: { | ||||
|   [TriggerFilterType.DocumentTypeIs]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_document_type = Array.isArray(values) | ||||
|         ? (values[0] ?? null) | ||||
| @@ -336,7 +333,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_document_type, | ||||
|     hasValue: (value) => value !== null && value !== undefined, | ||||
|   }, | ||||
|   [TriggerConditionType.DocumentTypeNot]: { | ||||
|   [TriggerFilterType.DocumentTypeNot]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_not_document_types = Array.isArray(values) | ||||
|         ? [...values] | ||||
| @@ -345,7 +342,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_not_document_types, | ||||
|     hasValue: (value) => Array.isArray(value) && value.length > 0, | ||||
|   }, | ||||
|   [TriggerConditionType.StoragePathIs]: { | ||||
|   [TriggerFilterType.StoragePathIs]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_storage_path = Array.isArray(values) | ||||
|         ? (values[0] ?? null) | ||||
| @@ -354,7 +351,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_storage_path, | ||||
|     hasValue: (value) => value !== null && value !== undefined, | ||||
|   }, | ||||
|   [TriggerConditionType.StoragePathNot]: { | ||||
|   [TriggerFilterType.StoragePathNot]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_has_not_storage_paths = Array.isArray(values) | ||||
|         ? [...values] | ||||
| @@ -363,7 +360,7 @@ const CONDITION_FILTER_HANDLERS: Record< | ||||
|     extract: (trigger) => trigger.filter_has_not_storage_paths, | ||||
|     hasValue: (value) => Array.isArray(value) && value.length > 0, | ||||
|   }, | ||||
|   [TriggerConditionType.CustomFieldQuery]: { | ||||
|   [TriggerFilterType.CustomFieldQuery]: { | ||||
|     apply: (aggregate, values) => { | ||||
|       aggregate.filter_custom_field_query = values as string | ||||
|     }, | ||||
| @@ -405,8 +402,8 @@ export class WorkflowEditDialogComponent | ||||
| { | ||||
|   public WorkflowTriggerType = WorkflowTriggerType | ||||
|   public WorkflowActionType = WorkflowActionType | ||||
|   public TriggerConditionType = TriggerConditionType | ||||
|   public conditionDefinitions = TRIGGER_CONDITION_DEFINITIONS | ||||
|   public TriggerFilterType = TriggerFilterType | ||||
|   public filterDefinitions = TRIGGER_FILTER_DEFINITIONS | ||||
|  | ||||
|   private correspondentService: CorrespondentService | ||||
|   private documentTypeService: DocumentTypeService | ||||
| @@ -426,9 +423,9 @@ export class WorkflowEditDialogComponent | ||||
|  | ||||
|   private allowedActionTypes = [] | ||||
|  | ||||
|   private readonly triggerConditionOptionsMap = new WeakMap< | ||||
|   private readonly triggerFilterOptionsMap = new WeakMap< | ||||
|     FormArray, | ||||
|     TriggerConditionOption[] | ||||
|     TriggerFilterOption[] | ||||
|   >() | ||||
|  | ||||
|   constructor() { | ||||
| @@ -639,7 +636,7 @@ export class WorkflowEditDialogComponent | ||||
|       formValues.triggers = formValues.triggers.map( | ||||
|         (trigger: any, index: number) => { | ||||
|           const triggerFormGroup = this.triggerFields.at(index) as FormGroup | ||||
|           const conditions = this.getConditionsFormArray(triggerFormGroup) | ||||
|           const filters = this.getFiltersFormArray(triggerFormGroup) | ||||
|  | ||||
|           const aggregate: TriggerFilterAggregate = { | ||||
|             filter_has_tags: [], | ||||
| @@ -654,8 +651,8 @@ export class WorkflowEditDialogComponent | ||||
|             filter_custom_field_query: null, | ||||
|           } | ||||
|  | ||||
|           for (const control of conditions.controls) { | ||||
|             const type = control.get('type').value as TriggerConditionType | ||||
|           for (const control of filters.controls) { | ||||
|             const type = control.get('type').value as TriggerFilterType | ||||
|             const values = control.get('values').value | ||||
|  | ||||
|             if (values === null || values === undefined) { | ||||
| @@ -666,7 +663,7 @@ export class WorkflowEditDialogComponent | ||||
|               continue | ||||
|             } | ||||
|  | ||||
|             const handler = CONDITION_FILTER_HANDLERS[type] | ||||
|             const handler = FILTER_HANDLERS[type] | ||||
|             handler?.apply(aggregate, values) | ||||
|           } | ||||
|  | ||||
| @@ -688,7 +685,7 @@ export class WorkflowEditDialogComponent | ||||
|           trigger.filter_custom_field_query = | ||||
|             aggregate.filter_custom_field_query ?? null | ||||
|  | ||||
|           delete trigger.conditions | ||||
|           delete trigger.filters | ||||
|  | ||||
|           return trigger | ||||
|         } | ||||
| @@ -702,40 +699,38 @@ export class WorkflowEditDialogComponent | ||||
|     return formGroup.get('matching_algorithm').value !== MATCH_NONE | ||||
|   } | ||||
|  | ||||
|   private createConditionFormGroup( | ||||
|     type: TriggerConditionType, | ||||
|   private createFilterFormGroup( | ||||
|     type: TriggerFilterType, | ||||
|     initialValue?: any | ||||
|   ): FormGroup { | ||||
|     const group = new FormGroup({ | ||||
|       type: new FormControl(type), | ||||
|       values: new FormControl(this.normalizeConditionValue(type, initialValue)), | ||||
|       values: new FormControl(this.normalizeFilterValue(type, initialValue)), | ||||
|     }) | ||||
|  | ||||
|     group | ||||
|       .get('type') | ||||
|       .valueChanges.subscribe((newType: TriggerConditionType) => { | ||||
|         if (newType === TriggerConditionType.CustomFieldQuery) { | ||||
|           this.ensureCustomFieldQueryModel(group) | ||||
|         } else { | ||||
|           this.clearCustomFieldQueryModel(group) | ||||
|           group.get('values').setValue(this.getDefaultConditionValue(newType), { | ||||
|             emitEvent: false, | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     group.get('type').valueChanges.subscribe((newType: TriggerFilterType) => { | ||||
|       if (newType === TriggerFilterType.CustomFieldQuery) { | ||||
|         this.ensureCustomFieldQueryModel(group) | ||||
|       } else { | ||||
|         this.clearCustomFieldQueryModel(group) | ||||
|         group.get('values').setValue(this.getDefaultFilterValue(newType), { | ||||
|           emitEvent: false, | ||||
|         }) | ||||
|       } | ||||
|     }) | ||||
|  | ||||
|     if (type === TriggerConditionType.CustomFieldQuery) { | ||||
|     if (type === TriggerFilterType.CustomFieldQuery) { | ||||
|       this.ensureCustomFieldQueryModel(group, initialValue) | ||||
|     } | ||||
|  | ||||
|     return group | ||||
|   } | ||||
|  | ||||
|   private buildConditionFormArray(trigger: WorkflowTrigger): FormArray { | ||||
|     const conditions = new FormArray([]) | ||||
|   private buildFiltersFormArray(trigger: WorkflowTrigger): FormArray { | ||||
|     const filters = new FormArray([]) | ||||
|  | ||||
|     for (const definition of this.conditionDefinitions) { | ||||
|       const handler = CONDITION_FILTER_HANDLERS[definition.id] | ||||
|     for (const definition of this.filterDefinitions) { | ||||
|       const handler = FILTER_HANDLERS[definition.id] | ||||
|       if (!handler) { | ||||
|         continue | ||||
|       } | ||||
| @@ -745,24 +740,24 @@ export class WorkflowEditDialogComponent | ||||
|         continue | ||||
|       } | ||||
|  | ||||
|       conditions.push(this.createConditionFormGroup(definition.id, value)) | ||||
|       filters.push(this.createFilterFormGroup(definition.id, value)) | ||||
|     } | ||||
|  | ||||
|     return conditions | ||||
|     return filters | ||||
|   } | ||||
|  | ||||
|   getConditionsFormArray(formGroup: FormGroup): FormArray { | ||||
|     return formGroup.get('conditions') as FormArray | ||||
|   getFiltersFormArray(formGroup: FormGroup): FormArray { | ||||
|     return formGroup.get('filters') as FormArray | ||||
|   } | ||||
|  | ||||
|   getConditionTypeOptions(formGroup: FormGroup, conditionIndex: number) { | ||||
|     const conditions = this.getConditionsFormArray(formGroup) | ||||
|     const options = this.getConditionTypeOptionsForArray(conditions) | ||||
|     const currentType = conditions.at(conditionIndex).get('type') | ||||
|       .value as TriggerConditionType | ||||
|   getFilterTypeOptions(formGroup: FormGroup, filterIndex: number) { | ||||
|     const filters = this.getFiltersFormArray(formGroup) | ||||
|     const options = this.getFilterTypeOptionsForArray(filters) | ||||
|     const currentType = filters.at(filterIndex).get('type') | ||||
|       .value as TriggerFilterType | ||||
|     const usedTypes = new Set( | ||||
|       conditions.controls.map( | ||||
|         (control) => control.get('type').value as TriggerConditionType | ||||
|       filters.controls.map( | ||||
|         (control) => control.get('type').value as TriggerFilterType | ||||
|       ) | ||||
|     ) | ||||
|  | ||||
| @@ -778,15 +773,15 @@ export class WorkflowEditDialogComponent | ||||
|     return options | ||||
|   } | ||||
|  | ||||
|   canAddCondition(formGroup: FormGroup): boolean { | ||||
|     const conditions = this.getConditionsFormArray(formGroup) | ||||
|   canAddFilter(formGroup: FormGroup): boolean { | ||||
|     const filters = this.getFiltersFormArray(formGroup) | ||||
|     const usedTypes = new Set( | ||||
|       conditions.controls.map( | ||||
|         (control) => control.get('type').value as TriggerConditionType | ||||
|       filters.controls.map( | ||||
|         (control) => control.get('type').value as TriggerFilterType | ||||
|       ) | ||||
|     ) | ||||
|  | ||||
|     return this.conditionDefinitions.some((definition) => { | ||||
|     return this.filterDefinitions.some((definition) => { | ||||
|       if (definition.allowMultipleEntries) { | ||||
|         return true | ||||
|       } | ||||
| @@ -794,19 +789,19 @@ export class WorkflowEditDialogComponent | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   addCondition(triggerFormGroup: FormGroup): FormGroup | null { | ||||
|   addFilter(triggerFormGroup: FormGroup): FormGroup | null { | ||||
|     const triggerIndex = this.triggerFields.controls.indexOf(triggerFormGroup) | ||||
|     if (triggerIndex === -1) { | ||||
|       return null | ||||
|     } | ||||
|  | ||||
|     const conditions = this.getConditionsFormArray(triggerFormGroup) | ||||
|     const filters = this.getFiltersFormArray(triggerFormGroup) | ||||
|  | ||||
|     const availableDefinition = this.conditionDefinitions.find((definition) => { | ||||
|     const availableDefinition = this.filterDefinitions.find((definition) => { | ||||
|       if (definition.allowMultipleEntries) { | ||||
|         return true | ||||
|       } | ||||
|       return !conditions.controls.some( | ||||
|       return !filters.controls.some( | ||||
|         (control) => control.get('type').value === definition.id | ||||
|       ) | ||||
|     }) | ||||
| @@ -815,72 +810,67 @@ export class WorkflowEditDialogComponent | ||||
|       return null | ||||
|     } | ||||
|  | ||||
|     conditions.push(this.createConditionFormGroup(availableDefinition.id)) | ||||
|     filters.push(this.createFilterFormGroup(availableDefinition.id)) | ||||
|     triggerFormGroup.markAsDirty() | ||||
|     triggerFormGroup.markAsTouched() | ||||
|  | ||||
|     return conditions.at(-1) as FormGroup | ||||
|     return filters.at(-1) as FormGroup | ||||
|   } | ||||
|  | ||||
|   removeCondition(triggerFormGroup: FormGroup, conditionIndex: number) { | ||||
|   removeFilter(triggerFormGroup: FormGroup, filterIndex: number) { | ||||
|     const triggerIndex = this.triggerFields.controls.indexOf(triggerFormGroup) | ||||
|     if (triggerIndex === -1) { | ||||
|       return | ||||
|     } | ||||
|  | ||||
|     const conditions = this.getConditionsFormArray(triggerFormGroup) | ||||
|     const conditionGroup = conditions.at(conditionIndex) as FormGroup | ||||
|     if ( | ||||
|       conditionGroup?.get('type').value === | ||||
|       TriggerConditionType.CustomFieldQuery | ||||
|     ) { | ||||
|       this.clearCustomFieldQueryModel(conditionGroup) | ||||
|     const filters = this.getFiltersFormArray(triggerFormGroup) | ||||
|     const filterGroup = filters.at(filterIndex) as FormGroup | ||||
|     if (filterGroup?.get('type').value === TriggerFilterType.CustomFieldQuery) { | ||||
|       this.clearCustomFieldQueryModel(filterGroup) | ||||
|     } | ||||
|     conditions.removeAt(conditionIndex) | ||||
|     filters.removeAt(filterIndex) | ||||
|     triggerFormGroup.markAsDirty() | ||||
|     triggerFormGroup.markAsTouched() | ||||
|   } | ||||
|  | ||||
|   getConditionDefinition( | ||||
|     type: TriggerConditionType | ||||
|   ): TriggerConditionDefinition | undefined { | ||||
|     return this.conditionDefinitions.find( | ||||
|       (definition) => definition.id === type | ||||
|     ) | ||||
|   getFilterDefinition( | ||||
|     type: TriggerFilterType | ||||
|   ): TriggerFilterDefinition | undefined { | ||||
|     return this.filterDefinitions.find((definition) => definition.id === type) | ||||
|   } | ||||
|  | ||||
|   getConditionName(type: TriggerConditionType): string { | ||||
|     return this.getConditionDefinition(type)?.name ?? '' | ||||
|   getFilterName(type: TriggerFilterType): string { | ||||
|     return this.getFilterDefinition(type)?.name ?? '' | ||||
|   } | ||||
|  | ||||
|   isTagsCondition(type: TriggerConditionType): boolean { | ||||
|     return this.getConditionDefinition(type)?.inputType === 'tags' | ||||
|   isTagsFilter(type: TriggerFilterType): boolean { | ||||
|     return this.getFilterDefinition(type)?.inputType === 'tags' | ||||
|   } | ||||
|  | ||||
|   isCustomFieldQueryCondition(type: TriggerConditionType): boolean { | ||||
|     return this.getConditionDefinition(type)?.inputType === 'customFieldQuery' | ||||
|   isCustomFieldQueryFilter(type: TriggerFilterType): boolean { | ||||
|     return this.getFilterDefinition(type)?.inputType === 'customFieldQuery' | ||||
|   } | ||||
|  | ||||
|   isMultiValueCondition(type: TriggerConditionType): boolean { | ||||
|   isMultiValueFilter(type: TriggerFilterType): boolean { | ||||
|     switch (type) { | ||||
|       case TriggerConditionType.TagsAny: | ||||
|       case TriggerConditionType.TagsAll: | ||||
|       case TriggerConditionType.TagsNone: | ||||
|       case TriggerConditionType.CorrespondentNot: | ||||
|       case TriggerConditionType.DocumentTypeNot: | ||||
|       case TriggerConditionType.StoragePathNot: | ||||
|       case TriggerFilterType.TagsAny: | ||||
|       case TriggerFilterType.TagsAll: | ||||
|       case TriggerFilterType.TagsNone: | ||||
|       case TriggerFilterType.CorrespondentNot: | ||||
|       case TriggerFilterType.DocumentTypeNot: | ||||
|       case TriggerFilterType.StoragePathNot: | ||||
|         return true | ||||
|       default: | ||||
|         return false | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   isSelectMultiple(type: TriggerConditionType): boolean { | ||||
|     return !this.isTagsCondition(type) && this.isMultiValueCondition(type) | ||||
|   isSelectMultiple(type: TriggerFilterType): boolean { | ||||
|     return !this.isTagsFilter(type) && this.isMultiValueFilter(type) | ||||
|   } | ||||
|  | ||||
|   getConditionSelectItems(type: TriggerConditionType) { | ||||
|     const definition = this.getConditionDefinition(type) | ||||
|   getFilterSelectItems(type: TriggerFilterType) { | ||||
|     const definition = this.getFilterDefinition(type) | ||||
|     if (!definition || definition.inputType !== 'select') { | ||||
|       return [] | ||||
|     } | ||||
| @@ -917,36 +907,36 @@ export class WorkflowEditDialogComponent | ||||
|     return model.isEmpty() || model.isValid() | ||||
|   } | ||||
|  | ||||
|   private getConditionTypeOptionsForArray( | ||||
|     conditions: FormArray | ||||
|   ): TriggerConditionOption[] { | ||||
|     let cached = this.triggerConditionOptionsMap.get(conditions) | ||||
|   private getFilterTypeOptionsForArray( | ||||
|     filters: FormArray | ||||
|   ): TriggerFilterOption[] { | ||||
|     let cached = this.triggerFilterOptionsMap.get(filters) | ||||
|     if (!cached) { | ||||
|       cached = this.conditionDefinitions.map((definition) => ({ | ||||
|       cached = this.filterDefinitions.map((definition) => ({ | ||||
|         ...definition, | ||||
|         disabled: false, | ||||
|       })) | ||||
|       this.triggerConditionOptionsMap.set(conditions, cached) | ||||
|       this.triggerFilterOptionsMap.set(filters, cached) | ||||
|     } | ||||
|     return cached | ||||
|   } | ||||
|  | ||||
|   private ensureCustomFieldQueryModel( | ||||
|     conditionGroup: FormGroup, | ||||
|     filterGroup: FormGroup, | ||||
|     initialValue?: any | ||||
|   ): CustomFieldQueriesModel { | ||||
|     const existingModel = this.getStoredCustomFieldQueryModel(conditionGroup) | ||||
|     const existingModel = this.getStoredCustomFieldQueryModel(filterGroup) | ||||
|     if (existingModel) { | ||||
|       return existingModel | ||||
|     } | ||||
|  | ||||
|     const model = new CustomFieldQueriesModel() | ||||
|     this.setCustomFieldQueryModel(conditionGroup, model) | ||||
|     this.setCustomFieldQueryModel(filterGroup, model) | ||||
|  | ||||
|     const rawValue = | ||||
|       typeof initialValue === 'string' | ||||
|         ? initialValue | ||||
|         : (conditionGroup.get('values').value as string) | ||||
|         : (filterGroup.get('values').value as string) | ||||
|  | ||||
|     if (rawValue) { | ||||
|       try { | ||||
| @@ -962,46 +952,45 @@ export class WorkflowEditDialogComponent | ||||
|     const subscription = model.changed | ||||
|       .pipe(takeUntil(this.unsubscribeNotifier)) | ||||
|       .subscribe(() => { | ||||
|         this.onCustomFieldQueryModelChanged(conditionGroup, model) | ||||
|         this.onCustomFieldQueryModelChanged(filterGroup, model) | ||||
|       }) | ||||
|     conditionGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?.unsubscribe() | ||||
|     conditionGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY] = subscription | ||||
|     filterGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?.unsubscribe() | ||||
|     filterGroup[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY] = subscription | ||||
|  | ||||
|     this.onCustomFieldQueryModelChanged(conditionGroup, model) | ||||
|     this.onCustomFieldQueryModelChanged(filterGroup, model) | ||||
|  | ||||
|     return model | ||||
|   } | ||||
|  | ||||
|   private clearCustomFieldQueryModel(conditionGroup: FormGroup) { | ||||
|     const group = conditionGroup as CustomFieldConditionGroup | ||||
|   private clearCustomFieldQueryModel(filterGroup: FormGroup) { | ||||
|     const group = filterGroup as CustomFieldFilterGroup | ||||
|     group[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY]?.unsubscribe() | ||||
|     delete group[CUSTOM_FIELD_QUERY_SUBSCRIPTION_KEY] | ||||
|     delete group[CUSTOM_FIELD_QUERY_MODEL_KEY] | ||||
|   } | ||||
|  | ||||
|   private getStoredCustomFieldQueryModel( | ||||
|     conditionGroup: FormGroup | ||||
|     filterGroup: FormGroup | ||||
|   ): CustomFieldQueriesModel | null { | ||||
|     return ( | ||||
|       (conditionGroup as CustomFieldConditionGroup)[ | ||||
|         CUSTOM_FIELD_QUERY_MODEL_KEY | ||||
|       ] ?? null | ||||
|       (filterGroup as CustomFieldFilterGroup)[CUSTOM_FIELD_QUERY_MODEL_KEY] ?? | ||||
|       null | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   private setCustomFieldQueryModel( | ||||
|     conditionGroup: FormGroup, | ||||
|     filterGroup: FormGroup, | ||||
|     model: CustomFieldQueriesModel | ||||
|   ) { | ||||
|     const group = conditionGroup as CustomFieldConditionGroup | ||||
|     const group = filterGroup as CustomFieldFilterGroup | ||||
|     group[CUSTOM_FIELD_QUERY_MODEL_KEY] = model | ||||
|   } | ||||
|  | ||||
|   private onCustomFieldQueryModelChanged( | ||||
|     conditionGroup: FormGroup, | ||||
|     filterGroup: FormGroup, | ||||
|     model: CustomFieldQueriesModel | ||||
|   ) { | ||||
|     const control = conditionGroup.get('values') | ||||
|     const control = filterGroup.get('values') | ||||
|     if (!control) { | ||||
|       return | ||||
|     } | ||||
| @@ -1020,26 +1009,26 @@ export class WorkflowEditDialogComponent | ||||
|     control.setValue(serialized, { emitEvent: false }) | ||||
|   } | ||||
|  | ||||
|   private getDefaultConditionValue(type: TriggerConditionType) { | ||||
|     if (type === TriggerConditionType.CustomFieldQuery) { | ||||
|   private getDefaultFilterValue(type: TriggerFilterType) { | ||||
|     if (type === TriggerFilterType.CustomFieldQuery) { | ||||
|       return null | ||||
|     } | ||||
|     return this.isMultiValueCondition(type) ? [] : null | ||||
|     return this.isMultiValueFilter(type) ? [] : null | ||||
|   } | ||||
|  | ||||
|   private normalizeConditionValue(type: TriggerConditionType, value?: any) { | ||||
|   private normalizeFilterValue(type: TriggerFilterType, value?: any) { | ||||
|     if (value === undefined || value === null) { | ||||
|       return this.getDefaultConditionValue(type) | ||||
|       return this.getDefaultFilterValue(type) | ||||
|     } | ||||
|  | ||||
|     if (type === TriggerConditionType.CustomFieldQuery) { | ||||
|     if (type === TriggerFilterType.CustomFieldQuery) { | ||||
|       if (typeof value === 'string') { | ||||
|         return value | ||||
|       } | ||||
|       return value ? JSON.stringify(value) : null | ||||
|     } | ||||
|  | ||||
|     if (this.isMultiValueCondition(type)) { | ||||
|     if (this.isMultiValueFilter(type)) { | ||||
|       return Array.isArray(value) ? [...value] : [value] | ||||
|     } | ||||
|  | ||||
| @@ -1065,7 +1054,7 @@ export class WorkflowEditDialogComponent | ||||
|         matching_algorithm: new FormControl(trigger.matching_algorithm), | ||||
|         match: new FormControl(trigger.match), | ||||
|         is_insensitive: new FormControl(trigger.is_insensitive), | ||||
|         conditions: this.buildConditionFormArray(trigger), | ||||
|         filters: this.buildFiltersFormArray(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( | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon