При разработке конфигураций для транспортных компаний или служб доставки часто возникает классическая задача: необходимо назначить на выполнение задания (наряда) определенную машину, водителя и нескольких помощников (грузчиков). При этом критически важно, чтобы ни один из этих ресурсов не был задействован в другом наряде в то же самое время. Рассмотрим подробнее, как реализовать такой контроль, проанализируем типичные ошибки новичков и разберем наиболее эффективные программные методы.
Разберем ситуацию, с которой сталкиваются многие начинающие разработчики. Попытка создать РегистрСведений, где измерениями выступают одновременно Машина, Водитель и Экспедитор, часто приводит к логической ошибке. В платформе 1С уникальность записи в регистре сведений определяется совокупностью всех измерений.
Проанализируем пример: если мы создадим запись, где в одном наборе измерений указаны Машина А, Водитель Б и Грузчик В, то система запретит создать точно такую же комбинацию. Однако она разрешит создать запись, где будет та же Машина А, но другой водитель. С точки зрения логики бизнеса это ошибка — машина-то уже занята! Выясним причину: для платформы комбинация (А+Б+В) и (А+Г+Д) — это разные ключи записи. Чтобы решить эту задачу правильно, нам нужно изменить подход к структуре хранения данных.
Вместо того чтобы пытаться уместить весь состав бригады в одну строку регистра, мы применим метод атомарных записей. Это наиболее архитектурно верный способ для использования РегистраСведений.
Рассмотрим по шагам реализацию этого метода:
РегистрСведений с названием ЗанятостьРесурсов.Ресурс. Тип этого поля должен быть составным (включать справочники ТранспортныеСредства и Сотрудники) или использовать ОпределяемыйТип.Период (если контроль нужен по времени) или Дата (если ресурс бронируется на весь день).Посмотрим на пример кода в модуле документа при обработке проведения:
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.ЗанятостьРесурсов.Записывать = Истина;
// 1. Добавляем запись для машины
НоваяЗапись = Движения.ЗанятостьРесурсов.Добавить();
НоваяЗапись.Период = Дата;
НоваяЗапись.Ресурс = Объект.Автомобиль;
// 2. Добавляем запись для водителя
НоваяЗапись = Движения.ЗанятостьРесурсов.Добавить();
НоваяЗапись.Период = Дата;
НоваяЗапись.Ресурс = Объект.Водитель;
// 3. Добавляем записи для грузчиков (циклом по табличной части)
Для Каждого СтрокаГрузчик Из Объект.Грузчики Цикл
НоваяЗапись = Движения.ЗанятостьРесурсов.Добавить();
НоваяЗапись.Период = Дата;
НоваяЗапись.Ресурс = СтрокаГрузчик.Сотрудник;
КонецЦикла;
КонецПроцедуры
При таком подходе, если Машина А уже была записана в регистр другим документом на эту же дату, система 1С сама выбросит исключение «Запись с такими ключевыми полями уже существует», так как измерение Ресурс совпадет. Это встроенный механизм платформы, который гарантирует 100% уникальность без написания сложных запросов.
Иногда разработчики предпочитают использовать РегистрНакопления. Этот метод основывается на идее, что каждый ресурс — это единица товара, которую мы «отгружаем» при создании наряда и «возвращаем» при его завершении. Рассмотрим, как это реализовать.
Создадим РегистрНакопления (вид — Остатки) под названием ДоступностьРесурсов. В измерениях укажем Ресурс, а в ресурсах — Количество. При проведении документа наряда будем делать движение «Расход» с количеством 1. При закрытии наряда — «Приход» с количеством 1.
Важный момент: Чтобы этот метод работал, необходимо включить в свойствах документа Удаление движений: Удалять автоматически при перепроведении и настроить контроль остатков. Если при попытке провести «Расход» остаток по ресурсу станет меньше нуля (уйдет в минус), проведение должно быть заблокировано.
Проанализируем плюсы и минусы этого метода. Плюс заключается в гибкости: вы можете легко увидеть, кто из сотрудников сейчас свободен, просто построив отчет по остаткам. Минус — риск «зависания» ресурса. Если по какой-то причине документ прихода не будет проведен, сотрудник навсегда останется в системе «занятым».
Если ваша транспортная компания работает не по дням, а по часам, и один водитель может совершить несколько рейсов за день, уникальность по дате не поможет. В этом случае мы должны использовать программную проверку пересечения временных интервалов. Выясним, как это сделать правильно.
В модуле документа в процедуре ПередЗаписью или ОбработкаПроведения необходимо выполнить запрос к регистру сведений, который хранит периоды занятости. Мы должны проверить, нет ли записей для наших ресурсов, у которых ВремяНачала меньше нашего ВремениОкончания, а ВремяОкончания больше нашего ВремениНачала.
Пример логики запроса для проверки:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Занятость.Ресурс КАК Ресурс
|ИЗ
| РегистрСведений.ЗанятостьРесурсов КАК Занятость
|ГДЕ
| Занятость.Ресурс В(&СписокРесурсов)
| И Занятость.ДатаНачала < &КонецРаботы
| И Занятость.ДатаОкончания > &НачалоРаботы
| И Занятость.Регистратор <> &Ссылка";
Обратите внимание: условие Занятость.Регистратор <> &Ссылка критически важно. Без него при перепроведении уже существующего документа система будет «натыкаться» на свои собственные старые записи и выдавать ошибку занятости.
Рассмотрим ситуацию в многопользовательском режиме: два диспетчера одновременно открывают наряды и выбирают одну и ту же машину. В момент нажатия кнопки «Провести» оба документа начинают проверку. Если не использовать блокировки, оба документа могут «увидеть», что машина свободна, и успешно записать данные. Это называется «состоянием гонки».
Для предотвращения этой ситуации необходимо использовать объект БлокировкаДанных. Мы должны заблокировать записи регистра по конкретным ресурсам перед тем, как выполнять проверку их занятости. Посмотрим, как это выглядит в коде:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЗанятостьРесурсов");
ЭлементБлокировки.УстановитьПараметр("Ресурс", Объект.Автомобиль);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// Только после этого выполняем запрос на проверку занятости или запись данных
Подводя итог, можно выделить следующие правила проектирования систем контроля занятости в 1С:
Ресурс и одной строкой на каждый объект (машину, водителя). Это самый простой и надежный путь, использующий встроенные индексы платформы.БлокировкаДанных для исключения ошибок при одновременной работе нескольких пользователей.Составной тип или Определяемый тип для измерений, описывающих ресурсы, чтобы в одном регистре можно было учитывать и технику, и людей. Это значительно упростит ваши запросы и отчеты в будущем.Проанализировав данные подходы, мы видим, что задача уникальности в 1С решается не только правильным выбором типа регистра, но и грамотной структурой его измерений. Понимание того, как работает КлючЗаписи, позволяет создавать надежные системы без лишнего кода.