/** * Модуль для управления 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 };