From 383c74f7062d9aa54f36fb75ee34717030f6c88f Mon Sep 17 00:00:00 2001 From: WASRUSGEN Date: Sat, 30 May 2026 14:28:24 +0300 Subject: [PATCH] feat: damage estimate shown inline in audit gaps + doc checklist + Sokolov ready --- mockup.html | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 4 deletions(-) diff --git a/mockup.html b/mockup.html index 5b5211e..1d48cf7 100644 --- a/mockup.html +++ b/mockup.html @@ -8076,6 +8076,55 @@ function _initOrgUser() { _updateSidebarUser(); } +// ── ИЗВЛЕЧЕНИЕ ПАРАМЕТРОВ ДОГОВОРА ─────────────────────────────────────────── + +function _extractContractParams(contractType) { + var params = {}; + // 1. Из загруженного договора (_DEADLINES и contracts store) + var contracts = _getContracts(); + if (contracts.length) { + var c = contracts.find(function(x){ return (x.type||'').toLowerCase().includes(contractType); }) || contracts[0]; + if (c) { + // Пробуем вытащить суммы из preview + var text = (c.preview || '') + ' ' + (c.type || ''); + var amtMatch = text.match(/(\d[\d\s]{3,})\s*(?:руб|₽)/i); + if (amtMatch) params.amount = parseInt(amtMatch[1].replace(/\s/g,'')); + } + } + + // 2. Из истории чата — ищем суммы + var history = _chatHistory.slice(-10).map(function(m){ return m.content||''; }).join(' '); + var deposits = history.match(/депозит[а-я\s]*(\d[\d\s]+)\s*(?:руб|₽|тыс|к₽)/i); + if (deposits) params.deposit = parseInt(deposits[1].replace(/\s/g,'')) * (history.match(/тыс|к₽/) ? 1000 : 1); + + var days_match = history.match(/(\d+)\s*(?:день|дней|дня)/i); + if (days_match) params.days = parseInt(days_match[1]); + + // 3. Из активных дедлайнов + if (typeof _DEADLINES !== 'undefined' && _DEADLINES.length) { + var dl = _DEADLINES[0]; + if (dl && dl.date) { + var diff = Math.round((new Date(dl.date) - new Date()) / 86400000); + params.days = Math.abs(diff); + } + } + + // 4. Из b2b-реквизитов + try { + var b2b = JSON.parse(localStorage.getItem('zashita_b2b') || 'null'); + if (b2b && b2b.amount) params.amount = b2b.amount; + } catch(e){} + + // Дефолты по типу + if (!params.amount) { + var defaults = { аренда: 80000, подряд: 500000, 'купля-продажа': 300000, трудовой: 60000, займ: 100000, дду: 5000000 }; + params.amount = defaults[contractType] || 100000; + } + if (!params.deposit && contractType === 'аренда') params.deposit = params.amount; + + return params; +} + // ── КАРТА ДЕЛА ─────────────────────────────────────────────────────────────── var _CASE_NOTES_KEY = 'zashita_case_notes'; @@ -8722,6 +8771,54 @@ function _showAuditGaps(contractType, intent) { }); } + // ── РАСЧЁТ УЩЕРБА INLINE ── + // Запускаем /api/estimate чтобы показать финансовый риск прямо в блоке + var _contractParams = _extractContractParams(contractType); + var _situationText = _chatHistory.slice(-6).map(function(m){ return m.content||''; }).join(' '); + + fetch(API_BASE + '/api/estimate', { + method: 'POST', + headers: {'Content-Type':'application/json'}, + body: JSON.stringify({ + situation: _situationText, + contract_type: contractType, + contract_params: _contractParams, + history: _chatHistory.slice(-4) + }) + }) + .then(function(r){ return r.json(); }) + .then(function(est) { + if (!est.damage_min && !est.damage_max) return; + var dmgDiv = document.getElementById('audit-damage'); + if (dmgDiv) dmgDiv.remove(); + var dmg = document.createElement('div'); + dmg.id = 'audit-damage'; + dmg.className = 'hc-msg hc-elena'; + var minF = (est.damage_min||0).toLocaleString('ru'); + var maxF = (est.damage_max||0).toLocaleString('ru'); + dmg.innerHTML = + '' + + '
' + + '
Финансовый риск при текущем состоянии документов:
' + + '
' + + '
Примерный ущерб
' + + '
' + + minF + ' — ' + maxF + ' ₽' + + '
' + + (est.damage_comment ? '
' + est.damage_comment + '
' : '') + + (est.damage_formula ? '
Формула: ' + est.damage_formula + '
' : '') + + '
' + + (est.summary ? '
' + est.summary + '
' : '') + + '
'; + var msgs2 = document.getElementById('rchat-msgs'); + if (msgs2) { msgs2.appendChild(dmg); dmg.scrollIntoView({behavior:'smooth'}); } + // Сохраняем в карту дела + _addCaseNote('risk', 'Расчётный ущерб: ' + minF + '–' + maxF + '₽ · ' + (est.damage_comment||''), {contractType: contractType}); + // После показа ущерба — оффер услуги + setTimeout(function(){ _checkAndOfferService(msgs2); }, 1500); + }) + .catch(function(){}); + // Кнопки: исправить + ПРИНЯТЬ РИСКИ И ПРОДОЛЖИТЬ gapsHtml += '
'; @@ -8787,11 +8884,16 @@ function _acknowledgeRisksAndProceed(contractType, intent) { _rcAddBubble(reply, false); if (apiActions && apiActions.length) _renderElenaActions(apiActions, msgs); - // Небольшая пауза → запускаем оффер услуги + // Небольшая пауза → аудит документов (Блок Д) → потом оффер + var detectedType = _detectContractType(ctx + ' ' + reply); setTimeout(function(){ - var wrap = document.querySelector('.chatwrap') || msgs; - _checkAndOfferService(wrap); - }, 1500); + if (detectedType) { + _showDocAuditInChat(detectedType, intent); + } else { + var wrap2 = document.querySelector('.chatwrap') || msgs; + _checkAndOfferService(wrap2); + } + }, 1000); _rcShowControls(); }); @@ -8972,6 +9074,31 @@ function renderDocChecklist(contractType) { }); html += '
'; + // Расчёт ущерба по пробелам + if (missing.length && _apiAvailable) { + var params = _extractContractParams(key); + var situation = 'Договор ' + key + '. Отсутствуют документы: ' + missing.map(function(d){ return d.label; }).join(', '); + fetch(API_BASE + '/api/estimate', { + method: 'POST', headers: {'Content-Type':'application/json'}, + body: JSON.stringify({ situation: situation, contract_type: key, contract_params: params, history: [] }) + }) + .then(function(r){ return r.json(); }) + .then(function(est) { + if (!est.damage_min) return; + var dmgEl = document.getElementById('checklist-damage'); + if (!dmgEl) return; + var minF = (est.damage_min||0).toLocaleString('ru'); + var maxF = (est.damage_max||0).toLocaleString('ru'); + dmgEl.innerHTML = + '
' + + '
Примерный ущерб при текущих пробелах
' + + '
' + minF + ' — ' + maxF + ' ₽
' + + (est.damage_comment ? '
' + est.damage_comment + '
' : '') + + '
'; + }).catch(function(){}); + } + html += '
'; + // Кнопка загрузить недостающие if (missing.length) { html += '
' +