Разработчикам на платформе 1С часто требуется реализовать функционал, при котором информация, введенная пользователем в одно поле, должна быть добавлена к содержимому другого поля, сохраняя при этом предыдущие записи. При этом исходное поле необходимо очищать для последующего ввода. Рассмотрим подробно, как осуществить такую задачу, избегая распространенных ошибок.
Представим ситуацию: у нас есть форма документа с двумя строковыми полями – Замечание (для текущего ввода) и ЗамечаниеДо (для хранения истории всех замечаний). Наша цель – при записи документа переносить текст из Замечание в ЗамечаниеДо, добавляя его новой строкой, и очищать Замечание.
ПриЗаписиНаСервере или ПередЗаписьюНаСервере.ТекущийОбъект. Параметр Объект в этом контексте ссылается на объект формы и не позволяет напрямую модифицировать данные, которые будут записаны в базу.+) и специальный символ переноса строки Символы.ПС.Замечание необходимо очистить, присвоив ему пустую строку.ЗамечаниеДо еще пустое (первая запись). В этом случае не нужно добавлять символ переноса строки в самом начале.Прежде чем приступать к написанию кода, убедимся, что оба поля – Замечание и ЗамечаниеДо – существуют в объекте документа и имеют тип Строка. Для поля ЗамечаниеДо желательно установить многострочный режим ввода на форме, чтобы отображать все накопленные замечания корректно.
Мы будем использовать обработчик события ПриЗаписиНаСервере. Этот обработчик вызывается на сервере после того, как были выполнены проверки перед записью, но до самой физической записи объекта в базу данных. Это идеальное место для модификации данных объекта перед их сохранением.
Рассмотрим по шагам, как построить логику нашего обработчика.
Прежде всего, нам необходимо убедиться, что пользователь ввел какой-либо текст в поле Замечание. Если поле пустое, нет смысла что-либо добавлять или очищать. Для этого используем функцию ЗначениеЗаполнено().
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверяем, заполнено ли поле Замечание
Если ЗначениеЗаполнено(ТекущийОбъект.Замечание) Тогда
// Дальнейшая логика будет здесь
КонецЕсли;
КонецПроцедуры
Часто возникает потребность не добавлять одно и то же замечание несколько раз подряд, если оно уже является последним или присутствует в истории. Чтобы избежать добавления того же самого текста, который уже был добавлен в ЗамечаниеДо, мы можем сравнить текущее ТекущийОбъект.Замечание с последней добавленной строкой в ТекущийОбъект.ЗамечаниеДо или со всем содержимым ЗамечаниеДо. Простой, но не всегда универсальный вариант — это сравнить с содержимым поля ЗамечаниеДо, которое было сохранено в базе до текущей записи документа. Для этого мы можем обратиться к параметру Объект.Ссылка.ЗамечаниеДо, который содержит значение поля из базы данных.
Однако, более надежный подход для предотвращения дублирования *новых* заметок в *аккумулируемом* поле ЗамечаниеДо, если оно может содержать множество строк, заключается в проверке, не содержит ли ЗамечаниеДо уже эту конкретную строку. Для этого можно использовать функцию СтрНайти() или предварительно разбить ЗамечаниеДо на отдельные строки.
Для нашей базовой задачи, где мы просто хотим добавлять новые записи, мы можем пропустить проверку на совпадение со всей историей, но оставим сравнение с *сохраненным* значением, чтобы убедиться, что новое замечание действительно отличается от *последнего сохраненного*. Это предотвратит дублирование, если пользователь нажмет "Записать" несколько раз без изменения поля Замечание. Хотя, если Замечание очищается, то эта проверка может быть менее актуальной, но мы её рассмотрим как часть общего подхода.
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЗначениеЗаполнено(ТекущийОбъект.Замечание) Тогда
// Сравниваем новое замечание с тем, что хранится в базе для предотвращения
// повторного добавления одинакового замечания, если оно уже было сохранено
// и поле ЗамечаниеДо еще не изменилось.
// Объект.Ссылка.ЗамечаниеДо - это значение поля ЗамечаниеДо, которое уже есть в базе данных.
Если ТекущийОбъект.Замечание <> Объект.Ссылка.ЗамечаниеДо Тогда
// Дальнейшая логика добавления
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важное примечание: Если ЗамечаниеДо содержит несколько строк, простое сравнение ТекущийОбъект.Замечание <> Объект.Ссылка.ЗамечаниеДо может быть не совсем тем, что нам нужно, так как оно сравнивает новую строку со *всей* историей. Если нам нужно проверить, что *текущее* замечание *уже не содержится* в истории ЗамечаниеДо, более подходящей будет проверка с СтрНайти(), как было предложено в одном из сообщений форума. Рассмотрим это ниже.
Это ключевой шаг. Нам нужно взять текущее значение ЗамечаниеДо, добавить символ переноса строки и затем добавить содержимое Замечание. При этом необходимо учесть случай, когда ЗамечаниеДо еще пустое.
Используем тернарный оператор ? для удобства:
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЗначениеЗаполнено(ТекущийОбъект.Замечание) Тогда
// Опциональная проверка на наличие нового замечания в уже накопленной истории.
// Если требуется не добавлять замечание, если оно уже есть в ЗамечаниеДо,
// можно использовать такую конструкцию:
// Если Не СтрНайти(ТекущийОбъект.ЗамечаниеДо, ТекущийОбъект.Замечание) Тогда
// ...
// КонецЕсли;
// Для нашей базовой задачи (всегда добавлять, если Замечание заполнено)
// можно не использовать эту проверку или использовать упрощенную:
Если ТекущийОбъект.ЗамечаниеДо = "" ИЛИ ТекущийОбъект.ЗамечаниеДо <> ТекущийОбъект.Замечание Тогда
ТекущийОбъект.ЗамечаниеДо = ?(ТекущийОбъект.ЗамечаниеДо = "",
ТекущийОбъект.Замечание,
ТекущийОбъект.ЗамечаниеДо + Символы.ПС + ТекущийОбъект.Замечание);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Разберем строку с тернарным оператором:
ТекущийОбъект.ЗамечаниеДо = ?(Условие, ЗначениеЕслиИстина, ЗначениеЕслиЛожь);Условие: ТекущийОбъект.ЗамечаниеДо = "". Проверяем, пусто ли поле ЗамечаниеДо.ЗначениеЕслиИстина: ТекущийОбъект.Замечание. Если ЗамечаниеДо пусто, просто присваиваем ему текущее Замечание (без лишнего символа переноса строки в начале).ЗначениеЕслиЛожь: ТекущийОбъект.ЗамечаниеДо + Символы.ПС + ТекущийОбъект.Замечание. Если ЗамечаниеДо уже содержит текст, добавляем к нему символ переноса строки Символы.ПС, а затем новое Замечание.После того как текст успешно перенесен в ЗамечаниеДо, поле Замечание должно быть очищено, чтобы оно было готово для ввода следующей заметки. Это делается простым присвоением пустой строки.
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЗначениеЗаполнено(ТекущийОбъект.Замечание) Тогда
// Проверяем, что новое замечание отличается от последнего сохраненного
// или если ЗамечаниеДо пустое. Это предотвращает добавление пустоты
// или уже существующего текста при повторной записи без изменений.
// Более сложная проверка на уникальность внутри многострочного ЗамечаниеДо
// может быть реализована с СтрНайти или ручным парсингом строк.
Если ТекущийОбъект.ЗамечаниеДо = "" ИЛИ
(ТекущийОбъект.ЗамечаниеДо <> ТекущийОбъект.Замечание И Не СтрНайти(ТекущийОбъект.ЗамечаниеДо, ТекущийОбъект.Замечание + Символы.ПС) И Не СтрНайти(ТекущийОбъект.ЗамечаниеДо, Символы.ПС + ТекущийОбъект.Замечание)) Тогда
ТекущийОбъект.ЗамечаниеДо = ?(ТекущийОбъект.ЗамечаниеДо = "",
ТекущийОбъект.Замечание,
ТекущийОбъект.ЗамечаниеДо + Символы.ПС + ТекущийОбъект.Замечание);
ТекущийОбъект.Замечание = ""; // Очищаем поле после успешного переноса
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Обратите внимание на уточненную проверку в условии Если. Мы добавили более универсальное условие, которое пытается предотвратить дублирование: либо ЗамечаниеДо пустое, либо новое замечание не равно текущему значению ЗамечаниеДо *и* не содержится внутри ЗамечаниеДо как отдельная строка. Проверка с СтрНайти может быть доработана, если требуется более точное сравнение, например, по последней строке.
Давайте скомбинируем все эти шаги в единый, более надежный код. Для сравнения, чтобы не добавлять уже существующую заметку, мы можем использовать функцию СтрНайти, которая проверяет наличие подстроки в строке. Это полезно, если мы хотим убедиться, что *данное конкретное замечание* еще не было добавлено в список.
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// 1. Проверяем, заполнено ли поле для нового замечания.
// Если поле Замечание пустое, то нет смысла выполнять дальнейшие действия.
Если ЗначениеЗаполнено(ТекущийОбъект.За замечание) Тогда
// 2. Определяем, нужно ли добавлять символ переноса строки.
// Если ЗамечаниеДо пустое, то новая строка будет первой, и символ ПС не нужен в начале.
// Иначе, добавляем Символы.ПС перед новым замечанием.
ПереносСтроки = ?(ТекущийОбъект.ЗамечаниеДо = "", "", Символы.ПС);
// 3. Проверяем, не является ли новое замечание дубликатом.
// Можно реализовать несколько стратегий:
// а) Проверить, не содержит ли ТекущийОбъект.ЗамечаниеДо уже такую строку.
// Пример: Если СтрНайти(ТекущийОбъект.ЗамечаниеДо, ТекущийОбъект.Замечание) = 0 Тогда ...
// Это может быть проблемой, если части замечаний совпадают.
// б) Проверить, не является ли новое замечание идентичным последней добавленной строке.
// Это требует более сложного парсинга ЗамечаниеДо по Символы.ПС.
// в) Простой вариант: добавляем, если текущее замечание не равно последнему сохраненному
// значению ЗамечаниеДо из базы (Объект.Ссылка.ЗамечаниеДо) И
// если само ЗамечаниеДо еще не содержит текущее замечание как отдельную строку.
// Используем более универсальную проверку:
// Если ТекущийОбъект.ЗамечаниеДо пусто, то добавляем без проверки на дубликат.
// ИЛИ (если ЗамечаниеДо не пусто) если ТекущийОбъект.Замечание не найдено
// в ТекущийОбъект.ЗамечаниеДо как полная строка.
// Важно: для СтрНайти лучше искать полное совпадение строки,
// включая возможные Символы.ПС, чтобы избежать частичных совпадений.
// Упростим проверку для данного случая, чтобы избежать чрезмерного усложнения:
// Будем добавлять, если новое замечание отличается от текущего ЗамечаниеДо
// или если ЗамечаниеДо пусто. Это покроет большинство случаев.
// Проверяем, чтобы не добавлять одно и то же замечание несколько раз подряд,
// если пользователь не меняет его в поле "Замечание", но нажимает "Записать".
// Также проверяем, что новое замечание еще не содержится в накопленной строке.
// Для более точной проверки на дубликаты строк в многострочном поле
// потребуется разбить ЗамечаниеДо на массив строк и проверить каждую.
// Для текущей задачи, будем считать уникальным, если строка не входит в ЗамечаниеДо
// в том виде, в котором она добавляется.
// СтрНайти(ТекущийОбъект.ЗамечаниеДо, ТекущийОбъект.Замечание) > 0
// Проверяет, содержится ли ТекущийОбъект.Замечание в ТекущийОбъект.ЗамечаниеДо.
// Мы хотим добавить, если его там НЕТ.
Если НЕ (ТекущийОбъект.ЗамечаниеДо <> "" И СтрНайти(ТекущийОбъект.ЗамечаниеДо, ТекущийОбъект.Замечание) > 0) Тогда
// Если ЗамечаниеДо пусто, то просто присваиваем Замечание.
// Иначе, добавляем ПереносСтроки и Замечание.
ТекущийОбъект.ЗамечаниеДо = ТекущийОбъект.ЗамечаниеДо + ПереносСтроки + ТекущийОбъект.Замечание;
// 4. Очищаем поле для нового ввода.
ТекущийОбъект.Замечание = "";
Иначе
// Если замечание уже существует или дублируется (по нашей упрощенной логике),
// можно очистить поле без добавления или вывести сообщение.
Сообщить("Замечание '" + ТекущийОбъект.Замечание + "' уже добавлено или совпадает с существующим.", СтатусСообщения.Внимание);
ТекущийОбъект.Замечание = ""; // Очищаем поле, даже если не добавили, чтобы пользователь ввел новое.
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Мы включили в пример проверку Если НЕ (ТекущийОбъект.ЗамечаниеДо <> "" И СтрНайти(ТекущийОбъект.ЗамечаниеДо, ТекущийОбъект.Замечание) > 0) Тогда. Эта проверка означает: "Если ЗамечаниеДо не пусто И текущее Замечание содержится в ЗамечаниеДо, то НЕ выполнять добавление". Это предотвратит добавление одной и той же строки, если она уже есть в истории. Обратите внимание, что СтрНайти ищет подстроку, поэтому "АБВ" будет найдено в "АБВГД". Если вам нужна проверка на полное совпадение строки в многострочном поле, то придется предварительно разбить ЗамечаниеДо на массив строк (например, с помощью СтрРазделить()) и проверить каждую. Для большинства случаев предложенный вариант достаточен.
Объект и ТекущийОбъектМы уже упоминали об этом, но повторим: в обработчиках форм &НаСервере параметр ТекущийОбъект – это объект, который будет записан в базу данных. Все изменения, внесенные в него, сохранятся. Параметр Объект – это объект данных формы, его изменение на сервере не приводит к изменению данных в базе. Поэтому всегда используйте ТекущийОбъект для модификации данных, которые должны быть записаны.
ПередЗаписьюНаСервереОбработчик ПередЗаписьюНаСервере вызывается до ПриЗаписиНаСервере. Его основное назначение – выполнять проверки и, возможно, отменять запись (устанавливая параметр Отказ = Истина). Вы также можете модифицировать ТекущийОбъект в этом обработчике. В нашем случае, ПриЗаписиНаСервере подходит не хуже, поскольку мы просто модифицируем поля и не имеем условий для отказа от записи.
Для улучшения функционала вы можете добавлять к каждому замечанию дату и время его добавления. Это сделает историю замечаний более информативной.
&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЗначениеЗаполнено(ТекущийОбъект.Замечание) Тогда
// Форматируем дату и время для добавления к замечанию
ДатаВремяСтрокой = Формат(ТекущаяДата(), "ДЛФ=ДВ"); // Пример: 25.04.2024 10:30:00
НовоеЗамечаниеСФлагом = "[" + ДатаВремяСтрокой + "] " + ТекущийОбъект.Замечание;
ПереносСтроки = ?(ТекущийОбъект.ЗамечаниеДо = "", "", Символы.ПС);
// Добавляем, если новое замечание с датой еще не содержится в истории
// или если ЗамечаниеДо пусто.
Если НЕ (ТекущийОбъект.ЗамечаниеДо <> "" И СтрНайти(ТекущийОбъект.ЗамечаниеДо, НовоеЗамечаниеСФлагом) > 0) Тогда
ТекущийОбъект.ЗамечаниеДо = ТекущийОбъект.ЗамечаниеДо + ПереносСтроки + НовоеЗамечаниеСФлагом;
ТекущийОбъект.Замечание = "";
Иначе
Сообщить("Замечание с такой датой и текстом уже было добавлено.", СтатусСообщения.Внимание);
ТекущийОбъект.Замечание = "";
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Помимо обычного переноса строки, вы можете использовать различные функции для работы со строками, такие как СтрЗаменить(), СтрДлина(), Лев(), Прав(), СокрЛП() и другие, для более сложной обработки текста. Например, можно удалять лишние пробелы в начале или конце замечания перед добавлением, или приводить его к определенному регистру.
Для более продвинутой проверки на дубликаты, если ЗамечаниеДо может содержать сотни строк, и СтрНайти() становится неэффективным или дает ложные срабатывания (например, при поиске подстроки), можно рассмотреть следующий подход:
ТекущийОбъект.ЗамечаниеДо на массив строк с помощью СтрРазделить(), используя Символы.ПС в качестве разделителя.ТекущийОбъект.Замечание (предварительно обработав, например, удалив лишние пробелы и приведя к одному регистру).ДубликатНайден = Истина и не добавлять замечание.Пример разбиения строки:
МассивСтрокЗамечаний = СтрРазделить(ТекущийОбъект.ЗамечаниеДо, Символы.ПС, Ложь);
Для Каждого СтрокаЗамечания Из МассивСтрокЗамечаний Цикл
Если СокрЛП(СтрокаЗамечания) = СокрЛП(ТекущийОбъект.Замечание) Тогда
ДубликатНайден = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если НЕ ДубликатНайден Тогда
// ... логика добавления ...
КонецЕсли;
Мы подробно разобрали, как реализовать механизм добавления строк из одного поля в другое с накоплением истории и очисткой исходного поля в 1С. Ключевыми моментами являются использование серверных обработчиков (таких как ПриЗаписиНаСервере), правильное обращение к ТекущийОбъект, применение конкатенации строк с Символы.ПС для переноса на новую строку и условный оператор ? для корректной обработки первой записи. Предложенные варианты с проверкой на дубликаты и добавлением даты/времени помогут сделать функционал более robustным и полезным для пользователей.