feat: damage estimate shown inline in audit gaps + doc checklist + Sokolov ready

This commit is contained in:
WASRUSGEN 2026-05-30 14:28:24 +03:00
parent f4136f6d31
commit 383c74f706

View File

@ -8076,6 +8076,55 @@ function _initOrgUser() {
_updateSidebarUser(); _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'; 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 =
'<img class="hc-av" src="logos/elena-photo.jpg">' +
'<div class="hc-bubble" style="max-width:480px">' +
'<div style="font-size:12px;color:var(--mut);margin-bottom:6px">Финансовый риск при текущем состоянии документов:</div>' +
'<div style="background:#fef2f2;border:1.5px solid #fca5a5;border-radius:10px;padding:12px 16px;margin-bottom:10px">' +
'<div style="font-size:12px;color:#6b7280;margin-bottom:2px">Примерный ущерб</div>' +
'<div style="font-family:var(--font-logo,\'Montserrat\');font-size:22px;font-weight:900;color:#dc2626">' +
minF + ' — ' + maxF + ' ₽' +
'</div>' +
(est.damage_comment ? '<div style="font-size:11px;color:#6b7280;margin-top:4px">' + est.damage_comment + '</div>' : '') +
(est.damage_formula ? '<div style="font-size:11px;color:#9ca3af;margin-top:2px">Формула: ' + est.damage_formula + '</div>' : '') +
'</div>' +
(est.summary ? '<div style="font-size:13px;color:#374151">' + est.summary + '</div>' : '') +
'</div>';
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 += '<div style="margin-top:14px">'; gapsHtml += '<div style="margin-top:14px">';
@ -8787,11 +8884,16 @@ function _acknowledgeRisksAndProceed(contractType, intent) {
_rcAddBubble(reply, false); _rcAddBubble(reply, false);
if (apiActions && apiActions.length) _renderElenaActions(apiActions, msgs); if (apiActions && apiActions.length) _renderElenaActions(apiActions, msgs);
// Небольшая пауза → запускаем оффер услуги // Небольшая пауза → аудит документов (Блок Д) → потом оффер
var detectedType = _detectContractType(ctx + ' ' + reply);
setTimeout(function(){ setTimeout(function(){
var wrap = document.querySelector('.chatwrap') || msgs; if (detectedType) {
_checkAndOfferService(wrap); _showDocAuditInChat(detectedType, intent);
}, 1500); } else {
var wrap2 = document.querySelector('.chatwrap') || msgs;
_checkAndOfferService(wrap2);
}
}, 1000);
_rcShowControls(); _rcShowControls();
}); });
@ -8972,6 +9074,31 @@ function renderDocChecklist(contractType) {
}); });
html += '</div>'; html += '</div>';
// Расчёт ущерба по пробелам
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 =
'<div style="background:#fef2f2;border:1.5px solid #fca5a5;border-radius:10px;padding:12px 16px;margin-top:12px">' +
'<div style="font-size:11px;color:#6b7280;margin-bottom:3px">Примерный ущерб при текущих пробелах</div>' +
'<div style="font-size:20px;font-weight:900;color:#dc2626;font-family:inherit">' + minF + ' — ' + maxF + ' ₽</div>' +
(est.damage_comment ? '<div style="font-size:11px;color:#6b7280;margin-top:3px">' + est.damage_comment + '</div>' : '') +
'</div>';
}).catch(function(){});
}
html += '<div id="checklist-damage"></div>';
// Кнопка загрузить недостающие // Кнопка загрузить недостающие
if (missing.length) { if (missing.length) {
html += '<div style="margin-top:16px;padding:12px;background:#fffbeb;border:1.5px solid #fcd34d;border-radius:10px;font-size:13px">' + html += '<div style="margin-top:16px;padding:12px;background:#fffbeb;border:1.5px solid #fcd34d;border-radius:10px;font-size:13px">' +