File

src/lib/service/bpm/bpm.service.ts

Description

BpmService handles interaction related to BPM tasks.

The main parts provided and used by this service are Processes and WorkItems. Processes are defined on the backend side. They describe a set of tasks that will be executed by the process. Tasks that require user interaction are referred to as WorkItems.

Processes can be related to a special object type, but they don't have to. In addition to that they can refer to dms objects.

Index

Properties
Methods

Constructor

constructor(capabilities: CapabilitiesService, backend: BackendService, eventService: EventService, inboxService: InboxService)
Parameters :
Name Type Optional
capabilities CapabilitiesService no
backend BackendService no
eventService EventService no
inboxService InboxService no

Methods

addFileElements
addFileElements(processId: string, workItemId: string, elements: DmsObject[])

Add contents to a work items file.

Parameters :
Name Type Optional Description
processId string no

The ID of the process

workItemId string no

The ID of the activity

elements DmsObject[] no

The Elements to be added

The updated WorkItem

addFileElementsFromClipboard
addFileElementsFromClipboard(workItem: WorkItem, clipboard: Clipboard)

Add a objects from the clipboard to a WorkItem.

Parameters :
Name Type Optional Description
workItem WorkItem no

The workItem to add the objects to

clipboard Clipboard no

Clipboard object holding user selected objects

The updated WorkItem

fetchProcessModels
fetchProcessModels()

Fetch all available bpm projects including all of their model data

Returns : Observable<>

list of all the projects including the models

forwardWorkItem
forwardWorkItem(item: WorkItem, action?: WorkItemAction)

Forwards a work item. If a WorkItem provides different actions (@link WorkItemAction) you can provide the selected action too.

Parameters :
Name Type Optional Description
item WorkItem no

The work item to be forwarded

action WorkItemAction yes

Action used for forwarding

Returns : Observable<any>
getExecutableProcesses
getExecutableProcesses(types?: string[], useCached?: boolean, additionalData?: boolean, modelid?: string, global?: boolean)

Getter for the executable Processes. If types are provided, you'll get only the processes that are executable for all of them

Parameters :
Name Type Optional Description
types string[] yes

List of dms object types to fetch executable processes for

useCached boolean yes

If the cached data should be returned rather than requesting again

additionalData boolean yes

If the additional form data should be also included

modelid string yes

The process model ID to only return the data for one process

global boolean yes

List of executable processes

getExecutableProcessesForDmsObjects
getExecutableProcessesForDmsObjects(dmsObjects: DmsObject[])

Gets executable Processes for a given set of dms object.You'll get only the processes that are executable for all of them.

Parameters :
Name Type Optional Description
dmsObjects DmsObject[] no

List of dms objects to fetch executable processes for

List of executable processes

getProcessData
getProcessData(id: string)
Parameters :
Name Type Optional
id string no
getProcesses
getProcesses(size: number)

Retrieve all processes of the current user.

Parameters :
Name Type Optional Default value Description
size number no 1000

Maximum number of processes to be fetched

List of Processes

getProcessFile
getProcessFile(processId: string)

Getter for a processes file. The file of a process contains dms objects attached to the process.

Parameters :
Name Type Optional Description
processId string no

ID of the process to fetch attached objects for

getProcessHistory
getProcessHistory(processId: string)

Returns the history for a process. Which entries are returned depends on the permissions of the current user.

Parameters :
Name Type Optional Description
processId string no

The ID of the process to fetch history for

Observable<WorkItemHistoryEntry[]>

getWorkItem
getWorkItem(processId: string, itemId: string, options?: any)

Retrieves a work item.

Parameters :
Name Type Optional Description
processId string no

ID of the parent process

itemId string no

ID of the work item itself

options any yes

Options to be added to the backend call

a WorkItem

initExecutableProcesses
initExecutableProcesses()
Returns : Observable<any>
lockWorkItem
lockWorkItem(item: WorkItem)

Locks a WorkItem for the current user (personalization), meaning that the current user is now in charge of fulfilling the WorkItem task.

Parameters :
Name Type Optional Description
item WorkItem no

The WorkItem to be locked

The updated WorkItem

removeFileElement
removeFileElement(processId: string, workItemId: string, workItemContentId: string)

Removes a content from the work items file.

Parameters :
Name Type Optional Description
processId string no

The ID of the process to remove the item from

workItemId string no

The ID of the work item containing the file element to be removed

workItemContentId string no

The the ID of the file element to be removed

Returns : Observable<any>
saveWorkItem
saveWorkItem(item: WorkItem)

Saves a work item.

Parameters :
Name Type Optional Description
item WorkItem no

The work item to be saved

The updated WorkItem

startProcess
startProcess(executableProcessId: string, data: any, contents: literal type[])

