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