Files
mkd/backend/paymentInvoiceWorkflow.js
2026-02-04 00:17:04 +05:00

154 lines
4.8 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Модуль для управления 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
};