Starts an executable process.

Parameters :
Name Type Optional Description
executableProcessId string no

ID of the executable Process to be started

data any no

Data to be passed to the process (simple object of key/value pairs)

contents literal type[] no

Objects to be attached to the process (process.file).

Returns : Observable<any>
unlockWorkItem
unlockWorkItem(item: WorkItem)

Releases the lock for a WorkItem.

Parameters :
Name Type Optional Description
item WorkItem no

The work item to be unlocked

The updated WorkItem

Properties

hasMainExecutableProcesses
hasMainExecutableProcesses: boolean
Type : boolean
processItems$
processItems$: Observable<Process[]>
Type : Observable<Process[]>
Default value : this.processItemsSource.asObservable()

Long term observable emitting the current processes

BpmService

BpmService handles interaction related to BPM tasks.

import {forkJoin as observableForkJoin, of as observableOf, Observable, ReplaySubject, throwError as observableThrowError} from 'rxjs';
import {mergeMap, tap, map} from 'rxjs/operators';
import {Injectable} from '@angular/core';
import {Utils} from '../../util/utils';
import {BackendService} from '../backend/backend.service';
import {WorkItemHistoryEntry} from '../../model/work-item-history.model';
import {DmsObject} from '../../model/dms-object.model';
import {InboxService} from '../inbox/inbox.service';
import {EventService} from '../events/event.service';
import {CapabilitiesService} from '../capabilities/capabilities.service';
import {ExecutableProcess, FileEntry, Process, WorkItem, WorkItemAction} from '../../model/bpm.model';
import {Clipboard} from '../clipboard/clipboard.interface';
import {ClipboardAction} from '../clipboard/clipboard-action.enum';
import {ProcessItem} from '../../model/process-item.model';

/**
 * `BpmService` handles interaction related to BPM tasks.
 *
 * The main parts provided and used by this service are Processes and WorkItems.
 * Processes are defined on the backend side. They describe a set of tasks that will be executed by the process.
 * Tasks that require user interaction are referred to as WorkItems.
 *
 * Processes can be related to a special object type, but they don't have to. In addition to that they can refer
 * to dms objects.
 */
@Injectable({
  providedIn: 'root'
})
export class BpmService {
  hasMainExecutableProcesses: boolean;
  private executableProcesses: ExecutableProcess[];
  private processItems: Process[];
  private processItemsSource = new ReplaySubject<Process[]>(1);
  /**
   * Long term observable emitting the current processes
   */
  processItems$: Observable<Process[]> = this.processItemsSource.asObservable();

  constructor(private capabilities: CapabilitiesService,
    private backend: BackendService,
    private eventService: EventService,
    private inboxService: InboxService) {
  }

  /**
   * Retrieve all processes of the current user.
   *
   * @param size Maximum number of processes to be fetched
   * @returns List of Processes
   */
  getProcesses(size: number = 1000): Observable<Process[]> {
    return this.backend
      .getJson(`/process/user?size=${size}`, this.backend.getBpmBase())
      .pipe(
        map(response => response.content.map(item => new Process(item))),
        tap((processes: Process[]) => {
          this.processItems = processes;
          this.processItemsSource.next(this.processItems);
        })
      );
  }

  /**
   * Getter for the executable Processes. If types are provided, you'll get
   * only the processes that are executable for all of them
   * @param types List of dms object types to fetch executable processes for
   * @param useCached If the cached data should be returned rather than requesting again
   * @param additionalData If the additional form data should be also included
   * @param modelid The process model ID to only return the data for one process
   * @returns List of executable processes
   */
  getExecutableProcesses(types?: string[], useCached?: boolean, additionalData?: boolean, modelid?: string, global?: boolean): Observable<ExecutableProcess[]> {
    if (this.capabilities.hasCapability('bpm')) {
      return (!!this.executableProcesses && useCached) ? observableOf(this.executableProcesses) : this.fetchExecutableProcesses(types, additionalData, modelid, global);
    } else {
      return observableOf([]);
    }
  }

  initExecutableProcesses(): Observable<any> {
    let uri = '/bpm/process/executable';
    return this.backend.get(uri).pipe(tap(res => this.hasMainExecutableProcesses = res?.length));
  }

  /**
   * Gets executable Processes for a given set of dms object.You'll get
   * only the processes that are executable for all of them.
   * @param dmsObjects List of dms objects to fetch executable processes for
   * @returns List of executable processes
   */
  getExecutableProcessesForDmsObjects(dmsObjects: DmsObject[]): Observable<ExecutableProcess[]> {
    return this.getExecutableProcesses(dmsObjects.map(o => o.typeName), false, true);
  }

