154 lines
4.8 KiB
JavaScript
Executable File
154 lines
4.8 KiB
JavaScript
Executable File
/**
|
||
* Модуль для управления workflow согласования счетов на оплату
|
||
*/
|
||
|
||
const WORKFLOW_STAGES = {
|
||
draft: {
|
||
next: 'pending_manager_approval',
|
||
role: null,
|
||
description: 'Черновик'
|
||
},
|
||
pending_manager_approval: {
|
||
next: 'pending_finance_manager_approval',
|
||
role: 'manager',
|
||
description: 'На согласовании у руководителя'
|
||
},
|
||
pending_finance_manager_approval: {
|
||
next: 'approved',
|
||
role: 'finance_manager',
|
||
description: 'На согласовании у финансового руководителя'
|
||
},
|
||
approved: {
|
||
next: 'scheduled',
|
||
role: 'financier',
|
||
description: 'Согласован, ожидает постановки в график'
|
||
},
|
||
scheduled: {
|
||
next: 'paid',
|
||
role: null,
|
||
description: 'В графике платежей'
|
||
},
|
||
paid: {
|
||
next: null,
|
||
role: null,
|
||
description: 'Оплачен'
|
||
},
|
||
postponed: {
|
||
next: 'scheduled',
|
||
role: null,
|
||
description: 'Отложен'
|
||
},
|
||
cancelled: {
|
||
next: null,
|
||
role: null,
|
||
description: 'Отменен'
|
||
},
|
||
rejected: {
|
||
next: 'draft',
|
||
role: null,
|
||
description: 'Отклонен'
|
||
},
|
||
completed: {
|
||
next: null,
|
||
role: null,
|
||
description: 'Выполнено'
|
||
}
|
||
};
|
||
|
||
// Роли высшего звена, которые пропускают этап manager
|
||
const TOP_MANAGEMENT_ROLES = ['finance_director', 'director', 'top_management'];
|
||
|
||
/**
|
||
* Определяет следующий статус для счета
|
||
* @param {string} currentStatus - текущий статус
|
||
* @param {string[]} userRoles - роли пользователя
|
||
* @returns {string|null} следующий статус
|
||
*/
|
||
function getNextStatus(currentStatus, userRoles = []) {
|
||
const stage = WORKFLOW_STAGES[currentStatus];
|
||
if (!stage) return null;
|
||
|
||
// Если текущий статус pending_manager_approval и пользователь из высшего звена
|
||
// пропускаем этап manager и идем сразу на finance_manager
|
||
if (currentStatus === 'pending_manager_approval' &&
|
||
userRoles.some(role => TOP_MANAGEMENT_ROLES.includes(role))) {
|
||
return 'pending_finance_manager_approval';
|
||
}
|
||
|
||
return stage.next;
|
||
}
|
||
|
||
/**
|
||
* Определяет, может ли пользователь согласовать счет на текущем этапе
|
||
* @param {string} currentStatus - текущий статус счета
|
||
* @param {string[]} userRoles - роли пользователя
|
||
* @returns {boolean}
|
||
*/
|
||
function canApprove(currentStatus, userRoles = []) {
|
||
const stage = WORKFLOW_STAGES[currentStatus];
|
||
if (!stage || !stage.role) return false;
|
||
|
||
// Если требуется роль manager, но пользователь из высшего звена - может согласовать
|
||
if (stage.role === 'manager' &&
|
||
userRoles.some(role => TOP_MANAGEMENT_ROLES.includes(role))) {
|
||
return true;
|
||
}
|
||
|
||
return userRoles.includes(stage.role);
|
||
}
|
||
|
||
/**
|
||
* Определяет роль следующего согласующего
|
||
* @param {string} currentStatus - текущий статус
|
||
* @param {string[]} userRoles - роли пользователя (для определения пропуска этапа)
|
||
* @returns {string|null}
|
||
*/
|
||
function getNextApproverRole(currentStatus, userRoles = []) {
|
||
const nextStatus = getNextStatus(currentStatus, userRoles);
|
||
if (!nextStatus) return null;
|
||
|
||
const nextStage = WORKFLOW_STAGES[nextStatus];
|
||
return nextStage ? nextStage.role : null;
|
||
}
|
||
|
||
/**
|
||
* Создает запись в истории согласования
|
||
* @param {string} userId - ID пользователя
|
||
* @param {string} userRole - роль пользователя
|
||
* @param {string} action - действие (approve, reject, etc.)
|
||
* @param {string} comment - комментарий
|
||
* @returns {object}
|
||
*/
|
||
function createApprovalHistoryEntry(userId, userRole, action, comment = '') {
|
||
return {
|
||
userId,
|
||
userRole,
|
||
action,
|
||
comment,
|
||
date: new Date().toISOString()
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Генерирует номер счета
|
||
* @param {Date} date - дата создания
|
||
* @returns {string}
|
||
*/
|
||
function generateInvoiceNumber(date = new Date()) {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const timestamp = Date.now().toString().slice(-6);
|
||
return `INV-${year}${month}${day}-${timestamp}`;
|
||
}
|
||
|
||
module.exports = {
|
||
WORKFLOW_STAGES,
|
||
TOP_MANAGEMENT_ROLES,
|
||
getNextStatus,
|
||
canApprove,
|
||
getNextApproverRole,
|
||
createApprovalHistoryEntry,
|
||
generateInvoiceNumber
|
||
};
|