1C
January 30

Задача 4

Условие данной задачи является своеобразной комбинацией условий задач 3 и 1. Так, снова на сцене появляется учетная политика, которая действует год, а потом может смениться. Как и прежде, политика влияет на метод списания себестоимости (ФИФО/ЛИФО), а партия указывается в табличной части документа. Однако ничего не говорится про услуги и товары, и про то, что они могут указываться в одной табличной части.

Основное требование - списывать товар из указанной в табличной части партии, а в случае если его по выбранной партии не хватает (или нет), то необходимо организовать списание в соответствии с текущей учетной политикой. Реализация этого требования и будет основной сложностью данной задачи, потому что со всем остальным нам уже приходилось сталкиваться.

Кроме того, необходимо построить пару отчетов, которые по своей сложности ничуть не превосходят те, с которыми мы имели дело в задачах 1 - 3. Поэтому эти отчеты будут оставлены нами без внимания, а вместо них предлагаем детально обсудить нюансы, вытекающие из основного требования.


Суть проблемы заключается в так называемых «Дублях строк». Под этим термином обычно подразумевается, что пользователь дважды (или более) совершает одно и то же действие, например, списывает один и тот же товар. Возможно, он делает это по вполне объективным причинам. Например, одна часть товара продается по одной цене, а другая часть по другой цене. Но наличие повторов номенклатуры может существенно повлиять на логику программы.

Например, пользователь дважды списывает по три единицы товара - итого шесть, а на складе остатки по двум партиям: в одной пять единиц, в другой две – итого семь. Если в запросе мы будем подбирать остатки к каждой записи о списании товара, то, поскольку записей две, мы подберём одни и те же остатки дважды. И каждый раз будем преспокойненько списывать по три единицы с первой партии, думая, что у нас ещё даже остается две единицы в запасе. То есть из-за дубля строк у нас происходит неявное задвоение остатков в алгоритме: четырнадцать вместо семи.

Чтобы не допускать такой ошибки, от «Дублей строк», по мере возможности, сразу избавляются. В предыдущих задачах вы могли видеть, что мы всегда группируем записи расходной накладной по номенклатуре. Таким образом, в нашем примере, вместо двух записей по три единицы мы получаем одну запись на шесть единиц. И естественно к одной записи мы и остаток подбираем один раз, в нашем примере семь единиц. Теперь не будет ни какой ошибки. Сразу видно: "Шесть больше пяти - остатка не хватает", - нужно переходить к следующей партии.

В предыдущих задачах возможность сразу избавиться от «Дублей строк» у нас была, поскольку нигде в отчётах не требовалось знать какая часть товаров, по какой конкретно цене, была реализована: отчет Продажи составляется в целом по итогам указанного периода, а поскольку дубли строк встречаются в одном документе, они проводятся датой документа и попадают в один период.

В этой задаче ситуация существенно отличная: «Дубли строк» могут отличаться не только ценой, но и указанной в них партией. Если в разных строках для одной и той же номенклатуры указана одна и та же партия, мы можем их сгруппировать. Но если в разных строках для одной и той же номенклатуры указаны разные партии, в этих партиях может не хватить товара и нам придётся подбирать остатки из других партий. Следовательно, у нас снова произойдет неявное задвоение остатков в алгоритме.

В задаче 2 партия указывалась в шапке документа и, следовательно, была одной и той же для любой строки табличной части - там такого произойти не могло. В задаче 3 мы не подбирали остатки из других партий и, следовательно, остатки по другим партиям не могли задваиваться. Как же нам поступить здесь?

В принципе ответ очевиден: чтобы остатки не задваивались, мы должны подбирать их один лишь раз. А вот списывать с указанных партий мы должны персонально. То есть мы должны списать с указанных партий ровно столько, сколько указано и, если в них что-то останется, то тогда уже подбирать эти остатки для списания по методу указанному в учетной политике. Поскольку остатки подбираются один раз, нам нужно будет сгруппировать все записи по номенклатуре, потом со всех указанных партий списать (если хватает) указанное количество товара. Если в каких-то партиях товара не хватает, суммировать недостачу, и списывать её с других партий, в которых что-то ещё осталось.

То есть с каждой партии товар может списываться дважды: первый раз в количестве не больше указанного, второй раз в порядке очерёдности согласно Учётной политике в пределах того, чего не хватило в указанных партиях. Значит, мы должны все остатки по партиям разбить на две части: 1) в пределах указанного, 2) сверх указанного. Первые части запрос должен располагать впереди в любом порядке, вторые - следом в порядке определённом Учётной политикой.

Ну что ж, будем считать, что аналитические процедуры мы завершили успешно, переходим к реализации!


Итак, особенность задачи в том лишь и состоит, что нам нужно исхитриться в одном пакетном запросе подготовить данные для алгоритма так, чтобы не пришлось менять его логику, отточенную в предыдущих задачах. Давайте приступим:

В первом запросе пакета мы выберем данные из Расходной накладной, сгруппируем их по Номенклатуре и Партии, и поместим во временную таблицу РасходнаяНакладная. Во втором запросе пакета мы выберем данные из Расходной накладной, сгруппируем их по Номенклатуре, и поместим во временную таблицу РасходнаяНакладнаяБезПартий.

Поскольку нам придётся много работать с остатками, в третьем запросе пакета мы выберем их и поместим во временную таблицу ОстаткиНоменклатуры. Как водится, чтобы не читать лишнего и не перегружать компьютер бесполезной информацией, выбираем остатки только по той Номенклатуре, которая упоминается в Расходной накладной. И не забываем указать на какой момент времени нас интересуют остатки.

Далее, определяем, сколько можно списать по указанной партии: Если остаток по партии больше чем требуется, списываем, сколько нужно, а нет, так списываем, сколько есть. Помещаем эту информацию во временную таблицу ДоступноДляСписания, с ней нам ещё придётся поработать.

Потом мы объединяем данные запроса о том, сколько мы можем списать по партиям указанным в документе, с тем, что после этого останется. И тут же определяем Порядок сортировки. Для доступных для списания данных используем 0, они будут первыми. А для сортировки того, что остается, используем 1. Помещаем результат объединения во временную таблицу ПартииДляСписания.

В последнем запросе мы соединяем данные расходной накладной, с подготовленными данными об остатках. Сортируем по ранее определённому полю Порядку, потом по моменту времени Партии в порядке, определяемом учетной политикой. Создаем итоги по Номенклатуре. Всё, пакет запросов готов!

И вот, что у нас должно было получиться:

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

Алгоритм проведения можем взять хотя бы из задачи 1 Только нужно будет устранить из него всё, что касается услуг.