diff --git a/docs/crm.html b/docs/crm.html
index 663dc83..96dd373 100644
--- a/docs/crm.html
+++ b/docs/crm.html
@@ -182,11 +182,24 @@ function renderDashboard(){
${money(expected)}
Ожидается (остатки)
${money(inwork)} в активных сделках
${conv}%
Конверсия в сделку
${done} завершено
+ ${renderRevenueChart()}
${renderUpcomingTasks()}
Все клиенты · ${total}
${projects.map(p=>{const pc=pipeMap[(p.crm&&p.crm.pipeline)||"lead"];return `${esc((p.client_name||'?')[0])}
${esc(p.client_name)}
${esc(p.niche)} · ${p.msg_count} сообщений
${pc[1]}${money((p.crm&&p.crm.deal_amount)||0)} `}).join("")||'Создайте первого клиента
'}`;
}
+function renderRevenueChart(){
+ const months={};
+ projects.forEach(p=>(p.crm&&p.crm.payments||[]).forEach(pay=>{const m=(pay.date||"").slice(0,7);if(m)months[m]=(months[m]||0)+(pay.amount||0)}));
+ const keys=Object.keys(months).sort();
+ if(!keys.length)return"";
+ const max=Math.max(...keys.map(k=>months[k]));
+ const MN=["янв","фев","мар","апр","май","июн","июл","авг","сен","окт","ноя","дек"];
+ const lbl=k=>{const[y,m]=k.split("-");return MN[+m-1]+" "+y.slice(2)};
+ const totalRev=keys.reduce((s,k)=>s+months[k],0);
+ return `📈 Выручка по месяцам · ${money(totalRev)}
+ ${keys.map(k=>`
${(months[k]/1000).toFixed(0)}к
${lbl(k)}
`).join("")}
`;
+}
function renderUpcomingTasks(){
const today=new Date().toISOString().slice(0,10);
const all=[];
@@ -230,8 +243,10 @@ function renderPayments(){
if(sb)sb.innerHTML=`Оплата
${money(paid)}${deal>0?`${st[0]}`:''}
`;
const box=document.getElementById("paymentsBox");if(!box)return;
box.innerHTML=`
-
💰 Платежи
- ${deal>0?`Сделка ${money(deal)} · Получено ${money(paid)} · Остаток ${money(left)}`:''}
+
💰 Платежи
+ ${deal>0?`Сделка ${money(deal)} · Получено ${money(paid)} · Остаток ${money(left)}`:''}
+ ${left>0?``:''}
+ ${pays.length?``:''}
${pays.map((p,i)=>`
${esc(p.date||'')}${esc(p.note||'Платёж')}${money(p.amount)}
`).join("")||'
Платежей нет
'}
`;
@@ -239,6 +254,19 @@ function renderPayments(){
async function savePayments(){await fetch(`${API}/api/project/crm`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:current,payments:state.crm.payments})});await loadProjects();}
function addPayment(){const amt=+document.getElementById("payAmt").value;if(!amt){alert("Укажите сумму");return}const date=document.getElementById("payDate").value;const note=document.getElementById("payNote").value;state.crm=state.crm||{};state.crm.payments=state.crm.payments||[];state.crm.payments.push({date,amount:amt,note});renderPayments();savePayments();}
function delPayment(i){state.crm.payments.splice(i,1);renderPayments();savePayments();}
+function remindBalance(left){
+ const due=new Date(Date.now()+7*864e5).toISOString().slice(0,10);
+ state.tasks=state.tasks||[];state.tasks.push({text:`Получить остаток ${money(left)} от ${state.client_name||'клиента'}`,due,done:false});
+ saveTasks();renderTasks();
+ alert(`Задача создана: «Получить остаток ${money(left)}»\nСрок: через 7 дней`);
+}
+function exportPayments(){
+ const pays=state.crm.payments||[];
+ let csv="Дата;Сумма;Назначение\n"+pays.map(p=>`${p.date||''};${p.amount||0};${(p.note||'').replace(/;/g,',')}`).join("\n");
+ csv=""+csv; // BOM для Excel
+ const blob=new Blob([csv],{type:"text/csv;charset=utf-8"});
+ const a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download=`Платежи_${(state.client_name||'клиент').replace(/[^а-яёa-z0-9]/gi,'_')}.csv`;a.click();
+}
function renderTasks(){
const box=document.getElementById("tasksBox");if(!box)return;
const tasks=state.tasks||[];