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