Спрашивайте, отвечает реальный человек
Чат
// Функция отдачи веб-интерфейса voicesheet = SpreadsheetApp.getActiveSpreadsheet(); function doGet() { setupDatabase_(); // Проверяем и создаем листы при необходимости return HtmlService.createHtmlOutputFromFile('Index') .setTitle('Мини-CRM (Лайт)') .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL) .addMetaTag('viewport', 'width=device-width, initial-scale=1'); } // Автоматическая инициализация структуры таблицы function setupDatabase_() { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheetsConfig = { 'Компании': ['ID', 'Название', 'ИНН', 'Сфера', 'Статус', 'Ответственный', 'Сумма сделки', 'Дата создания'], 'Контакты': ['ID', 'ID Компании', 'Имя', 'Должность', 'Телефон', 'Email', 'Мессенджер'], 'История': ['ID', 'ID Компании', 'Дата', 'Тип', 'Текст', 'Автор'], 'Задачи': ['ID', 'ID Компании', 'Задача', 'Срок', 'Статус', 'Ответственный'] }; for (let sheetName in sheetsConfig) { let sheet = ss.getSheetByName(sheetName); if (!sheet) { sheet = ss.insertSheet(sheetName); sheet.appendRow(sheetsConfig[sheetName]); sheet.getRange(1, 1, 1, sheetsConfig[sheetName].length).setFontWeight('bold').setBackground('#f3f4f6'); } } } // Получение всех данных для CRM function getCRMData() { setupDatabase_(); const ss = SpreadsheetApp.getActiveSpreadsheet(); const getSheetData = (name) => { const sheet = ss.getSheetByName(name); const rows = sheet.getDataRange().getValues(); if (rows.length <= 1) return []; const headers = rows[0]; return rows.slice(1).map(row => { let obj = {}; headers.forEach((h, i) => obj[h] = row[i]); return obj; }); }; return { companies: getSheetData('Компании'), contacts: getSheetData('Контакты'), history: getSheetData('История'), tasks: getSheetData('Задачи') }; } // Добавление или обновление компании function saveCompany(data) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('Компании'); const rows = sheet.getDataRange().getValues(); let id = data.id; if (!id) { id = 'COMP_' + Date.now(); sheet.appendRow([ id, data.name, data.inn || '', data.sphere || '', data.status || 'Новый лид', data.responsible || 'Менеджер', data.amount || 0, new Date().toLocaleString() ]); // Добавим стартовую запись в историю addHistoryRecord_({ companyId: id, type: 'Создание', text: 'Компания добавлена в CRM' }); } else { for (let i = 1; i < rows.length; i++) { if (rows[i][0] === id) { sheet.getRange(i + 1, 2).setValue(data.name); sheet.getRange(i + 1, 3).setValue(data.inn || ''); sheet.getRange(i + 1, 4).setValue(data.sphere || ''); sheet.getRange(i + 1, 5).setValue(data.status); sheet.getRange(i + 1, 6).setValue(data.responsible); sheet.getRange(i + 1, 7).setValue(data.amount || 0); break; } } } return id; } // Изменение статуса компании (для перетаскивания в воронке) function updateCompanyStatus(companyId, newStatus) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('Компании'); const rows = sheet.getDataRange().getValues(); for (let i = 1; i < rows.length; i++) { if (rows[i][0] === companyId) { sheet.getRange(i + 1, 5).setValue(newStatus); addHistoryRecord_({ companyId: companyId, type: 'Статус', text: `Статус изменен на: ${newStatus}` }); return true; } } return false; } // Добавление контакта function saveContact(data) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('Контакты'); const id = 'CONT_' + Date.now(); sheet.appendRow([id, data.companyId, data.name, data.position || '', data.phone || '', data.email || '', data.messenger || '']); return id; } // Добавление комментария / истории function addHistory(data) { return addHistoryRecord_(data); } function addHistoryRecord_(data) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('История'); const id = 'HIST_' + Date.now(); sheet.appendRow([id, data.companyId, new Date().toLocaleString(), data.type || 'Заметка', data.text, data.author || 'Менеджер']); return id; } // Добавление задачи function saveTask(data) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('Задачи'); const id = 'TASK_' + Date.now(); sheet.appendRow([id, data.companyId, data.task, data.dueDate, 'Активна', data.responsible || 'Менеджер']); return id; } // Закрыть задачу function completeTask(taskId) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName('Задачи'); const rows = sheet.getDataRange().getValues(); for (let i = 1; i < rows.length; i++) { if (rows[i][0] === taskId) { sheet.getRange(i + 1, 5).setValue('Выполнена'); return true; } } return false; } // Генерация коммерческого предложения (создает документ Google Docs по шаблону) function generateKP(companyId) { const data = getCRMData(); const company = data.companies.find(c => c.ID === companyId); const contacts = data.contacts.filter(c => c['ID Компании'] === companyId); if (!company) return { success: false, error: 'Компания не найдена' }; // Создаем документ КП const docTitle = `КП для ${company['Название']} — ${new Date().toLocaleDateString()}`; const doc = DocumentApp.create(docTitle); const body = doc.getBody(); body.appendHeading(DocumentApp.ParagraphHeading.HEADING1, `Коммерческое предложение`); body.appendParagraph(`Уважаемые партнеры, компания ${company['Название']}!`); body.appendParagraph(`Сфера деятельности: ${company['Сфера'] || 'Не указана'}`); body.appendParagraph(`\nНаша компания рада предложить вам современные рекламные решения в сети indoor-форматов. Мы уверены, что наше сотрудничество поможет масштабировать ваш бизнес.`); if (contacts.length > 0) { body.appendHeading(DocumentApp.ParagraphHeading.HEADING3, `Контактное лицо:`); contacts.forEach(c => { body.appendParagraph(`• ${c['Имя']} (${c['Должность'] || 'Сотрудник'}) — Тел: ${c['Телефон']}, Email: ${c['Email']}`); }); } body.appendParagraph(`\n\nС уважением,\nКоманда проекта`); const url = doc.getUrl(); // Запишем в историю addHistoryRecord_({ companyId: companyId, type: 'КП', text: `Сгенерировано коммерческое предложение: ${docTitle}` }); return { success: true, url: url, title: docTitle }; }