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