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