Как спроектировать систему учета заявок в 1С и почему нельзя дублировать справочники

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При проектировании учетных систем в среде «1С:Предприятие» начинающие разработчики часто сталкиваются с интерфейсными задачами, которые пытаются решить «в лоб» программными методами. Классический пример такого подхода — разделение одного бизнес-процесса на несколько таблиц базы данных. В этой статье мы подробно разберем, почему идея переносить закрытые заявки из одного справочника в другой является грубейшей архитектурной ошибкой, к каким системным сбоям это может привести и как решить задачу красиво, производительно и по стандартам разработки 1С.

Суть проблемы: зачем «раздувать» базу данных?

Представим типичную ситуацию: в компании внедряется учет заявок в службу технической поддержки или сервисный отдел. Разработчик создает справочник Заявки с набором реквизитов (контрагент, описание проблемы, исполнитель, дата создания, дата закрытия). По мере работы системы количество закрытых заявок растет. У исполнителей возникает логичное пожелание: «Мы хотим видеть на экране только те заявки, которые сейчас находятся в работе, чтобы закрытые задачи нам не мешали и не отвлекали внимание».

Начинающий программист, опасаясь «перегрузки» базы данных и не зная механизмов платформы 1С, решает задачу кардинальным и опасным путем:

  1. Создает второй справочник ВыполненныеЗаявки с абсолютно такой же структурой реквизитов.
  2. Пишет код, который при заполнении поля ДатаЗакрытия копирует данные из первого справочника во второй.
  3. Физически удаляет исходную запись из первого справочника.

Давайте проанализируем, почему этот метод заслуживает звания «костыля» и какие опасности он в себе таит.

Почему архитектура с двумя одинаковыми справочниками — это ошибка

Опытные разработчики 1С сразу укажут на несколько критических проблем такого решения:

  1. Миф о перегрузке базы данных. Современные СУБД (MS SQL, PostgreSQL) и файловые базы 1С без проблем работают со справочниками, содержащими миллионы записей. Объем в 6–10 заявок в день — это всего около 2500 записей в год. Даже за 10 лет непрерывной работы база данных накопит всего 25 тысяч строк. Для СУБД это микроскопический объем данных, поиск по которому занимает сотые доли миллисекунды. Разделение данных «для оптимизации» при таких объемах бессмысленно.
  2. Проблема нарушения ссылочной целостности («Битые ссылки»). В 1С объекты связаны между собой уникальными идентификаторами (GUID). Если элемент справочника Заявки был выбран в каком-то документе (например, в «Акте выполненных работ») или зафиксирован в регистре сведений, а затем физически удален, ссылка на него в базе данных превратится в битую. Пользователи увидят в интерфейсе пугающую надпись <Объект не найден>....
  3. Высокая ресурсоемкость операций удаления. Физическое удаление объектов из базы данных в 1С — это сложная транзакционная операция. Платформа должна проверить, нет ли ссылок на удаляемый объект по всей базе данных с помощью функции НайтиПоСсылкам(). Постоянное создание и удаление записей нагружает систему в десятки раз сильнее, чем изменение одного единственного реквизита в существующей строке.
  4. Сложность построения аналитики. Если руководству компании потребуется отчет по эффективности работы сотрудников за прошлый год, разработчику придется писать сложнейшие запросы, объединяющие две таблицы с одинаковой структурой, что снижает производительность отчетов и усложняет поддержку кода.

Решение №1. Использование одного справочника со статусами и динамическими списками

Правильным подходом к решению данной задачи является разделение физического хранения данных и их визуального представления на форме. Данные должны храниться в одном справочнике (или документе), а настраивать видимость для пользователей нужно на уровне интерфейса.

Разберем по шагам, как это реализовать:

  1. Добавим в справочник Заявки реквизит Статус с типом ссылки на перечисление СтатусыЗаявок (значения: Принята, ВРаботе, Выполнена).
  2. В форме списка справочника, которую открывает исполнитель (техник), настроим динамический список.
  3. В свойствах динамического списка перейдем в настройки отбора и укажем условие: показывать только те элементы, у которых статус не равен значению Выполнена (или ДатаЗакрытия НЕ ЗАПОЛНЕНО).

Посмотрим на пример запроса для динамического списка формы «Активные заявки»:


ВЫБРАТЬ
    СправочникЗаявки.Ссылка КАК Ссылка,
    СправочникЗаявки.Код КАК Код,
    СправочникЗаявки.Наименование КАК Наименование,
    СправочникЗаявки.ДатаСоздания КАК ДатаСоздания,
    СправочникЗаявки.Исполнитель КАК Исполнитель,
    СправочникЗаявки.Статус КАК Статус
