Предистория.
Внедряли пару лет назад УТ 11.1 (11.1.9.70) на складе. Не обошлось и без заказов клиента. Когда речь зашла о резервировани, выяснили, что проставить резерв можно исключительно руками. Ну и хорошо, подумали мы, программисты. Непозволительно! - воскликнули они, пользователи.
Как говорил наш генеральный - "нужно чуть-чуть поднастроить, программу и всё заработает", при этом, слегка жестикулируя правой рукой. Разве можно отказать генеральному, который исправно платит тебе оклад? Конечно нет, принимая во внимание, что УТ работала в жесткой связке с управленческой базой на 1с 7, и часть бизнес процессов должна была быть идентична в обеих базах.
Постановка задачи.
При поступлении товара на склад, необходимо зарезервировать его в первых (по дате) заказах клиента.
Решение.
Основная идея была такая. Раз в заказе клиента есть кнопка, которая позволяет зарезервировать все товары заказа имеющиеся в свободном остатке, следовательно, есть и механизм на программном уровне, который можно использовать.
Однако все оказалось не так уж радужно. Как выяснилось, этот механизм работает с ФОРМОЙ документа, и работает только с одним документом. Он не документирован, сложен, и черт ногу в нем сломит.
Однако путем проб и ошибок его удалось приспособить под собственные нужды.
В целом работает это всё следующим образом. При интерактивном проведении приходного ордера (при желании можно переделать и под поступление), вызывается процедура, которая выбирает все заказы с такиме же товарами в статусе "к обеспечению", применяет к ним последовательность штатных процедур по резервированию, резервирует товар, и отражает результат в регистре сведений.
Ниже приведен листинг используемых процедур и функций.
Во вложенном файле все тоже самое, но в формате 1С.
Напоминаю, что механизм разрабатывался под конфигурацию 11.1.9.70. Не исключено, что подойдет и для других версий.
#Область ОбработчикиСобытий
// Программный код процедуры необходимо добавить в модуль формы документа Приходный Ордер, в процедуру ПослеЗаписиНаСервере
Процедура ПослеЗаписиНаСервере(Форма, ТекущийОбъект, ПараметрыЗаписи) Экспорт
Если Форма.ИмяФормы = "Документ.ПриходныйОрдерНаТовары.Форма.ФормаДокумента" Тогда
РезервированиеАвтоматическое(Форма, ТекущийОбъект, ПараметрыЗаписи);
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область РезервированиеАвтоматическое
Процедура РезервированиеАвтоматическое(Форма, ТекущийОбъект, ПараметрыЗаписи)
Если ПараметрыЗаписи.РежимЗаписи <> РежимЗаписиДокумента.Проведение Тогда
Возврат;
КонецЕсли;
ЕстьЗарезервированныеТовары = Ложь;
// МассивРаспределяемыхТоваров - будет служить для того, чтобы со временем, прекратить распределение
МассивРаспределяемыхТоваров = ТекущийОбъект.Товары.ВыгрузитьКолонку("Номенклатура");
// Формируем запрос, выбираем незарезервированные (не обеспеченные) заказы
РезультатЗапросаТоваровЗаказовКлиентовКОбеспечению = ПолучитьРезультатЗапросаТоваровЗаказовКлиентовКОбеспечению(МассивРаспределяемыхТоваров);
// Обходим выборку, начинаем резервировать товар (обеспечивать заказы)
ВыборкаЗаказы = РезультатЗапросаТоваровЗаказовКлиентовКОбеспечению.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаЗаказы.Следующий() Цикл
ПереченьВариантов = Новый Массив();
ИндексыСтрок = Новый Массив();
ТоварыСтрок = Новый Массив();
ВыборкаСтрокиЗаказа = ВыборкаЗаказы.Выбрать();
Пока ВыборкаСтрокиЗаказа.Следующий() Цикл
Если ПереченьВариантов.Найти(ВыборкаСтрокиЗаказа.ВариантОбеспечения) = Неопределено Тогда
ПереченьВариантов.Добавить(ВыборкаСтрокиЗаказа.ВариантОбеспечения);
КонецЕсли;
ИндексыСтрок.Добавить(ВыборкаСтрокиЗаказа.НомерСтроки - 1);
ТоварыСтрок.Добавить(ВыборкаСтрокиЗаказа.Номенклатура);
КонецЦикла;
Если ПереченьВариантов.Найти(Перечисления.ВариантыОбеспечения.Отгрузить) = Неопределено Тогда
ПереченьВариантов.Добавить(Перечисления.ВариантыОбеспечения.Отгрузить);
КонецЕсли;
ВыполнитьОбеспечениеЗаказа(ВыборкаЗаказы.Ссылка, ИндексыСтрок, ПереченьВариантов,МассивРаспределяемыхТоваров,ТоварыСтрок,ТекущийОбъект,ЕстьЗарезервированныеТовары);
Если МассивРаспределяемыхТоваров.Количество() = 0 Тогда // все распределили
Прервать;
КонецЕсли;
КонецЦикла;
Если ЕстьЗарезервированныеТовары Тогда
// Если возникнет необходимость выводить сообщение, даже при нажатии на кнопку "Провести и закрыть",
// это можно будет организовать через оповещение, переместив начало процесса резервирования на клиент,
// в процедуру послезаписи, на клиенте.
ТекстСообщения = НСтр("ru = 'Документ ""%ДокументОснование%"" зарезервировал товары.'");
ТекстСообщения = СтрЗаменить(ТекстСообщения, "%ДокументОснование%", ТекущийОбъект);
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения,ТекущийОбъект);
КонецЕсли;
КонецПроцедуры
Функция ПолучитьРезультатЗапросаТоваровЗаказовКлиентовКОбеспечению(ОтборТовары)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.Ссылка КАК Ссылка,
| ЗаказКлиентаТовары.Ссылка.Дата КАК Дата,
| ЗаказКлиентаТовары.НомерСтроки КАК НомерСтроки,
| ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
| ЗаказКлиентаТовары.Количество КАК Количество,
| ЗаказКлиентаТовары.ВариантОбеспечения
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
|ГДЕ
| ЗаказКлиентаТовары.Ссылка.Проведен
| И ЗаказКлиентаТовары.Ссылка.Склад = ЗНАЧЕНИЕ(Справочник.Склады.ОптовыйСклад)
| И ЗаказКлиентаТовары.Ссылка.Статус В(&ОтборСтатусы)
| И ЗаказКлиентаТовары.Номенклатура В(&ОтборТовары)
| И ЗаказКлиентаТовары.ВариантОбеспечения В(&ОтборВариантыОбеспечения)
|
|УПОРЯДОЧИТЬ ПО
| Дата,
| НомерСтроки
|ИТОГИ ПО
| Ссылка"
;
// можно дополнить ножными статусами
ОтборСтатусы = Новый Массив;
ОтборСтатусы.Добавить(Перечисления.СтатусыЗаказовКлиентов.КОбеспечению);
ОтборВариантыОбеспечения = Новый Массив;
ОтборВариантыОбеспечения.Добавить(Перечисления.ВариантыОбеспечения.Требуется);
Запрос.УстановитьПараметр("ОтборТовары",ОтборТовары);
Запрос.УстановитьПараметр("ОтборСтатусы",ОтборСтатусы);
Запрос.УстановитьПараметр("ОтборВариантыОбеспечения",ОтборВариантыОбеспечения);
Возврат Запрос.Выполнить();
КонецФункции
Процедура ЗаписьРегистраСведений(Действие,Заказ,ТекущийОбъект,ТаблицаЗарезервированныхТоваров = Неопределено)
Если ТаблицаЗарезервированныхТоваров = Неопределено Тогда
ТаблицаЗарезервированныхТоваров = Новый ТаблицаЗначений; // Для совместимости последующих операций
ТаблицаЗарезервированныхТоваров.Колонки.Добавить("Номенклатура",Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаЗарезервированныхТоваров.Колонки.Добавить("Количество",Новый ОписаниеТипов("Число"));
ТаблицаЗарезервированныхТоваров.Добавить();
КонецЕсли;
Для Каждого Строка ИЗ ТаблицаЗарезервированныхТоваров Цикл
МЗ = РегистрыСведений.РезервированиеАвтоматическое.СоздатьМенеджерЗаписи();
МЗ.Действие = Действие;
МЗ.Заказ = Заказ;
МЗ.Основание = ТекущийОбъект.Ссылка;
МЗ.Дата = ТекущаяДата();
МЗ.Автор = Пользователи.АвторизованныйПользователь();
МЗ.Номенклатура = Строка.Номенклатура;
МЗ.Количество = Строка.Количество;
МЗ.Записать();
КонецЦикла;
КонецПроцедуры
Функция ПолучитьЗарезервированныеТовары(ТаблицаОбеспечения,МассивРаспределяемыхТоваров,ТоварыСтрок,ТоварыДокументаДоОбеспечения)
Отбор = Новый Структура();
Отбор.Вставить("ВариантОбеспечения",Перечисления.ВариантыОбеспечения.Отгрузить);
Строки = ТаблицаОбеспечения.НайтиСтроки(Отбор);
ТаблицаЗарезервированныхТоваров = ТаблицаОбеспечения.Скопировать(Строки,"Идентификатор,Количество");
ТаблицаЗарезервированныхТоваров.Колонки.Добавить("Номенклатура",Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Для Каждого Строка ИЗ ТаблицаЗарезервированныхТоваров Цикл
СтрокаДокумента = ТоварыДокументаДоОбеспечения.Найти(Строка.Идентификатор+1,"НомерСтроки");
Строка.Номенклатура = СтрокаДокумента.Номенклатура;
КонецЦикла;
ТаблицаЗарезервированныхТоваров.Колонки.Удалить("Идентификатор");
Для Каждого Товар ИЗ ТоварыСтрок Цикл
СтрокаТаблицаЗарезервированныхТоваров = ТаблицаЗарезервированныхТоваров.Найти(Товар,"Номенклатура");
Если СтрокаТаблицаЗарезервированныхТоваров = Неопределено Тогда // этого товара уже нет больше в свободном остатке
ИндексЭлемента = МассивРаспределяемыхТоваров.Найти(Товар);
Если ИндексЭлемента <> Неопределено Тогда
МассивРаспределяемыхТоваров.Удалить(ИндексЭлемента);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат ТаблицаЗарезервированныхТоваров;
КонецФункции
Процедура ВернутьПрежнююДатуОтгрузки(ТаблицаОбеспечения,ТоварыДокументаДоОбеспечения,Документ)
Для Каждого Строка ИЗ ТаблицаОбеспечения Цикл
СтрокаДокумента = ТоварыДокументаДоОбеспечения.Найти(Строка.Идентификатор+1,"НомерСтроки");
Строка.ДатаОтгрузки = СтрокаДокумента.ДатаОтгрузки;
КонецЦикла;
Документ.ДополнительныеСвойства.Вставить("АвтоматическоеРезервирование",Истина);
КонецПроцедуры
Процедура ВыполнитьОбеспечениеЗаказа(Заказ, ИндексыСтрок, ПереченьВариантов, МассивРаспределяемыхТоваров,ТоварыСтрок,ТекущийОбъект,ЕстьЗарезервированныеТовары)
МенеджерДокумента = ОбщегоНазначения.МенеджерОбъектаПоСсылке(Заказ);
Документ = Заказ.ПолучитьОбъект();
Попытка
Документ.Заблокировать();
Исключение
ЗаписьРегистраСведений(Перечисления.РезервированиеАвтоматическоеДействия.НеУдалосьЗарезервировать,Заказ,ТекущийОбъект);
Возврат;
КонецПопытки;
ТоварыДокументаДоОбеспечения = Документ.Товары.Выгрузить(,"НомерСтроки,Номенклатура,ДатаОтгрузки");
ПараметрыЗаполнения = МенеджерДокумента.ПараметрыВыбораОбеспечения(Документ.Статус);
ТаблицаОбеспечения = ОбеспечениеСервер.ТаблицаЗаполнениеОбеспеченияДокумента(Документ, ПереченьВариантов, ПараметрыЗаполнения, ИндексыСтрок);
ВернутьПрежнююДатуОтгрузки(ТаблицаОбеспечения,ТоварыДокументаДоОбеспечения,Документ);
МенеджерДокумента.ЗаполнитьВариантОбеспечения(Документ, Неопределено, "ИндексыСтрок", ТаблицаОбеспечения);
Попытка
Документ.Записать(РежимЗаписиДокумента.Проведение);
Исключение
ЗаписьРегистраСведений(Перечисления.РезервированиеАвтоматическоеДействия.НеУдалосьЗарезервировать,Заказ,ТекущийОбъект);
Возврат;
КонецПопытки;
ТаблицаЗарезервированныхТоваров = ПолучитьЗарезервированныеТовары(ТаблицаОбеспечения,МассивРаспределяемыхТоваров,ТоварыСтрок,ТоварыДокументаДоОбеспечения);
Если ТаблицаЗарезервированныхТоваров.Количество() > 0 Тогда
ЕстьЗарезервированныеТовары = Истина;
КонецЕсли;
ЗаписьРегистраСведений(Перечисления.РезервированиеАвтоматическоеДействия.Зарезервировано,Заказ,ТекущийОбъект,ТаблицаЗарезервированныхТоваров);
КонецПроцедуры
#КонецОбласти