  getProcessData(id: string): Observable<ProcessItem[]> {
    let uri = Utils.buildUri(`/processes?objectid=${id}&page=0&size=20`, {});
    return this.backend.getJson(uri, this.backend.getBpmBase()).pipe(
      map(res => res.content.map(i => {
        return {
          endtime: i.endtime,
          iconid: i.iconid,
          id: i.id,
          modelDisplayName: i.modelDisplayName,
          modelid: i.modelid,
          starttime: i.starttime,
          modelname: i.modelname,
          name: i.name,
          parentProcessId: i.parentProcessId,
          processeditor: i.processeditor,
          state: i.state,
          subject: i.subject
        };
      }))
    );
  }

  /**
   * Fetches executable Processes from the backend.
   * @param types List of dms object types to fetch executable processes for
   * @param additionalData If the additional form data should be also included
   * @param modelid The process model ID to only return the data for one process
   * @returns List of executable processes
   */
  private fetchExecutableProcesses(types?: string[], additionalData?: boolean, modelid?: string, global?: boolean): Observable<ExecutableProcess[]> {
    let uri = '/bpm/process/executable';
    if (additionalData) {
      uri += '?form=true&fields=true';
    }
    if (types) {
      uri += (additionalData ? '&' : '?') + `type=${types.join(',')}`;
    }
    if (modelid) {
      uri += (additionalData || types ? '&' : '?') + `modelid=${modelid}`;
    }
    return this.backend.get(uri).pipe(
      map(res => res as ExecutableProcess[]),
      tap(res => {
        // Extend existing processes with additional data, instead of overwriting all processes
        if (global) {
          if (additionalData) {
            if (!this.executableProcesses) {
              this.executableProcesses = [];
            }
            res.forEach(proc => {
              const existingProcIndex = this.executableProcesses.findIndex(item => item.id === proc.id);
              if (existingProcIndex === -1) {
                this.executableProcesses.push(proc);
              } else {
                this.executableProcesses[existingProcIndex] = proc;
              }
            });
          } else {
            this.executableProcesses = res;
          }
        }
      }));
  }

  /**
   * Getter for a processes file. The file of a process contains
   * dms objects attached to the process.
   * @param processId ID of the process to fetch attached objects for
   */
  getProcessFile(processId: string): Observable<FileEntry[]> {
    return this.backend
      .getJson(`/process/${processId}/file`, this.backend.getBpmBase()).pipe(
        map(res => res.content.map(i => {
          return {
            id: i.elementid,
            title: i.title,
            description: i.description ? i.description : '',
            iconid: i.iconid,
            creator: i.creator,
            addtime: i.addtime,
            type: i.type
          };
        }))
      );
  }

  /**
   * Retrieves a work item.
   * @param processId ID of the parent process
   * @param itemId ID of the work item itself
   * @param options Options to be added to the backend call
   * @returns a WorkItem
   */
  getWorkItem(processId: string, itemId: string, options?: any): Observable<WorkItem> {
    const params = options ? options : {form: true, fields: true};
    const tasks = [
      this.backend.getJson(Utils.buildUri(`/bpm/process/${processId}/activity/${itemId}/inboxitem`, params)),
      this.getProcessFile(processId)
    ];
    return observableForkJoin(tasks).pipe(
      map(res => {
        const wi = new WorkItem(res[0]);
        wi.setFile(res[1]);
        return wi;
      }));
  }

  /**
   * Saves a work item.
   * @param item The work item to be saved
   * @returns The updated WorkItem
   */
  saveWorkItem(item: WorkItem): Observable<WorkItem> {
    const {id, processId, file, data} = item;
    const payload = {contents: file, data};
    const params = {keeplock: true, isautolock: false};

    return this.backend
      .post(Utils.buildUri(`/bpm/process/${processId}/activity/${id}/inboxitem/save`, params), payload)
      .pipe(
        mergeMap(() => this.getWorkItem(processId, id))
      );
  }

  /**
   * Forwards a work item. If a WorkItem provides different actions (@link WorkItemAction)
   * you can provide the selected action too.
   * @param item The work item to be forwarded
   * @param action Action used for forwarding
   */
  forwardWorkItem(item: WorkItem, action?: WorkItemAction): Observable<any> {
    const params = {keeplock: false, isautolock: false};
    const payload = {
      action: action,
      contents: item.file,
      data: item.data
    };
    return this.backend
      .post(Utils.buildUri(`/bpm/process/${item.processId}/activity/${item.id}/inboxitem/save`, params), payload)
      .pipe(
        tap(() => this.inboxService.updateInboxItems(item.id))
      );
  }