ИЗ
    Справочник.Заявки КАК СправочникЗаявки
ГДЕ
    СправочникЗаявки.Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыЗаявок.Выполнена)

Благодаря этому техник на своем экране будет видеть только незакрытые задачи, а руководители в своей форме списка смогут видеть абсолютно все заявки, используя простые стандартные отборы платформы 1С.

Решение №2. Архитектурный переход от справочника к документам

В философии проектирования систем «1С:Предприятие» объекты делятся по своей экономической сути:

Заявка на ремонт, обслуживание или IT-поддержку — это классическое **событие**, которое имеет жизненный цикл во времени, дату возникновения и дедлайн. Поэтому правильнее создавать не справочник Заявки, а документ Заявка. Документы в 1С имеют встроенный механизм проведения, умеют делать движения по регистрам и позволяют легко строить отчеты по остаткам невыполненных задач с помощью регистров накопления.

Решение №3. Контроль состояний через регистр сведений

Если вам требуется не просто фиксировать факт закрытия заявки, но и отслеживать историю её изменения (когда была создана, когда принята в работу, сколько времени находилась на тестировании), идеальным решением будет связка документа и регистра сведений.

Создадим периодический регистр сведений ИсторияСтатусовЗаявок со следующей структурой:

При каждом изменении состояния заявки в этот регистр программно записывается новая строка с текущей датой и временем. Это позволит не только отфильтровать активные заявки на текущий момент (получив срез последних), но и построить детальные отчеты для руководства по KPI сотрудников (например, среднее время обработки одной заявки на каждом этапе).

Альтернативный вариант: Использование подписок на события (если перенос неизбежен)

Если вы столкнулись с жестким техническим заданием в существующей нетиповой конфигурации, которое запрещает менять структуру метаданных, и вам действительно необходимо переносить данные, выполните это корректно, без физического удаления исходных элементов.

Рассмотрим, как реализовать механизм синхронизации данных через **Подписку на событие** платформы 1С:

  1. Создадим подписку на событие ПриЗаписи для справочника Заявки.
  2. В модуле подписки напишем обработчик, который анализирует реквизит ДатаЗакрытия.
  3. В справочнике ВыполненныеЗаявки обязательно добавим реквизит ИсходнаяЗаявка (тип: СправочникСсылка.Заявки), чтобы связать эти два объекта и избежать дублирования при повторной перезаписи.

Пример программного кода для обработчика подписки на событие:


Процедура ПриЗаписиЗаявкиПриЗаписи(Источник, Отказ) Экспорт
    // Если дата закрытия не заполнена, ничего не делаем
    Если Не ЗначениеЗаполнено(Источник.ДатаЗакрытия) Тогда
        Возврат;
    КонецЕсли;
    
    // Проверяем, существует ли уже копия в справочнике ВыполненныеЗаявки
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	Выполненные.Ссылка КАК Ссылка
        |ИЗ
        |	Справочник.ВыполненныеЗаявки КАК Выполненные
        |ГДЕ
        |	Выполненные.ИсходнаяЗаявка = &ИсходнаяЗаявка";
    Запрос.УстановитьПараметр("ИсходнаяЗаявка", Источник.Ссылка);
    
    Результат = Запрос.Выполнить();
    
    Если Результат.Пустой() Тогда
        // Создаем новую запись во втором справочнике
        НоваяЗапись = Справочники.ВыполненныеЗаявки.СоздатьЭлемент();
        ЗаполнитьЗначенияСвойств(НоваяЗапись, Источник, , "Код, Родитель");
        НоваяЗапись.ИсходнаяЗаявка = Источник.Ссылка;
        НоваяЗапись.Записать();
    Иначе
        // Обновляем существующую запись
        Выборка = Результат.Выбрать();
        Выборка.Следующий();
        ОбъектКопии = Выборка.Ссылка.ПолучитьОбъект();
        ЗаполнитьЗначенияСвойств(ОбъектКопии, Источник, , "Код, Родитель");
        ОбъектКопии.Записать();
    КонецЕсли;
КонецПроцедуры

Важное предупреждение: Даже при переносе данных ни в коем случае не удаляйте физически элемент из исходного справочника! Вместо удаления используйте пометку на удаление или просто скрывайте закрытые заявки из видимости пользователей с помощью отборов, как это было описано в Решении №1. Это убережет вашу базу данных от появления битых ссылок и сохранит целостность всей системы учета.

← На главную