Печать ценников и этикеток — одна из самых частых задач при автоматизации розничной торговой точки. Нередко владельцы магазинов хотят видеть на ценнике сразу две цены: например, обычную розничную и цену по акции (или со скидкой), оптовую и розничную, цену за единицу товара и цену за упаковку. В типовой конфигурации «1С:Розница 2.2» логика печати построена на использовании механизмов компоновки данных, что иногда вызывает трудности у начинающих разработчиков.
В этой статье мы подробно разберем, как устроена печать ценников, проанализируем решение, предложенное пользователем на форуме, выясним, почему оно является небезопасным для рабочей базы данных, и рассмотрим три альтернативных правильных способа реализации этой задачи.
Когда мы открываем обработку Обработка.ПечатьЭтикетокИЦенников, мы видим на форме таблицу Товары. Логично предположить, что добавление новой колонки (например, ЦенаХХ) в эту таблицу позволит сразу вывести ее на печать. Однако при нажатии кнопки «Печать» система не передает эту таблицу напрямую в макет ценника.
Вместо этого обработка использует механизм Схемы компоновки данных (СКД). Макет ценника — это фактически отчет СКД, который берет из таблицы на форме только ссылки на номенклатуру, характеристики и склады, а затем выполняет собственный запрос к базе данных (в частности, к регистру сведений ЦеныНоменклатуры). Именно поэтому добавленная «на лету» в таблицу на форме колонка не появляется в макете — СКД просто ничего не знает о её существовании.
---
Рассмотрим решение, к которому пришел автор темы. Его идея заключается в том, чтобы использовать предопределенный механизм дополнительных реквизитов 1С для временного хранения расчетной цены.
Алгоритм реализации автора:
ЦенаХХ.ПечатьЭтикетокИЦенников добавляется колонка ЦенаХХ в таблицу товаров на форме, где рассчитывается нужное значение.ЦенаХХ непосредственно в карточку каждой номенклатуры в базе данных.Автор применил следующий код на сервере для записи значений перед печатью:
&НаСервере
Процедура ОстЦена_ПечатьПередНаСервере()
Свойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ЦенаХХ (Доп. свойства справочника ""Номенклатура"" (Общие))", Истина);
Для каждого Стр Из Объект.Товары Цикл
Если Стр.ЦенаХХ <> 0 Тогда
Об = Стр.Номенклатура.Ссылка.ПолучитьОбъект();
НайденнаяСтрока = Об.ДополнительныеРеквизиты.Найти(Свойство, "Свойство");
Если НайденнаяСтрока = Неопределено Тогда
НоваяСтрока = Об.ДополнительныеРеквизиты.Добавить();
НоваяСтрока.Значение = Стр.ЦенаХХ;
НоваяСтрока.Свойство = Свойство;
Об.Записать();
Иначе
Если НайденнаяСтрока.Value <> Стр.ЦенаХХ Тогда
НайденнаяСтрока.Значение = Стр.ЦенаХХ;
Об.Записать();
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Хотя этот метод работоспособен и решает задачу «здесь и сейчас», внедрять его в реальные рабочие базы данных категорически не рекомендуется по следующим причинам:
Об.Записать()) внутри цикла. Если пользователь отправляет на печать 500 товаров, система выполнит 500 транзакций записи. Это гарантированно вызовет зависание сеанса пользователя и заблокирует таблицы для других участников процесса.Поэтому проанализируем более безопасные и архитектурно правильные альтернативы.
---
Если ваша вторая цена рассчитывается по какому-то математическому алгоритму (например, «минус 10% от розничной» или «розничная цена деленная на коэффициент»), этот функционал можно настроить полностью стандартными средствами программы.
Разберем этот процесс по шагам:
Цена со скидкой (расчетная).Динамический (по формуле).[Розничная] * 0.9 (что означает автоматическое вычисление цены со скидкой 10% от текущей розничной цены). Сохраним вид цены.[Цена] для базовой цены и параметр [СпецЦена] (или [СтараяЦена]) для вывода нашей расчетной динамической цены.При таком подходе расчет происходит мгновенно на стороне сервера «в памяти» во время формирования печатной формы, абсолютно не нагружая базу данных операциями записи.
---
Если алгоритм расчета цены слишком сложный и его невозможно описать стандартной формулой в справочнике «Виды цен», правильным решением будет доработка схемы компоновки данных (СКД), которая отвечает за выборку полей для ценника.
Выполнить настройку можно следующим образом:
ЦенаХХ.Выбор Когда Цена > 500 Тогда Цена * 0.85 Иначе Цена * 0.95 Конец.
ЦенаХХ, которое можно перетащить в любую ячейку ценника.---
Бывают ситуации, когда расчет второй цены зависит от ручного ввода пользователя непосредственно в ТЧ Товары на форме печати ценников. Чтобы вывести эти данные без записи в базу данных, мы можем использовать перехват таблицы данных перед отправкой в процессор компоновки.
Для реализации этого метода:
ПечатьЭтикетокИЦенников экспортный метод, отвечающий за подготовку таблицы значений для печати (в разных релизах это может быть функция ПодготовитьДанныеДляПечати в модуле объекта или менеджера обработки).&После или &Вместо.ЦенаХХ, и заполним её данными из реквизита формы:
&После("ПодготовитьДанныеДляПечати")
Функция Расш1_ПодготовитьДанныеДляПечати(СписокТоваров, ИсточникДанных)
// Получаем результат стандартного выполнения функции
ТаблицаРезультат = ПродолжитьВызов(СписокТоваров, ИсточникДанных);
// Добавляем колонку для нашей специальной цены
ТаблицаРезультат.Колонки.Добавить("ЦенаХХ", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// Заполняем колонку данными, сопоставляя строки по Номенклатуре и Характеристике
Для Каждого СтрокаТЗ Из ТаблицаРезультат Цикл
ПараметрыОтбора = Новый Структура("Номенклатура, Характеристика", СтрокаТЗ.Номенклатура, СтрокаТЗ.Характеристика);
НайденныеСтроки = Объект.Товары.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() > 0 Тогда
СтрокаФормы = НайденныеСтроки[0];
СтрокаТЗ.ЦенаХХ = СтрокаФормы.ЦенаХХ;
КонецЕсли;
КонецЦикла;
Возврат ТаблицаРезультат;
КонецФункции
Используя этот подход, мы полностью сохраняем производительность системы, избегаем блокировок базы данных и обеспечиваем вывод абсолютно любых расчетных данных на ценник пользователя.