UNPKG

tuain-bpm-lib

Version:

Servicio de gestión de manejo de procesos de la plataforma Tuain

345 lines (324 loc) 15.2 kB
const InstanceEventData = require('./instance-event'); const { collections: { execution: { tasks: tasksColl, processes: procsCol }, }, dbQueries: { execution: { process: processQueries }, }, process: { elements: { task: TASK }, // taskTypes: { manual: MANUAL, sync: SYNC, async: ASYNC }, elementStatus: { active: ACTIVE, complete: COMPLETE, closed: CLOSED }, emmiterTypes: { taskStart: TASKSTART, taskComplete: TASKCOMPLETE, taskClose: TASKCLOSE }, }, } = require('../../../config'); const modErrs = { startNewTask: { notDefined: ['01', 'Tarea no existe en la definición del proceso'], notAssigned: ['02', 'Solicitud de tarea manual sin asignación'], }, newTaskInstance: { saveFailure: ['01', 'Error registrando la instancia de la tarea en base de datos'], }, getTaskInstance: { notFound: ['01', 'No se encotró la instancia de la tarea solicitada'], }, updateProc: { notFound: ['01', 'No se encotró la instancia de la tarea solicitada'], }, completeTask: { missingRequiredVariable: ['01', 'Variable requerida para completar tarea no definida'], }, }; async function getTaskInstance(taskId, procId = null, name = null, status = null) { let message = `[BPM] : [getTaskInstance]: Inicio obtención tarea ${name}/${taskId} sobre ${procId} en estado ${status}`; this.logger.log({ level: 'degug', label: 'task', action: 'getTaskInstance', message }); const taskCol = this.roPool.collection(tasksColl); const findTaskQuery = processQueries.findTaskInstance(taskId, procId, name, status); message = JSON.stringify(findTaskQuery); this.logger.log({ level: 'degug', label: 'task', action: 'getTaskInstance', message }); const taskInstance = await taskCol.findOne(findTaskQuery); if (!taskInstance) { const errorDetail = `Tarea ${taskId} del proceso ${this.name} no se encontró`; const errorObj = this.errMgr.get(modErrs.getTaskInstance.notFound, errorDetail); return [errorObj, null]; } return [null, taskInstance]; } async function createTaskInstance(procId, name, origin, userRole, groupRole, userId, groupId) { let message = `[BPM] : [createTaskInstance]: Inicio creación nueva tarea ${name} sobre ${procId} desde ${origin.name}`; this.logger.log({ level: 'silly', label: 'gatewayInstance', action: 'startNewTask', message }); const taskDef = this.processDefinition?.tasks?.[name]; if (!taskDef) { const message = `[BPM] : La tarea ${name} no se encuentra en la definición del proceso`; this.logger.log({ level: 'error', label: 'instanceTask', action: 'startNewTask', message }); const errorObj = this.errMgr.get(modErrs.startNewTask.notDefined, message); return [errorObj, null]; } const [, existTaskRes] = await this.getTaskInstance(null, procId, name, ACTIVE); if (existTaskRes) { let message = `[BPM] : Existe tarea activa ${name} sobre ${procId} con id: ${existTaskRes._id}`; this.logger.log({ level: 'debug', label: 'gatewayInstance', action: 'startNewTask', message }); return [null, existTaskRes._id]; } const taskCol = this.rwPool.collection(tasksColl); const taskVarNames = Object.keys(taskDef.variables ?? {}); let userAssigned = null; let groupAssigned = null; let userTypeAssigned = null; let groupTypeAssigned = null; if (userId || userRole) { userAssigned = userId ?? userRole; userTypeAssigned = userId ? 'user' : 'role'; } if (groupId || groupRole) { groupAssigned = groupId ?? groupRole; groupTypeAssigned = groupId ? 'group' : 'role'; } const newTaskInstance = { ecosystem: this._ecosystem, procName: this.name, procId, name, source: origin, variables: {}, creation: new Date(), status: ACTIVE, userAssigned, groupAssigned, userTypeAssigned, groupTypeAssigned, }; for (let index = 0; index < taskVarNames.length; index++) { const eventVarName = taskVarNames[index]; newTaskInstance.variables[eventVarName] = null; } message = `[BPM] : Se almacena tarea ${name} sobre ${this.name}/${procId} ${JSON.stringify(newTaskInstance, null, 2)}`; this.logger.log({ level: 'silly', label: 'taskInstance', action: 'createTaskInstanceDB', message }); const actionResult = await taskCol.insertOne(newTaskInstance); const taskId = actionResult?.insertedId.toString(); if (!taskId) { message = `[BPM] : No fue posible la creación de una instancia de la tarea ${name} para el proceso ${this.name}`; this.logger.log({ level: 'error', label: 'taskInstance', action: 'createTaskInstanceDB', message }); const errorObj = this.errorObj.get(modErrs.newTaskInstance.saveFailure); return [errorObj, null]; } return [null, taskId]; } async function updateTaskNextNode(procId, taskId, nextNodeId, nextNodeType, nextNodeName) { let message = `[BPM] : Se actualiza nodo siguiente a la tarea ${taskId}/${procId} como id:${nextNodeId} type:${nextNodeType} name:${nextNodeName}`; this.logger.log({ level: 'silly', label: 'taskInstance', action: 'updateTaskInstanceDB', message }); const updateData = { nextNodes: { [nextNodeName]: { id: nextNodeId, type: nextNodeType, }, }, }; return this.updateTaskInstance(taskId, updateData); } async function updateTaskInstance(taskId, updateData) { const taskCol = this.rwPool.collection(tasksColl); const taskInstance = await taskCol.findOne(processQueries.findTaskInstance(taskId)); if (!taskInstance) { const errorDetail = `Instancia ${taskId} del proceso ${this.name} no existe`; const errorObj = this.errMgr.get(modErrs.updateProc.notFound, errorDetail); return [errorObj, null]; } const actionResult = await taskCol.updateOne(...processQueries.updateTask(taskId, updateData)); const result = actionResult?.result?.nModified > 0; if (!result) { let message = `[BPM] : No se pudo efectuar modificación de la instancia de tarea ${taskId}`; this.logger.log({ level: 'silly', label: 'taskInstance', action: 'updateTaskInstanceDB', message }); } return [null, { result }]; } async function startNewTask(procId, name, origin, userRole = null, groupRole = null, userId = null, groupId = null) { let message = `[BPM] : [startNewTask]: Se inicia nueva tarea ${name}/${procId} desde ${origin}`; this.logger.log({ level: 'silly', label: 'instanceTask', action: 'startNewTask', message }); const taskDef = this.processDefinition?.tasks?.[name]; if (!taskDef) { message = `[BPM] : La tarea ${name} no se encuentra en la definición del proceso`; this.logger.log({ level: 'error', label: 'instanceTask', action: 'startNewTask', message }); const errorObj = this.errMgr.get(modErrs.startNewTask.notDefined, message); return [errorObj, null]; } const [eventErr, taskId] = await this.createTaskInstance(procId, name, origin, userRole, groupRole, userId, groupId); if (eventErr) { return [eventErr, null]; } message = `[BPM] : Nueva tarea ${name}/${taskId} en proceso ${this.name}/${procId}`; this.logger.log({ level: 'info', label: 'instanceTask', action: 'startNewTask', message }); const taskEvent = new InstanceEventData(this.name, procId, taskId, name); const callbakResult = (await this.execCustomCallback(TASKSTART, name, taskEvent)) ?? {}; return [null, taskId]; } async function cancelActiveTasks(procId) { const taskCol = this.rwPool.collection(tasksColl); let result = false; if (procId) { const actionResult = await taskCol.updateMany({ procId, status: 'active' }, { $set: { status: 'canceled' } }); result = actionResult?.result?.nModified > 0; } return [null, { result }]; } // async function checkCustomResult() {} async function assignTaskToUser(taskId, procId, name, userId) { const taskCol = this.rwPool.collection(tasksColl); const locateInfo = taskId || { procId, name }; const actionResult = await taskCol.updateOne(...processQueries.updateTask(locateInfo, { userAssigned: userId })); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async function stopTask(inputTaskId, procId, name) { const [getTaskErr, taskInstance] = await this.getTaskInstance(inputTaskId, procId, name, ACTIVE); if (getTaskErr) { return [getTaskErr, null]; } const taskId = taskInstance._id.toString(); return this.updateTaskInstance(taskId, { status: CLOSED }); } async function assignTaskToGroup(taskId, procId, name, groupId) { const taskCol = this.rwPool.collection(tasksColl); const locateInfo = taskId || { procId, name }; const actionResult = await taskCol.updateOne(...processQueries.updateTask(locateInfo, { groupAssigned: groupId })); const result = actionResult?.result?.nModified > 0; return [null, { result }]; } async function completeTask(inputTaskId, inputVars = {}, processId = null, taskName = null) { const [getTaskErr, taskInstance] = await this.getTaskInstance(inputTaskId, processId, taskName, ACTIVE); if (getTaskErr) { return [getTaskErr, null]; } const { name, procId, procName, variables: taskCurrentVars } = taskInstance; const taskId = taskInstance._id.toString(); let message = `[BPM] : [completeTask]: Inicio completar tarea ${name}/${taskId} sobre ${procId}`; this.logger.log({ level: 'silly', label: 'taskInstance', action: 'completeTask', message }); const taskVariables = { ...taskCurrentVars, ...inputVars }; if (procName !== this.name) { message = `[BPM] : La tarea ${name} asociada a la instancia ${taskId} no pertenece a una instancia del proceso ${this.name}`; const errorObj = this.errMgr.get(modErrs.completeTask.notDefined, message); this.logger.log({ level: 'error', label: 'taskInstance', action: 'completeTask', message }); return [errorObj, null]; } const taskDef = this.processDefinition?.tasks?.[name]; if (!taskDef) { message = `[BPM] : La tarea ${name} asociada a la instancia ${taskId} no está definida para el proceso ${this.name}`; const errorObj = this.errMgr.get(modErrs.completeTask.notDefined, message); this.logger.log({ level: 'error', label: 'taskInstance', action: 'completeTask', message }); return [errorObj, null]; } const { variables = {}, next } = taskDef; const variableNames = Object.keys(variables); for (let index = 0; index < variableNames.length; index++) { const variableName = variableNames[index]; const { required } = variables[variableName]; if (required && (typeof taskVariables?.[variableName] === 'undefined' || taskVariables?.[variableName] === null)) { message = `[BPM] : Variable ${variableName} requerida para finalizar la tarea ${name}`; this.logger.log({ level: 'error', label: 'taskInstance', action: 'completeTask', message }); const errorObj = this.errMgr.get(modErrs.completeTask.missingRequiredVariable, message); return [errorObj, null]; } } const taskVarNames = Object.keys(taskVariables); for (let index = 0; index < taskVarNames.length; index++) { const taskVarName = taskVarNames[index]; if (!variables[taskVarName]) { delete taskVariables[taskVarName]; } } const taskEvent = new InstanceEventData(this.name, procId, taskId, name, taskVariables); const completeResult = (await this.execCustomCallback(TASKCOMPLETE, name, taskEvent)) ?? {}; const closeResult = (await this.execCustomCallback(TASKCLOSE, name, taskEvent)) ?? {}; this.updateTaskInstance(taskId, { completion: new Date(), status: COMPLETE, variables: taskVariables }); this.goToNode(procId, next.name, next.type, TASK, name, taskId); return [null, { ...completeResult, ...closeResult }]; } async function getProcessActiveTasks(procId, name, status = ACTIVE) { const taskCol = this.roPool.collection(tasksColl); const userTask = await taskCol.findOne(processQueries.findTask({ name, procId, status })); return [null, userTask]; } async function getUserProcess(userType, userId, status = ACTIVE, name = null) { const procCol = this.roPool.collection(procsCol); const taskCol = this.roPool.collection(tasksColl); const procDetail = await procCol.findOne(processQueries.findProcessByUser(userType, userId)); if (!procDetail) { const errorDetail = `Usuario con id ${userId} no tiene procesos asociados con el tipo ${userType}`; const errorObj = this.errMgr.get(modErrs.updateProc.notFound, errorDetail); return [errorObj, null]; } const userTask = await taskCol.find(processQueries.findTask({ procId: procDetail?._id?.toString(), status, name })).toArray(); return [null, userTask]; } async function getActiveTasks(name, status = ACTIVE, pageNumber = null, pageSize = null) { const taskCol = this.roPool.collection(tasksColl); let userTasks; let totalTasks; if (pageNumber && pageSize) { const numberSkip = (pageNumber - 1) * pageSize; const userTasksCursor = await taskCol.find(processQueries.findTask({ name, status })).sort({ creation: -1 }); totalTasks = await userTasksCursor.count(); userTasks = await userTasksCursor.skip(numberSkip).limit(pageSize).toArray(); } else { userTasks = await taskCol.find(processQueries.findTask({ name, status })).toArray(); } return [null, { userTasks, totalTasks }]; } async function getUserTasks(userId, taskName = null, status = ACTIVE, pageNumber = null, pageSize = null) { const taskCol = this.roPool.collection(tasksColl); let userTasks; let totalTasks; if (pageNumber && pageSize) { const numberSkip = (pageNumber - 1) * pageSize; const query = { userAssigned: userId, status }; taskName && Object.assign(query, { name: taskName }); const userTasksCursor = await taskCol.find(processQueries.findTask(query)); totalTasks = await userTasksCursor.count(); userTasks = await userTasksCursor.skip(numberSkip).limit(pageSize).toArray(); } else { const query = { userAssigned: userId, status }; taskName && Object.assign(query, { name: taskName }); userTasks = await taskCol.find(processQueries.findTask(query)).toArray(); } return [null, { userTasks, totalTasks }]; } async function getGroupsTasks(groupIdArray) { const taskCol = this.roPool.collection(tasksColl); const groupsTasks = await taskCol.find(processQueries.findGroupsTasks(groupIdArray)).toArray(); return [null, groupsTasks]; } async function getProcessTaskHistory(procId) { const taskCol = this.roPool.collection(tasksColl); const userTasks = await taskCol.find(processQueries.findTask({ procId, status: COMPLETE })).toArray(); return [null, userTasks]; } async function getTasksInProgress(procId) { const taskCol = this.roPool.collection(tasksColl); const userTasks = await taskCol.find(processQueries.findTask({ procId, status: ACTIVE })).toArray(); return [null, userTasks]; } module.exports = { methods: { getTaskInstance, createTaskInstance, updateTaskNextNode, updateTaskInstance, startNewTask, cancelActiveTasks, assignTaskToUser, assignTaskToGroup, stopTask, completeTask, getProcessActiveTasks, getUserProcess, getActiveTasks, getUserTasks, getGroupsTasks, getProcessTaskHistory, getTasksInProgress, }, errors: modErrs, };