Files
crm.clientright.ru/DOC_TEMPLATE_MODULE_PLAN.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
16 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.

# 📦 ПЛАН СОЗДАНИЯ МОДУЛЯ DocTemplate
## 🎯 ЦЕЛЬ
Создать отдельный модуль `DocTemplate` для генерации документов из шаблонов Nextcloud с переменными модулей CRM, аналогично PDFMaker.
---
## 📁 СТРУКТУРА МОДУЛЯ
```
modules/DocTemplate/
├── DocTemplate.php # Основной класс модуля
├── schema.xml # Схема БД
├── language/ # Языковые файлы
│ └── ru_ru.lang.php
├── models/ # Модели
│ ├── Module.php # Модель модуля
│ ├── Record.php # Модель записи
│ ├── Template.php # Модель шаблона
│ ├── TemplateGenerator.php # Генератор документов
│ ├── VariableProcessor.php # Обработчик переменных
│ └── FieldMapper.php # Маппер полей
├── views/ # Представления
│ ├── List.php # Список шаблонов
│ ├── Detail.php # Детальный вид шаблона
│ ├── Edit.php # Редактирование шаблона
│ └── Generate.php # Генерация документа
├── actions/ # Действия
│ ├── GenerateDocument.php # Генерация документа
│ ├── ListTemplates.php # Список шаблонов для модуля
│ ├── PreviewVariables.php # Предпросмотр переменных
│ └── SaveMapping.php # Сохранение маппинга
├── resources/ # Ресурсы
│ ├── DocTemplate.js # JS для UI
│ ├── DocTemplate.css # Стили
│ └── images/ # Иконки
└── helpers/ # Вспомогательные классы
└── NextcloudClient.php # Клиент Nextcloud
```
---
## 🗄️ СТРУКТУРА БД
### **Таблица: `vtiger_doctemplate`**
Хранит шаблоны и их настройки:
```sql
CREATE TABLE `vtiger_doctemplate` (
`templateid` int(11) NOT NULL AUTO_INCREMENT,
`templatename` varchar(255) NOT NULL,
`filename` varchar(255) NOT NULL, -- Имя файла в Nextcloud
`module` varchar(100) NOT NULL, -- Для какого модуля
`description` text,
`mapping` longtext, -- JSON маппинг переменных
`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;
```
### **Таблица: `vtiger_doctemplate_seq`**
Счетчик для ID:
```sql
CREATE TABLE `vtiger_doctemplate_seq` (
`id` int(11) NOT NULL DEFAULT '1'
) ENGINE=InnoDB;
```
### **Таблица: `vtiger_doctemplate_settings`**
Настройки шаблона:
```sql
CREATE TABLE `vtiger_doctemplate_settings` (
`templateid` int(11) NOT NULL,
`output_folder` varchar(255), -- Папка для сохранения
`file_naming` varchar(255), -- Правило именования файлов
`auto_open` tinyint(1) DEFAULT '1', -- Автоматически открывать
`owner` int(11) NOT NULL,
`sharingtype` char(7) DEFAULT 'public',
PRIMARY KEY (`templateid`)
) ENGINE=InnoDB;
```
### **Таблица: `vtiger_doctemplate_mappings`**
Маппинг переменных (альтернатива JSON в основной таблице):
```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
`module` varchar(100) NOT NULL, -- PROJECT
`fieldname` varchar(100) NOT NULL, -- projectname или $PROJECT_PROJECTNAME$
`field_type` varchar(50), -- direct, related, function, constant
`related_module` varchar(100), -- Для связанных модулей
`function_name` varchar(100), -- Для функций
`default_value` text, -- Значение по умолчанию
`sequence` int(11) DEFAULT '0',
PRIMARY KEY (`mappingid`),
KEY `templateid` (`templateid`)
) ENGINE=InnoDB;
```
---
## 📝 ОСНОВНОЙ КЛАСС МОДУЛЯ
### **`DocTemplate.php`**
```php
<?php
class DocTemplate {
public $log;
public $db;
public $name = 'DocTemplate';
public $id;
public $moduleName = 'DocTemplate';
public $parentName = 'Tools';
public function __construct() {
global $log;
$this->log = $log;
$this->db = PearDatabase::getInstance();
$this->id = getTabId("DocTemplate");
}
/**
* Обработчик событий модуля
*/
public function vtlib_handler($modulename, $event_type) {
switch ($event_type) {
case 'module.postinstall':
$this->executeSql();
$this->addCustomLinks();
break;
case 'module.enabled':
case 'module.postupdate':
$this->addCustomLinks();
break;
case 'module.disabled':
case 'module.preuninstall':
$this->deleteCustomLinks();
break;
}
}
/**
* Выполнение SQL из schema.xml
*/
private function executeSql() {
// Логика выполнения SQL
}
/**
* Добавление кастомных ссылок в модули
*/
private function addCustomLinks() {
// Добавить кнопку "Создать из шаблона" в модули
$modules = ['Project', 'HelpDesk', 'Contacts'];
foreach ($modules as $moduleName) {
$module = Vtiger_Module::getInstance($moduleName);
if ($module) {
$module->addLink(
'DETAILVIEWBASIC',
'Создать из шаблона',
"javascript:DocTemplate.showTemplateDialog('{$moduleName}', '{$recordId}')",
'icon-document',
10
);
}
}
}
private function deleteCustomLinks() {
// Удаление ссылок
}
}
```
---
## 🔧 КЛЮЧЕВЫЕ КОМПОНЕНТЫ
### **1. TemplateGenerator.php**
```php
class DocTemplate_TemplateGenerator_Model {
/**
* Генерация документа из шаблона
*/
public function generate($module, $recordId, $templateId) {
// 1. Получить шаблон
$template = DocTemplate_Template_Model::getInstanceById($templateId);
// 2. Получить данные записи (как PDFMaker)
$focus = CRMEntity::getInstance($module);
$focus->retrieve_entity_info($recordId, $module);
$focus->id = $recordId;
// 3. Получить маппинг
$mapping = $template->getMapping();
// 4. Построить переменные
$variables = $this->buildVariables($mapping, $focus, $module);
// 5. Скачать шаблон из Nextcloud
$templateContent = $this->downloadTemplate($template->getFilename());
// 6. Заменить переменные
$content = $this->replaceVariables($templateContent, $variables);
// 7. Сохранить документ
return $this->saveDocument($content, $module, $recordId, $template);
}
/**
* Построение переменных из маппинга
*/
private function buildVariables($mapping, $focus, $module) {
$variables = [];
$processor = new DocTemplate_VariableProcessor_Model();
foreach ($mapping as $templateVar => $config) {
$variables[$templateVar] = $processor->process($config, $focus, $module);
}
return $variables;
}
/**
* Замена переменных в шаблоне
*/
private function replaceVariables($content, $variables) {
// Для DOCX через PHPWord
if (pathinfo($template->getFilename(), PATHINFO_EXTENSION) == 'docx') {
return $this->replaceDocxVariables($content, $variables);
}
// Для других форматов - простая замена
foreach ($variables as $var => $value) {
$content = str_replace('{' . $var . '}', $value, $content);
$content = str_replace('{{' . $var . '}}', $value, $content);
}
return $content;
}
}
```
---
### **2. VariableProcessor.php**
```php
class DocTemplate_VariableProcessor_Model {
/**
* Обработка переменной из маппинга
*/
public function process($config, $focus, $module) {
if (is_string($config)) {
// Простое поле: 'projectname' или '$PROJECT_PROJECTNAME$'
return $this->getFieldValue($config, $focus, $module);
}
if (is_array($config)) {
$type = $config['type'] ?? 'direct';
switch ($type) {
case 'direct':
return $this->getFieldValue($config['field'], $focus, $module);
case 'related':
return $this->getRelatedFieldValue(
$focus->id,
$config['module'],
$config['field']
);
case 'function':
return $this->callFunction(
$config['function'],
$config['params'] ?? [],
$focus,
$module
);
case 'constant':
return $config['value'];
default:
return '';
}
}
return '';
}
/**
* Получение значения поля (поддержка формата PDFMaker)
*/
private function getFieldValue($fieldConfig, $focus, $module) {
// Если формат PDFMaker: $PROJECT_PROJECTNAME$
if (preg_match('/\$([A-Z]+)_([A-Z_]+)\$/', $fieldConfig, $matches)) {
$varModule = $matches[1];
$fieldName = strtolower($matches[2]);
if ($varModule == strtoupper($module)) {
return $focus->column_fields[$fieldName] ?? '';
} else {
return $this->getRelatedFieldValue($focus->id, $varModule, $fieldName);
}
}
// Простое поле
return $focus->column_fields[$fieldConfig] ?? '';
}
}
```
---
## 🎨 UI КОМПОНЕНТЫ
### **1. Кнопка в детальном виде**
**JavaScript: `DocTemplate.js`**
```javascript
DocTemplate = {
/**
* Показать диалог выбора шаблона
*/
showTemplateDialog: function(module, recordId) {
// 1. Получить список шаблонов для модуля
AppConnector.request({
module: 'DocTemplate',
action: 'ListTemplates',
module_name: module,
record_id: recordId
}).then(function(response) {
if (response.success) {
// 2. Показать модальное окно
DocTemplate.showTemplateSelector(response.result.templates, module, recordId);
}
});
},
/**
* Показать селектор шаблонов
*/
showTemplateSelector: function(templates, module, recordId) {
// Создать модальное окно с выбором шаблона
// При выборе → вызвать generateDocument
},
/**
* Генерация документа
*/
generateDocument: function(templateId, module, recordId) {
// Показать индикатор загрузки
AppConnector.request({
module: 'DocTemplate',
action: 'GenerateDocument',
template_id: templateId,
module_name: module,
record_id: recordId
}).then(function(response) {
if (response.success) {
// Открыть документ в OnlyOffice
window.open(response.result.fileUrl, '_blank');
} else {
Vtiger_Helper_Js.showPnotify(response.error.message);
}
});
}
};
```
---
## 📋 ПЛАН РЕАЛИЗАЦИИ
### **Этап 1: Базовая структура**
1. ✅ Создать структуру папок
2. ✅ Создать `DocTemplate.php`
3. ✅ Создать `schema.xml`
4. ✅ Создать базовые модели
### **Этап 2: Интеграция с PDFMaker**
1. ✅ Использовать логику получения данных через `CRMEntity`
2. ✅ Поддержка формата переменных PDFMaker
3. ✅ Использовать `PDFMaker_Fields_Model` для списка полей
### **Этап 3: Генерация документов**
1. ✅ Интеграция с Nextcloud (скачивание шаблонов)
2. ✅ Замена переменных через PHPWord
3. ✅ Сохранение в S3
### **Этап 4: UI**
1. ✅ Кнопка в детальном виде модулей
2. ✅ Модальное окно выбора шаблона
3. ✅ Предпросмотр переменных (опционально)
### **Этап 5: Тестирование**
1. ✅ Тестирование на реальных данных
2. ✅ Проверка производительности
3. ✅ Обработка ошибок
---
## 🔗 ИНТЕГРАЦИЯ С МОДУЛЯМИ
### **Добавление кнопки в модули**
```php
// В DocTemplate.php
private function addCustomLinks() {
$modules = ['Project', 'HelpDesk', 'Contacts', 'Accounts'];
foreach ($modules as $moduleName) {
$module = Vtiger_Module::getInstance($moduleName);
if ($module) {
$module->addLink(
'DETAILVIEWBASIC',
'LBL_CREATE_FROM_TEMPLATE',
"javascript:DocTemplate.showTemplateDialog('{$moduleName}', jQuery('#recordId').val())",
'icon-document',
10
);
}
}
}
```
---
## 📊 ПРЕИМУЩЕСТВА ОТДЕЛЬНОГО МОДУЛЯ
1.**Изолированность** - не влияет на другие модули
2.**Расширяемость** - легко добавлять функции
3.**Управление** - можно включать/выключать
4.**Права доступа** - настройка через профили
5.**Версионирование** - можно обновлять отдельно
6.**Логирование** - отдельные логи модуля
---
## 🚀 ГОТОВ НАЧАТЬ РЕАЛИЗАЦИЮ!
**С чего начнем?**
1. Создать базовую структуру модуля?
2. Создать schema.xml с таблицами?
3. Реализовать TemplateGenerator с интеграцией PDFMaker?