Практика использования существующего типового функционала резервирования товаров в УТ 11 выявила недостатки:
- отсутствие механизма по контролю за резервами ответственными (нет сводного отчета).
- отсутствие временных лимитов по резервам и как следствие образование неликвидов (зависшие товары) или недоступность резервирования по актуальным заказам клиентов.
- два вида резервирования «СоСклада» и «ИзЗаказов» одинаково «минусуют» складской остаток.
Я подумал и пошёл ...шагами.
Шаг 1 - Составление.
Приняты решения:
- Установить временной лимит для хранения статуса резерва товара (10 дней).
- По истечении лимита отменять резервирование товаров, как по неактуальным (автоматически).
- Создать возможность пролонгирования резервов товаров по заказам (ответственным).
- Создать отчет для мониторинга остатков сроков по резервам.
- Разделить доступность остатков при резервировании из наличия и резервирования к дате (СоСклада и ИзЗаказов).
Шаг 2 – Создание скелета.
Созданы объекты:
Регистр сведений «ООО_ПролонгацияРезервов» , где
измерения: ДатаУстановкиРезерва, ЗаказКлиента, Номенклатура, Склад, ДатаОкончанияРезерва, Ответственный;
ресурсы: Количество;
реквизиты: ПричинаПродленияРезерва.
Роль ООО_ ПролонгацияРезервов(для просмотра и изменения менеджерами).
Внешняя обработка «Продлить резервы по заказу» - Необходима для создания записей в новом регистре сведений «ООО_ПролонгацияРезервов» из формы заказа клиента без вмешательства в типовой код формы документа.
Шаг 3 – Визуализация.
Мониторинг менеджерами и руководством сроков резервов по созданному внешнему отчету.
Например:
Ищем сроки резервов запросом по табличным частям «Товары» документов «Заказ клиента», соединяя с новым регистром сведений («ООО_Пролонгация Резервов»). Если в регистре данных по срокам нет (не делали продления), то срок равен дата заказа + количество временного лимита (10 дней).
Ответственные мониторят сроки своих резервов, прорабатывают с клиентами актуальность заказанных позиций и при необходимости проваливаются в заказ, актуализируют данные в заказе и жмут кнопку "Продлить резервы по заказу". Дополнительная, но нужная работа (пользователям удобно).
В отчете пытался сделать фишку по сворачиванию группировки с «Номенклатурой».
Удалось через условное оформление – через ограничение максимальной высоты и включением/выключением через параметр «Подробно»(Да/НЕТ).
Но такой вариант резко теряет красоту при наличии заказов с большим разнообразием номенклатуры (на каждую номенклатуру по одной минимальной строчке болотного оттенка).
Если возможны варианты с несложной реализацией – буду рад прочитать в комментариях!
Шаг 4 – Автоматизация (или механизация).
Создан код по удалению «просроченных» резервов, где поиск данных осуществлен аналогично алгоритму в отчете, после по данным с «просрочкой» (относительно текущей даты) в табличной части «Товары» (документов «Заказ клиента») изменяется действие обеспечения с «Резервировать на складе» на «Не обеспечивать» (возможно нужно использовать «Отменено») и заказы перепроводятся.
В идеале (автоматизация) код помещается в общий модуль (не типовой) и выполняется регламентированным заданием.
Для отладки и выявления исключений предлагаю использовать сперва внешнюю обработку (механизация).
Пример обработки приведен на рисунке.
Пример запроса:
Запрос.Текст = "ВЫБРАТЬ
| ООО_ПролонгацияРезервов.ЗаказКлиента КАК ЗаказКлиента,
| ООО_ПролонгацияРезервов.Номенклатура КАК Номенклатура,
| ООО_ПролонгацияРезервов.Склад КАК Склад,
| МАКСИМУМ ООО_ПролонгацияРезервов.ДатаУстановкиРезерва) КАК ДатаУстановкиРезерва
|ПОМЕСТИТЬ ВТ_Пролонгации
|ИЗ
| РегистрСведений. ООО_ПролонгацияРезервов КАК ООО_ПролонгацияРезервов
|
|СГРУППИРОВАТЬ ПО
| ООО_ПролонгацияРезервов.ЗаказКлиента,
| ООО_ПролонгацияРезервов.Номенклатура,
| ООО_ПролонгацияРезервов.Склад
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СвободныеОстаткиОстатки.Номенклатура КАК Номенклатура,
| СвободныеОстаткиОстатки.ВРезервеСоСкладаОстаток КАК ВРезервеСоСкладаОстаток,
| СвободныеОстаткиОстатки.ВРезервеПодЗаказОстаток КАК ВРезервеПодЗаказОстаток,
| СвободныеОстаткиОстатки.Склад КАК Склад
|ПОМЕСТИТЬ ВТОстатки
|ИЗ
| РегистрНакопления.СвободныеОстатки.Остатки(&ТекущаяДата, ) КАК СвободныеОстаткиОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СвободныеОстатки.Регистратор КАК Регистратор,
| СвободныеОстатки.Период КАК Период,
| СвободныеОстатки.Номенклатура КАК Номенклатура,
| СвободныеОстатки.Склад КАК Склад,
| СвободныеОстатки.ВРезервеСоСклада КАК ВРезервеСоСклада,
| ЕСТЬNULL(ВТ_Пролонгации.ДатаУстановкиРезерва, СвободныеОстатки.Регистратор.Дата) КАК ДатаУстановки,
| РАЗНОСТЬДАТ(ЕСТЬNULL(ВТ_Пролонгации.ДатаУстановкиРезерва, СвободныеОстатки.Регистратор.Дата), &ДатаАктуальности, ДЕНЬ) КАК РазностьКоличество
|ПОМЕСТИТЬ ВТПриходы
|ИЗ
| РегистрНакопления.СвободныеОстатки КАК СвободныеОстатки
| ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Пролонгации КАК ВТ_Пролонгации
| ПО СвободныеОстатки.Регистратор = ВТ_Пролонгации.ЗаказКлиента
| И СвободныеОстатки.Номенклатура = ВТ_Пролонгации.Номенклатура
| И СвободныеОстатки.Склад = ВТ_Пролонгации.Склад
|ГДЕ
| СвободныеОстатки.ВидДвижения = &ВидДвижения
| И СвободныеОстатки.ВРезервеСоСклада > 0
| И РАЗНОСТЬДАТ(ЕСТЬNULL(ВТ_Пролонгации.ДатаУстановкиРезерва, СвободныеОстатки.Регистратор.Дата), &ДатаАктуальности, ДЕНЬ) > 0
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТОстатки.Номенклатура КАК Номенклатура,
| ВТОстатки.ВРезервеСоСкладаОстаток КАК ВРезервеСоСкладаОстаток,
| ВТОстатки.ВРезервеПодЗаказОстаток КАК ВРезервеПодЗаказОстаток,
| ВТОстатки.Склад КАК Склад,
| ВТПриходы.Регистратор КАК Регистратор,
| ВТПриходы.ВРезервеСоСклада КАК ВРезервеСоСклада,
| ВТПриходы.Период КАК Период,
| ЕСТЬNULL(ВТПриходы.Регистратор.Менеджер, &ПустойПользователь) КАК Менеджер,
| ЕСТЬNULL(ВТПриходы.Регистратор.Контрагент, &ПустойКонтрагент) КАК Контрагент,
| ВТПриходы.ДатаУстановки КАК ДатаУстановки,
| ДОБАВИТЬКДАТЕ(ВТПриходы.ДатаУстановки, ДЕНЬ, 10) КАК ДатаСрока
|ИЗ
| ВТОстатки КАК ВТОстатки
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТПриходы КАК ВТПриходы
| ПО ВТОстатки.Номенклатура = ВТПриходы.Номенклатура
| И ВТОстатки.Склад = ВТПриходы.Склад
|
|УПОРЯДОЧИТЬ ПО
| Регистратор,
| Номенклатура,
| Период УБЫВ";
Запрос.УстановитьПараметр("ДатаАктуальности", ТекущаяДата() - 3600 * 24 * Объект.КоличествоДнейАктуальностиРезервов );
Запрос.УстановитьПараметр("ВидДвижения", ВидДвиженияНакопления.Приход);
Запрос.УстановитьПараметр("ПустойПользователь", Справочники.Пользователи.ПустаяСсылка());
Запрос.УстановитьПараметр("ПустойКонтрагент", Справочники.Контрагенты.ПустаяСсылка());
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата());
В коде обработки за основу взята таблица «РегистрНакопления.СвободныеОстатки.Остатки», а не табличная часть «Товары», т.к. регистраторы возможны нескольких типов (теоретически) и для выявления возможных вариантов решил задействовать его. На практике записи с количеством «ВРезервеСоСклада» пишутся только по регистратору «Заказ клиента».
Шаг 5 – Разделение доступности остатков.
Первая проблема в типовом функционале УТ: Не удается «резервировать на складе» (недоступен вариант), если остаток на складе ещё есть (с учётом других «резервирований на складе»), но количество в заказах с «резервированием к дате» (по заказам поставщикам) превышает имеющийся складской остаток.
На рисунке (пример типового функционала) показано, как конечный остаток в количестве 7 невозможно зарезерировать (не к дате).
При выборе варианта обеспечения в заказе покупателя вызывается форма перечисления «ВариантыОбеспечения» - «ВыборВариантаОбеспечения» и варианты возможно выбрать только, если доступное количество больше нуля (а не конечный остаток).
Второй недостаток типового функционала – обратно позволяет «резервировать» и «отгружать» (устанавливать действие в заказе клиента) в отрицательный остаток, что вводит в заблуждение менеджеров (хорошо, если включен «контроль остатков товаров организаций» и проведение дальнейшей реализации не удается) и затрачивается время на разбирательства и исправление.
Команда «ЗаполнитьОбеспечение» вызывает форму «ИсполнениеЗаказа" (того-же перечисления «ВариантыОбеспечения») , где присутствуют необязательные флаги «К обеспечению» и «Не обеспечивать», не указав которые можно установить «Отгрузить» и «Резервировать на складе» без контроля остатков.
Решение первого недостатка: покопавшись в коде отладчиком находим функцию «ТаблицаДоступныеОстатки» в модуле менеджера Регистра накопления «ГрафикПоступленияТоваров», добавляем пару строк.
Пример изменения типового кода (с комментариями «Доработка ООО») :
Пока ЕстьЗаписи Цикл
ЗаполнитьЗначенияСвойств(КлючСтроки, Выборка);
НарастающийИтог = Выборка.Остаток;
МинимальноеЗначение = НарастающийИтог;
ИзмениласьЗапись = Ложь;
// Цикл по сочетанию номенклатура\характеристика\склад.
Пока Не ИзмениласьЗапись Цикл
НарастающийИтог = НарастающийИтог - Выборка.Оборот;
// Доработка ООО
МинимальноеЗначение_ООО = НарастающийИтог;
// Доработка ООО
Если МинимальноеЗначение > НарастающийИтог И (Не ТолькоПоложительные Или МинимальноеЗначение > 0) Тогда
СтрокаТаблицы = ДоступныеОстатки.Добавить();
ЗаполнитьЗначенияСвойств(СтрокаТаблицы, Выборка);
СтрокаТаблицы.ДатаДоступности = Выборка.Период; //доступно на дату.
СтрокаТаблицы.Количество = МинимальноеЗначение;
МинимальноеЗначение = НарастающийИтог;
КонецЕсли;
// Переход к следующей записи.
ЕстьЗаписи = Выборка.Следующий();
ИзмениласьЗапись = Не ЕстьЗаписи Или ОбеспечениеКлиентСервер.ИзменилсяКлюч(КлючСтроки, Выборка);
КонецЦикла;
Если Не ТолькоПоложительные Или МинимальноеЗначение > 0
// Доработка ООО
ИЛИ МинимальноеЗначение_ООО > 0
// Доработка ООО
Тогда
СтрокаТаблицы = ДоступныеОстатки.Добавить();
ЗаполнитьЗначенияСвойств(СтрокаТаблицы, КлючСтроки);
СтрокаТаблицы.ДатаДоступности = '00010101'; //доступно сейчас.
СтрокаТаблицы.Количество = МинимальноеЗначение;
// Доработка ООО
Если Справочники.итЗначенияПоУмолчанию.ИспользоватьНовоеРезервирование.ОсновноеЗначение = Истина Тогда
СтрокаТаблицы.Количество = МинимальноеЗначение_ООО;
КонецЕсли;
// Доработка ООО
КонецЕсли;
Пример кода отмены резервов:
Процедура ОтменитьРезервыНаСервере(МассивСтроки)
ЗаказРезерва = МассивСтроки[0];
ЗаказОбъект = ЗаказРезерва.ПолучитьОбъект();
НоменклатураСтроки = МассивСтроки[1];
СтруктураПоиска = Новый Структура;
СтруктураПоиска.Вставить("Номенклатура", НоменклатураСтроки);
СтрокиТоваров = ЗаказОбъект.Товары.НайтиСтроки(СтруктураПоиска);
Для Каждого СтрокаТоваров Из СтрокиТоваров Цикл
Если СтрокаТоваров.ВариантОбеспечения = Перечисления.ВариантыОбеспечения.СоСклада Тогда
СтрокаТоваров.ВариантОбеспечения = Перечисления.ВариантыОбеспечения.НеТребуется;
Попытка
ЗаказОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
//ОписаниеОшибки()
КонецПопытки;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Решение второго недостатка: изменить форму «ИсполнениеЗаказа», установив при открытии «К обеспечению» и ограничив изменение («ТолькоПросмотр»).
Пример на рисунке ниже.
Результаты.
Первые четыре шага реализованы более полугода назад и оказались рабочим решением в небольшой компании из нескольких организаций.
Жаль, что количественной (статистической) оценки нет, но зарезервированные и неактуальные завалы товаров прекратились, уменьшилась путаница.
Пятый шаг реализован в этом году и позволил значительно сократить временные затраты при оформлении заказов клиентов.
Послесловие.
Ни на какую гениальность идей не претендую, надеюсь, что кому- то сгодятся вышеизложенные мысли или будут дельные ответные советы по лучшим вариантам.
Обоснованная критика приветствуется.