Условие задачи из сборника:
Компания занимается оптовой торговлей. Принята следующая схема работы: поступление товаров отражается документом «Приходная накладная». По предварительной договоренности с покупателем менеджер может оформить резерв (документ «Резервирование товара»), причем наличие товара в этот момент не важно, товар может отсутствовать. Непосредственно отгрузка товара покупателю отражается документом «Расходная накладная», при этом происходит снятие резерва. Учет товаров ведется в разрезе складов. В документах «Приходная накладная» и Расходная накладная» склад только один (склад – реквизит шапки). При проведении расходной накладной необходимо проверить наличие товара на складе и «свободного» (будет описано далее) товара. В том случае, когда товара недостаточно, документ не проводится и выводится соответствующее сообщение об ошибке. У каждого менеджера есть приоритет, чем больше приоритет, тем более ответственный менеджер и тем важнее его продажи. Таким образом, если два менеджера одновременно зарезервировали один и тот же товара, то менеджер с большим приоритетом может продать товар, зарезервированный менеджером с меньшим приоритетом. Менеджер с низким приоритетом продать чужой резерв не имеет права. Таким образом, «свободный» товар менеджера определяется как товар на всех складах минус резерв всех остальных менеджеров с приоритетом большим либо таким же, как и у текущего менеджера. Приоритет устанавливается для каждого менеджера индивидуально и может меняться не чаще чем 1 раз в месяц. При продаже необходимо использовать приоритет менеджера, актуальный на дату продажи. Себестоимость товара рассчитывается как средняя по складу. Необходимо создать отчеты по состоянию остатков и резервов на дату.
Решение задачи :
1. Создадим недостающие объекты конфигурации:
Справочник Склады,
Документ «Приходная накладная» добавляем реквизит «Склад».
Документ «Расходная накладная» добавляем реквизиты «Склад»,»Менеджер» (тип — справочник «физ лица»)
Новый документ «Резервирование товара»
Реквизит шапки «Менеджер»
Табличная часть «Список Номенклатуры» с реквизитами «Номенклатура», «Количество».
Регистр «Остатки номенклатуры» добавим измерение «Склад»
Новый регистр «ТоварыВРезерве» измерения «Номенклатура», «Менеджер», ресурс -Количество.
Регистр сведений «CтатусыМенеджеров» с периодичностью месяц, измерение «Менеджер», ресурс «Приоритет».
2) Документ «Приходная накладная».
Создаем форму документа, в ней реализуем пересчет для реквизитов ТЧ «Цена», «Количество»
&НаКлиенте
Процедура СписокНоменклатурыКоличествоПриИзменении(Элемент)
ОМ.РассчитатьСумму(Элементы.СписокНоменклатуры.ТекущиеДанные);
КонецПроцедуры
&НаКлиенте
Процедура СписокНоменклатурыЦенаПриИзменении(Элемент)
ОМ.РассчитатьСумму(Элементы.СписокНоменклатуры.ТекущиеДанные);
КонецПроцедуры
вызывается процедура общего модуля ОМ( с признаком «клиент»).
Документ является регистратором регистра «Остатки номенклатуры», С помощью конструктора заполним обработку проведения.
3. Переходим к документу «Резервирование товара». Он является регистратором для регистра «ТоварыВРеерве».Аналогично с помощью конструктора движений забацаем проведение.
4. Переходим к документу «Расходная накладная товара». Аналогично приходной накладной создаем форму и реализуем пересчет ТЧ.
На этом прелюдия закончена, переходим к обработке проведения.
Комментировать код не буду просто выложу его.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
СуммаПоДокументу = СписокНоменклатуры.Итог(«Сумма»);
КонецПроцедуры
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
//1. Очищаем наборы записей
Движения.ОстаткиНоменклатуры.Записывать = Истина;
Движения.ОстаткиНоменклатуры.Очистить();
Движения.ОстаткиНоменклатуры.Записать();
Движения.ТоварыВРезерве.Записывать = Истина;
Движения.ТоварыВРезерве.Очистить();
Движения.ТоварыВРезерве.Записать();
//2 Установим блокировки
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить(«РегистрНакопления.ОстаткиНоменклатуры»);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Номенклатура», «Номенклатура»);
Блокировка.Заблокировать();
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить(«РегистрНакопления.ТоварыВРезерве»);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Номенклатура», «Номенклатура»);
Блокировка.Заблокировать();
// Сам запрос
Запрос = Новый(«Запрос»);
Запрос.Текст =»ВЫБРАТЬ
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура КАК Номенклатура,
| СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество
|ПОМЕСТИТЬ ДокТЧ
|ИЗ
| Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
|ГДЕ
| РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ОстаткиНоменклатурыОстатки.Номенклатура,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК НаСкладеКол,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.СуммаОстаток, 0) КАК НаСкладеСум
|ПОМЕСТИТЬ ОстаткиНаСкладе
|ИЗ
| ДокТЧ КАК ДокТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
| &ТочкаИтогов,
| Номенклатура В
| (ВЫБРАТЬ
| ДокТЧ.Номенклатура
| ИЗ
| ДокТЧ)
| И Склад = &Склад) КАК ОстаткиНоменклатурыОстатки
| ПО ДокТЧ.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ОстаткиНоменклатурыОстатки.Номенклатура,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК ОстатокКол,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.СуммаОстаток, 0) КАК ОстатокСум
|ПОМЕСТИТЬ ОстаткиНаВсехСкладах
|ИЗ
| ДокТЧ КАК ДокТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
| &ТочкаИтогов,
| Номенклатура В
| (ВЫБРАТЬ
| ДокТЧ.Номенклатура
| ИЗ
| ДокТЧ)) КАК ОстаткиНоменклатурыОстатки
| ПО ДокТЧ.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ТоварыВРезервеОстатки.Номенклатура,
| ТоварыВРезервеОстатки.Менеджер,
| ЕСТЬNULL(ТоварыВРезервеОстатки.КоличествоОстаток, 0) КАК Резерв,
| CтатусыМенеджеровСрезПоследних.Приоритет
|ПОМЕСТИТЬ ВсеРезервы
|ИЗ
| ДокТЧ КАК ДокТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезерве.Остатки(
| &ТочкаИтогов,
| Номенклатура В
| (ВЫБРАТЬ
| ДокТЧ.Номенклатура
| ИЗ
| ДокТЧ)) КАК ТоварыВРезервеОстатки
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.CтатусыМенеджеров.СрезПоследних(, ) КАК CтатусыМенеджеровСрезПоследних
| ПО ТоварыВРезервеОстатки.Менеджер = CтатусыМенеджеровСрезПоследних.Менеджер
| ПО ДокТЧ.Номенклатура = ТоварыВРезервеОстатки.Номенклатура
|ГДЕ
| CтатусыМенеджеровСрезПоследних.Менеджер <> &ТекМенеджер
| И CтатусыМенеджеровСрезПоследних.Приоритет >= &ТекПриоритет
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ОстаткиНаВсехСкладах.Номенклатура КАК Товар,
| ОстаткиНаВсехСкладах.ОстатокКол,
| ОстаткиНаВсехСкладах.ОстатокСум,
| ВЫБОР
| КОГДА ЕСТЬNULL(ОстаткиНаВсехСкладах.ОстатокКол, 0) — ЕСТЬNULL(ВсеРезервы.Резерв, 0) > 0
| ТОГДА ЕСТЬNULL(ОстаткиНаВсехСкладах.ОстатокКол, 0) — ЕСТЬNULL(ВсеРезервы.Резерв, 0)
| ИНАЧЕ 0
| КОНЕЦ КАК СвободныйОстаток
|ПОМЕСТИТЬ ДоступныеОстатки
|ИЗ
| ДокТЧ КАК ДокТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиНаВсехСкладах КАК ОстаткиНаВсехСкладах
| ПО ДокТЧ.Номенклатура = ОстаткиНаВсехСкладах.Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ ВсеРезервы КАК ВсеРезервы
| ПО ДокТЧ.Номенклатура = ВсеРезервы.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ДокТЧ.Номенклатура,
| МАКСИМУМ(ДокТЧ.Количество) КАК Количество,
| МАКСИМУМ(ОстаткиНаСкладе.НаСкладеКол) КАК НаСкладеКол,
| МАКСИМУМ(ОстаткиНаСкладе.НаСкладеСум) КАК НаСкладеСум,
| МАКСИМУМ(ЕСТЬNULL(ДоступныеОстатки.СвободныйОстаток, 0)) КАК СвободныйОстаток,
| МАКСИМУМ(ЕСТЬNULL(ТоварыВРезервеОстатки.КоличествоОстаток, 0)) КАК РезервМенеджера
|ИЗ
| ДокТЧ КАК ДокТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиНаСкладе КАК ОстаткиНаСкладе
| ПО ДокТЧ.Номенклатура = ОстаткиНаСкладе.Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ ДоступныеОстатки КАК ДоступныеОстатки
| ПО ДокТЧ.Номенклатура = ДоступныеОстатки.Товар
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыВРезерве.Остатки(
| &ТочкаИтогов,
| Менеджер = &ТекМенеджер
| И Номенклатура В
| (ВЫБРАТЬ
| ДокТЧ.Номенклатура
| ИЗ
| ДокТЧ)) КАК ТоварыВРезервеОстатки
| ПО ДокТЧ.Номенклатура = ТоварыВРезервеОстатки.Номенклатура
|
|СГРУППИРОВАТЬ ПО
| ДокТЧ.Номенклатура»;
ТочкаИтогов = Новый Граница(МоментВремени(),ВидГраницы.Исключая);
Отбор = Новый(«Структура»);
Отбор.Вставить(«Менеджер», Менеджер);
Приоритет = РегистрыСведений.CтатусыМенеджеров.ПолучитьПоследнее(Дата,Отбор).Приоритет;
Запрос.Параметры.Вставить(«ТекМенеджер»,Менеджер);
Запрос.Параметры.Вставить(«ТочкаИтогов», ТочкаИтогов);
Запрос.Параметры.Вставить(«ТекПриоритет»,Приоритет);
Запрос.Параметры.Вставить(«Ссылка», Ссылка);
Запрос.Параметры.Вставить(«Склад», Склад);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Количество > Выборка.СвободныйОстаток Тогда
Сообщиение = Новый(«СообщениеПользователю»);
Сообщиение.Текст =»Недостаточно свободного остатка по номенклатуре «+Выборка.Номенклатура + » доступно «+ Выборка.Свободныйостаток + «из запрошенных» + Выборка.Количество;
Сообщиение.Сообщить();
Отказ = Истина;
Продолжить;
КонецЕсли;
Движение = Движения.ОстаткиНоменклатуры.ДобавитьРасход();
Движение.Период = Дата;
Движение.Склад = Склад;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Количество = Выборка.Количество;
Если Выборка.НаСкладеКол<>0 Тогда
Движение.Сумма = Выборка.Количество *( Выборка.НаСкладеСум/ Выборка.НаСкладеКол);
КонецЕсли;
Движение = Движения.ТоварыВРезерве.ДобавитьРасход();
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Менеджер = Менеджер;
Движение.Количество = Мин(Выборка.Количество, Выборка.РезервМенеджера)
КонецЦикла;
КонецПроцедуры
5) Переходим к отчетам.
Отчеты «Остатки товаров» идентичен отчету из предыдущей задачи (1.11), а отчет «Резервы товаров» аналогичен отчету «Остатки товаров» только вместо склада здесь менеджер.
Компания занимается оптовой торговлей. Поступление товаров отражается
документом «Приходная накладная», продажа — «Расходная накладная». Помимо
продажи товара, могут оказываться дополнительные услуги, например по
доставке. И услуги и товары указываются в одной табличной части.
Учет товаров ведется в разрезе складов. Поступление и продажа
осуществляются с указанием склада (в шапке документа).
При проведении документа «Расходная накладная» необходимо
производить только списание товара со склада. В том случае, когда товара не
хватает, документ проводиться не должен. Расчет себестоимости списанного
товара должен происходить позднее с помощью регламентной обработки «Расчет
себестоимости». Запуск этой обработки должен происходить автоматически с
использованием регламентного задания, но может быть произведен и вручную по
желанию пользователя. Обработка должна автоматически определять самый
первый документ, нуждающийся в расчете себестоимости, и рассчитывать
себестоимость для всех остальных документов начиная с него.
Списание себестоимости должно быть организовано по партиям, в
зависимости от текущего значения принятого на этот год в учетной политике
метода списания себестоимости (FIFO или LIFO). Еще раз подчеркивается –
учетная политика действует год. На следующий год метод списания может
смениться.
Для расчета себестоимости при списании товара необходимо учитывать
только момент поступления товара в компанию, вне зависимости от того, на
какой склад он пришел. Предположим, для метода списания FIFO первое
поступление портсигара произошло на склад «Основной» документом
«Приходная накладная №1», а потом на склад «Транзитный» документом
«Приходная накладная №2». В этом случае при продаже товара со склада
«Транзитный» в первую очередь должна быть списана себестоимость портсигара
по документу «Приходная накладная №1», так как она пришла раньше.
Вот мое решение можете прокомментировать, отчеты не делал, но проверял запросам что на основаниии моих регистров накоплений отчеты построить можно,меня волнует перерасчет , можете проверить корректно ли я его реализовал?
Чтобы получить от этой работы максимум пользы, старайтесь ответить на вопросы перед тем, как смотреть следующий шаг.
Вот фрагмент задачи 1.12 из сборника для подготовки к экзамену «1С:Специалист»:
«Компания занимается оптовой торговлей. Принята следующая схема
работы: поступление товаров отражается документом «Приходная накладная». По
предварительной договоренности с покупателем менеджер может оформить
резерв (документ «Резервирование товара»), причем наличие товара в этот
момент не важно, товар может отсутствовать. Непосредственно отгрузка товара
покупателю отражается документом «Расходная накладная», при этом происходит
снятие резерва.
Учет товаров ведется в разрезе складов. В документах «Приходная
накладная» и Расходная накладная» склад только один (склад – реквизит шапки).
При проведении расходной накладной необходимо проверить наличие
товара на складе и «свободного» (будет описано далее) товара. В том случае,
когда товара недостаточно, документ не проводится и выводится
соответствующее сообщение об ошибке.
У каждого менеджера есть приоритет, чем больше приоритет, тем более
ответственный менеджер и тем важнее его продажи. Таким образом, если два
менеджера одновременно зарезервировали один и тот же товара, то менеджер с
большим приоритетом может продать товар, зарезервированный менеджером с
меньшим приоритетом. Менеджер с низким приоритетом продать чужой резерв
не имеет права. Таким образом, «свободный» товар менеджера определяется как
товар на всех складах минус резерв всех остальных менеджеров с приоритетом
большим либо таким же, как и у текущего менеджера.
Приоритет устанавливается для каждого менеджера индивидуально и
может меняться не чаще чем 1 раз в месяц. При продаже необходимо
использовать приоритет менеджера, актуальный на дату продажи.
Себестоимость товара рассчитывается как средняя по складу.»
Недостаток этой задачи состоит в том, что не сформулировано чётко, как должен сниматься резерв.
В контексте этой задачи товар можно разделить на четыре категории:
- Зарезервированный менеджерами с приоритетом, высшим ии равным приоритету продающего менеджера
- Зарезервированный менеджерами с приоритетом, меньшим приоритета продающего менеджера
- Зарезервированный самим менеджером
- Никем не зарезервированный
Товар из первой категории продавать нельзя. Что касается товара остальных категорий, то давайте добавим своё условие:
«При продаже товара снятие резерва происходит так: Сначала снимается резерв продающего менеджера. Затем учитывается никем не зарезервированный товар (если он есть). И в последнюю очередь снимается резерв остальных менеджеров (то есть менеджеров с низшим приоритетом), причём сначала снимается резерв менеджеров с самым низким приоритетом.»
Начнём решать. Сделаем регистр накопления «Остатки номенклатуры», измерения «Номенклатура», «Склад», ресурсы «Количество», «Сумма».
Регистр накопления «Резервы номенклатуры», измерения «Номенклатура», «Менеджер», ресурс «Количество».
Регистр сведений «Приоритеты менеджеров», периодический в пределах месяца, режим записи независимый, одно измерение «Менеджер», один ресурс «Приоритет».
Документ «Приходная накладная» имеет реквизит «Склад» и табличную часть «Остатки номенклатуры» и делает движения в регистр «Остатки номенклатуры». Обработку его проведения сделаем конструктором.
Чтобы увидеть полный код обработки проведения документа «Приходная накладная», кликните здесь.
Также конструктором сделаем обработку проведения документа «Резервирование товара». Он будет делать движения в регистр «Резервы номенклатуры», и будет иметь реквизит «Менеджер» и табличную часть «Остатки номенклатуры» (Номенклатура, Количество).
Чтобы увидеть полный код обработки проведения документа «Приходная накладная», кликните здесь.
Документ «Расходная накладная» имеет реквизиты «Склад» и «Менеджер», Табличную часть «Остатки номенклатуры» (Номенклатура, Количество).
Вся «соль» этой задачи в обработке проведения Расходной накладной. Что должна делать Расходная накладная? Списывать номенклатуру в регистре «Остатки номенклатуры» и, при необходимости, в регистре «Резервы номенклатуры». Какая информация нам для этого потребуется?
- Потребуются данные документа.
- Потребуется приоритет продающего менеджера.
- Потребуется количество остатка каждого вида списываемой номенклатуры на данном складе.
- Также, для списания, потребуется соответствующая сумма остатка на данном складе.
-
По каждому виду номенклатуры потребуется
- количество, которое зарезервировано продающим менеджером
- каждым из менеджеров, приоритет которых ниже, чем у данного
- никем не зарезервировано.
Он потребуется для того, чтобы получить список менеджеров, приоритет которых ниже, чем у данного менеджера.
Давайте начнём делать обработку проведения. Что напишем сначала?
Запишем пустые наборы записей в регистры, на случай, если там имеются записи этого документа. Затем сделаем запрос к табличной части документа и сохраним её данные во временной таблице. Выберем из неё номенклатуру и заблокируем регистры по значениям этой номенклатуры.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения.ОстаткиНоменклатуры.Записывать=Истина;
Движения.РезервыНоменклатуры.Записывать=Истина;
Движения.Записать();
Движения.ОстаткиНоменклатуры.Записывать=Истина;
Движения.РезервыНоменклатуры.Записывать=Истина;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура КАК Номенклатура,
| СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество
|ПОМЕСТИТЬ ВТСписокНоменклатуры
|ИЗ
| Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
|ГДЕ
| РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТСписокНоменклатуры.Номенклатура КАК Номенклатура
|ИЗ
| ВТСписокНоменклатуры КАК ВТСписокНоменклатуры";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = РезультатЗапроса;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = РезультатЗапроса;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();
Теперь давайте выберем и поместим во временную таблицу всех менеджеров, чей приоритет меньше приоритета данного менеджера. Причём сделаем это одним пакетным запросом. Попробуйте придумать такой запрос, прежде чем смотреть дальше.
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ПриоритетыМенеджеровСрезПоследних.Приоритет КАК Приоритет
|ПОМЕСТИТЬ ВТПриоритетЭтогоМенеджера
|ИЗ
| РегистрСведений.ПриоритетыМенеджеров.СрезПоследних(&МоментВремени, Менеджер = &Менеджер) КАК ПриоритетыМенеджеровСрезПоследних
|
|ИНДЕКСИРОВАТЬ ПО
| Приоритет
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ПриоритетыМенеджеровСрезПоследних.Менеджер КАК Менеджер,
| ПриоритетыМенеджеровСрезПоследних.Приоритет КАК Приоритет
|ПОМЕСТИТЬ МенеджерыНизкогоПриоритета
|ИЗ
| ВТПриоритетЭтогоМенеджера КАК ВТПриоритетЭтогоМенеджера
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПриоритетыМенеджеров.СрезПоследних(&МоментВремени, ) КАК ПриоритетыМенеджеровСрезПоследних
| ПО ВТПриоритетЭтогоМенеджера.Приоритет > ПриоритетыМенеджеровСрезПоследних.Приоритет
|
|ИНДЕКСИРОВАТЬ ПО
| Менеджер
|;
|
Ранее мы сделали временную таблицу «ВТсписокНоменклатуры». Давайте сделаем на основе этой таблицы другую временную таблицу (назовём её «ВТВсёПоНоменклатуре»), в которой соберём информацию, связанную с номенклатурой
- количество, зарезервированное данным менеджером
- количество, которое никем не зарезервировано (а кстати, как его вычислить?)
- количество на складе
- сумму на складе
Пусть это будет третий запрос пакета.
|ВЫБРАТЬ
| ВТСписокНоменклатуры.Номенклатура КАК Номенклатура,
| ВТСписокНоменклатуры.Количество КАК КоличествоПоДокументу,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) - ЕСТЬNULL(РезервыНоменклатурыОстатки.КоличествоОстаток, 0) КАК НезарезервированноеКоличество,
| ЕСТЬNULL(РезервыНоменклатурыОстатки1.КоличествоОстаток, 0) КАК КоличествоЗарезервированноеЭтимМенеджером,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки1.КоличествоОстаток, 0) КАК КоличествоНаСкладе,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки1.СуммаОстаток, 0) КАК СуммаНаСкладе
|ПОМЕСТИТЬ ВТВсёПоНоменклатуре
|ИЗ
| ВТСписокНоменклатуры КАК ВТСписокНоменклатуры
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
| &МоментВремени,
| Номенклатура В
| (ВЫБРАТЬ
| ВТСписокНоменклатуры.Номенклатура
| ИЗ
| ВТСписокНоменклатуры)) КАК ОстаткиНоменклатурыОстатки
| ПО ВТСписокНоменклатуры.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РезервыНоменклатуры.Остатки(
| &МоментВремени,
| Номенклатура В
| (ВЫБРАТЬ
| ВТСписокНоменклатуры.Номенклатура
| ИЗ
| ВТСписокНоменклатуры)) КАК РезервыНоменклатурыОстатки
| ПО ВТСписокНоменклатуры.Номенклатура = РезервыНоменклатурыОстатки.Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РезервыНоменклатуры.Остатки(
| &МоментВремени,
| Номенклатура В
| (ВЫБРАТЬ
| ВТСписокНоменклатуры.Номенклатура
| ИЗ
| ВТСписокНоменклатуры)
| И Менеджер = &Менеджер) КАК РезервыНоменклатурыОстатки1
| ПО ВТСписокНоменклатуры.Номенклатура = РезервыНоменклатурыОстатки1.Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
| &МоментВремени,
| Номенклатура В
| (ВЫБРАТЬ
| ВТСписокНоменклатуры.Номенклатура
| ИЗ
| ВТСписокНоменклатуры)
| И Склад = &Склад) КАК ОстаткиНоменклатурыОстатки1
| ПО ВТСписокНоменклатуры.Номенклатура = ОстаткиНоменклатурыОстатки1.Номенклатура
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
Количество, которое никем не зарезервировано мы вычислили как разницу между полным остатком и полным резервом. Для того, чтобы их получить, мы прописали в параметрах виртуальных таблиц только момент времени и список номенклатуры. Чтобы получить резерв данного менеджера, пришлось обратиться к виртуальной таблице остатков регистра «Резервы номенклатуры» ещё раз, но уже добавив параметр «Менеджер».
Предлагаю также добавить в имеющуюся временную таблицу «ВТМенеджерыНизкогоПриоритета» данные по резервам по каждому менеджеру из этой таблицы каждой номенклатуры из документа, и создать на её основе другую временную таблицу «ВТРезервыМенеджеров».
|ВЫБРАТЬ
| ВТМенеджерыНизкогоПриоритета.Менеджер КАК Менеджер,
| ВТМенеджерыНизкогоПриоритета.Приоритет КАК Приоритет,
| РезервыНоменклатурыОстатки.Номенклатура КАК Номенклатура,
| ЕСТЬNULL(РезервыНоменклатурыОстатки.КоличествоОстаток, 0) КАК РезервМенеджераПоНоменклатуре
|ПОМЕСТИТЬ ВТРезервыМенеджеров
|ИЗ
| ВТМенеджерыНизкогоПриоритета КАК ВТМенеджерыНизкогоПриоритета
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.РезервыНоменклатуры.Остатки(
| &МоментВремени,
| Менеджер В
| (ВЫБРАТЬ
| ВТМенеджерыНизкогоПриоритета.Менеджер КАК Менеджер
| ИЗ
| ВТМенеджерыНизкогоПриоритета КАК ВТМенеджерыНизкогоПриоритета)
| И Номенклатура В
| (ВЫБРАТЬ
| ВТВсёПоНоменклатуре.Номенклатура КАК Номенклатура
| ИЗ
| ВТВсёПоНоменклатуре КАК ВТВсёПоНоменклатуре)) КАК РезервыНоменклатурыОстатки
| ПО ВТМенеджерыНизкогоПриоритета.Менеджер = РезервыНоменклатурыОстатки.Менеджер
|;
Теперь мы можем объединить временные таблицы «ВТВсёПоНоменклатуре» и «ВТРезервыМенеджеров» в финальном запросе пакета, который выберет всё, что нам надо. Какое соединение используем?
Используем левое соединение по номенклатуре, слева будет «ВТВсёПоНоменклатуре», поскольку нам надо будет обходить всю номенклатуру. В таблице же «ВТРезервыМенеджеров» может быть несколько значений для той или иной номенклатуры (то есть несколько менеджеров, имеющих по ней резервы), а может не быть ни одного. Из этого следует важный вывод: придётся прописать в запросе итоги, чтобы в итоговой выборке обходить номенклатуру, в в выборке детальных записей обходить менеджеров.
Как пропишем итоги? Как произведём упорядочивание?
|ВЫБРАТЬ
| ВТВсёПоНоменклатуре.Номенклатура КАК Номенклатура,
| ВТВсёПоНоменклатуре.КоличествоПоДокументу КАК КоличествоПоДокументу,
| ВТВсёПоНоменклатуре.НезарезервированноеКоличество КАК НезарезервированноеКоличество,
| ВТВсёПоНоменклатуре.КоличествоЗарезервированноеЭтимМенеджером КАК КоличествоЗарезервированноеЭтимМенеджером,
| ВТВсёПоНоменклатуре.КоличествоНаСкладе КАК КоличествоНаСкладе,
| ВТВсёПоНоменклатуре.СуммаНаСкладе КАК СуммаНаСкладе,
| ВТРезервыМенеджеров.Менеджер КАК Менеджер,
| ВТРезервыМенеджеров.Приоритет КАК Приоритет,
| ЕСТЬNULL(ВТРезервыМенеджеров.РезервМенеджераПоНоменклатуре, 0) КАК РезервМенеджераПоНоменклатуре
|ИЗ
| ВТВсёПоНоменклатуре КАК ВТВсёПоНоменклатуре
| ЛЕВОЕ СОЕДИНЕНИЕ ВТРезервыМенеджеров КАК ВТРезервыМенеджеров
| ПО ВТВсёПоНоменклатуре.Номенклатура = ВТРезервыМенеджеров.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура,
| Приоритет
|ИТОГИ
| МАКСИМУМ(КоличествоПоДокументу),
| МАКСИМУМ(НезарезервированноеКоличество),
| МАКСИМУМ(КоличествоЗарезервированноеЭтимМенеджером),
| МАКСИМУМ(КоличествоНаСкладе),
| МАКСИМУМ(СуммаНаСкладе)
|ПО
| Номенклатура";
В итогах я прописал агрегатную функцию МАКСИМУМ, хотя думаю, подошли бы и функции МИНИМУМ, СРЕДНЕЕ. Для нас важно, чтобы в итогах не изменились те значения, которые были в основном теле запроса. Упорядочивание я произвёл сначала по номенклатуре, хотя само по себе это упорядочивание для нас не важно. Важно упорядочивание по приоритету, так как сперва должны списываться резервы с низшим приоритетом.
РезервМенеджераПоНоменклатуре я обернул в функцию ЕСТЬNULL, так как резервов менеджеров может не быть, и тогда при обходе выборки будет некорректно работать сравнение.
Всё, с запросом закончили. Пора делать выборку. Как спишем остатки номенклатуры? Какую проверку проведём?
РезультатЗапроса = Запрос.Выполнить();
ВыборкаИтог = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаИтог.Следующий() Цикл
Если
ВыборкаИтог.КоличествоПоДокументу > ВыборкаИтог.КоличествонаСкладе
Тогда
Отказ = ИСТИНА;
Нехватка = ВыборкаИтог.КоличествоПоДокументу - ВыборкаИтог.КоличествонаСкладе;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не хватает на складе номенклатуры "+ ВыборкаИтог.Номенклатура + "в количестве " + Нехватка;
Сообщение.Сообщить();
КонецЕсли;
Если Отказ Тогда
Продолжить;
КонецЕсли;
Движение = Движения.ОстаткиНоменклатуры.ДобавитьРасход();
Движение.Период = Дата;
Движение.Склад = Склад;
Движение.Номенклатура = ВыборкаИтог.Номенклатура;
Движение.Количество = ВыборкаИтог.КоличествоПоДокументу;
Если
ВыборкаИтог.КоличествоПоДокументу = ВыборкаИтог.КоличествоНаСкладе
Тогда
Движение.Сумма = ВыборкаИтог.СуммаНаСкладе
Иначе
Движение.Сумма = ВыборкаИтог.СуммаНаСкладе*ВыборкаИтог.КоличествоПоДокументу/ВыборкаИтог.КоличествоНаСкладе;
КонецЕсли;
Списание остатков произвёл «классическим» способом, с проверкой остатков на складе, с полным списанием суммы, если количество в документе равно количеству остатка.
Теперь пора списывать резервы. Что следует сделать в первую очередь? Как учесть тот факт, что любого вида резервов, которые мы будем последовательно списывать, может и не быть?
Думаю, что прежде всего надо создать переменную, в которую записать количество, которое надо списать из резервов, и значение которой по мере списания резервов будет уменьшаться. А перед списанием следующего вида резервов будем выполнять проверки, что этот вид резерва имеется, и что количество, которое надо списать из резервов больше нуля.
НадоСписатьРезервов = ВыборкаИтог.КоличествоПоДокументу;
Если ВыборкаИтог.КоличествоЗарезервированноеЭтимМенеджером>0 Тогда
Движение = Движения.РезервыНоменклатуры.ДобавитьРасход();
Движение.Период = Дата;
Движение.Менеджер = Менеджер;
Движение.Номенклатура = ВыборкаИтог.Номенклатура;
Движение.Количество = Мин(ВыборкаИтог.КоличествоЗарезервированноеЭтимМенеджером, НадоСписатьРезервов);
НадоСписатьРезервов = НадоСписатьРезервов- Движение.Количество;
КонецЕсли;
Если НадоСписатьРезервов > 0
И ВыборкаИтог.НеЗарезервированноеКоличество>0
Тогда
Движение = Движения.РезервыНоменклатуры.ДобавитьРасход();
Движение.Период = Дата;
Движение.Менеджер = Менеджер;
Движение.Номенклатура = ВыборкаИтог.Номенклатура;
Движение.Количество = Мин(ВыборкаИтог.НеЗарезервированноеКоличество, НадоСписатьРезервов);
НадоСписатьРезервов = НадоСписатьРезервов - Движение.Количество;
КонецЕсли;
Если НадоСписатьРезервов > 0 Тогда
ВыборкаДетальныезаписи = ВыборкаИтог.Выбрать();
Пока ВыборкаДетальныезаписи.Следующий() И НадоСписатьРезервов > 0 Цикл
Движение = Движения.РезервыНоменклатуры.ДобавитьРасход();
Движение.Период = Дата;
Движение.Менеджер = ВыборкаДетальныезаписи.Менеджер;
Движение.Номенклатура = ВыборкаДетальныеЗаписи.Номенклатура;
Движение.Количество = Мин(ВыборкаДетальныеЗаписи.РезервМенеджераПоНоменклатуре, НадоСписатьРезервов);
НадоСписатьРезервов = НадоСписатьРезервов - Движение.Количество;
КонецЦикла;
КонецЕсли;
Однако после прохождения цикла по детальным записям может оказаться, что переменная «НадоСписатьРезервов» всё равно больше нуля. Что будем делать тогда?
Если переменная «НадоСписатьРезервов» после всех списаний больше нуля, то резервов не хватает, и придётся отказаться от проведения. Также надо сообщить об этом пользователю. Где напишем отказ?
Отказ и сообщение напишем после окончания цикла по детальным записям.
НадоСписатьРезервов = НадоСписатьРезервов - Движение.Количество;
КонецЦикла;
КонецЕсли;
Если НадоСписатьРезервов > 0 Тогда
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не хватает резерва "+ ВыборкаИтог.Номенклатура+" в количестве "+ НадоСписатьРезервов;
Сообщение.Сообщить();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Чтобы увидеть полный код обработки проведения документа «Расходная накладная», кликните здесь.
Конфигурацию решения задачи можно скачать здесь.
Об ошибках сообщайте по электронной почте obuchmat@mail.ru
Вот ещё одна задача
Наверх
На домашнюю страницу