При импорте больших объемов данных из внешних файлов (например, таблиц Excel) в конфигурацию «1С:Розница 1.0» (работающую на обычных формах) нередко возникает неприятная ситуация. Новые карточки товаров успешно создаются в справочнике Номенклатура, однако сопутствующий регистр сведений КодыТоваровPLU остается незаполненным. В результате эти товары становится невозможно выгрузить в кассы offline или электронные весы с печатью этикеток, поскольку торговое оборудование идентифицирует весовой и штучный товар именно по кодам PLU (Price Look-Up).
В рамках данной статьи мы подробно разберем, как решить эту задачу за два этапа. Сначала мы спроектируем оптимальный поисковый запрос к базе данных, чтобы быстро локализовать все проблемные позиции, а затем напишем эффективный алгоритм на встроенном языке 1С для автоматического присвоения и записи недостающих PLU-кодов.
Для начала проанализируем структуру хранения данных. Связь между справочником товаров и их кодами PLU в «1С:Розница 1.0» реализована через независимый непериодический регистр сведений КодыТоваровPLU. На форумах часто предлагают использовать конструкции с подзапросами и отрицанием связи, например, через оператор НЕ ... В. Рассмотрим типичный пример такого запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.Ссылка В
(ВЫБРАТЬ
КодыТоваровPLU.Номенклатура
ИЗ
РегистрСведений.КодыТоваровPLU КАК КодыТоваровPLU)
Давайте проанализируем этот запрос подробнее. Хотя на небольших тестовых базах он покажет работоспособность, на реальных базах с десятками тысяч SKU данный подход имеет критические недостатки:
НЕ ... В заставляет СУБД выполнять ресурсоемкую операцию сканирования таблиц, что приводит к долгому выполнению запроса и избыточной нагрузке на дисковую подсистему сервера.Номенклатура является иерархическим. Запрос в примере выше вернет не только конкретные товары, но и папки (группы справочника). Попытка программно присвоить PLU-код группе приведет к ошибкам в логике работы программы.Чтобы избежать этих проблем, применим более элегантное и быстрое решение — связь таблиц через ЛЕВОЕ СОЕДИНЕНИЕ с последующей фильтрацией по значению NULL в правой таблице, а также исключим группы справочника с помощью обращения к реквизиту ЭтоГруппа.
Рассмотрим оптимальный вариант поискового запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КодыТоваровPLU КАК КодыТоваровPLU
ПО Номенклатура.Ссылка = КодыТоваровPLU.Номенклатура
ГДЕ
Номенклатура.ЭтоГруппа = ЛОЖЬ
И КодыТоваровPLU.Номенклатура ЕСТЬ NULL
Выясним причину высокой скорости работы такого решения. Использование ЛЕВОЕ СОЕДИНЕНИЕ позволяет СУБД эффективно использовать индексы таблиц. Мы соединяем справочник товаров с регистром сведений по полю Ссылка. Проверка КодыТоваровPLU.Номенклатура ЕСТЬ NULL позволяет отобрать только те записи из левой таблицы (справочника), для которых не нашлось соответствия в правой (регистре сведений). Фильтр ЭтоГруппа = ЛОЖЬ гарантирует, что в выборку попадут только конечные элементы (карточки товаров), готовые к продаже.
Прежде чем приступать к написанию исполняемого кода заполнения, важно четко понимать структуру регистра сведений КодыТоваровPLU в конфигурации «1С:Розница 1.0». Этот регистр содержит следующие измерения и ресурсы:
СправочникСсылка.Номенклатура.СправочникСсылка.ХарактеристикиНоменклатуры. Если учет по характеристикам для данного товара не ведется, в это поле должна записываться пустая ссылка.СправочникСсылка.ЕдиницыИзмерения. Обычно это базовый реквизит карточки товара — единица хранения остатков.Для создания новых записей нам понадобится механизм генерации уникальных кодов. Чтобы не затереть уже существующие коды PLU в базе данных, мы должны сначала определить максимальный занятый код, а затем последовательно наращивать его значение (использовать инкремент) для каждой новой записи.
Ниже представлен готовый программный код, предназначенный для использования во внешней обработке. Разберем его по шагам.
// Шаг 1. Вычисляем текущий максимальный PLU-код в информационной базе
ЗапросМакс = Новый Запрос;
ЗапросМакс.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(МАКСИМУМ(КодыТоваровPLU.Код), 0) КАК МаксКод
|ИЗ
| РегистрСведений.КодыТоваровPLU КАК КодыТоваровPLU";
ВыборкаМакс = ЗапросМакс.Выполнить().Выбрать();
ТекущийКод = 1;
Если ВыборкаМакс.Следующий() Тогда
// Начинаем нумерацию со следующего свободного числа
ТекущийКод = ВыборкаМакс.МаксКод + 1;
КонецЕсли;
// Шаг 2. Выгружаем список номенклатуры, у которой отсутствуют PLU-коды
ЗапросБезPLU = Новый Запрос;
ЗапросБезPLU.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КодыТоваровPLU КАК КодыТоваровPLU
| ПО Номенклатура.Ссылка = КодыТоваровPLU.Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ
| И КодыТоваровPLU.Номенклатура ЕСТЬ NULL";
Выборка = ЗапросБезPLU.Выполнить().Выбрать();
// Шаг 3. Запись новых PLU в транзакции для обеспечения целостности данных
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
// Создаем менеджер записи для независимого регистра сведений
МенеджерЗаписи = РегистрыСведений.КодыТоваровPLU.СоздатьМенеджерЗаписи();
// Заполняем ключевые поля (измерения)
МенеджерЗаписи.Номенклатура = Выборка.Номенклатура;
МенеджерЗаписи.ЕдиницаИзмерения = Выборка.ЕдиницаИзмерения;
// Устанавливаем пустую ссылку для характеристик, если они не используются
МенеджерЗаписи.ХарактеристикаНоменклатуры = Справочники.ХарактеристикиНоменклатуры.ПустаяСсылка();
// Присваиваем сгенерированный код PLU
МенеджерЗаписи.Код = ТекущийКод;
// Записываем данные с флагом замещения существующих записей
МенеджерЗаписи.Записать(Истина);
// Инкрементируем счетчик кодов для следующего товара
ТекущийКод = ТекущийКод + 1;
КонецЦикла;
// Подтверждаем транзакцию, если все записи прошли успешно
ЗафиксироватьТранзакцию();
Сообщить("Заполнение завершено! Все недостающие PLU-коды успешно сформированы.");
Исключение
// В случае возникновения непредвиденной ошибки откатываем изменения целиком
ОтменитьТранзакцию();
Сообщить("Произошла критическая ошибка при записи кодов: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Рассмотрим подробнее важные технологические особенности этого алгоритма:
НачатьТранзакцию() / ЗафиксироватьТранзакцию()). При обработке больших массивов данных (тысячи позиций) запись каждой строки по отдельности напрямую в базу данных может занять много времени из-за постоянных обращений к диску. Обертывание цикла в единую транзакцию значительно ускоряет процесс записи (в десятки раз) и защищает базу данных от частичного заполнения в случае сбоя.СоздатьМенеджерЗаписи()). Так как регистр сведений является независимым, мы работаем с каждой записью индивидуально через специальный объект метаданных 1С. Метод Записать(Истина) гарантирует, что даже если в системе возникнут дублирующие ключи, запись перезапишет старые некорректные данные, исключая зависание транзакции по причине конфликта блокировок.Посмотрим на практическую сторону использования PLU-кодов в розничных продажах. Прежде чем запускать массовую обработку заполнения, проанализируем следующие технологические аспекты:
21 + [PLU-код фиксированной длины] + [вес товара] + [контрольный символ]. Если PLU-код в регистре отсутствует или записан некорректно, касса не сможет расшифровать этикетку весового товара при сканировании на узле расчета.Таким образом, комбинируя оптимизированный запрос и безопасную транзакционную запись через менеджер записи, мы можем быстро восстановить целостность данных в «1С:Розница 1.0» и обеспечить бесперебойную выгрузку товаров на кассы и весовое оборудование.