При разработке конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с архитектурными ограничениями прошлых лет. Одно из наиболее существенных ограничений — отсутствие табличных частей у справочников (которые появились только в версии 8.0) и отсутствие стандартного типа данных «Хранилище значения» для хранения произвольных структур. Рассмотрим классическую задачу: в ветеринарной аптеке для каждой номенклатурной позиции необходимо хранить список животных, которым подходит данный препарат. Животных может быть много, и один товар может подходить сразу десятку разных видов. Это классическое отношение типа «многие ко многим» (Many-to-Many).
В этой статье мы подробно разберем, какими методами можно решить эту задачу в среде 1С 7.7, проанализируем их сильные и слабые стороны, а также приведем практические примеры реализации.
---
Это классический и самый очевидный подход в рамках штатных возможностей метаданных 1С 7.7. Разберем его по шагам.
Создаем новый справочник (например, СовместимостьТоваров) и делаем его подчиненным справочнику Номенклатура. В качестве единственного реквизита этого подчиненного справочника указываем ссылку на справочник ВидыЖивотных (или просто текстовый реквизит, если полноценного справочника животных в системе нет).
Плюсы данного подхода:
Для программного обхода таких связей мы используем следующий шаблон кода:
// Выберем всех животных, закрепленных за конкретным товаром
СпрСовместимость = СоздатьОбъект("Справочник.СовместимостьТоваров");
СпрСовместимость.ИспользоватьВладельца(ТекущийТовар);
СпрСовместимость.ВыбратьЭлементы();
Пока СпрСовместимость.ПолучитьЭлемент() = 1 Цикл
Сообщить("Подходит для: " + СокрЛП(СпрСовместимость.Животное.Наименование));
КонецЦикла;
Минусы подхода:
---
Чтобы обойти проблему односторонней выборки подчиненного справочника, мы можем спроектировать независимый связующий справочник. Назовем его СвязиНоменклатурыИЖивотных.
В этом справочнике создаем два обязательных реквизита:
Товар (тип: Справочник.Номенклатура)Животное (тип: Справочник.ВидыЖивотных)Важная деталь конфигуратора: Для обоих реквизитов в свойствах обязательно устанавливаем флаг «Сортировка». В терминологии 1С 7.7 это означает автоматическое создание индексов в базе данных (как DBF, так и MS SQL) по этим полям.
Теперь мы можем одинаково быстро производить выборку в обе стороны. Проанализируем пример получения товаров для конкретного животного:
СпрСвязей = СоздатьОбъект("Справочник.СвязиНоменклатурыИЖивотных");
// Используем быстрый поиск по индексу реквизита
СпрСвязей.ВыбратьЭлементыПоРеквизиту("Животное", ВыбранноеЖивотное, 1, 1);
Пока СпрСвязей.ПолучитьЭлемент() = 1 Цикл
Сообщить("Товар для этого животного: " + СокрЛП(СпрСвязей.Товар.Наименование));
КонецЦикла;
---
В большинстве типовых конфигураций 1С 7.7 (например, «Торговля и Склад 9.2») уже существует встроенный механизм свойств. Он базируется на плане видов характеристик (или справочнике видов свойств) и подчиненном справочнике значений свойств.
Рассмотрим, как задействовать этот механизм без изменения метаданных конфигурации:
ВидыСвойств создаем элемент «Подходит для животных».ЗначенияСвойств создаем перечень: «Кошки», «Собаки», «Кролики».Важное предупреждение: Как справедливо заметили участники обсуждения, ведение ручного ввода в свойствах может привести к хаосу в данных. Без жесткого программного контроля пользователи начнут создавать дублирующие значения: «кошка», «киска», «кошечки». Если вы выбираете этот путь, обязательно предусмотрите программное ограничение на добавление новых элементов справочника значений свойств рядовыми операторами.
---
Если перед нами стоит задача не изменять структуру конфигурации созданием новых справочников, мы можем хранить данные прямо в элементе номенклатуры, используя сериализацию динамических объектов 1С.
Для этого в справочнике Номенклатура создаем реквизит СписокЖивотныхТекст с типом данных Строка неограниченной длины (или фиксированной, например, 200–500 символов, в зависимости от объема данных).
При редактировании элемента справочника на форме размещается стандартный элемент управления типа «СписокЗначений» с пометками (галочками). При сохранении элемента мы превращаем объект списка в системную строку с помощью функции ЗначениеВСтрокуВнутр().
Рассмотрим программную реализацию сохранения данных:
// Код в модуле формы элемента при записи
СписокДляЗаписи = СоздатьОбъект("СписокЗначений");
// Выгружаем данные из экранной формы списка с галочками
СписокЖивотныхНаФорме.Выгрузить(СписокДляЗаписи);
// Преобразуем структуру в системную строку
СтрокаСериализации = ЗначениеВСтрокуВнутр(СписокДляЗаписи);
// Записываем в реквизит справочника
СписокЖивотныхТекст = СтрокаСериализации;
При открытии формы элемента выполняем обратную процедуру восстановления списка:
// Код при открытии формы элемента
Если ПустаяСтрока(СписокЖивотныхТекст) = 0 Тогда
СписокВосстановленный = ЗначениеИзСтрокиВнутр(СписокЖивотныхТекст);
СписокВосстановленный.ЗагрузитьВ(СписокЖивотныхНаФорме);
КонецЕсли;
Критический недостаток метода: Данный метод полностью лишает нас возможности использовать стандартные запросы 1С для поиска товаров по конкретному животному. База данных хранит текстовую строку вида {"ValueList",{"0","0",...}}, которую СУБД не может разобрать на логические составляющие. Метод подходит исключительно для информационного отображения данных внутри карточки товара.
---
Этот метод считается вершиной оптимизации в условиях жестких ресурсных ограничений платформ прошлых поколений. Он идеально подходит, если список животных относительно небольшой (до 30–50 видов) и редко меняется.
Идея заключается в том, что каждому животному сопоставляется свой уникальный бит (степень двойки):
В справочнике номенклатуры создается один числовой реквизит БитоваяМаска. Если товар подходит, например, кошкам и птицам, мы складываем их биты: $2 + 8 = 10$. Число 10 записывается в реквизит товара.
При анализе применимости товара программный код производит быстрое математическое вычисление. В связи с отсутствием в штатном языке 1С 7.7 побитового оператора И (AND), операцию можно реализовать через целочисленное деление:
// Проверим, подходит ли товар Птицам (бит 3, значение 8)
Функция ПодходитПтицам(ЗначениеМаски)
// Делим значение на вес бита и проверяем остаток от деления на 2
Если Целое(ЗначениеМаски / 8) % 2 = 1 Тогда
Возврат 1; // Да, подходит
КонецЕсли;
Возврат 0; // Нет
КонецФункции
Такой подход гарантирует минимальный размер базы данных и невероятно высокую скорость работы даже при миллионных объемах записей, однако требует высокой квалификации разработчика для поддержки кода.
---
Выбор конкретной реализации зависит от масштаба вашей информационной системы: