Вместо введения
В данной публикации мы расширим типовой функционал стандартной конфигурации "Управление нашей фирмы", который касается документов "Дополнительные расходы" и "Перемещение запасов". С помощью расширения, доработаем документ "Дополнительные расходы", что позволит использовать в нем документы "Перемещение запасов".
Опишем процесс расчета себестоимости для документа "Перемещение запасов". Проверим изменение себестоимости товара с помощью стандартного отчета себестоимость товаров в "УНФ".
При создании расширения я использовал конфигурацию "Управление нашей фирмы 1.6 (1.6.18.88)" и Платформу 8.3.15.1700.
Цель задачи. Основной подход
Цель задачи — это учет дополнительных расходов при использовании документа "Перемещение запасов". Мы делаем перемещение запасов — несем дополнительные расходы (например, транспортные). Поэтому, на складе, куда будет перемещена продукция, ее себестоимость должна быть выше, чем на складе откуда отправлена.
Привяжем дополнительные расходы к документы "Перемещение товаров". Типовой функционал УНФ такую привязку не предусматривает.
Основной подход — это доработка документа "Дополнительные расходы" в два шага.
Первый шаг.
Нам все-таки придется поменять несколько типов реквизитов в документе "Дополнительные расходы" (расширить внутри конфигурации, поскольку механизм расширений не позволяет менять тип реквизита в самом расширении, можно только добавить новый реквизит).
Второй шаг.
Создадим расширение, перенесем необходимые объекты в расширение и доработаем существующие процедуры.
Шаг №1. Доработка типов реквизитов в основной конфигурации
Как я написал ранее, механизм расширений не позволяет изменять тип реквизитов. А это нам понадобится. Делаем следующее: В документе "Дополнительные расходы" в табличной части запасы, добавляем в реквизит "ДокументПоступления" дополнительный тип "Документ — ПеремещениеЗапасов" (см рис.1)
Рис.1 Изменение типа реквизита "ДокументПоступления" в документе "Дополнительные расходы".
Так же, нам потребуется изменить типы на форме "ФормаПодбораПоДокументам". Изменения такие — ТаблицаЗначений "ОтобранныеДокументы" — реквизит "ДокументПоступления", добавляем тип "ПеремещениеЗапасов" (см. рис.2)
Рис.2. Изменение типа реквизита на форме подбора по документам — ОтобранныеДокументы — ДокументПоступления.
и здесь — ТаблицаЗначений — "ОтобранныеЗапасы" — так же реквизит "ДокументПоступления" — добавляем тип "ПеремещениеЗапасов" (см. рис.3):
Рис.3. Изменение типа реквизита на форме подбора по документам — ОтобранныеЗапасы — ДокументПоступления.
На этом изменения основной конфигурации больше не потребуется, сохраняем конфигурацию.
Шаг №2. Создание и запуск расширения
Теперь, "накидаем" объекты, которые нам понадобятся, в расширение конфигурации. Делается это достаточно просто — правой кнопкой "Добавить в расширение" (см. рис.4).
Рис.4. Добавление в расширение
Что мы добавляем:
Документ "ДополнительныеРасходы", его реквизиты и табличную часть "Запасы". Так же "перекинем" форму документа "ФормаПодбораПоДокументов". Все связанные объекты "перенесутся сами" по типам реквизитов. Исключения составят реквизиты, у которых несколько типов — как раз те, куда мы добавляли дополнительный тип.
Переназначим на этих реквизитах типы соответствующие типа реквизитов основной конфигурации.
В итоге переноса должно получиться примерно так (см. рис.5):
Рис.5. Пример состава расширения.
Переходим к основным доработкам кода. Нам потребуется доработать несколько процедур в модуле формы "ФормаПодбораПоДокументам". Мы перенесли эту форму и дорабатывать будем в перенесенной конечно.
МножественныйПодборДокументов:
&НаКлиенте
&Вместо("МножественныйПодборДокументов")
Процедура Расш1_МножественныйПодборДокументов(Команда)
СписокТиповДокументов = Новый СписокЗначений();
СписокТиповДокументов.Добавить("АвансовыйОтчет", "Авансовый отчет");
СписокТиповДокументов.Добавить("ПриходнаяНакладная", "Приходная накладная");
СписокТиповДокументов.Добавить("ПеремещениеЗапасов", "Перемещение запасов");
Оповещение = Новый ОписаниеОповещения("МножественныйПодборДокументовЗавершение", ЭтаФорма);
СписокТиповДокументов.ПоказатьВыборЭлемента(Оповещение, "Выберите тип документа для добавления");
Отказ = Истина;
КонецПроцедуры
СписокДокументовПередаНачаломДобавления:
&НаКлиенте
&Вместо("СписокДокументовПередНачаломДобавления")
Процедура Расш1_СписокДокументовПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, Группа)
СписокТиповДокументов = Новый СписокЗначений();
СписокТиповДокументов.Добавить("АвансовыйОтчет", "Авансовый отчет");
СписокТиповДокументов.Добавить("ПриходнаяНакладная", "Приходная накладная");
СписокТиповДокументов.Добавить("ПеремещениеЗапасов", "Перемещение запасов");
Оповещение = Новый ОписаниеОповещения("СписокДокументовПередНачаломДобавленияЗавершение",ЭтаФорма);
СписокТиповДокументов.ПоказатьВыборЭлемента(Оповещение,"Выберите тип документа для добавления");
Отказ = Истина;
КонецПроцедуры
ОтобранныеЗапасыВыбор:
&НаКлиенте
&Вместо("ОтобранныеЗапасыВыбор")
Процедура Расш1_ОтобранныеЗапасыВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ТекущаяСтрокаТабличнойЧасти = Элементы.ОтобранныеЗапасы.ТекущиеДанные;
Если НЕ ТекущаяСтрокаТабличнойЧасти = Неопределено Тогда
Если ТипЗнч(ТекущаяСтрокаТабличнойЧасти.ДокументПоступления) = Тип("ДокументСсылка.ПриходнаяНакладная") Тогда
ОткрытьФорму("Документ.ПриходнаяНакладная.ФормаОбъекта", Новый Структура("Ключ", ТекущаяСтрокаТабличнойЧасти.ДокументПоступления));
ИначеЕсли ТипЗнч(ТекущаяСтрокаТабличнойЧасти.ДокументПоступления) = Тип("ДокументСсылка.АвансовыйОтчет") Тогда
ОткрытьФорму("Документ.АвансовыйОтчет.ФормаОбъекта", Новый Структура("Ключ", ТекущаяСтрокаТабличнойЧасти.ДокументПоступления));
ИначеЕсли ТипЗнч(ТекущаяСтрокаТабличнойЧасти.ДокументПоступления) = Тип("ДокументСсылка.ПеремещениеЗапасов") Тогда
ОткрытьФорму("Документ.ПеремещениеЗапасов.ФормаОбъекта", Новый Структура("Ключ", ТекущаяСтрокаТабличнойЧасти.ДокументПоступления));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Так же, переносим серверную процедуру ЗаполнитьСписокНоменклатуры и дорабатываем в ней "запрос" — дополняем документом "ПеремещениеЗапасов", а так же проводим расчет себестоимости в этом документе. Не ставил задачу доработать код процедуры максимально красиво, показываю, что данная серверная процедура будет выполняться и замещать типовую.
Если кто-то хочет использовать прилагаемое расширения для своих задач — пересмотрите код именно вот это процедуры в расширении.
Код процедуры вот такой:
&НаСервере
&Вместо("ЗаполнитьСписокНоменклатуры")
Процедура Расш1_ЗаполнитьСписокНоменклатуры(МассивДокументов)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ИСТИНА КАК Отметка,
| АвансовыйОтчетЗапасы.Ссылка КАК ДокументПоступления,
| АвансовыйОтчетЗапасы.Номенклатура КАК Номенклатура,
| АвансовыйОтчетЗапасы.Характеристика КАК Характеристика,
| АвансовыйОтчетЗапасы.Партия КАК Партия,
| АвансовыйОтчетЗапасы.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
| АвансовыйОтчетЗапасы.Количество КАК Количество,
| АвансовыйОтчетЗапасы.Цена КАК Цена,
| АвансовыйОтчетЗапасы.Сумма КАК Сумма,
| АвансовыйОтчетЗапасы.СтавкаНДС КАК СтавкаНДС,
| АвансовыйОтчетЗапасы.СуммаНДС КАК СуммаНДС,
| АвансовыйОтчетЗапасы.Всего КАК Всего,
| АвансовыйОтчетЗапасы.ЗаказПокупателя КАК ЗаказПокупателя,
| 1 КАК Коэффициент,
| АвансовыйОтчетЗапасы.Ссылка.НалогообложениеНДС КАК НалогообложениеНДС,
| АвансовыйОтчетЗапасы.Ссылка.СуммаВключаетНДС КАК СуммаВключаетНДС,
| NULL КАК Поле1,
| NULL КАК НоменклатураВидСтавкиНДС,
| NULL КАК Сумма1,
| NULL КАК Поле2,
| NULL КАК Поле3
|ИЗ
| Документ.АвансовыйОтчет.Запасы КАК АвансовыйОтчетЗапасы
|ГДЕ
| АвансовыйОтчетЗапасы.Ссылка В(&МассивДокументов)
| И &УсловиеОтбораНоменклатурыДляАвансовогоОтчета
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ИСТИНА,
| ПриходнаяНакладнаяЗапасы.Ссылка,
| ПриходнаяНакладнаяЗапасы.Номенклатура,
| ПриходнаяНакладнаяЗапасы.Характеристика,
| ПриходнаяНакладнаяЗапасы.Партия,
| ПриходнаяНакладнаяЗапасы.ЕдиницаИзмерения,
| ПриходнаяНакладнаяЗапасы.Количество,
| ПриходнаяНакладнаяЗапасы.Цена,
| ПриходнаяНакладнаяЗапасы.Сумма,
| ПриходнаяНакладнаяЗапасы.СтавкаНДС,
| ПриходнаяНакладнаяЗапасы.СуммаНДС,
| ПриходнаяНакладнаяЗапасы.Всего,
| ВЫБОР
| КОГДА ПриходнаяНакладнаяЗапасы.Заказ ССЫЛКА Документ.ЗаказПоставщику
| ТОГДА ПриходнаяНакладнаяЗапасы.Заказ.ЗаказПокупателя
| ИНАЧЕ ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
| КОНЕЦ,
| 1,
| ПриходнаяНакладнаяЗапасы.Ссылка.НалогообложениеНДС,
| ПриходнаяНакладнаяЗапасы.Ссылка.СуммаВключаетНДС,
| NULL,
| NULL,
| NULL,
| NULL,
| NULL
|ИЗ
| Документ.ПриходнаяНакладная.Запасы КАК ПриходнаяНакладнаяЗапасы
|ГДЕ
| ПриходнаяНакладнаяЗапасы.Ссылка В(&МассивДокументов)
| И &УсловиеОтбораНоменклатурыДляПриходнойНакладной
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ИСТИНА,
| ПеремещениеЗапасовЗапасы.Ссылка,
| ПеремещениеЗапасовЗапасы.Номенклатура,
| ПеремещениеЗапасовЗапасы.Характеристика,
| ПеремещениеЗапасовЗапасы.Партия,
| ПеремещениеЗапасовЗапасы.ЕдиницаИзмерения,
| ПеремещениеЗапасовЗапасы.Количество,
| ПеремещениеЗапасовЗапасы.Сумма / ПеремещениеЗапасовЗапасы.Количество,
| ПеремещениеЗапасовЗапасы.Сумма,
| &СтавкаНДССправочник,
| NULL,
| ПеремещениеЗапасовЗапасы.Сумма,
| ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка),
| 1,
| ИСТИНА,
| ИСТИНА,
| NULL,
| NULL,
| NULL,
| NULL,
| NULL
|ИЗ
| Документ.ПеремещениеЗапасов.Запасы КАК ПеремещениеЗапасовЗапасы
|ГДЕ
| ПеремещениеЗапасовЗапасы.Ссылка В(&МассивДокументов)
| И &УсловиеОтбораНоменклатурыДляПеремещениеЗапасов";
Запрос.УстановитьПараметр("МассивДокументов", МассивДокументов);
Запрос.УстановитьПараметр("СтавкаНДССправочник", Справочники.СтавкиНДС.НайтиПоНаименованию("Без НДС"));
Если ЗаполнятьТолькоПоУказаннойНоменклатуре Тогда
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбораНоменклатурыДляАвансовогоОтчета", "АвансовыйОтчетЗапасы.Номенклатура В(&МассивНоменклатуры)");
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбораНоменклатурыДляПриходнойНакладной", "ПриходнаяНакладнаяЗапасы.Номенклатура В(&МассивНоменклатуры)");
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбораНоменклатурыДляПеремещениеЗапасов", "ПеремещениеЗапасовЗапасы.Номенклатура В(&МассивНоменклатуры)");
Запрос.УстановитьПараметр("МассивНоменклатуры", ОтобраннаяНоменклатура.Выгрузить());
Иначе
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбораНоменклатурыДляАвансовогоОтчета", "Истина");
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбораНоменклатурыДляПриходнойНакладной", "Истина");
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбораНоменклатурыДляПеремещениеЗапасов", "Истина");
КонецЕсли;
ВыборкаЗапроса = Запрос.Выполнить().Выбрать();
Пока ВыборкаЗапроса.Следующий() Цикл
Если ДобавлятьНовыеПозицииВПодвалТаблицы Тогда
ЗаполнитьЗначенияСвойств(ОтобранныеЗапасы.Добавить(), ВыборкаЗапроса);
Иначе
Цена = ВыборкаЗапроса.Цена;
Если ВыборкаЗапроса.СуммаВключаетНДС <> СуммаВключаетНДС Тогда
Цена = Окр(УправлениеНебольшойФирмойСервер.ПересчитатьСуммуПриИзмененииФлаговНДС(Цена, СуммаВключаетНДС, ВыборкаЗапроса.СтавкаНДС), 2);
КонецЕсли;
СтруктураПоиска = Новый Структура("ДокументПоступления, Номенклатура, Характеристика, Партия, ЕдиницаИзмерения, СтавкаНДС, Цена",
ВыборкаЗапроса.ДокументПоступления,
ВыборкаЗапроса.Номенклатура,
ВыборкаЗапроса.Характеристика,
ВыборкаЗапроса.Партия,
ВыборкаЗапроса.ЕдиницаИзмерения,
ВыборкаЗапроса.СтавкаНДС,
Цена); //Цена должна совпадать, иначе две строки и пусть определяют нужную цену для долей распределения
СтрокаДубликата = ОтобранныеЗапасы.НайтиСтроки(СтруктураПоиска);
Если СтрокаДубликата.Количество() > 0 Тогда
СтрокаДубликата[0].Количество = СтрокаДубликата[0].Количество + ВыборкаЗапроса.Количество;
Если ВыборкаЗапроса.СуммаВключаетНДС = СуммаВключаетНДС Тогда
СтрокаДубликата[0].Сумма = СтрокаДубликата[0].Количество * СтрокаДубликата[0].Цена;
ИначеЕсли ВыборкаЗапроса.СуммаВключаетНДС И НЕ СуммаВключаетНДС Тогда
СтрокаДубликата[0].Сумма = СтрокаДубликата[0].Сумма + Окр((ВыборкаЗапроса.Сумма / ((ВыборкаЗапроса.СтавкаНДС.Ставка + 100) / 100)), 2);
ИначеЕсли ВыборкаЗапроса.СуммаВключаетНДС И НЕ СуммаВключаетНДС Тогда
СтрокаДубликата[0].Сумма = СтрокаДубликата[0].Сумма + Окр((ВыборкаЗапроса.Сумма - ВыборкаЗапроса.Сумма / ((ВыборкаЗапроса.СтавкаНДС.Ставка + 100) / 100)), 2);
КонецЕсли;
СтрокаДубликата[0].СуммаНДС = СтрокаДубликата[0].СуммаНДС + ВыборкаЗапроса.СуммаНДС;
СтрокаДубликата[0].Всего = СтрокаДубликата[0].Всего + ВыборкаЗапроса.Всего;
Иначе
НоваяСтрока = ОтобранныеЗапасы.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ВыборкаЗапроса, "ДокументПоступления, Номенклатура, Характеристика, Партия, ЕдиницаИзмерения, Количество, СтавкаНДС");
НоваяСтрока.Отметка = Истина;
Если ВыборкаЗапроса.СуммаВключаетНДС = СуммаВключаетНДС Тогда
НоваяСтрока.Цена = Цена;
НоваяСтрока.Сумма = ВыборкаЗапроса.Сумма;
НоваяСтрока.СуммаНДС = ВыборкаЗапроса.СуммаНДС;
НоваяСтрока.Всего = ВыборкаЗапроса.Всего;
ИначеЕсли ВыборкаЗапроса.СуммаВключаетНДС И НЕ СуммаВключаетНДС Тогда
НоваяСтрока.Цена = Цена;
НоваяСтрока.Сумма = Окр(ВыборкаЗапроса.Сумма / ((ВыборкаЗапроса.СтавкаНДС.Ставка + 100) / 100), 2);
НоваяСтрока.СуммаНДС = ВыборкаЗапроса.СуммаНДС;
НоваяСтрока.Всего = ВыборкаЗапроса.Всего;
ИначеЕсли НЕ ВыборкаЗапроса.СуммаВключаетНДС И СуммаВключаетНДС Тогда
СтавкаНДС = Справочники.СтавкиНДС.СтавкаНДС(ВыборкаЗапроса.Номенклатура.ВидСтавкиНДС);
НоваяСтрока.Цена = Цена;
НоваяСтрока.Сумма = Окр(ВыборкаЗапроса.Сумма + (ВыборкаЗапроса.Сумма * ?(ЗначениеЗаполнено(СтавкаНДС), СтавкаНДС.Ставка, 0) / 100), 2);
НоваяСтрока.СуммаНДС = ВыборкаЗапроса.СуммаНДС;
НоваяСтрока.Всего = ВыборкаЗапроса.Всего;
КонецЕсли;
Если ТипЗнч(ВыборкаЗапроса.ДокументПоступления) = Тип("ДокументСсылка.ПеремещениеЗапасов") Тогда
ПоискСебестоимостиНоменклатуры = Новый Запрос("ВЫБРАТЬ
| ЗапасыОстатки.СуммаОстаток / ЗапасыОстатки.КоличествоОстаток КАК Себестоимость
|ИЗ
| РегистрНакопления.Запасы.Остатки(
| &Дата,
| Организация = &Организация
| И СтруктурнаяЕдиница = &СтруктурнаяЕдиница
| И Номенклатура = &Номенклатура) КАК ЗапасыОстатки
|ГДЕ
| ЗапасыОстатки.КоличествоОстаток > 0");
ПоискСебестоимостиНоменклатуры.УстановитьПараметр("Дата", ВыборкаЗапроса.ДокументПоступления.Дата);
ПоискСебестоимостиНоменклатуры.УстановитьПараметр("Организация", ВыборкаЗапроса.ДокументПоступления.Организация);
ПоискСебестоимостиНоменклатуры.УстановитьПараметр("СтруктурнаяЕдиница", ВыборкаЗапроса.ДокументПоступления.СтруктурнаяЕдиница);
ПоискСебестоимостиНоменклатуры.УстановитьПараметр("Номенклатура", ВыборкаЗапроса.Номенклатура);
НашлиСебестоимость = ПоискСебестоимостиНоменклатуры.Выполнить().Выгрузить();
Если НашлиСебестоимость[0].Себестоимость > 0 Тогда
Цена11 = НашлиСебестоимость[0].Себестоимость;
Сумма11 = Цена11*ВыборкаЗапроса.Количество;
КонецЕсли;
НоваяСтрока.Цена = Цена11;
НоваяСтрока.Сумма = Сумма11;
НоваяСтрока.СуммаНДС = 0;
НоваяСтрока.Всего = Сумма11;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Мы доработали все, что нужно. Перейдем к тестированию результата.
Тестирование результата. Себестоимость
Запускаем конфигурацию с активным расширением. Идем в документ "ДополнительныеЗатраты" Нажимаем кнопку "Подобрать — по документам". В окне отбора "Подобор по документам" — "Добавить" у нас появился тип документа "Перемещение запасов" (Рис.6).
Рис.6. Возможность отбора типов.
Выбираем документ "Перемещение запасов". Заполняем по выбранному документу раздел 3 "Отобранная номенклатура" и переносим эту табличную часть в документ "Дополнительные расходы" (рис.7).
Рис.7. Перенесен в табличную часть документ "Перемещение запасов".
Заполняем документ "Дополнительные расходы" и делаем распределение либо по сумме/количеству. Сумма дополнительных расходов на перемещение у нас составляет 250 руб (Рис.8).
Рис.8. Распределение услуг.
Заходим в типовой отчет "Себестоимость товаров на складах" в УНФ. Видим, что себестоимость товара на складе, куда сделано перемещение — увеличилась на сумма дополнительных расходов на 250 руб (Рис.9).
Рис.9. Увеличение себестоимости на складе, куда переместили.
Все работает. Расширение пригодилось. Теперь, напишем заключение.
Заключение
Вообще, по собственному опыту использования, механизм расширений очень прост и удобен.
А именно, "накидал" объекты в расширение, затем "накидал" нужные функции и процедуры, изменил их под себя и заставил работать вместо тех, что прописаны в модулях.
Но все-таки, расширение — это конечно не универсальная таблетка по названием "все и сразу", а скорее какая-то "припарка". Например, в данной статье, мы столкнулись с тем, что в расширении НЕЛЬЗЯ менять тип реквизита, установленного ранее, поэтому пришлось дорабатывать конфигурацию, но весь измененный код был перенесен в расширение конфигурации.
По собственному опыту применения, еще раз отмечу, удобство расширения можно почувствовать при обновлении конфигурации — это такой "обход" процедуры "сравнить и объединить", касаемо программного кода.
Выключил расширение и обновил все, избежав процедуры сравнения измененного кода функций и процедур.
Но, могут возникнуть сложности работоспособности расширения после обновления конфигурации — расширение может вообще не заработать ввиду сильного рефакторинга кода в обновлении. И придется дорабатывать расширение или писать новое.
Надеюсь, моя статья вам оказалась полезной. Я постарался как можно подробнее описать базовую практическую работу по использованию расширения в конфигурации.
Созданное расширение можно скачать внизу статьи. Всем спасибо, всем привет.
Ранее опубликованные материалы
Так же, прошу посмотреть мои предыдущие статьи:
Работа с механизмом отладки 1С. Базовые настройки
Методика независимой системы "Подписки на события"
(0) спасибо за такой формат изложения! поучительно!
Ой
(1) пожалуйста
+1 Классный пример по допрасходам