Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
17 KiB
17 KiB
🔧 ВСТАВКА ПЕРЕМЕННЫХ В ШАБЛОН: ГДЕ И КАК?
🎯 ВОПРОС: ГДЕ ВСТАВЛЯТЬ ПЕРЕМЕННЫЕ В DOCX?
📋 ВАРИАНТЫ
ВАРИАНТ 1: ВРУЧНУЮ В ONLYOFFICE/NEXTCLOUD ✍️
Как работает:
- Пользователь открывает шаблон в OnlyOffice
- Вручную пишет переменные:
{CLIENT_NAME},{DATE},{AMOUNT} - Сохраняет в Nextcloud
- В CRM настраивает маппинг:
CLIENT_NAME→$PROJECT_PROJECTNAME$
Плюсы:
- ✅ Полный контроль над расположением переменных
- ✅ Можно использовать в любом месте документа
- ✅ Гибкость форматирования
Минусы:
- ❌ Нужно помнить названия переменных
- ❌ Легко ошибиться в написании
- ❌ Нет автодополнения
ВАРИАНТ 2: ЧЕРЕЗ ИНТЕРФЕЙС CRM (ВСТАВКА КНОПКОЙ) 🖱️
Как работает:
- Пользователь открывает шаблон в OnlyOffice (через CRM)
- В CRM показывается панель с переменными
- Нажимает на переменную → она вставляется в курсор в OnlyOffice
- Или копирует переменную и вставляет вручную
Плюсы:
- ✅ Не нужно помнить названия
- ✅ Нет опечаток
- ✅ Визуальный выбор
Минусы:
- ❌ Нужна интеграция OnlyOffice API
- ❌ Сложнее реализация
ВАРИАНТ 3: ГИБРИДНЫЙ (РЕКОМЕНДУЕТСЯ) ✅
Как работает:
- В OnlyOffice: Пользователь вручную пишет переменные
{CLIENT_NAME} - В CRM: При сохранении шаблона показывается список найденных переменных
- В CRM: Настраивается маппинг для каждой переменной
- В CRM: Можно добавить новые переменные, которых нет в документе
Плюсы:
- ✅ Простота использования
- ✅ Гибкость
- ✅ Автоматическое обнаружение переменных
Минусы:
- ⚠️ Нужно парсить DOCX для поиска переменных
🏆 РЕКОМЕНДУЕМЫЙ ПОДХОД: ГИБРИДНЫЙ
📝 СЦЕНАРИЙ РАБОТЫ
Шаг 1: Создание/редактирование шаблона в OnlyOffice
Пользователь открывает шаблон в OnlyOffice и пишет:
┌─────────────────────────────────────────────┐
│ ПРЕТЕНЗИЯ │
│ │
│ Кому: УК "Жилищник" │
│ От: {CLIENT_NAME} │
│ │
│ Дата: {DATE} │
│ │
│ Текст претензии: │
│ {CLAIM_TEXT} │
│ │
│ Требования: │
│ 1. Возместить ущерб в размере {AMOUNT} ₽ │
│ │
│ С уважением, │
│ {CLIENT_NAME} │
└─────────────────────────────────────────────┘
Пользователь вручную пишет: {CLIENT_NAME}, {DATE}, {AMOUNT}, {CLAIM_TEXT}
Шаг 2: Сохранение и настройка маппинга в CRM
После сохранения в OnlyOffice, в CRM показывается:
┌─────────────────────────────────────────────────────────┐
│ Настройка маппинга для шаблона "Претензия" │
├─────────────────────────────────────────────────────────┤
│ │
│ Найдены переменные в шаблоне: │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Переменная │ Поле модуля │ [×] │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ CLIENT_NAME │ [Выбрать поле ▼] │ [×] │ │
│ │ DATE │ [Выбрать поле ▼] │ [×] │ │
│ │ AMOUNT │ [Выбрать поле ▼] │ [×] │
│ │ CLAIM_TEXT │ [Выбрать поле ▼] │ [×] │
│ └──────────────────────────────────────────────────┘ │
│ │
│ [+ Добавить переменную] │
│ │
│ [💾 Сохранить маппинг] │
│ │
└─────────────────────────────────────────────────────────┘
CRM автоматически:
- Парсит DOCX файл
- Находит все переменные
{VAR_NAME} - Показывает их в списке для настройки маппинга
🔧 РЕАЛИЗАЦИЯ: ПАРСИНГ ПЕРЕМЕННЫХ ИЗ DOCX
1. Поиск переменных в шаблоне
class DocTemplate_VariableExtractor_Model {
/**
* Извлечь все переменные из DOCX шаблона
*/
public function extractVariables($docxContent) {
$variables = [];
// Сохранить во временный файл
$tempFile = tempnam(sys_get_temp_dir(), 'template_') . '.docx';
file_put_contents($tempFile, $docxContent);
try {
// Загрузить через PHPWord
$phpWord = \PhpOffice\PhpWord\IOFactory::load($tempFile);
// Найти все переменные в формате {VAR_NAME} или {{VAR_NAME}}
$pattern = '/\{([A-Z_][A-Z0-9_]*)\}|\{\{([A-Z_][A-Z0-9_]*)\}\}/';
// Обойти все секции и элементы
foreach ($phpWord->getSections() as $section) {
foreach ($section->getElements() as $element) {
$text = $this->extractTextFromElement($element);
preg_match_all($pattern, $text, $matches);
// Собрать все найденные переменные
foreach ($matches[1] as $var) {
if (!empty($var) && !in_array($var, $variables)) {
$variables[] = $var;
}
}
foreach ($matches[2] as $var) {
if (!empty($var) && !in_array($var, $variables)) {
$variables[] = $var;
}
}
}
}
} catch (Exception $e) {
error_log("Error extracting variables: " . $e->getMessage());
} finally {
unlink($tempFile);
}
return $variables;
}
/**
* Извлечь текст из элемента PHPWord
*/
private function extractTextFromElement($element) {
$text = '';
if ($element instanceof \PhpOffice\PhpWord\Element\Text) {
$text = $element->getText();
} elseif ($element instanceof \PhpOffice\PhpWord\Element\TextRun) {
foreach ($element->getElements() as $textElement) {
if ($textElement instanceof \PhpOffice\PhpWord\Element\Text) {
$text .= $textElement->getText();
}
}
}
return $text;
}
}
2. Автоматическое обнаружение при сохранении
class DocTemplate_Save_Action {
public function process(Vtiger_Request $request) {
$templateId = $request->get('record');
$template = DocTemplate_Template_Model::getInstanceById($templateId);
// Если шаблон уже существует в Nextcloud
if ($template->getFilename()) {
// 1. Скачать шаблон из Nextcloud
$templateContent = $this->downloadFromNextcloud($template->getFilename());
// 2. Извлечь переменные
$extractor = new DocTemplate_VariableExtractor_Model();
$variables = $extractor->extractVariables($templateContent);
// 3. Сохранить найденные переменные
$template->set('detected_variables', json_encode($variables));
$template->save();
// 4. Если маппинг не настроен - показать форму настройки
if (empty($template->getMapping())) {
return [
'success' => true,
'variables_detected' => $variables,
'show_mapping_editor' => true
];
}
}
return ['success' => true];
}
}
3. UI: Автоматическое заполнение маппинга
JavaScript:
DocTemplate = {
/**
* Показать редактор маппинга с автозаполнением
*/
showMappingEditor: function(templateId, detectedVariables) {
// 1. Получить список полей модуля
AppConnector.request({
module: 'DocTemplate',
action: 'GetModuleFields',
template_id: templateId
}).then(function(response) {
var fields = response.result.fields;
var module = response.result.module;
// 2. Показать форму с найденными переменными
var html = '<div class="mapping-editor">';
detectedVariables.forEach(function(variable) {
// Попытаться угадать поле по названию переменной
var suggestedField = DocTemplate.suggestField(variable, fields);
html += '<div class="mapping-row">' +
'<input type="text" value="' + variable + '" readonly>' +
'<select class="field-selector">' +
'<option value="">-- Выберите поле --</option>';
// Добавить варианты полей
fields.forEach(function(field) {
var selected = (field.key === suggestedField) ? 'selected' : '';
html += '<option value="' + field.key + '" ' + selected + '>' +
field.label + '</option>';
});
html += '</select>' +
'<button onclick="DocTemplate.removeMappingRow(this)">×</button>' +
'</div>';
});
html += '</div>';
// Показать модальное окно
DocTemplate.showModal('Настройка маппинга', html);
});
},
/**
* Предложить поле по названию переменной
*/
suggestField: function(variable, fields) {
// Простая эвристика:
// CLIENT_NAME → PROJECTNAME, CONTACTNAME
// DATE → CREATEDTIME, MODIFIEDTIME
// AMOUNT → CF_1885, TOTAL
var suggestions = {
'CLIENT_NAME': ['PROJECTNAME', 'CONTACTNAME', 'ACCOUNTNAME'],
'DATE': ['CREATEDTIME', 'MODIFIEDTIME'],
'AMOUNT': ['CF_1885', 'TOTAL', 'AMOUNT']
};
var varUpper = variable.toUpperCase();
if (suggestions[varUpper]) {
for (var i = 0; i < fields.length; i++) {
var fieldKey = fields[i].key.toUpperCase();
if (suggestions[varUpper].some(function(s) {
return fieldKey.indexOf(s) !== -1;
})) {
return fields[i].key;
}
}
}
return null;
}
};
🎨 АЛЬТЕРНАТИВА: ВСТАВКА ЧЕРЕЗ ONLYOFFICE API
Вариант: Интеграция с OnlyOffice Document Server
Если нужна вставка переменных прямо в OnlyOffice:
// В OnlyOffice редакторе
function insertVariable(variable) {
// Получить API OnlyOffice
var oDocument = Api.GetDocument();
var oRange = oDocument.GetRange();
// Вставить переменную в текущую позицию курсора
oRange.InsertText('{' + variable + '}');
}
Но это требует:
- Настройку OnlyOffice Document Server API
- Сложную интеграцию
- Не всегда работает стабильно
✅ РЕКОМЕНДУЕМЫЙ ПОДХОД
1. Пользователь пишет переменные вручную в OnlyOffice
{CLIENT_NAME}
{DATE}
{AMOUNT}
2. CRM автоматически находит переменные при сохранении
// Парсинг DOCX → находит {CLIENT_NAME}, {DATE}, {AMOUNT}
3. CRM показывает форму настройки маппинга
CLIENT_NAME → [Выбрать поле: $PROJECT_PROJECTNAME$]
DATE → [Выбрать поле: $PROJECT_CREATEDTIME$]
AMOUNT → [Выбрать поле: $PROJECT_CF_1885$]
4. Сохранение маппинга в БД
🔄 ПРОЦЕСС РАБОТЫ (ПОЛНЫЙ ЦИКЛ)
1. Пользователь создает шаблон в CRM
↓
2. Открывается пустой DOCX в OnlyOffice
↓
3. Пользователь пишет текст и переменные:
"От: {CLIENT_NAME}, Дата: {DATE}"
↓
4. Сохраняет в Nextcloud
↓
5. В CRM автоматически обнаруживаются переменные:
CLIENT_NAME, DATE
↓
6. Показывается форма настройки маппинга:
CLIENT_NAME → $PROJECT_PROJECTNAME$
DATE → $PROJECT_CREATEDTIME$
↓
7. Сохраняется маппинг
↓
8. Готово! Можно генерировать документы
🎯 ИТОГОВОЕ РЕШЕНИЕ
Вставка переменных:
- ✅ Вручную в OnlyOffice - пишет
{CLIENT_NAME} - ✅ CRM автоматически находит - парсит DOCX
- ✅ CRM предлагает маппинг - выбор поля из списка
Преимущества:
- Простота использования
- Нет сложной интеграции с OnlyOffice API
- Гибкость - можно использовать любые названия переменных
- Автоматизация - не нужно вручную настраивать каждую переменную
🚀 ГОТОВ РЕАЛИЗОВАТЬ!
Подходит такой подход? Или хочешь другой вариант?