Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
399 lines
17 KiB
Markdown
399 lines
17 KiB
Markdown
# 🔗 АРХИТЕКТУРА: NEXTCLOUD ШАБЛОНЫ + CRM МОДУЛЬ
|
||
|
||
## 🎯 КОНЦЕПЦИЯ
|
||
|
||
**Шаблоны хранятся в Nextcloud, метаданные и маппинг - в CRM модуле.**
|
||
|
||
---
|
||
|
||
## 📊 СХЕМА РАБОТЫ
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ NEXTCLOUD │
|
||
│ /Templates/ │
|
||
│ ├── pretenziya.docx ← ФАЙЛ ШАБЛОНА │
|
||
│ ├── iskovoe_zayavlenie.docx ← ФАЙЛ ШАБЛОНА │
|
||
│ └── soglashenie.docx ← ФАЙЛ ШАБЛОНА │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
▲
|
||
│ WebDAV GET
|
||
│
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ CRM MODULE DocTemplate │
|
||
│ │
|
||
│ vtiger_doctemplate: │
|
||
│ ├── templateid: 1 │
|
||
│ ├── templatename: "Претензия" │
|
||
│ ├── filename: "pretenziya.docx" ← Ссылка на Nextcloud │
|
||
│ ├── module: "Project" │
|
||
│ └── mapping: { │
|
||
│ "CLIENT_NAME": "$PROJECT_PROJECTNAME$", │
|
||
│ "DATE": "$PROJECT_CREATEDTIME$", │
|
||
│ "AMOUNT": "$PROJECT_CF_1885$" │
|
||
│ } │
|
||
│ │
|
||
│ vtiger_doctemplate_mappings: │
|
||
│ (альтернатива JSON в mapping) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
▲
|
||
│ Использует маппинг
|
||
│
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ ПРОЦЕСС ГЕНЕРАЦИИ │
|
||
│ │
|
||
│ 1. Пользователь выбирает шаблон в CRM │
|
||
│ 2. CRM получает маппинг из БД │
|
||
│ 3. CRM скачивает шаблон из Nextcloud (WebDAV) │
|
||
│ 4. CRM получает данные записи (CRMEntity) │
|
||
│ 5. CRM заполняет переменные по маппингу │
|
||
│ 6. CRM сохраняет готовый документ в S3 │
|
||
│ 7. CRM открывает документ в OnlyOffice │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 КАК ЭТО РАБОТАЕТ
|
||
|
||
### **1. Хранение шаблонов**
|
||
|
||
**В Nextcloud:**
|
||
- Физические файлы DOCX хранятся в `/Templates/`
|
||
- Управление через веб-интерфейс Nextcloud
|
||
- Версионирование (если нужно) через Nextcloud
|
||
|
||
**В CRM:**
|
||
- Метаданные шаблона (название, описание)
|
||
- Маппинг переменных (какая переменная → какое поле)
|
||
- Настройки (для какого модуля, папка сохранения)
|
||
|
||
---
|
||
|
||
### **2. Структура таблицы `vtiger_doctemplate`**
|
||
|
||
```sql
|
||
CREATE TABLE `vtiger_doctemplate` (
|
||
`templateid` int(11) NOT NULL AUTO_INCREMENT,
|
||
`templatename` varchar(255) NOT NULL, -- "Претензия"
|
||
`filename` varchar(255) NOT NULL, -- "pretenziya.docx" (в Nextcloud)
|
||
`module` varchar(100) NOT NULL, -- "Project"
|
||
`description` text, -- Описание шаблона
|
||
`mapping` longtext, -- JSON маппинг или NULL
|
||
`nextcloud_path` varchar(255) DEFAULT '/Templates/', -- Путь в Nextcloud
|
||
`is_active` tinyint(1) DEFAULT '1',
|
||
`owner` int(11) NOT NULL,
|
||
`createdtime` datetime NOT NULL,
|
||
`modifiedtime` datetime NOT NULL,
|
||
`deleted` tinyint(1) DEFAULT '0',
|
||
PRIMARY KEY (`templateid`)
|
||
) ENGINE=InnoDB;
|
||
```
|
||
|
||
**Пример записи:**
|
||
```php
|
||
[
|
||
'templateid' => 1,
|
||
'templatename' => 'Претензия',
|
||
'filename' => 'pretenziya.docx',
|
||
'module' => 'Project',
|
||
'description' => 'Шаблон претензии для проектов',
|
||
'mapping' => json_encode([
|
||
'CLIENT_NAME' => '$PROJECT_PROJECTNAME$',
|
||
'DATE' => '$PROJECT_CREATEDTIME$',
|
||
'AMOUNT' => '$PROJECT_CF_1885$',
|
||
'CONTACT_NAME' => '$CONTACTS_LASTNAME$'
|
||
]),
|
||
'nextcloud_path' => '/Templates/'
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### **3. Процесс генерации документа**
|
||
|
||
```php
|
||
class DocTemplate_TemplateGenerator_Model {
|
||
|
||
public function generate($module, $recordId, $templateId) {
|
||
// 1. Получить метаданные шаблона из CRM БД
|
||
$template = DocTemplate_Template_Model::getInstanceById($templateId);
|
||
|
||
// 2. Скачать шаблон из Nextcloud
|
||
$templateContent = $this->downloadFromNextcloud(
|
||
$template->getFilename(),
|
||
$template->getNextcloudPath()
|
||
);
|
||
|
||
// 3. Получить данные записи (как PDFMaker)
|
||
$focus = CRMEntity::getInstance($module);
|
||
$focus->retrieve_entity_info($recordId, $module);
|
||
$focus->id = $recordId;
|
||
|
||
// 4. Получить маппинг из CRM БД
|
||
$mapping = $template->getMapping();
|
||
|
||
// 5. Построить переменные по маппингу
|
||
$variables = $this->buildVariables($mapping, $focus, $module);
|
||
|
||
// 6. Заменить переменные в шаблоне
|
||
$content = $this->replaceVariables($templateContent, $variables);
|
||
|
||
// 7. Сохранить готовый документ в S3
|
||
$filePath = $this->saveToS3($content, $module, $recordId, $template);
|
||
|
||
// 8. Вернуть URL для открытия в OnlyOffice
|
||
return $this->getOnlyOfficeUrl($filePath);
|
||
}
|
||
|
||
/**
|
||
* Скачивание шаблона из Nextcloud
|
||
*/
|
||
private function downloadFromNextcloud($filename, $path = '/Templates/') {
|
||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||
$username = 'admin';
|
||
$password = 'office';
|
||
|
||
$webdavUrl = $nextcloudUrl . '/remote.php/dav/files/' .
|
||
$username . $path . $filename;
|
||
|
||
$ch = curl_init($webdavUrl);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
|
||
$content = curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
if ($httpCode !== 200) {
|
||
throw new Exception("Шаблон не найден в Nextcloud: {$filename}");
|
||
}
|
||
|
||
return $content;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎨 UI: УПРАВЛЕНИЕ ШАБЛОНАМИ
|
||
|
||
### **Вариант 1: Синхронизация с Nextcloud**
|
||
|
||
**Кнопка "Синхронизировать с Nextcloud":**
|
||
|
||
```php
|
||
// В DocTemplate/actions/SyncWithNextcloud.php
|
||
class DocTemplate_SyncWithNextcloud_Action {
|
||
|
||
public function process() {
|
||
// 1. Получить список файлов из Nextcloud /Templates/
|
||
$templates = $this->listNextcloudTemplates();
|
||
|
||
// 2. Для каждого файла проверить, есть ли запись в CRM
|
||
foreach ($templates as $template) {
|
||
$existing = $this->findTemplateByFilename($template['name']);
|
||
|
||
if (!$existing) {
|
||
// 3. Создать новую запись в CRM
|
||
$this->createTemplateRecord($template);
|
||
} else {
|
||
// 4. Обновить метаданные (размер, дата изменения)
|
||
$this->updateTemplateRecord($existing, $template);
|
||
}
|
||
}
|
||
|
||
return ['success' => true, 'synced' => count($templates)];
|
||
}
|
||
|
||
private function listNextcloudTemplates() {
|
||
// WebDAV PROPFIND запрос к /Templates/
|
||
// Вернуть список файлов
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **Вариант 2: Ручное создание записи**
|
||
|
||
**В интерфейсе CRM:**
|
||
|
||
1. Пользователь создает запись шаблона
|
||
2. Указывает имя файла в Nextcloud (например, `pretenziya.docx`)
|
||
3. Выбирает модуль (Project, HelpDesk, etc.)
|
||
4. Настраивает маппинг переменных
|
||
5. Сохраняет
|
||
|
||
**При сохранении:**
|
||
- Проверяется, существует ли файл в Nextcloud
|
||
- Если нет - показывается предупреждение
|
||
|
||
---
|
||
|
||
## 🔄 ДВА ПОДХОДА К МАППИНГУ
|
||
|
||
### **Подход 1: JSON в поле `mapping`**
|
||
|
||
**Плюсы:**
|
||
- ✅ Простота
|
||
- ✅ Гибкость
|
||
- ✅ Легко редактировать
|
||
|
||
**Минусы:**
|
||
- ❌ Сложно искать по маппингу
|
||
- ❌ Нет версионирования отдельных переменных
|
||
|
||
```php
|
||
$mapping = json_decode($template->mapping, true);
|
||
// [
|
||
// 'CLIENT_NAME' => '$PROJECT_PROJECTNAME$',
|
||
// 'DATE' => '$PROJECT_CREATEDTIME$'
|
||
// ]
|
||
```
|
||
|
||
---
|
||
|
||
### **Подход 2: Отдельная таблица `vtiger_doctemplate_mappings`**
|
||
|
||
**Плюсы:**
|
||
- ✅ Легко искать
|
||
- ✅ Версионирование
|
||
- ✅ Можно редактировать через UI
|
||
|
||
**Минусы:**
|
||
- ❌ Сложнее структура
|
||
|
||
```sql
|
||
CREATE TABLE `vtiger_doctemplate_mappings` (
|
||
`mappingid` int(11) NOT NULL AUTO_INCREMENT,
|
||
`templateid` int(11) NOT NULL,
|
||
`template_variable` varchar(100) NOT NULL, -- CLIENT_NAME
|
||
`field_config` varchar(255) NOT NULL, -- $PROJECT_PROJECTNAME$ или JSON
|
||
`sequence` int(11) DEFAULT '0',
|
||
PRIMARY KEY (`mappingid`),
|
||
KEY `templateid` (`templateid`)
|
||
) ENGINE=InnoDB;
|
||
```
|
||
|
||
**Рекомендация:** Начать с JSON, потом при необходимости перейти на отдельную таблицу.
|
||
|
||
---
|
||
|
||
## 📋 ПРОЦЕСС СОЗДАНИЯ ШАБЛОНА
|
||
|
||
### **Сценарий 1: Шаблон уже есть в Nextcloud**
|
||
|
||
1. Пользователь загружает `pretenziya.docx` в Nextcloud `/Templates/`
|
||
2. В CRM создает запись шаблона:
|
||
- Название: "Претензия"
|
||
- Файл: `pretenziya.docx`
|
||
- Модуль: Project
|
||
3. Настраивает маппинг:
|
||
- `CLIENT_NAME` → `$PROJECT_PROJECTNAME$`
|
||
- `DATE` → `$PROJECT_CREATEDTIME$`
|
||
4. Сохраняет
|
||
|
||
---
|
||
|
||
### **Сценарий 2: Создание нового шаблона**
|
||
|
||
1. В CRM создает запись шаблона
|
||
2. Указывает имя файла (например, `new_template.docx`)
|
||
3. Настраивает маппинг
|
||
4. Сохраняет
|
||
5. **Опционально:** Кнопка "Создать пустой шаблон в Nextcloud" - создает пустой DOCX файл
|
||
|
||
---
|
||
|
||
## 🔍 ПРОВЕРКА СУЩЕСТВОВАНИЯ ШАБЛОНА
|
||
|
||
```php
|
||
class DocTemplate_Template_Model {
|
||
|
||
/**
|
||
* Проверка существования файла в Nextcloud
|
||
*/
|
||
public function checkNextcloudFile() {
|
||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||
$username = 'admin';
|
||
$password = 'office';
|
||
|
||
$webdavUrl = $nextcloudUrl . '/remote.php/dav/files/' .
|
||
$username . $this->getNextcloudPath() .
|
||
$this->getFilename();
|
||
|
||
$ch = curl_init($webdavUrl);
|
||
curl_setopt($ch, CURLOPT_NOBODY, true);
|
||
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
|
||
curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
return $httpCode === 200;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 ИТОГОВАЯ АРХИТЕКТУРА
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ NEXTCLOUD (/Templates/) │
|
||
│ ├── pretenziya.docx ← ФИЗИЧЕСКИЙ ФАЙЛ │
|
||
│ ├── iskovoe_zayavlenie.docx ← ФИЗИЧЕСКИЙ ФАЙЛ │
|
||
│ └── soglashenie.docx ← ФИЗИЧЕСКИЙ ФАЙЛ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
▲
|
||
│ WebDAV
|
||
│
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ CRM MODULE DocTemplate │
|
||
│ │
|
||
│ vtiger_doctemplate: │
|
||
│ ┌─────────────────────────────────────┐ │
|
||
│ │ templateid: 1 │ │
|
||
│ │ templatename: "Претензия" │ │
|
||
│ │ filename: "pretenziya.docx" ←──────┼─── Ссылка │
|
||
│ │ module: "Project" │ │
|
||
│ │ mapping: { │ │
|
||
│ │ "CLIENT_NAME": "$PROJECT_..." │ │
|
||
│ │ } │ │
|
||
│ └─────────────────────────────────────┘ │
|
||
│ │
|
||
│ ПРОЦЕСС: │
|
||
│ 1. Получить метаданные из БД │
|
||
│ 2. Скачать файл из Nextcloud │
|
||
│ 3. Заполнить переменные │
|
||
│ 4. Сохранить в S3 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ ПРЕИМУЩЕСТВА ТАКОГО ПОДХОДА
|
||
|
||
1. ✅ **Шаблоны в одном месте** - Nextcloud (удобно редактировать)
|
||
2. ✅ **Метаданные в CRM** - маппинг, настройки, права доступа
|
||
3. ✅ **Версионирование** - можно через Nextcloud
|
||
4. ✅ **Резервное копирование** - шаблоны в Nextcloud/S3
|
||
5. ✅ **Разделение ответственности** - файлы там, логика здесь
|
||
|
||
---
|
||
|
||
## 🚀 ГОТОВ РЕАЛИЗОВАТЬ!
|
||
|
||
**Вопросы для уточнения:**
|
||
|
||
1. **Синхронизация:** Нужна ли автоматическая синхронизация с Nextcloud или ручное создание записей?
|
||
2. **Маппинг:** JSON в поле или отдельная таблица?
|
||
3. **Создание шаблонов:** Можно ли создавать пустые шаблоны из CRM или только в Nextcloud?
|
||
|
||
**Готов начать реализацию! 🎉**
|