вторник, 20 января 2015 г.

1с специалист задача 1.11

Условие задачи из сборника:

Организация занимается продажей экзотических товаров. Принята следующая схема работы: в момент оформления заказа на товар (документ «Заказ покупателя») происходит его (товара) резервирование, причем реально этого товара в это время может и не быть. Непосредственно отгрузка товара по заказу со склада и снятие резерва оформляется документом «Расходная накладная».Следует учесть, что отгрузка товаров по одному заказу может производиться с разных складов, но в документе «Расходная накладная» склад может быть только один. Необходимо предоставить пользователю возможность указать в заказе количество резервируемого товара. При этом следует контролировать количество резерва, чтобы оно не превышало количество заказанного товара.В момент проведения «Расходной накладной» производится проверка наличия свободного товара (товар на всех складах минус резерв по всем остальным заказам). В том случае, если свободного товара достаточно для отгрузки, и на выбранном складе есть необходимое количество, то документ проводится, в противном случае выдается сообщение о недостаче свободного товара или товара на складе, и документ не проводится.Отгрузка может происходить только на основании заказа, накладная, не относящаяся ни к одному заказу, не может быть введена. Предполагается, что заказ будет отгружен полностью.Себестоимость товара рассчитывается как средняя по складу.Например:Если на складе есть 10 штук авторских ручек и выписан заказ на 7 ручек,то по этому заказу можно отгрузить товар, поскольку есть свободное количество и на складе товара хватает.Если те же 10 ручек находятся на разных складах – 8 и 2, то тогда с первого склада товар отгрузить можно, а со второго нет.Если 10 ручек находятся на одном складе, но заказов выписано 2, на 7 и 8 штук. В этом случае свободного товара недостаточно и отгрузить его нельзя.Создать отчет, который в сводной таблице в разрезе товаров будет показывать на заданную дату количество в резерве и количество в разрезе складов.

Решение:
После первого прочтения задание показалось мне вполне годным, но после последующего "вкуривания" пришел к глубокому убеждению что постановка "так себе" мягко говоря. Но ничего не поделаешь пришлось решать как понял.
Потребуется завести новые объекты:
Регистр накопления:"Зарезервированные Товары" с измерениями "Номенклатура" и "Заказ" и ресурсами "количество " и "резерв". 
Регистр "Остатки номенклатуры" имеет измерения "Номенклатура" , "Склад" , ресурсы "Себестоимость" и "Количество".

Создаем документ "Заказ Покупателя" с реквизитом "Покупатель" и ТЧ ""СписокНоменклатуры" с рекизитами "Номенклатура", "Количество", "Резерв". С помощью конструктора запроса создаем движения по регистру "зарезервированные товары".

        Движения.ЗарезервированныеТовары.Записывать = Истина;
Для Каждого ТекСтрокаСписокНоменклатуры Из СписокНоменклатуры Цикл
Движение = Движения.ЗарезервированныеТовары.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаСписокНоменклатуры.Номенклатура;
Движение.Заказ = Ссылка;
Движение.Количество = ТекСтрокаСписокНоменклатуры.Количество;
Движение.Резерв = ТекСтрокаСписокНоменклатуры.Резерв;

КонецЦикла;


Документ "Приходная накладная" будет делать движения по регистру "Остатки номенклатуры". 
       Движения.ОстаткиНоменклатуры.Записывать = Истина;
Для Каждого ТекСтрокаСписокНоменклатуры Из СписокНоменклатуры Цикл
Движение = Движения.ОстаткиНоменклатуры.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаСписокНоменклатуры.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрокаСписокНоменклатуры.Количество;
Движение.Себестоимость = ТекСтрокаСписокНоменклатуры.Сумма;
КонецЦикла;


Как всегда самое интересное происходит в с документом "Расходная накладная".
Во-первых необходимо реализовать ввод только на основании заказа.
Для этого заведем новый реквизит "ДокОснование" ( тип Заказ покупателя) и в обработке заполнения его будем заполнять вместе с другими данными.
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Если ДанныеЗаполнения = Неопределено Тогда
Сообщение = Новый("СообщениеПользователю");
Сообщение.Текст =" Документ можно вводить только на основании заказа";
Сообщение.Сообщить();
Возврат;
Иначе
ДокОснование = ДанныеЗаполнения;
Покупатель   = ДанныеЗаполнения.Покупатель;
СписокНоменклатуры.Загрузить(ДанныеЗаполнения.СписокНоменклатуры.Выгрузить());
КонецЕсли;
КонецПроцедуры

Саму проверку и отказ в случае надобности реализуем в процедуре формы.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Не ЗначениеЗаполнено(Объект.ДокОснование) Тогда
Отказ = Истина;
КонецЕсли;

КонецПроцедуры

Переходим к обработке проведения.
В данном случае использовать новую методику не целесообразно в силу запутанности условий,  Для реализации пришлось в запросе использовать 5!! временных таблиц. Не припомню чтобы даже в реальной практике я писал такие длинные запросы.
Собственно сама обработка проведения.

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
//1  Установим Блокировки
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();
Блокировка2 = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ЗарезервированныеТовары");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка2.Заблокировать();
 
// 2 Очистим Движения регистров
Движения.ОстаткиНоменклатуры.Записывать = Истина;
Движения.ОстаткиНоменклатуры.Очистить();
//Движения.Записать();
Движения.ЗарезервированныеТовары.Записывать = Истина;
Движения.ЗарезервированныеТовары.Очистить();
//Движения.Записать();


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

КонецЕсли;
КонецЦикла;
КонецПроцедуры

После "тщательного" тестирования переходим к отчету. 
Отчет тоже не такой простой как кажется на первый взгляд. 
В запросе пришлось делать Объединение 2-х таблиц для того чтобы вывести колонку "Резерв" как еще один склад. Для того чтобы форма отчета как в задании и не выводилась колонка итого необходимо на закладке "другие. настройки" установить "расположение общих итогов по горизонтали" равным "нет" для группировки "Номенклатура" и для всей таблицы.
Ниже прилагаю свое решение.Скачать















Комментариев нет:

Отправить комментарий