gifts2017

Автоматизация заполнения документа "Возврат товаров от покупателя" в УТ 10.3

Опубликовал Александр Сидоров (splitter01) в раздел Программирование - Практика программирования

Изменение логики заполнения документа "ВозвратТовароОтПокупателя" на основании документа "РеализацияТоваровУслуг".

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

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

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

		Если ОтКомиссионера Тогда
			
			КолвоПереданных = ?(Выборка.КоличествоОстатокПереданных = NULL, 0, Выборка.КоличествоОстатокПереданных);
			Если КолвоПереданных>0 Тогда
				Сумма = МодульВалютногоУчета.ПересчитатьИзВалютыВВалюту(Выборка.СтоимостьОтданнаяВалОстаток * СтрокаТабличнойЧасти.Количество / КолвоПереданных, 
				                   ДоговорКонтрагента.ВалютаВзаиморасчетов, ВалютаДокумента, КурсВзаиморасчетов, мКурсДокумента,
				                   КратностьВзаиморасчетов, мКратностьДокумента);

				СтрокаТабличнойЧасти.Сумма = Ценообразование.ПересчитатьЦенуПриИзмененииФлаговНалогов(Сумма, 
				                                              Перечисления.СпособыЗаполненияЦен.ПоЦенамНоменклатуры, 
				                                              Истина, УчитыватьНДС, СуммаВключаетНДС, 
				                                              УчетНДС.ПолучитьСтавкуНДС(СтрокаТабличнойЧасти.СтавкаНДС));
				ОбработкаТабличныхЧастей.ПриИзмененииСуммыТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект, ТекПользователь,,
				                                              ЕстьРеквизитПроцентСкидкиНаценки,ПересчитыватьСкидку,
				                                              ЕстьАвтоматическиеСкидки,"Товары");
			КонецЕсли;
		Иначе

			СтрокаТабличнойЧасти.Цена = Выборка.Цена;

			// Пересчитаем цену в валюту документа (может отличаться от валюты основания).
			Цена = МодульВалютногоУчета.ПересчитатьИзВалютыВВалюту(СтрокаТабличнойЧасти.Цена, Выборка.ВалютаДокумента, ВалютаДокумента, 
			                                                   Выборка.КурсДокумента, мКурсДокумента,
			                                                   Выборка.КратностьДокумента, мКратностьДокумента);

			СтрокаТабличнойЧасти.Цена = Ценообразование.ПересчитатьЦенуПриИзмененииФлаговНалогов(Цена,
										Перечисления.СпособыЗаполненияЦен.ПоЦенамНоменклатуры,
										Выборка.УчитыватьНДС И Выборка.СуммаВключаетНДС,
										УчитыватьНДС, СуммаВключаетНДС,
										УчетНДС.ПолучитьСтавкуНДС(СтрокаТабличнойЧасти.СтавкаНДС));

			ОбработкаТабличныхЧастей.РассчитатьСуммуТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);

		КонецЕсли;

		ОбработкаТабличныхЧастей.РассчитатьКоличествоМестТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);
		ОбработкаТабличныхЧастей.РассчитатьСуммуНДСТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);

		СтрокаТабличнойЧасти.ХарактеристикаНоменклатуры = Выборка.ХарактеристикаНоменклатуры;

		ОбработкаТабличныхЧастей.ЗаполнитьСкладИОрдерТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);
		
		Если ОснованиеРеализацияТоваров Тогда

			СтрокаТабличнойЧасти.СерияНоменклатуры      = Выборка.СерияНоменклатуры;
			СтрокаТабличнойЧасти.ДокументПартии         = ДокументОснование;
			СтрокаТабличнойЧасти.ЗаказПокупателя        = Выборка.ЗаказПокупателя;

		КонецЕсли;

		Если ЕстьСоставНабора
		   И Выборка.Комплект Тогда
			СтрокаТабличнойЧасти.КлючСтроки = СтрокаТабличнойЧасти.НомерСтроки;
		КонецЕсли;
	КонецЕсли;
	КонецЦикла;