  /**
   * Locks a WorkItem for the current user (personalization), meaning that the
   * current user is now in charge of fulfilling the WorkItem task.
   * @param item The WorkItem to be locked
   * @returns The updated WorkItem
   */
  lockWorkItem(item: WorkItem): Observable<WorkItem> {
    return this.backend.put(`/bpm/process/${item.processId}/activity/${item.id}/inboxitem/lock`)
      .pipe(
        mergeMap(() => this.getWorkItem(item.processId, item.id))
      );
  }

  /**
   * Releases the lock for a WorkItem.
   * @param item The work item to be unlocked
   * @returns The updated WorkItem
   */
  unlockWorkItem(item: WorkItem): Observable<WorkItem> {
    return this.backend.del(`/bpm/process/${item.processId}/activity/${item.id}/inboxitem/lock`).pipe(
      mergeMap(() => this.getWorkItem(item.processId, item.id))
    );
  }

  /**
   * Starts an executable process.
   * @param executableProcessId ID of the executable Process to be started
   * @param data Data to be passed to the process (simple object of key/value pairs)
   * @param contents Objects to be attached to the process (process.file).
   */
  startProcess(executableProcessId: string, data: any, contents: {id: string, type: string}[]): Observable<any> {
     // Create updatedData by transforming 'data' to extract value if the property has 'actualOperator'
     const updatedData = {
      ...data,
      ...Object.fromEntries(
        Object.entries(data).map(([key, value]) => {
          const isObjectWithActualOperator = value && typeof value === 'object' && 'actualOperator' in value;
          const hasArrayValue = isObjectWithActualOperator && Array.isArray((value as any).value) && (value as any).value.length > 0;
          return hasArrayValue ? [key, (value as any).value[0]] : [key, value];
        })
      )
    };

    return this.backend.post(`/bpm/process/?modelid=${executableProcessId}`, {
      data: updatedData,
      contents: contents
    });
  }

  /**
   * Returns the history for a process. Which entries are returned depends on the
   * permissions of the current user.
   * @param processId The ID of the process to fetch history for
   * @returns Observable<WorkItemHistoryEntry[]>
   */
  getProcessHistory(processId: string): Observable<WorkItemHistoryEntry[]> {
    return this.backend
      .getJson(`/bpm/process/${processId}/history`)
      .pipe(map(res => res && res.length ? res.map(item => new WorkItemHistoryEntry(item)) : []));
  }

  /**
   * Removes a content from the work items file.
   * @param processId The ID of the process to remove the item from
   * @param workItemId The ID of the work item containing the file element to be removed
   * @param workItemContentId The the ID of the file element to be removed
   */
  removeFileElement(processId: string, workItemId: string, workItemContentId: string): Observable<any> {
    return this.backend
      .del(`/process/${processId}/file/${workItemContentId}?activityid=${workItemId}`, this.backend.getBpmBase());
  }

  /**
   * Add contents to a work items file.
   * @param processId The ID of the process
   * @param workItemId The ID of the activity
   * @param elements The Elements to be added
   * @returns The updated WorkItem
   */
  addFileElements(processId: string, workItemId: string, elements: DmsObject[]): Observable<WorkItem> {
    const tasks = [];
    elements.forEach((e) => {
      tasks.push(this.backend.post(
        `/process/${processId}/file/${e.id}?type=${e.type.name}&activityid=${workItemId}`,
        {}, this.backend.getBpmBase()));
    });
    return observableForkJoin(tasks)
      .pipe(
        mergeMap(() => this.getWorkItem(processId, workItemId))
      );
  }

/**
 * Fetch all available bpm projects including all of their model data
 * @returns list of all the projects including the models
 */
  fetchProcessModels(): Observable<[]> {
    const uri = Utils.buildUri(`/bpm/management/project?includeModels=true`, {});
    return this.backend.get(uri);
  }

  /**
   * Add a objects from the clipboard to a WorkItem.
   * @param workItem The workItem to add the objects to
   * @param clipboard Clipboard object holding user selected objects
   * @returns The updated WorkItem
   */
  addFileElementsFromClipboard(workItem: WorkItem, clipboard: Clipboard): Observable<WorkItem> {
    if ((workItem && workItem.fileEntryPermissions.add) && clipboard && clipboard.elements.length && clipboard.action === ClipboardAction.COPY) {
      let contents: DmsObject[];
      if (workItem.file.length) {
        // only add items that are not already part of the attachments
        const attachmentIds = workItem.file.map(e => e.id);
        contents = clipboard.elements.filter(e => attachmentIds.indexOf(e.id) === -1);
      } else {
        contents = clipboard.elements;
      }
      return this.addFileElements(workItem.processId, workItem.id, contents);
    } else {
      return observableThrowError('error');
    }
  }
}

results matching ""

    No results matching ""