Files
crm.clientright.ru/DOC_TEMPLATE_UX_DESIGN.md
Fedor 01c4fe80b5 chore: snapshot current working tree changes
Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
2026-03-26 14:19:01 +03:00

468 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🎨 UX ДИЗАЙН: СОЗДАНИЕ И РЕДАКТИРОВАНИЕ ШАБЛОНОВ
## 🎯 ВОПРОС: ГДЕ РЕДАКТИРОВАТЬ ШАБЛОНЫ?
---
## 📋 ВАРИАНТЫ РЕШЕНИЯ
### **ВАРИАНТ 1: ГИБРИДНЫЙ ПОДХОД (РЕКОМЕНДУЕТСЯ) ✅**
**Создание шаблона:**
- В CRM интерфейсе
- Можно создать пустой шаблон или загрузить существующий файл
**Редактирование шаблона:**
- **Содержимое (DOCX)** → в Nextcloud через OnlyOffice
- **Метаданные и маппинг** → в CRM интерфейсе
**Преимущества:**
- ✅ Удобно редактировать DOCX в OnlyOffice (богатый редактор)
- ✅ Метаданные в CRM (логика рядом с данными)
- ✅ Гибкость - можно редактировать где удобно
---
### **ВАРИАНТ 2: ТОЛЬКО В NEXTCLOUD**
**Создание/редактирование:**
- Все в Nextcloud
- В CRM только настройка маппинга
**Недостатки:**
- ❌ Нужно переключаться между системами
- ❌ Маппинг отдельно от шаблона
---
### **ВАРИАНТ 3: ТОЛЬКО В CRM**
**Создание/редактирование:**
- Все в CRM
- Загрузка файла через интерфейс CRM
**Недостатки:**
- ❌ Нет удобного редактора DOCX в CRM
- ❌ Дублирование файлов (в CRM и Nextcloud)
---
## 🏆 РЕКОМЕНДУЕМЫЙ ПОДХОД: ГИБРИДНЫЙ
---
## 📝 СЦЕНАРИЙ 1: СОЗДАНИЕ НОВОГО ШАБЛОНА
### **В CRM интерфейсе:**
```
┌─────────────────────────────────────────────────────────┐
│ Создание шаблона документа │
├─────────────────────────────────────────────────────────┤
│ │
│ Название шаблона: [________________] │
│ │
│ Модуль: [Project ▼] │
│ │
│ Файл шаблона: │
│ ○ Создать пустой шаблон │
│ ○ Загрузить существующий файл │
│ [Выбрать файл...] │
│ │
│ Имя файла в Nextcloud: [pretenziya.docx] │
│ │
│ [Сохранить и настроить маппинг] │
│ │
└─────────────────────────────────────────────────────────┘
```
**Что происходит:**
1. **Если "Создать пустой шаблон":**
- CRM создает пустой DOCX через PHPWord
- Загружает в Nextcloud `/Templates/pretenziya.docx`
- Создает запись в `vtiger_doctemplate`
- Открывает шаблон в OnlyOffice для редактирования
2. **Если "Загрузить существующий файл":**
- Пользователь выбирает файл
- CRM загружает в Nextcloud `/Templates/`
- Создает запись в `vtiger_doctemplate`
- Переходит к настройке маппинга
---
## 📝 СЦЕНАРИЙ 2: РЕДАКТИРОВАНИЕ ШАБЛОНА
### **В CRM интерфейсе (детальный вид шаблона):**
```
┌─────────────────────────────────────────────────────────┐
│ Шаблон: Претензия [Редактировать] │
├─────────────────────────────────────────────────────────┤
│ │
│ Модуль: Project │
│ Файл: pretenziya.docx │
│ │
│ [📝 Редактировать содержимое в OnlyOffice] │
│ → Откроет файл в Nextcloud для редактирования │
│ │
│ ──────────────────────────────────────────────────── │
│ │
│ Маппинг переменных: │
│ │
│ Переменная шаблона → Поле модуля │
│ ┌─────────────────┐ ┌──────────────────────┐ │
│ │ CLIENT_NAME │ → │ $PROJECT_PROJECTNAME$│ │
│ └─────────────────┘ └──────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────────┐ │
│ │ DATE │ → │ $PROJECT_CREATEDTIME$ │ │
│ └─────────────────┘ └──────────────────────┘ │
│ │
│ [+ Добавить переменную] │
│ │
│ ──────────────────────────────────────────────────── │
│ │
│ [💾 Сохранить маппинг] │
│ │
└─────────────────────────────────────────────────────────┘
```
**Кнопка "Редактировать содержимое":**
- Открывает файл в OnlyOffice через Nextcloud
- Пользователь редактирует DOCX
- Сохраняет в Nextcloud
- CRM автоматически подхватывает изменения
---
## 🔧 РЕАЛИЗАЦИЯ
### **1. Создание пустого шаблона**
```php
class DocTemplate_Template_Model {
/**
* Создать пустой шаблон в Nextcloud
*/
public function createEmptyTemplate($filename) {
// 1. Создать пустой DOCX через PHPWord
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
$section->addText('Шаблон документа');
$tempFile = tempnam(sys_get_temp_dir(), 'template_') . '.docx';
$writer = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
$writer->save($tempFile);
// 2. Загрузить в Nextcloud через WebDAV
$this->uploadToNextcloud($tempFile, $filename);
// 3. Удалить временный файл
unlink($tempFile);
return true;
}
/**
* Загрузка файла в Nextcloud
*/
private function uploadToNextcloud($localFile, $filename) {
$nextcloudUrl = 'https://office.clientright.ru:8443';
$username = 'admin';
$password = 'office';
$path = '/Templates/';
$webdavUrl = $nextcloudUrl . '/remote.php/dav/files/' .
$username . $path . $filename;
$ch = curl_init($webdavUrl);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, fopen($localFile, 'r'));
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFile));
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 201 && $httpCode !== 204) {
throw new Exception("Ошибка загрузки в Nextcloud");
}
}
}
```
---
### **2. Загрузка существующего файла**
```php
class DocTemplate_Edit_View {
public function process(Vtiger_Request $request) {
if ($request->has('file_upload')) {
$uploadedFile = $_FILES['template_file'];
// 1. Валидация файла
if ($uploadedFile['type'] !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
throw new Exception("Только DOCX файлы");
}
// 2. Загрузить в Nextcloud
$filename = $request->get('filename');
$this->uploadToNextcloud($uploadedFile['tmp_name'], $filename);
// 3. Создать запись в CRM
$templateModel = new DocTemplate_Template_Model();
$templateModel->set('templatename', $request->get('templatename'));
$templateModel->set('filename', $filename);
$templateModel->set('module', $request->get('module'));
$templateModel->save();
// 4. Редирект на редактирование маппинга
header("Location: index.php?module=DocTemplate&view=Edit&record=" . $templateModel->getId());
}
}
}
```
---
### **3. Редактирование содержимого (открытие в OnlyOffice)**
```php
class DocTemplate_EditContent_Action {
public function process(Vtiger_Request $request) {
$templateId = $request->get('record');
$template = DocTemplate_Template_Model::getInstanceById($templateId);
// Получить URL для открытия в OnlyOffice
$nextcloudUrl = 'https://office.clientright.ru:8443';
$filename = $template->getFilename();
$path = $template->getNextcloudPath();
// URL для открытия в OnlyOffice
$onlyOfficeUrl = $nextcloudUrl . '/apps/files/?dir=' .
urlencode($path) .
'&openfile=' . urlencode($filename);
// Или через прямой вызов OnlyOffice
$onlyOfficeUrl = $nextcloudUrl . '/apps/onlyoffice/' .
urlencode($path . $filename);
header("Location: " . $onlyOfficeUrl);
exit;
}
}
```
---
### **4. Интерфейс редактирования маппинга**
**JavaScript:**
```javascript
DocTemplate = {
/**
* Показать редактор маппинга
*/
showMappingEditor: function(templateId, module) {
// 1. Получить список полей модуля (как PDFMaker)
AppConnector.request({
module: 'DocTemplate',
action: 'GetModuleFields',
module_name: module
}).then(function(response) {
// 2. Показать модальное окно с редактором маппинга
DocTemplate.showMappingDialog(response.result.fields, templateId);
});
},
/**
* Диалог редактирования маппинга
*/
showMappingDialog: function(fields, templateId) {
var html = '<div class="mapping-editor">' +
'<table class="table">' +
'<thead><tr><th>Переменная шаблона</th><th>Поле модуля</th><th></th></tr></thead>' +
'<tbody id="mapping-rows"></tbody>' +
'</table>' +
'<button onclick="DocTemplate.addMappingRow()">+ Добавить</button>' +
'</div>';
// Показать модальное окно
// При сохранении → вызвать SaveMapping
},
/**
* Сохранение маппинга
*/
saveMapping: function(templateId, mapping) {
AppConnector.request({
module: 'DocTemplate',
action: 'SaveMapping',
template_id: templateId,
mapping: JSON.stringify(mapping)
}).then(function(response) {
if (response.success) {
Vtiger_Helper_Js.showPnotify('Маппинг сохранен');
}
});
}
};
```
---
## 🎨 UI КОМПОНЕНТЫ
### **1. Список шаблонов (List View)**
```
┌─────────────────────────────────────────────────────────┐
│ Шаблоны документов [+ Создать] │
├─────────────────────────────────────────────────────────┤
│ │
│ Название │ Модуль │ Файл │ Действия│
│ ───────────────────────────────────────────────────── │
│ Претензия │ Project │ pretenziya.docx │ [✏️][👁️]│
│ Исковое заявл. │ Project │ isk.docx │ [✏️][👁️]│
│ Соглашение │ Contacts │ soglashenie.docx │ [✏️][👁️]│
│ │
└─────────────────────────────────────────────────────────┘
```
---
### **2. Детальный вид шаблона**
```
┌─────────────────────────────────────────────────────────┐
│ Претензия [Редактировать] [×] │
├─────────────────────────────────────────────────────────┤
│ │
│ Модуль: Project │
│ Файл: pretenziya.docx │
│ Путь: /Templates/ │
│ │
│ [📝 Редактировать содержимое в OnlyOffice] │
│ │
│ ──────────────────────────────────────────────────── │
│ │
│ Маппинг переменных: │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Переменная │ Поле модуля │ [×] │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ CLIENT_NAME │ $PROJECT_PROJECTNAME$ │ [×] │ │
│ │ DATE │ $PROJECT_CREATEDTIME$ │ [×] │ │
│ │ AMOUNT │ $PROJECT_CF_1885$ │ [×] │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ [+ Добавить переменную] │
│ │
│ [💾 Сохранить] │
│ │
└─────────────────────────────────────────────────────────┘
```
---
### **3. Диалог добавления переменной**
```
┌─────────────────────────────────────────────────────────┐
│ Добавить переменную │
├─────────────────────────────────────────────────────────┤
│ │
│ Переменная шаблона: │
│ [CLIENT_NAME________________] │
│ │
│ Поле модуля: │
│ [Выбрать поле ▼] │
│ ├─ Прямые поля │
│ │ ├─ $PROJECT_PROJECTNAME$ │
│ │ ├─ $PROJECT_CREATEDTIME$ │
│ │ └─ ... │
│ ├─ Связанные модули │
│ │ ├─ $CONTACTS_LASTNAME$ │
│ │ └─ ... │
│ └─ Функции │
│ ├─ formatDate($PROJECT_CREATEDTIME$) │
│ └─ ... │
│ │
│ [Отмена] [Добавить] │
│ │
└─────────────────────────────────────────────────────────┘
```
---
## 🔄 АЛЬТЕРНАТИВНЫЙ ПОДХОД: ВСТРОЕННЫЙ РЕДАКТОР
### **Вариант: Редактирование прямо в CRM**
Если нужен встроенный редактор в CRM (без перехода в Nextcloud):
```php
// Использовать OnlyOffice Document Server API
// Встроить редактор в iframe
class DocTemplate_EditContent_View {
public function process(Vtiger_Request $request) {
$templateId = $request->get('record');
$template = DocTemplate_Template_Model::getInstanceById($templateId);
// Получить URL для OnlyOffice Document Server
$onlyOfficeUrl = $this->getOnlyOfficeEditorUrl($template);
$viewer = $this->getViewer($request);
$viewer->assign('ONLYOFFICE_URL', $onlyOfficeUrl);
$viewer->assign('TEMPLATE', $template);
$viewer->view('EditContent.tpl', 'DocTemplate');
}
}
```
**Template:**
```html
<iframe src="{$ONLYOFFICE_URL}" width="100%" height="800px"></iframe>
```
---
## ✅ РЕКОМЕНДАЦИИ
### **Для создания шаблона:**
1.В CRM интерфейсе
2. ✅ Можно создать пустой или загрузить файл
3. ✅ Автоматическая загрузка в Nextcloud
### **Для редактирования содержимого:**
1.В Nextcloud через OnlyOffice (богатый редактор)
2. ✅ Кнопка "Редактировать содержимое" открывает в новой вкладке
3. ✅ После сохранения в Nextcloud - изменения сразу доступны
### **Для редактирования маппинга:**
1.В CRM интерфейсе
2. ✅ Визуальный редактор с выбором полей
3. ✅ Поддержка формата PDFMaker (`$MODULE_FIELDNAME$`)
---
## 🚀 ГОТОВ РЕАЛИЗОВАТЬ!
**Какой вариант тебе больше нравится?**
1. **Гибридный** (содержимое в Nextcloud, маппинг в CRM) - РЕКОМЕНДУЮ ✅
2. **Только в CRM** (встроенный редактор)
3. **Только в Nextcloud** (маппинг отдельно)