...

После чего логика заполнения документа ВозвратТоваровОтПокупателя изменилась в лучшую сторону. А именно:

На основании реализации может быть введено несколько возвратов. Логика доработки предусматривает этот вариант и добавляет только те позиции, которые не были возвращены ранее.

Далее. После формирования документа возврата продавец может изменить состав документа. Чтобы предотвратить подобные действия в Процедуре ПередЗаписью были внесены следующие изменения:

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

	КонецЕсли;
...

После отработки этого кода мы гарантированно получаем правильно оформленный документ возврата товаров от покупателя.

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Дмитрий Бухалов (Re:аниматор) 01.09.16 07:19
Ни чего не запрещает продавцу добавить товар через подбор и указать любое кол-во.
У себя решил эту проблему через контроль возвращаемого товара при проведении документа по регистру накопления "Продажи".
Как известно при проведении возврата кол-во продажи уменьшается.

часть кода

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

...Показать Скрыть
splitter01; +1 Ответить 1
2. Александр Сидоров (splitter01) 01.09.16 15:18
(1) Re:аниматор, Верное замечание. На данный момент проблема уже устранена. Но в Вашем предложении не учитываются ранее возвращенные товары. В ближайшее время исправлю публикацию. И еще: в Вашем варианте рассмотрите ситуацию, когда было продано 2 холодильника и 1 пылесос, а в документе возврата указать 2 пылесоса и 1 холодильник.
3. Дмитрий Бухалов (Re:аниматор) 06.09.16 07:19
(2)
Но в Вашем предложении не учитываются ранее возвращенные товары.


Вообще то учитываются ранее возвращенные товары. Возврат делает записи в регистр "Продажи" со знаком "-", СУММА(ПродажиОбороты.КоличествоОборот) это учтется в суммировании


И еще: в Вашем варианте рассмотрите ситуацию, когда было продано 2 холодильника и 1 пылесос, а в документе возврата указать 2 пылесоса и 1 холодильник.


Разрешит сделать возврат только на 1 холоднильник, т.к. пылесоса продано 1

А для чего рассмотреть данную ситуацию?
4. Вадим Никонов (V.Nikonov) 09.09.16 19:49
В некоторых конторах, осуществляющих регулярные продажи однотипных товаров, фактически покупатель возвращает товар из неизвестной поставки. Однако для взаиморасчетов действует правило - цена возврата по последней отгрузке (не текущие продажные цены и не Закупочные).
При первичном заполнении документа возврата обычно используется Подбор по справочнику, при этом не заполняются данные по Себестоимости...
Добавил обработку Табличных частей, которая анализировала продажи (с учетом проведенных возвратов) за период (например, 3 мес.). При необходимости разбивала строку на несколько строк под каждую отгрузку (Учет по Заказам и Заказ=Реализация). При необходимости снимались ограничения по Договорам и добавлялся период анализа. Найденные отгрузки использовались как ПартиеОбразующие документы, использовались для определения Цены возвращаемого товара и становилось видны попытки вернуть "НеОтгруженный товар". Возможно отгружались двойники номенклатуры...
Для строк под которые не нашлось отгрузок, подставлялась Плановая себестоимость (Закупочная цена).
Обработка позволяла при необходимости повторить распределение по отгрузкам,предварительно свернув строки по Номенклатуре (очистив документы партий). Дополнительно Обработка печатала отчет по сформированному распределению (практиковалась корректировка "свежих Реализаций").
Кстати, добавил проверку перед записью: наличие нужного товара в заявленных документах партий...
5. white mount (white-mount) 10.09.16 14:44
Возвратом товаров обычно занимается не продавец, а менеджер, имеющий выше полномочия. Это всегда чп. Либо дефект, либо пересортица - продано не то. Для маленькой фирмы предложенный механизм подходит, для крупной нет.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа