Блог

Deluge: от КП в Zoho CRM → превью в Sheets → файл в WorkDrive → вложение в CRM

Скрипт берёт Quote (Коммерческое предложение) из Zoho CRM, собирает позиции, вытягивает превью-файлы по fileId, заливает их в WorkDrive потоковой загрузкой, формирует строки на листе Sheet2 в Zoho Sheets (с превью через формулу =IMAGE()), копирует книгу в целевую папку WorkDrive с названием КП, прикрепляет ссылку на книгу к записи Quote в CRM и, напоследок, очищает диапазон на рабочем листе под следующую генерацию.

Загрузка данных КП из CRM
  • Получает QuoteID и дважды делает GET https://www.zohoapis.eu/crm/v8/Quotes/{QuoteID}.
  • Достаёт ключевые поля: Subject, Account_Name, Contact_Name, Owner.name, адрес доставки, даты, суммы и массив Quoted_Items.
Подготовка заголовков «таблицы» (для будущих строк в Sheets)
  • Создаётся список row_data и карта header с колонками: «Product Name», «Description», «Quantity», «Price».
  • header добавляется в row_data (далее обратите внимание: переменная header позже используется ещё и как HTTP-заголовки — это потенциальный источник путаницы, см. «Замечания» ниже).
Проход по позициям КП (Quoted_Items)
  • Для каждой позиции вытаскиваются:
  • Product_Name.name, артикул, цвет, упаковка, модель/ткань, размеры и типы печати, количество, прайс, сумма, ссылка, ID родителя и вложения Preview.
Получение и публикация превью файлов
  • Для каждого файла в Preview берётся File_Id__s и, если он есть, выполняется:
Скачивание файла: GET https://www.zohoapis.eu/crm/v8/files?id={fileId}.
  • Подготовка потоковой загрузки в WorkDrive (https://upload.zoho.eu/workdrive-api/v1/stream/upload) с заголовками:
  • x-filename: исходное имя файла,
  • x-parent_id: папка WorkDrive, куда кладём файл,
  • upload-id: fileId,
  • x-streammode: 1.
Из ответа берётся resource_id загруженного объекта и строится прямая ссылка на превью-картинку:
https://previewengine-accl.zoho.eu/image/WD/{resource_id}.
Формирование строки для Zoho Sheets (лист Sheet2)
  • Строится формула превью для ячейки:
  • =IMAGE("https://previewengine-accl.zoho.eu/image/WD/{resource_id}";1)
(обратите внимание на точку с запятой — это региональные настройки европейского разделителя аргументов).
  • Собирается row с полями товара, атрибутами печати, ссылками, данными проекта, датами — и отправляется в Zoho Sheet API методом zoho.sheet.createRecords(resourceId, "Sheet2", rowsData, ...).
  • Так добавляются строки по всем позициям КП.
Копирование книги и присвоение названия как у КП
Вызов POST https://sheet.zoho.eu/api/v2/copy с параметрами:
  • resource_id: исходная книга-шаблон,
  • workbook_name: Subject КП,
  • parent_id: целевая папка WorkDrive,
  • copy_lock_settings: true.
Из ответа берётся workbook_url скопированной книги.
Прикрепление ссылки на книгу к записи КП в CRM
  • POST https://www.zohoapis.eu/crm/v7/Quotes/{QuoteID}/Attachments с attachmentUrl = workbook_url и title = Subject.
  • Если пришёл DUPLICATE_DATA, скрипт возвращает сообщение системы, иначе — Files Generated.
Очистка диапазона в листе Sheet2
  • Финальный POST https://sheet.zoho.eu/api/v2/{resourceId} с методом range.content.clear и диапазоном A2:Q50.
  • Это «сбрасывает» рабочую область для следующего запуска, чтобы данные не копились.
info QuoteID;
// Получаем данные по Quote из Zoho CRM
Quote = invokeurl
[
	url :"https://www.zohoapis.eu/crm/v8/Quotes/" + QuoteID
	type :GET
	connection:"crm_zoho_dev"
];
Quote = Quote.get("data").toMap();
//
// Таблица 
Quote = invokeurl
[
	url :"https://www.zohoapis.eu/crm/v8/Quotes/" + QuoteID
	type :GET
	connection:"crm_zoho_dev"
];
Quote = Quote.get("data").toMap();
Subject = Quote.get("Subject");
Account_Name = Quote.get("Account_Name");
if(Account_Name.isNull() == false)
{
	Account_Name = Account_Name.get("name");
}
Contact_Name = Quote.get("Contact_Name");
if(Contact_Name.isNull() == false)
{
	Contact_Name = Contact_Name.get("name");
}
Owner = Quote.get("Owner").get("name");
Delivery_address = Quote.get("Delivery_address");
Ready_by_date = Quote.get("Ready_by_date");
Sub_Total = Quote.get("Sub_Total");
Valid_Till = Quote.get("Valid_Till");
Grand_Total = Quote.get("Grand_Total");
Tax = Quote.get("Tax");
Adjustment = Quote.get("Adjustment");
currency_symbol = Quote.get("$currency_symbol");
Quoted_Items = Quote.get("Quoted_Items");
info Quoted_Items;
row_data = list();
// Создаем список строк
// Заголовки для таблицы
header = Map();
header.put("Product Name","Product Name");
header.put("Description","Description");
header.put("Quantity","Quantity");
header.put("Price","Price");
row_data.add(header);
// Заполняем данными
resourceId = "m78m951ed261da6564fe898b2892823e9af51";
GrandTotal = 0;
for each  rec in Quoted_Items
{
	Product_Name = rec.get("Product_Name").get("name");
	Article = rec.get("Article");
	Color = rec.get("Color");
	Packaging = rec.get("Packaging");
	Fabric_model = rec.get("Fabric_model");
	Print_size = rec.get("Print_size");
	Product_size = rec.get("Product_size");
	Print_type = rec.get("Print_type");
	Quantity = rec.get("Quantity");
	List_Price = rec.get("List_Price");
	Total = rec.get("Total");
	Link = rec.get("Link");
	Parent_Id = rec.get("id");
	Preview = rec.get("Preview");
	for each  rec in Preview
	{
		fileId = rec.get("File_Id__s");
		info "fileId: " + fileId;
		File_Name__s = rec.get("File_Name__s");
		if(rec.get("File_Id__s") != null)
		{
			// Загружаем файл по fileId
			fileDownloadUrl = "https://www.zohoapis.eu/crm/v8/files?id=" + fileId;
			// Выполняем GET-запрос
			file = invokeurl
			[
				url :fileDownloadUrl
				type :GET
				connection:"crm_zoho_dev"
			];
			info file;
			file.setParamName("file");
			//
			header.put("x-filename",File_Name__s);
			header.put("x-parent_id","m78m95115e88cd68f4978895817486547edf4");
			header.put("upload-id",fileId);
			header.put("x-streammode","1");
			response = invokeurl
			[
				url :"https://upload.zoho.eu/workdrive-api/v1/stream/upload"
				type :POST
				parameters:file
				headers:header
				connection:"workdrive"
				content-type:"application/octet-stream"
			];
			info response;
			//
			resource_id = response.get("data").get(0).get("attributes").get("resource_id");
			info resource_id;
			Preview = "https://previewengine-accl.zoho.eu/image/WD/" + resource_id;
			//
		}
	}
	// 	Preview = "https://crmsandbox.zoho.eu/crm/devzn/specific/ViewAttachment?fileId=" + fileId + "&module=QuotedItems&fieldId=625426000000542577&parentId=" + Parent_Id + "&name=" + file + "&downLoadMode=default";
	Preview = "=IMAGE(\"" + Preview + "\";1)";
	info Preview;
	//
	row = Map();
	row.put("Product Name",Product_Name);
	row.put("Preview",Preview);
	row.put("Q",Quantity);
	row.put("Item Price",List_Price);
	row.put("Total Price",Total);
	row.put("Link",Link);
	row.put("Article",Article);
	row.put("Color",Color);
	row.put("Fabric / model",Fabric_model);
	row.put("Packaging",Packaging);
	row.put("Product Size",Print_size);
	row.put("Print Type",Print_type);
	row.put("Print Size",Print_size);
	row.put("Project name",Account_Name);
	row.put("Account manager",Owner);
	row.put("Delivery info",Delivery_address);
	row.put("Ready by date",Ready_by_date);
	rowsData = List();
	rowsData.add(row);
	queryData = Map();
	response = zoho.sheet.createRecords(resourceId,"Sheet2",rowsData,queryData,"zoho_sheet");
	// 		info response;
}
//
paramMap = Map();
paramMap.put('method','workbook.copy');
paramMap.put('resource_id',resourceId);
paramMap.put('workbook_name',Subject);
paramMap.put('parent_id','hmb7xd57420ae63934e46991c703d67eae9de');
paramMap.put('copy_lock_settings','true');
response = invokeurl
[
	url :"https://sheet.zoho.eu/api/v2/copy"
	type :POST
	parameters:paramMap
	connection:"zoho_sheet"
];
info response;
workbook_url = response.get("workbook_url");
info workbook_url;
//
formData = Map();
formData.put("attachmentUrl",workbook_url);
formData.put("title",Subject);
response = invokeurl
[
	url :"https://www.zohoapis.eu/crm/v7/Quotes/" + QuoteID + "/Attachments"
	type :POST
	parameters:formData
	connection:"crm_zoho_dev"
];
info response;
//
if(response.get("code").contains("DUPLICATE_DATA") == true)
{
	result = response.get("message");
}
else
{
	result = "Files Generated";
}
//
paramMap = Map();
paramMap.put('method','range.content.clear');
paramMap.put('worksheet_name','sheet2');
paramMap.put('start_row',2);
paramMap.put('start_column',1);
paramMap.put('end_row',50);
paramMap.put('end_column',17);
response = invokeurl
[
	url :"https://sheet.zoho.eu/api/v2/" + resourceId
	type :POST
	parameters:paramMap
	connection:"zoho_sheet"
];
info response;

Важные переменные/идентификаторы

  • resourceId — ID шаблонной книги Zoho Sheet, из которой делается копия.
  • x-parent_id — папка WorkDrive, куда льются превью-файлы (и куда копируется книга).
  • parent_id в copy — папка назначения скопированной книги (может совпадать/отличаться от x-parent_id).
  • workbook_url — ссылка на скопированную книгу (её прикрепляем к КП).
  • QuoteID — целевая запись Quotes в CRM.

Зачем так делать (практический эффект)

  • Визуализация КП: каждая позиция получает живое превью прямо в Zoho Sheets (удобно показывать клиентам/коллегам).
  • Единый артефакт: итоговая книга со всеми строками и картинками автоматически прикрепляется к КП в CRM.
  • Авто-очистка: последующие генерации не «засоряют» шаблон — диапазон очищается.