/** * @wasrusgen1 CRM — Единый источник данных * Подключается во все кабинеты: * Содержит: вертикаль управления, менеджеры, расписание, заявки, статистика * Версия: 2026-05-28 */ // ═══════════════════════════════════════════════════════════════════ // ВЕРТИКАЛЬ: КД → АДМИНИСТРАТОР → МЕНЕДЖЕРЫ // Единый источник истины для всех кабинетов // ═══════════════════════════════════════════════════════════════════ var _HIERARCHY = [ { salonId:'lenina', salon:'Салон Ленина', color:'#3B82F6', admin:{name:'Анна М.', short:'АМ', userId:'admin_lenina'}, orders:27, ordersPlan:30, revenue:1537000, revenuePlan:1700000, overdue:1, overdueRisk:98500, purchases:2, newLeads:14, status:'warn', managers:[ {id:'ak', name:'Анна К.', short:'АК', color:'#7C3AED', salon:'lenina', visits:42, deals:14, revenue:847000, conversion:33, avg:60500, rating:4.8, active:true}, {id:'ms', name:'Мария С.', short:'МС', color:'#0891B2', salon:'lenina', visits:35, deals:9, revenue:610000, conversion:26, avg:67800, rating:4.5, active:true}, ] }, { salonId:'pobedy', salon:'Салон Победы', color:'#8B5CF6', admin:{name:'Ирина С.', short:'ИС', userId:'admin_pobedy'}, orders:20, ordersPlan:22, revenue:1310000, revenuePlan:1500000, overdue:1, overdueRisk:113000, purchases:1, newLeads:12, status:'warn', managers:[ {id:'pv', name:'Пётр В.', short:'ПВ', color:'#059669', salon:'pobedy', visits:28, deals:7, revenue:490000, conversion:25, avg:70000, rating:4.3, active:true}, {id:'iv', name:'Иван В.', short:'ИВ', color:'#D97706', salon:'pobedy', visits:31, deals:8, revenue:530000, conversion:26, avg:66250, rating:4.1, active:false}, ] }, ]; // ═══════════════════════════════════════════════════════════════════ // ПЛОСКИЙ СПИСОК ВСЕХ МЕНЕДЖЕРОВ (производный от _HIERARCHY) // ═══════════════════════════════════════════════════════════════════ var _CHESS_MGRS = (function(){ var list = []; _HIERARCHY.forEach(function(h){ h.managers.forEach(function(m){ list.push(m); }); }); return list; })(); // ═══════════════════════════════════════════════════════════════════ // ШАХМАТКА: временные слоты и расписание // ═══════════════════════════════════════════════════════════════════ var _CHESS_HOURS = ['10:00','11:00','12:00','13:00','14:00','15:00','16:00','17:00','18:00','19:00','20:00']; var _CHESS_DATA = { 'ak':{'10:00':{client:'Орлова М.', type:'consult', status:'done'}, '11:00':{client:'Соколов А.', type:'follow', status:'done'}, '12:00':{client:'', type:'', status:'free'}, '13:00':{client:'', type:'', status:'free'}, '14:00':{client:'Ким Л.', type:'consult', status:'busy'}, '15:00':{client:'Ким Л.', type:'consult', status:'busy'}, '16:00':{client:'Захаров П.', type:'measure', status:'busy'}, '17:00':{client:'Новикова С.', type:'consult', status:'free'}, '18:00':{client:'', type:'', status:'free'}, '19:00':{client:'Громов И.', type:'follow', status:'free'}, '20:00':{client:'', type:'', status:'free'}}, 'ms':{'10:00':{client:'Козлов Р.', type:'follow', status:'done'}, '11:00':{client:'', type:'', status:'free'}, '12:00':{client:'Лебедев С.', type:'consult', status:'done'}, '13:00':{client:'', type:'', status:'free'}, '14:00':{client:'Петрова А.', type:'tech', status:'busy'}, '15:00':{client:'Петрова А.', type:'tech', status:'busy'}, '16:00':{client:'', type:'', status:'free'}, '17:00':{client:'Морозов В.', type:'consult', status:'busy'}, '18:00':{client:'', type:'', status:'free'}, '19:00':{client:'', type:'', status:'free'}, '20:00':{client:'', type:'', status:'free'}}, 'pv':{'10:00':{client:'', type:'', status:'free'}, '11:00':{client:'Сидорова Н.', type:'consult', status:'done'}, '12:00':{client:'Фёдоров К.', type:'follow', status:'noshow'}, '13:00':{client:'', type:'', status:'free'}, '14:00':{client:'', type:'', status:'free'}, '15:00':{client:'Баринова Т.', type:'consult', status:'busy'}, '16:00':{client:'Баринова Т.', type:'consult', status:'busy'}, '17:00':{client:'', type:'', status:'free'}, '18:00':{client:'Яковлев М.', type:'measure', status:'busy'}, '19:00':{client:'Яковлев М.', type:'measure', status:'busy'}, '20:00':{client:'', type:'', status:'free'}}, 'iv':{'10:00':{client:'Тихонова Р.', type:'consult', status:'done'}, '11:00':{client:'Тихонова Р.', type:'consult', status:'done'}, '12:00':{client:'', type:'', status:'free'}, '13:00':{client:'Волков Е.', type:'follow', status:'done'}, '14:00':{client:'', type:'', status:'free'}, '15:00':{client:'Кузьмин О.', type:'consult', status:'busy'}, '16:00':{client:'', type:'', status:'free'}, '17:00':{client:'Рыбаков Д.', type:'tech', status:'free'}, '18:00':{client:'Рыбаков Д.', type:'tech', status:'free'}, '19:00':{client:'', type:'', status:'free'}, '20:00':{client:'', type:'', status:'free'}}, }; // ═══════════════════════════════════════════════════════════════════ // СТАТИСТИКА МЕНЕДЖЕРОВ ПО МЕСЯЦАМ // ═══════════════════════════════════════════════════════════════════ var _MONTHLY_STATS = [ {id:'ak', name:'Анна К.', color:'#7C3AED', salon:'lenina', months:{ 'Май':{visits:42, deals:14, revenue:847000, conversion:33, avg:60500, rating:4.8}, 'Апр':{visits:38, deals:11, revenue:712000, conversion:29, avg:64700, rating:4.7}, 'Мар':{visits:45, deals:16, revenue:920000, conversion:36, avg:57500, rating:4.9}, }}, {id:'ms', name:'Мария С.', color:'#0891B2', salon:'lenina', months:{ 'Май':{visits:35, deals:9, revenue:610000, conversion:26, avg:67800, rating:4.5}, 'Апр':{visits:41, deals:13, revenue:780000, conversion:32, avg:60000, rating:4.6}, 'Мар':{visits:39, deals:12, revenue:695000, conversion:31, avg:57900, rating:4.4}, }}, {id:'pv', name:'Пётр В.', color:'#059669', salon:'pobedy', months:{ 'Май':{visits:28, deals:7, revenue:490000, conversion:25, avg:70000, rating:4.3}, 'Апр':{visits:32, deals:9, revenue:540000, conversion:28, avg:60000, rating:4.2}, 'Мар':{visits:30, deals:8, revenue:520000, conversion:27, avg:65000, rating:4.4}, }}, {id:'iv', name:'Иван В.', color:'#D97706', salon:'pobedy', months:{ 'Май':{visits:31, deals:8, revenue:530000, conversion:26, avg:66250, rating:4.1}, 'Апр':{visits:29, deals:7, revenue:480000, conversion:24, avg:68600, rating:4.0}, 'Мар':{visits:33, deals:10, revenue:610000, conversion:30, avg:61000, rating:4.3}, }}, ]; // ═══════════════════════════════════════════════════════════════════ // ЗАЯВКИ МЕНЕДЖЕРОВ → АДМИНИСТРАТОР (+ эскалация → КД) // ═══════════════════════════════════════════════════════════════════ window._MGR_REQUESTS = window._MGR_REQUESTS || [ {id:'r1', mgr:'Анна К.', mgrId:'ak', salon:'lenina', color:'#7C3AED', type:'supply', prio:'high', title:'Закончились образцы ткани', body:'Нет образцов искусственной замши — теряем клиентов. Нужно срочно дозаказать у Мебельтекстиля.', created:'сегодня 09:14', status:'new'}, {id:'r2', mgr:'Мария С.', mgrId:'ms', salon:'lenina', color:'#0891B2', type:'escalate', prio:'high', title:'Конфликт с клиентом Козлов Р.', body:'Клиент требует возврат 25 000 ₽ из-за задержки поставки. Говорит, что подаст жалобу. Прошу вмешаться.', created:'сегодня 10:30', status:'new'}, {id:'r3', mgr:'Пётр В.', mgrId:'pv', salon:'pobedy', color:'#059669', type:'visit', prio:'normal', title:'Нужна машина для выезда к клиенту', body:'Клиент Сидорова на Васильевском — хочет видеть образцы на дому. Нужен транспорт на 15:00.', created:'сегодня 11:05', status:'new'}, {id:'r4', mgr:'Анна К.', mgrId:'ak', salon:'lenina', color:'#7C3AED', type:'supply', prio:'normal', title:'Каталоги кухонь Hettich закончились', body:'Остался 1 экземпляр. Клиенты берут домой, не возвращают. Надо заказать 10 шт.', created:'вчера 17:22', status:'done'}, {id:'r5', mgr:'Мария С.', mgrId:'ms', salon:'lenina', color:'#0891B2', type:'schedule', prio:'normal', title:'Запрос на замену смены — 31 мая', body:'Прошу разрешить обменяться сменой с Петром: я работаю 31 мая вместо него, он — 1 июня вместо меня.', created:'вчера 14:10', status:'new'}, ]; // ═══════════════════════════════════════════════════════════════════ // ЗАПРОСЫ НА СМЕНЫ // ═══════════════════════════════════════════════════════════════════ window._SHIFT_REQS = window._SHIFT_REQS || [ {id:'sr1', mgr:'Мария С.', mgrId:'ms', salon:'lenina', color:'#0891B2', type:'swap', dates:'31 мая ↔ 1 июня', with:'Пётр В.', status:'pending'}, {id:'sr2', mgr:'Пётр В.', mgrId:'pv', salon:'pobedy', color:'#059669', type:'off', dates:'2 июня (отгул)', with:null, status:'pending'}, ]; // ═══════════════════════════════════════════════════════════════════ // ХЕЛПЕРЫ — утилиты доступные всем кабинетам // ═══════════════════════════════════════════════════════════════════ /** Найти запись _HIERARCHY по salonId */ function _getSalon(salonId){ return _HIERARCHY.find(function(h){ return h.salonId===salonId; }) || null; } /** Найти менеджера по id из плоского списка */ function _getMgr(mgrId){ return _CHESS_MGRS.find(function(m){ return m.id===mgrId; }) || null; } /** Получить KPI менеджера за период (из _MONTHLY_STATS) */ function _getMgrStats(mgrId, period){ var stat = _MONTHLY_STATS.find(function(s){ return s.id===mgrId; }); return stat ? (stat.months[period||'Май'] || {}) : {}; }