diff --git a/index.html b/index.html index 2df80cd..ce17f4c 100644 --- a/index.html +++ b/index.html @@ -509,16 +509,18 @@ function calcRow(r){ /* Стресс-тест (та же логика, что используется в расчёте): долг вместе с гарантиями +10%, EBITDA −15%, проценты +20%, капитал −5%. Расчёт — исходный calcRow() на изменённой копии строки; null-значения сохраняются. */ function stressRow(r){ + /* «Долг вместе с гарантиями +10%» — оба компонента (debt и guar) растут ровно на 10%, + поэтому c.debt + c.guar = (r.debt + r.guar) * 1.10 */ + const d = r.debt==null ? 0 : r.debt; + const g = r.guar==null ? 0 : r.guar; const c = {...r}; - c.debt = (isNum(r.debt) ? r.debt : 0) * 1.10; + if(isNum(r.debt)) c.debt = d*1.10; else c.debt = (d+g)*1.10; + if(isNum(r.guar)) c.guar = g*1.10; if(isNum(r.ebitda)) c.ebitda = r.ebitda * 0.85; if(isNum(r.interest)) c.interest = r.interest * 1.20; if(isNum(r.equity)) c.equity = r.equity * 0.95; const res = calcRow(c); - return {...res, debtStress: res.debt==null?null:+res.debt.toFixed(3), - ebitdaStress: res.e==null?null:+res.e.toFixed(3), - interestStress: res.i==null?null:+res.i.toFixed(3), - equityStress: res.q==null?null:+res.q.toFixed(3)}; + return {...res}; } /* ---------- registry ---------- */ @@ -618,7 +620,6 @@ function renderCalc(){ t.innerHTML = h; $('#kpi').innerHTML = `
${S.registry.length}компаний/периодов
${cnt.green}зелёная зона
${cnt.yellow}жёлтая зона
${cnt.red}красная зона
${cnt.na}без оценки (нет целей/порогов)
`; } -function kfmt(v){ if(v==null) return '—'; if(v===Infinity) return '∞'; if(typeof v==='string') return esc(v); return nf2.format(v); } function zoneChip(z){ const nm = z==='na' ? 'н/д' : ZNAME[z]; return `${nm}`; } function renderStress(){ const t = $('#stressTable'); if(!t) return; @@ -626,11 +627,15 @@ function renderStress(){ let h = `КомпанияК1 Долг/EBITDA (база → стресс)Зона стрессК2 EBITDA/Проценты (база → стресс)Зона стрессК3 Долг/Капитал (база → стресс)Зона стрессИтоговая зона (стресс)`; for(const r of S.registry){ const b = calcRow(r), s = stressRow(r); - const arrow = (base, stress) => `${kfmt(base)} → ${kfmt(stress)}`; + const arrow = (base, stress, ebBase, ebStress) => { + const fb = base===Infinity ? '∞' : (b.ebNeg?'EBITDA ≤ 0':fmtX(base)); + const fs = stress===Infinity ? '∞' : (s.ebNeg?'EBITDA ≤ 0':fmtX(stress)); + return `${fb} → ${fs}`; + }; h += `${esc(displayName(r.key,r.name))} - ${arrow(b.k1, s.k1)}${zoneChip(s.z1)} - ${arrow(b.k2, s.k2)}${zoneChip(s.z2)} - ${arrow(b.k3, s.k3)}${zoneChip(s.z3)} + ${arrow(b.k1, s.k1, b.ebNeg, s.ebNeg)}${zoneChip(s.z1)} + ${arrow(b.k2, s.k2, b.ebNeg, s.ebNeg)}${zoneChip(s.z2)} + ${arrow(b.k3, s.k3, false, false)}${zoneChip(s.z3)} ${zoneChip(s.worst)}`; } t.innerHTML = h; @@ -1381,16 +1386,29 @@ $('#btnAiMap').onclick = async () => { }; /* ===================== AGENT CHAT ===================== */ +/* Готовое экранные отображение (2 знака) + точное значение (6 знаков) — без повторного округления со стороны ИИ */ +const f3 = v => !isNum(v) ? null : +v.toFixed(3); +const f6 = v => v==null ? null : +v.toFixed(6); +const k6 = v => (v===Infinity?'inf':f6(v)); +const k2 = v => (v===Infinity?'∞':(v==null?'':nf2.format(v))); function stressRows(){ - return S.registry.map(r=>({компания: displayName(r.key,r.name), период:r.period, ...stressRow(r)})); + return S.registry.map(r=>{ const s=stressRow(r); return { + компания: displayName(r.key,r.name), период:r.period, + К1:{значение:k6(s.k1), на_экране:s.ebNeg?'EBITDA ≤ 0':k2(s.k1), зона:s.z1}, + К2:{значение:k6(s.k2), на_экране:s.ebNeg?'EBITDA ≤ 0':k2(s.k2), зона:s.z2}, + К3:{значение:k6(s.k3), на_экране:k2(s.k3), зона:s.z3}, + итог:s.worst }; }); } function agentContext(){ const rows = S.registry.map(r=>{ const c=calcRow(r); return { компания: displayName(r.key,r.name), период:r.period, источник:r.source, - долг_млрд:+((r.debt||0)).toFixed(3), гарантии_млрд:+((r.guar||0)).toFixed(3), итого_долг_млрд:+c.debt.toFixed(3), - ebitda_12м_млрд: isNum(r.ebitda)?+r.ebitda.toFixed(3):null, проценты_12м_млрд:isNum(r.interest)?+r.interest.toFixed(3):null, капитал_млрд:isNum(r.equity)?+r.equity.toFixed(3):null, - К1: c.ebNeg?'EBITDA<=0':(isNum(c.k1)?+c.k1.toFixed(3):null), К2: c.ebNeg?'EBITDA<=0':(c.k2===Infinity?'inf':(isNum(c.k2)?+c.k2.toFixed(3):null)), К3: isNum(c.k3)?+c.k3.toFixed(3):null, - цели:{К1:c.p.t1,К2:c.p.t2,К3:c.p.t3}, пороги:{К1:c.p.p1,К2:c.p.p2,К3:c.p.p3}, зоны:{К1:c.z1,К2:c.z2,К3:c.z3,итог:c.worst}, + долг_млрд:f3((r.debt||0)), гарантии_млрд:f3((r.guar||0)), итого_долг_млрд:f3(c.debt), + ebitda_12м_млрд:f3(r.ebitda), проценты_12м_млрд:f3(r.interest), капитал_млрд:f3(r.equity), + К1:{значение:c.ebNeg?'EBITDA<=0':k6(c.k1), на_экране:c.ebNeg?'EBITDA ≤ 0':k2(c.k1)}, + К2:{значение:c.ebNeg?'EBITDA<=0':k6(c.k2), на_экране:c.ebNeg?'EBITDA ≤ 0':k2(c.k2)}, + К3:{значение:k6(c.k3), на_экране:k2(c.k3)}, + цели:{К1:c.p.t1,К2:c.p.t2,К3:c.p.t3}, пороги:{К1:c.p.p1,К2:c.p.p2,К3:c.p.p3}, + зоны:{К1:c.z1,К2:c.z2,К3:c.z3,итог:c.worst}, месяцев:r.months||12, ltm:r.ltm!==false, примечание:r.note||'' }; }); return JSON.stringify(rows); } @@ -1402,8 +1420,9 @@ ${$('#methodText').value} ${$('#agentExtra').value.trim()?'Дополнительные указания пользователя: '+$('#agentExtra').value.trim():''} Данные реестра и расчёта (JSON): ${agentContext()} -Стресс-тест (уже пересчитан: долг +10%, EBITDA −15%, процентные расходы +20%, капитал −5%) — используй только эти значения, не пересчитывай самостоятельно (JSON): -${JSON.stringify(stressRows())}`; +Стресс-тест (уже пересчитан: долг вместе с гарантиями +10%, EBITDA −15%, процентные расходы +20%, капитал −5%) — используй только эти значения, не пересчитывай самостоятельно (JSON): +${JSON.stringify(stressRows())} +Правило вывода коэффициентов: при упоминании значений К1–К3 в ответе бери готовые на_экране (2 знака, запятая — десятичный разделитель, «x» не добавлять), не округляй «значение» (6 знаков) самостоятельно; «значение» использовать только для вычислений в формулах.`; } function chatAppend(role, text){ const log=$('#chatLog'); const d=document.createElement('div'); d.className='msg '+role; d.textContent=text; log.appendChild(d); log.scrollTop=log.scrollHeight; return d; } async function chatSend(q){