Продвинутый курс. Домашнее задание №1
Первое задание по 0-му блоку продвинутого курса.
Для выполнения рекомендуется изучить следующие главы 0-го курса.
Глава 1. Запуск информационных баз.
Глава 2. Автоматическая установка платформы.
Глава 3. Архитектура системы.
Глава 4. Клиент-серверный вариант работы ИБ.
Глава 5. Журнал регистрации.
К сожалению, у Вас недостаточно прав для просмотра этой записи. Если Вы еще не залогинены на сайте — залогиньтесь. Если Вы оплачивали курс, у Вас активирован токен доступа, Вы залогинены, но Вы видите эту запись — напишите нам на e-mail поддержки.
Задание выполнено. Затруднения были героически преодолены.
Это хорошо :)
Сделал задание. Обратил внимание на метод Выгрузить журнал регистрации.
Только 2 дня назад вернулся из командировки, придется догонять.
Задание выполнил.
Для записи изменений цен в проведенном документе использовал обработчик события “ПередЗаписью” модуля документа. Плохо то, что в последующих обработчиках может произойти отказ записи, но как красиво получить исходную таблицу номенклатуры и цен, я не сообразил((. Может следовало при открытии формы выгрузить номенклатуру и цены в таблицу значений и запомнить ее в переменной?
Код общего серверного модуля:
Если Отказ Тогда
Возврат;
КонецЕсли;
//Запись в журнал
Если НЕ Объект.Проведен Тогда
Возврат;
КонецЕсли;
ТабЗнач = Объект.Товары.Выгрузить(,”Номенклатура,Цена”);
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
“ВЫБРАТЬ
| Товары.Номенклатура,
| Товары.Цена КАК ЦенаНовая
|ПОМЕСТИТЬ ВТТовары
|ИЗ
| &ТабЗнач КАК Товары”;
Запрос.УстановитьПараметр(“ТабЗнач”, ТабЗнач);
Запрос.Выполнить();
Запрос.Текст = “ВЫБРАТЬ
| ЕСТЬNULL(ДокТовары.Номенклатура, ВТТовары.Номенклатура) КАК Номенклатура,
| ЕСТЬNULL(ДокТовары.Цена, 0) КАК ЦенаСтарая,
| ЕСТЬNULL(ВТТовары.ЦенаНовая, 0) КАК ЦенаНовая
|ИЗ
| Документ.”+Объект.Метаданные().Имя+”.Товары КАК ДокТовары
| ПОЛНОЕ СОЕДИНЕНИЕ ВТТовары КАК ВТТовары
| ПО ДокТовары.Номенклатура = ВТТовары.Номенклатура
|ГДЕ
| ДокТовары.Ссылка = &Ссылка”;
Запрос.УстановитьПараметр(“Ссылка”, Объект.Ссылка);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.ЦенаСтарая<>Выборка.ЦенаНовая Тогда
ЗаписьЖурналаРегистрации(“Контроль.ИзменениеЦены”, , Объект.Метаданные(), Выборка.Номенклатура, “Было: “+Выборка.ЦенаСтарая+” Стало:”+Выборка.ЦенаНовая);
КонецЕсли;
КонецЦикла;
Отчет по изменению цены сделал обычным, без СКД. Выгрузил журнал регистрации в таблицу значений с отбором по событию “Контроль.ИзменениеЦен”, дальше обошел ее, при смене пользователя заполнял шапку отчета.
Задание сделал, для сохранения цен использовал регистр сведений. Недавно делал очень похожее, но чуть попроще. Нужно было высылать уведомление о изменении константы запрета редактирования во всех наших базах нашим МСФО-шникам, чтоб они вовремя бухам поршень вставляли.
Не совсем понял из задания когда именно следует логировать изменение цены – при любой попытке изменения цены в таблич части или только уже при перепроведнии документа. Выполнил задание для варианта 1. Порядок действий
1.Логирование Цены в ЖР.
В модуле формы документов (код для док ПоступлениеТоваровИУслуг)
– в событии ТоварыПередНачаломИзменения запоминаем старую цену (перем СтараяЦена);
– в событии ТоварыЦенаПриИзменении через серверную процедуру ЗаписатьВЖР(Номенклатура,НоваяЦена,СтараяЦена) делаем записи в журнал регистрации:
<code>&НаСервере
Процедура ЗаписатьВЖР(Номенклатура, НоваяЦена,СтараяЦена)
Если Объект.Проведен Тогда
ЗаписьЖурналаРегистрации(“НоменклатураЦена.СтараяЦена”,,Метаданные.Документы.ПоступлениеТоваровИУслуг,Номенклатура,СтараяЦена);
ЗаписьЖурналаРегистрации(“НоменклатураЦена.НоваяЦена”,,Метаданные.Документы.ПоступлениеТоваровИУслуг,Номенклатура, НоваяЦена);
КонецЕсли;
КонецПроцедуры</code>
Т.е цена записывается как комментарий;
2.Создание Отчета.
В отчете добавил кнопку, на нее повесил команду
<code>&НаКлиенте
Процедура Команда1(Команда)
Табдок = ВыгрузитьДанные();
ТабДок.Показать();
КонецПроцедуры</code>
В серверной функции ВыгрузитьДанные() выгружаю Жур.Рег в ТЗ для варианта новой и старой цены, затем циклом в первую ТЗ добавляю еще одну колонку и заполняю значениями из второй ТЗ, методом свернуть привожу ТЗ к виду без пустых значений. Далее передаю ТЗ в СКД, СКД в ТабДок.
<code>&НаСервере
Функция ВыгрузитьДанные()
ТЗ = Новый ТаблицаЗначений();
Структура = Новый Структура;
Структура.Вставить(“Событие”,”НоменклатураЦена.СтараяЦена”);
ВыгрузитьЖурналРегистрации(ТЗ,Структура,”ИмяПользователя,Дата,Данные,Комментарий”);
ТЗ.Колонки.Добавить(“СтараяЦена”);
ТЗ.Колонки.Добавить(“НоваяЦена”);
Для Каждого стр Из ТЗ Цикл
стр.СтараяЦена = Число(стр.Комментарий);
КонецЦикла;
Структура.Очистить();
Структура.Вставить(“Событие”,”НоменклатураЦена.НоваяЦена”);
ТЗ2 = Новый ТаблицаЗначений();
ВыгрузитьЖурналРегистрации(ТЗ2,Структура,”ИмяПользователя,Дата,Данные,Комментарий”);
Для Каждого стр Из ТЗ2 Цикл
Новстрока = ТЗ.Добавить();
Новстрока.ИмяПользователя = стр.ИмяПользователя;
Новстрока.Дата = стр.Дата;
Новстрока.Данные = стр.Данные;
Новстрока.НоваяЦена = Число(стр.Комментарий);
КонецЦикла;
ТЗ.Свернуть(“ИмяПользователя,Дата,Данные”,”НоваяЦена,СтараяЦена”);
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить(“тз”,тз);
СхемаКомпоновкиДанных = Отчеты.Отчет1.ПолучитьМакет(“ОсновнаяСхемаКомпоновкиДанных”);
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных,Настройки);
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки,ВнешниеНаборыДанных);
ТабДок = Новый ТабличныйДокумент;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
Возврат ТабДок;
КонецФункции</code>
Задание выполнила. Столкнулась с затруднениями (выделены в тексте курсивом).
В процедуре модулей объектов документов Поступление и Реализация ПередЗаписью() вызываю процедуру общего модуля, где запросом получаю изменения в документе. В случае, если какая-то строка удалена, то ЦенаНовая=0, если добавлена, ЦенаСтарая = 0:
Запрос = Новый Запрос;
Менеджер = Новый МенеджерВременныхТаблиц();
Запрос.Текст = “Выбрать * Поместить ТекущиеТовары Из &ТЗ Как ВрТ”;
ТЗ = Товары.Выгрузить(,”Номенклатура,Цена”);
Запрос.УстановитьПараметр(“ТЗ”,ТЗ);
Запрос.МенеджерВременныхТаблиц = Менеджер;
Запрос.Выполнить();
Запрос.Текст =
“ВЫБРАТЬ
| ЕСТЬNULL(ПоступлениеТоваровИУслугТовары.Номенклатура, ТекущиеТовары.Номенклатура) КАК Номенклатура,
| ЕСТЬNULL(ПоступлениеТоваровИУслугТовары.Цена, 0) КАК ЦенаСтарая,
| ЕСТЬNULL(ТекущиеТовары.Цена, 0) КАК ЦенаНовая
|ИЗ
| ТекущиеТовары КАК ТекущиеТовары
| ПОЛНОЕ СОЕДИНЕНИЕ Документ.ПоступлениеТоваровИУслуг.Товары КАК ПоступлениеТоваровИУслугТовары
| ПО ТекущиеТовары.Номенклатура = ПоступлениеТоваровИУслугТовары.Номенклатура
|ГДЕ
| ПоступлениеТоваровИУслугТовары.Ссылка = &Ссылка”;
//| И ЕСТЬNULL(ПоступлениеТоваровИУслугТовары.Цена,0) <> ЕСТЬNULL(ТекущиеТовары.Цена,0)”;
Запрос.УстановитьПараметр(“Ссылка”,Ссылка);
Если ТипЗнч(Ссылка) = Тип(“ДокументСсылка.РеализацияТоваровИУслуг”) Тогда
Запрос.Текст = СтрЗаменить(Запрос.Текст,”ПоступлениеТоваровИУслуг”,”РеализацияТоваровИУслуг”);
КонецЕсли;
Условие в запросе ЕСТЬNULL(ПоступлениеТоваровИУслугТовары.Цена,0) <> ЕСТЬNULL(ТекущиеТовары.Цена,0) не работает. Разобраться мне не удалось, пришлось добавить условие уже при переборе выборки результатов запроса.
В цикле по выборке результатов запроса делаю ЗаписьЖурналаРегистрации с нужным комментарием.
Пробовала поместить таблицу значений с изменениями в документе в Данные записи журнала регистрации на случай, если бы понадобился не просто отчет, показывающий, кто что менял, а детальный анализ (например, рассчитать, на сколько уменьшилась цена,…) . Для этого использовала процедуру УстановитьИспользованиеСобытияЖурналаРегистрации и привязывалась к событию доступа к регистру сведений (созданному специально для этой цели. В нем не предполагала хранить все записи изменений цен). В результате запись в данные в журнале регистрации кое-как удалось добиться, но при этом и в регистр сведений пришлось записывать все изменения. Т.е. цель по регистрации изменения документов в “Данных” журнала регистрации была достигнута, но смысла в этом уже не было, т.к. получилось, что всю информацию было легче получить из регистра сведений. :( Полный провал.
Вернула назад ЗаписьЖурналаРегистрации и сделала отчет, используя процедуру ВыгрузитьЖурналРегистрации() и отсортировав таблицу значений.
>ЕСТЬNULL(ПоступлениеТоваровИУслугТовары.Цена,0) ЕСТЬNULL(ТекущиеТовары.Цена,0)
Условие должно работать, не вижу в нем ничего криминального.
>Пробовала поместить таблицу значений с изменениями в документе в Данные записи журнала
Действительно не получается заставить платформу писать в свойство Данные таблицу значений. Только примитивные типы. Вот такие ограничения..
На этапе проектирования сначала решил реализовать самый простой вариант при изменении цены в процедуре ПриИзменении записывать событие в журнал регистрации и дело с концом. Немного подумав, понял, что вариант не выдерживает никакой критики, ведь документ может быть не записан. Далее мелькнула мысль, что неплохо бы использовать подписки на события, но потом подумал, что вроде их еще не изучали, и поэтому, сначала остановился на процедурах ПередЗаписьюНаСервере в формах документов. Но не нравилось мне в этом решении необходимость внесения изменений во все формы документов. Заглянув в комментарии к этому заданию, понял, что при выполнении нет необходимости обращать внимания на то изучали что-то или нет, другими словами пиши, используя все свои знания и умения. Ценным оказались сведения о том, что в ОбработкеПроведения можно отменить запись документа и поэтому использование только события ПередЗаписью не совсем верно. Ценным оказался комментарий Евгения о том, что можно использовать свойство ДополнительныеСвойства для передачи предыдущих данных о документе в другой обработчик подписки на события. Об этом механизме я и не подозревал. Таким образом, задачу реализовал с помощью двух подписок на события ПередЗаписью и ОбработкаПроведения при этом сделал сохранение и анализ изменений универсальными для этого организовывал цикл по табличным частям документов искал в них реквизиты «Номенклатура» и «Цена» и если находил то, только в этом случае выполнял дальнейшие действия. Это позволило в качестве источников подписок указывать просто ДокументОбъект, а не перечислять все документы конкретно. Такой подход гарантирует, что пи добавлении нового документа с нужными реквизитами по нему автоматически будет осуществляться логирование без необходимости изменять источники подписок на события.
Для поиска в ранее сохраненных данных использовал поле «Номенклатура», поскольку НомерСтроки не является надежным ключом поиска, строки ведь можно перемещать. Теоретически, конечно возможно существование в одном документе двух строк с одной и той же номенклатурой и разной ценой, но на практике это экзотичный вариант.
При записи в журнал регистрации неожиданно столкнулся со следующей проблемой вначале, я в метод ЗаписьЖурналаРегистрации в параметре Данные передавал структуру, в которую записывал информацию по номенклатуре, старой и новой цене, но при создании отчета эти данные никак не хотели считываться, принимая с завидной регулярностью значение Неопределено. Синакс-помощник по поводу параметра Данные выдавал достаточно скудную информацию, поразмыслив, я пришел к выводу, что, скорее всего, сюда можно записывать либо примитивные, либо ссылочные типы данных, а структуры и уж тем более таблицы значений этот параметр не переваривает. Поэтому пришлось в данном параметре передавать ссылку на документ, а всю информацию о номенклатуре и ценах сохранять в виде комментария.
Отдельно стоит упомянуть создание отчета по данным журнала. Шаман, танцуя с бубном, позеленел бы от зависти, увидев мои попытки реализовать этот отчет с использованием СКД. Могу сказать, что в конечном итоге у меня это получилось, правда корявенько, поскольку опыта работы с СКД у меня маловато, тем более здесь было необходимо работать с ним программно поскольку требовалось заполнять его из таблицы значений, предварительно сформированной по данным журнала. Могу сказать следующее, информация в Интернете по заполнению СКД из набора данных в виде таблицы значений весьма скудная и касается преимущественно версии 8.1, книга Хрусталевой «Разработка сложных отчетов в 1С:Предприятии 8. СКД» помогла мало, но именно благодаря ей и еще форуму ItLand у меня получилось это сделать, переделав код из 8.1 на 8.2.
Вы молодец, что смогли сделать через СКД, я сначала тоже принялся делать как и раньше делал в 8.1, как раз по книге Хрусталева, но попытки потерпели неудачу. Надеюсь Евгений покажет как сделать через СКД.
Пока нет, СКД пока не трогаем.
Задание выполнил.
1.Анализ документов вынес в привилигированый серверный общий модуль, в документах в модуле создал процедуру “ПередЗаписью” из которой для проведёных документов вызываю процедуру общего модулы.
2.Процедура серверного модуля получает в качестве параметра “ЭтотОбъект” из вызвавшего документа. ссылка полученная из объекта используется в качестве параметра запроса, текст запроса всегда модифицируется (для замены текста сделаного для документа “ПоступлениеТоваровИУслуг” на запрос для документа вида Объект.Ссылка.Метаданные().Имя). Запрос позволяет получить цены и номенклатуру из сохранённого ранее документа(Старая цена).
Данные по ценам документа (не сохранённые) выгружаю в массив (НомЦен = Объект.Товары.Выгрузить(,”Номенклатура, Цена”);)
далее при разборе результатов запроса произвожу поиск в массиве и если при совпадении номенклатуры цены не совпадают выполняю ЗаписьЖурналаРегистрации(“ИзменениеЦеныНоменклатуры.ВДокументе”,, …..
старую и новую цену заносил в коментарий.
3.Т.к. отсчёт скорее всего невозможно создать при помощи СКД сделал его посреждством макета “вручную” (разрисовав на макете области “Шапка” и “Строка”), выполняю процедуру ВыгрузитьЖурналРегистрации() с отбором по моему событию “ИзменениеЦеныНоменклатуры.ВДокументе”, сортирую таблицу по колонкам “ИмяПользователя” и “Дата” и в цикле для каждой строки вывожу область макета “Строка”.
>>Т.к. отсчёт скорее всего невозможно создать при помощи СКД
Почему нельзя? Выгрузить данные журнала в ТЗ, а ТЗ передать как источник данных в СКД. Но с этим надо морочиться, проще сделать обычным выводом в табличный документ.
В принципе и не надо было использовать реквизит, достаточно было экспортной переменной объекта для передачи между подписками старых цен.
Для выполнения задания использовал две подписки на события (ПередЗаписью и ОбработкаПроведения). В подписке ПередЗаписью (если проведен и режимЗаписи.Проведение, т.е. перепроведение) сохранял страрые цены в реквизит ТаблицаЦен (тип ХранилицеЗначения, сохранял ТаблицуЗначений). В подписке Обработка провдение уже сравнивал изменения цен (именно изменения, не добавление или удаление номенклатуры).
Подписка ОбработкаПроведения:
Процедура КонтрольЦенОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт
ТаблицаЦен=Источник.ТаблицаЦен.Получить();
Если ТипЗнч(ТаблицаЦен)<>Тип(“ТаблицаЗначений”) Тогда
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц=Новый МенеджерВременныхТаблиц;
Запрос.Текст=”ВЫБРАТЬ
| ВнешнийИсточник.Номенклатура КАК Номенклатура,
| ВнешнийИсточник.Количество КАК Количество,
| ВнешнийИсточник.Цена КАК Цена
|ПОМЕСТИТЬ ТаблицаЦен
|ИЗ
| &ВнешнийИсточник КАК ВнешнийИсточник”;
Запрос.УстановитьПараметр(“ВнешнийИсточник”, ТаблицаЦен);
Запрос.Выполнить();
Запрос.Текст = “ВЫБРАТЬ
| ТабЧасть.Номенклатура,
| ТабЧасть.Сумма / ТабЧасть.Количество КАК ЦенаНовая,
| ТаблицаЦен.Цена КАК ЦенаСтарая
|ИЗ
| (ВЫБРАТЬ
| ПоступлениеТоваровИУслугТовары.Номенклатура КАК Номенклатура,
| СУММА(ПоступлениеТоваровИУслугТовары.Количество) КАК Количество,
| СУММА(ПоступлениеТоваровИУслугТовары.Сумма) КАК Сумма
| ИЗ
| Документ.ПоступлениеТоваровИУслуг.Товары КАК ПоступлениеТоваровИУслугТовары
| ГДЕ
| ПоступлениеТоваровИУслугТовары.Ссылка = &Ссылка
|
| СГРУППИРОВАТЬ ПО
| ПоступлениеТоваровИУслугТовары.Номенклатура) КАК ТабЧасть
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаЦен КАК ТаблицаЦен
| ПО ТабЧасть.Номенклатура = ТаблицаЦен.Номенклатура
|ГДЕ
| ТаблицаЦен.Цена <> ТабЧасть.Сумма / ТабЧасть.Количество”;
Если ТипЗнч(Источник)= Тип(“ДокументОбъект.РеализацияТоваровИУслуг”) Тогда
Запрос.Текст=СтрЗаменить(Запрос.Текст,”ПоступлениеТоваровИУслугТовары”,”РеализацияТоваровИУслугТовары”);
Запрос.Текст=СтрЗаменить(Запрос.Текст,”Документ.ПоступлениеТоваровИУслуг”,”Документ.РеализацияТоваровИУслуг”);
КонецЕсли;
Запрос.УстановитьПараметр(“Ссылка”, Источник.Ссылка);
Результат = Запрос.Выполнить();
Выборка= Результат.Выбрать();
Пока Выборка.Следующий() Цикл
ЗаписьЖурналаРегистрации(“ИзменениеЦены”,,Источник.Метаданные(),Источник.Ссылка,”Товар “+Выборка.Номенклатура+”: Старая цена “+Выборка.ЦенаСтарая+” — Новая цена “+Выборка.ЦенаНовая);
КонецЦикла;
Задание сделал!
Т.к. нам нужно регистрировать изменения цен во всех проведенных документах, то логичным будет использовать подписки на события. Для этого создал общий модуль ПодпискаНаСобытия, в который вынес обработчики всех подписок.
В виду того, что нам нужно регистрировать и старую и новую цены, проще всего это сделать в событии ПередЗаписью(). Но есть одно но, а именно: транзакция записи могла не закончится успешно (Отказ = Ложь), т.е. изменения в базу не запишутся, поэтому в этом событии мы не можем делать запись в журнал регистрации. Для решения задачи поступаем сл. образом, приведу листинг:
<code>Процедура ДокументыПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт Если Источник.Проведен Тогда Если Источник.Метаданные().ТабличныеЧасти.Найти(“Товары”) = Неопределено Тогда Возврат; КонецЕсли; ТоварыДоЗаписи = Источник.Ссылка.ПолучитьОбъект().Товары; ТоварыПослеЗаписи = Источник.Товары; ТаблицаИзмененийЦенТоваров = Новый ТаблицаЗначений; ТаблицаИзмененийЦенТоваров.Колонки.Добавить(“Товар”); ТаблицаИзмененийЦенТоваров.Колонки.Добавить(“СтараяЦена”); ТаблицаИзмененийЦенТоваров.Колонки.Добавить(“НоваяЦена”); НайденнаяСтрока = Неопределено; Для Каждого СтрокаТЧ Из ТоварыДоЗаписи Цикл ТоварДоИзменения = СтрокаТЧ.Номенклатура; НайденнаяСтрока = ТоварыПослеЗаписи.Найти(ТоварДоИзменения, “Номенклатура”); Если НайденнаяСтрока = Неопределено Тогда Возврат; КонецЕсли; Если СтрокаТЧ.Цена <> НайденнаяСтрока.Цена Тогда СтрокаИзмененийЦен = ТаблицаИзмененийЦенТоваров.Добавить(); СтрокаИзмененийЦен.Товар = СтрокаТЧ.Номенклатура; СтрокаИзмененийЦен.СтараяЦена = СтрокаТЧ.Цена; СтрокаИзмененийЦен.НоваяЦена = НайденнаяСтрока.Цена; КонецЕсли; КонецЦикла; Если ТаблицаИзмененийЦенТоваров.Количество() > 0 Тогда Источник.ДополнительныеСвойства.Вставить(“ТаблицаИзмененийЦен”, ТаблицаИзмененийЦенТоваров); КонецЕсли; КонецЕсли; КонецПроцедуры</code>
Т.е. мы только выявляем изменения, и если они имели место быть, то передаем их дальше по “конвееру” в обработчик подписки на событии ОбработкаПроведения(), используя для этого специально предназначенную структуру ДополнительныеСвойства.
А теперь уже в обработчике подписки на событие ОбработкаПроведения() выполняем код:
<code>Процедура ДокументыОбработкаПроведения(Источник, Отказ, РежимПроведения) Экспорт
Перем ТаблицаИзмененийЦен;
Если Источник.ДополнительныеСвойства.Свойство(“ТаблицаИзмененийЦен”, ТаблицаИзмененийЦен) Тогда
Для Каждого СтрокаИзмененияЦен Из ТаблицаИзмененийЦен Цикл
РаботаСЖурналомРегистрации.ЗаписьСобытияВЖурналРегистрации(“Действия пользователей.Изменение цен”, Источник.Метаданные(), СтрокаИзмененияЦен.Товар, “Старая цена – ” + СтрокаИзмененияЦен.СтараяЦена + “; Новая цена – ” + СтрокаИзмененияЦен.НоваяЦена);
КонецЦикла;
КонецЕсли;
КонецПроцедуры</code>
Как видно из листинга, для регистрации событий в ЖР, я создал отдельный общий модуль. Там же располагается и функция по чтению ЖР, которая возвращает таблицу значений по переданному отбору. Ну а дальше вывод данной таблицы значений в отчет затруднений не вызывает.
Задание выполнил.
1.Логирование измения цены произвожу в два этапа.На первом – вызов общей процедуры СохранениеСостояния(ЭтотОбъект, ПредСост)
в событии перед записью документа, где в переменной модуля объекта ПредСост (тип – структура из табличных частей документа)
сохраняю состояние тех табличных частей ссылки документа, в которых встречаются реквизиты номенклатура и цена.
На втором этапе, при проведении документа, вызывается общая процедура ЗаписьВЖурналИзмененияЦены(ЭтотОбъект,ПредСост).
В ней в цикле по табличным частям объекта ищется соответствие строк из текущих ТЧ объекта и ТЧ из переменной ПредСост.
Соответствие ищется через СтарыеЦеныТЧ.НайтиСтроки(Новый Структура(“НомерСтроки,Номенклатура”, СтрокаНовыеЦеныТЧ.НомерСтроки, СтрокаНовыеЦеныТЧ.Номенклатура)).
При успешном поиске и различии цен производится запись в журнал через ЗаписьЖурналаРегистрации. Значения номенклатуры и цен вывожу в поле комментарий.
2.Отчет хотел было сделать в СКД, но пока не знаю как. Поэтому пока сделал его в обработке. По команде Сформировать в табличный документ формы обработки по заданному макету с параметрами выводится таблица значений, полученная из ВыгрузитьЖурналРегистрации(Таблица,Фильтр,Колонки). Фильтр – по событию изменения цены, колонки – имя пользователя, дата, комментарий.
Задание выполнил! Регистрацию изменений цены выполнял из процедур ПередЗаписьюНаСервере, путем сравнения данных документа и еще неизмененных данных на сервере. Данные в отчет получал с помощью ВыгрузитьЖурналРегистрации с фильтром по событию изменений цены.
Задание выполнено.
Логирование сделал через подписку на событие ПередЗаписью для документов ПоступлениеТоваровИУслуг и РеализацияТоваровИУслуг
Создал общий серверный модуль РаботаСДокументамиСервер, в нем процедура ЛогированиеЦенНоменклатурыПередЗаписью
В процедуре, если Источник.Проведен, выполняется запрос по табличной части Товары. Имя документа берется из метаданных и подставляется в текст запрос вместо явного имени документа. Результат запроса перебирается в цикле, на каждой итерации которого создается структура поиска (номенклатура+номер строки). Ищутся строки удовлетворяющие условиям поиска, сверяются старые цены с новыми. Если выявлены расхождения – запись журнал регистрации используя ЗаписьЖурналаРегистрации().
Создал отчет ОтчетОбИзмененииЦен, команда Сформировать вызывает серверную функцию СформироватьОтчетНаСервере(), в которой используя ВыгрузитьЖурналРегистрации() выгружаются отфильтрованные по событию данные в ТЗ. С СКД не заморачивался, просто заполняю табличный документ, который возвращается на клиент.
Почитал комментарии, про возможный Отказ. Действительно, выбрать для проверки обработчик ПередЗаписью было не самым оптимальным решением. Зато удобным, “старые” цены не нужно сохранять в стороннем объекте :)
А как правильно? Вероятно, лучше использовать обработчик ОбработкаПроведения(), но тогда возпользоваться подпиской на этот обработчик уже нельзя, т.к. “старые” цены уже будут затерты новыми. Полагаю, лучше в обработку проведения для каждого документа поместить вызов серверной процедуры общего модуля, где и выполнять сверку старых и новых цен и запись в ЖР. Так или нет?
>но тогда возпользоваться подпиской на этот обработчик уже нельзя, т.к. «старые» цены уже будут затерты новыми
Можно, но придется в ПередЗаписью запоминать старое состояние и класть его либо в экспортную переменную модуля объекта, либо в свойство ДополнительныеСвойства.
А далее в подписке на ОбработкуПроведения() делать сравнение с новым состоянием.
Ну да, потребуются дополнительные телодвижения по предварительному сохранению старого состояния цен в документе.
А смысл? Ведь мы же чётко разделяем сложную задачу на более простые “частные”. Зачем смешивать “факт регистрации события” с анализом динамики его изменения? Ведь мы фиксируем в журнале регистрации, что документом №234 цена стала 23 рэ (при обработке проведения) 3 мая и затем фиксируем что документом № 432 цена стала 32 рэ 6 июня – ну-у-у.. какая динамика изменения цены мы будем анализировать во второй части когда будем делать наш отчёт. Какой смысл заниматься анализом того “что пока не спрашивают”? :-)
Одинаков мыслим, замете, что если сравнивать по номенклатуре и цене, получается при перемещении позиций строк, ваша Номенклатура не найдется – что вы в таком случае делаете? я записываю – обозначаю это как изменение.
Это вопрос философский и зависит от требований заказчика.
В рамках нашей задачи можете поступать по своему усмотрению.
Добрый день, задание сделал.
1 запись в журнал регистраций делал при записи документа, я понимаю что это не правильно, так как позже может быть отказ. Вариант добавление кода в каждый документ мне тоже не нравится. Добавление дополнительных объектов, типа регистров сведений мне тоже не по душе. Сделал пока так, буду думать как лучше переделать. Пробовал при записи в хранилище записывать текущее значение товаров, а при событии Проведени получать данные из хранилища. Такой вариант у меня не получился – но если бы получилось был бы хороший вариант.
Что касается как я определял изменения то я все же искал сопоставление Номер Строки Номенклатура и проверял сумму. – в таком случае я понимаю, что могли поменять местами строки, поэтому я это учитывал как тоже изменения – лучше избыточная информация, чем ее нет совсем)) Добавление новой позиции тоже записываю в журнал.
2) Отчет сделал с помощью макета, но данный вариант мне тоже не нравится, было бы лучше если бы с помощью СКД и передать туда таблицу значений. «НаборДанных – Объект». Времени нету поэтому сделал максимально минимально – )))
Сделал задание
Пришлось создать новый регистр сведений, куда я записывал данные в обработке проведения документов с измерениями Объект, номенклатура, и ресурс цена
Создал подписку на события при записи в документах и общий модуль, где перебирал табличную часть и сравнивал данные с регистром. Если цена не сходилась, то записывал данные в журнал. <code>ЗаписьЖурналаРегистрации(“Данные.ИзмененияЦен”,УровеньЖурналаРегистрации.Информация,Метаданные.Документы.ПоступлениеТоваровИУслуг,Объект.ссылка,Комментарий);</code>
с отчетом долго мучился, но в итоге вывел в скд. Ну метод ВыгрузитьЖурналРегистрации, ес-но
Задание выполнил.
1. Для логирования изменения цен в документах был создан серверный общий модуль РаботаСДокументамиСервер с экспортной процедурой ЛогироватьИзменениеЦены(ДокументОбъект). Процедура вызывается из обработчиков ПередЗаписью документов «Поступление товаров и услуг» и «Реализация товаров и услуг»:
<code>
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ЭтотОбъект.Проведен Тогда
РаботаСДокументамиСервер.ЛогироватьИзменениеЦены(ЭтотОбъект);
КонецЕсли;
КонецПроцедуры
</code>
В качестве параметра в процедуру передается объект документа, из которого можно получить новые, еще не записанные в базу, значения цен, и сравнить их со значениями, хранящимися в базе данных. В случае, если цена изменилась, происходит запись события в журнал регистрации. В случае, если запись документа окончилась неудачей, это будет отражено в записи журнала регистрации. Для этого используется параметр РежимТранзакции метода ЗаписьЖурналаРегистрации().
Текст процедуры:
<code>
Процедура ЛогироватьИзменениеЦены(ДокументОбъект) Экспорт
Ссылка = ДокументОбъект.Ссылка;
МетаданныеДокумента = Ссылка.Метаданные();
ИмяДокумента = МетаданныеДокумента.Имя;
Запрос = Новый Запрос;
Запрос.Текст =
“ВЫБРАТЬ
| ПоступлениеТоваровИУслугТовары.Цена,
| ПоступлениеТоваровИУслугТовары.НомерСтроки
|ИЗ
| Документ.ПоступлениеТоваровИУслуг.Товары КАК ПоступлениеТоваровИУслугТовары
|ГДЕ
| ПоступлениеТоваровИУслугТовары.Ссылка = &Ссылка”;
Запрос.УстановитьПараметр(“Ссылка”, Ссылка);
Если ИмяДокумента <> “ПоступлениеТоваровИУслуг” Тогда
Запрос.Текст = СтрЗаменить(Запрос.Текст, “ПоступлениеТоваровИУслуг.Товары”, ИмяДокумента + “.Товары”);
КонецЕсли;
Результат = Запрос.Выполнить();
Таблица = Результат.Выгрузить();
Поиск = Новый Структура(“НомерСтроки”);
Для каждого СтрокаДокумента Из ДокументОбъект.Товары Цикл
Поиск.НомерСтроки = СтрокаДокумента.НомерСтроки;
МассивСтрок = Таблица.НайтиСтроки(Поиск);
СтараяЦена = МассивСтрок[0].Цена;
НоваяЦена = СтрокаДокумента.Цена;
Если СтараяЦена <> НоваяЦена Тогда
Описание = “Изменена цена для ” + СтрокаДокумента.Номенклатура + “: старое значение ” + СтараяЦена + “, новое значение ” + НоваяЦена;
ЗаписьЖурналаРегистрации(“ДанныеДокумента.ИзменениеЦены”,,МетаданныеДокумента,Ссылка, Описание, РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
</code>
2. Создан отчет ОтчетОбИзмененияхЦенВДокументах с макетом, содержащим области Шапка, Пользователь и Событие. Вывод на экран табличного документа происходит при нажатии на кнопку Сформировать.
<code>
&НаКлиенте
Процедура Сформировать(Команда)
ТабДок = СформироватьМакет();
ТабДок.Показать();
КонецПроцедуры
</code>
Заполнение макета и формирование табличного документа реализовано в серверной функции СформироватьМакет:
<code>
&НаСервере
Функция СформироватьМакет()
ТабДок = Новый ТабличныйДокумент;
Таблица = ПолучитьТаблицу();
Если Таблица.Количество() = 0 Тогда
Сообщить(“Нет данных для печати!”);
Возврат ТабДок;
КонецЕсли;
Макет = Отчеты.ОтчетОбИзмененияхЦенВДокументах.ПолучитьМакет(“Макет”);
ОбластьШапка = Макет.ПолучитьОбласть(“Шапка”);
ОбластьПользователь = Макет.ПолучитьОбласть(“Пользователь”);
ОбластьСобытие = Макет.ПолучитьОбласть(“Событие”);
ТабДок.Вывести(ОбластьШапка);
ТекущийПользователь = “”;
ТабДок.НачатьГруппуСтрок(“Пользователь”, Истина);
Для каждого Строка Из Таблица Цикл
ИмяПользователя = Строка.ИмяПользователя;
Если ТекущийПользователь <> ИмяПользователя Тогда
ТабДок.ЗакончитьГруппуСтрок();
ТекущийПользователь = ИмяПользователя;
ОбластьПользователь.Параметры.Пользователь = ТекущийПользователь;
ТабДок.Вывести(ОбластьПользователь);
ТабДок.НачатьГруппуСтрок(“Пользователь”, Истина);
КонецЕсли;
ОбластьСобытие.Параметры.Заполнить(Строка);
ТабДок.Вывести(ОбластьСобытие);
КонецЦикла;
ТабДок.ЗакончитьГруппуСтрок();
Возврат ТабДок;
КонецФункции;
</code>
Таблица значений с записями из журнала регистрации получается с помощью серверной функции ПолучитьТаблицу:
<code>
&НаСервере
Функция ПолучитьТаблицу()
Таблица = Новый ТаблицаЗначений;
Фильтр = Новый Структура(“Событие”, “ДанныеДокумента.ИзменениеЦены”);
ВыгрузитьЖурналРегистрации(Таблица, Фильтр, “Дата, ИмяПользователя, ПредставлениеДанных, Комментарий”);
Таблица.Сортировать(“ИмяПользователя, Дата”);
Возврат Таблица;
КонецФункции // ПолучитьТаблицу()</code>
Так вроде в задании было сказано, что нужно логировать не любое изменение цен, а только лишь изменение цены в проведённых документах. Поэтому лично на мой взгляд “ПередЗаписью” совсем не подходит. Во первых это может быть только “черновик” документа, во-вторых у нас может быть отказ от записи при проведении документа. Поэтому я считаю, что нужно хранить историю цен в самостоятельном регистре сведений и регистрировать изменение только исключительно при проведении документа (когда мы уверены, что данный документ проведён успешно).
В принципе ПередЗаписью можно использовать. Если проверять, что документ уже был проведен ранее.
В этом методе удобно обращаться и к текущему и прошлому состоянию документа.
Но одна проблема действительно есть, если документ был Отказ в последующих обработчиках.
И соответственно проблема если мы данный документ сняли с проведения – следовательно все изменения цен такого документа уже не должно попадать в наш отчет. Правильно?
Пожалуй, нет.
Если документ №44 был проведен, затем в нем поменяли цены, перепровели. Далее через некоторое время сняли с проведения.
Информация по документу все равно должна попасть в отчет.
Хм-м-м… В задании сказано что показывать изменение цен ПРОВЕДЁННЫМИ документами, т.е. если у нас 1.04 документом № 1 установили цену 100 рэ проведением документа , 3.05 документом № 2 цену увеличили до 120 рэ, а 3.06 проведением документа № 3 цену снизили до 90 рэ, а затем 5.07 сняли документ № 2 с проведения (и как вариант отметили как ошибочный), то по идее в отчете за 15.09 мы должны видеть только 2 записи – установку цены да 100 рэ и затем снижение цены до 90 рэ – запись “повышение цен” была признана ошибочной и следовательно почему она должна быть в отчете? Ведь отчет (как я понимаю) только по истории установки цен только теми документами которые проведены (и соответственно приняты к учёту).
или я не прав?
Секунду, надеюсь мы об одном задании говорим.
Цитата из текста “..изменение цены пользователем в проведенных документах..”.
Речь идет не о регистре сведений, а именно об изменении цены в конкретном документе.
Например, менеджер выписал счет клиенту на 10 000 руб., клиент оплатил его наличкой. Далее менеджер меняет сумму на 8000 руб. (разницу себе), и довольный идет домой.
Вот такие ситуации нужно отследить и записать в ЖР.
Ну-у-у.. именно… Менеджер сождал и провёл документ и довольный свалил домой, а старший менеджер схатился за голову, документ снял с проведения и поставил на удаление (типа явный косяк). И тогда какой смысл нам до скончания века мозолить этой ошибкой менеджера, которая исправлена и не лежит в учете (документ на дату отчета не является проведённым)?
ЗЫ.. просто с точки зрения учебной задачи мне кажется, что обработка подобной ситуации полезна (т.е. мы не просто вываливаем из журнала регистрации все события, но ещё и коррелируем их с текущим состоянием соответствующих документов)…. Но собственно говоря безусловно “постановщик задачи” Вы и следовательно Вы – “лучше знаете” :-)))))
Спорить можно долго о том, какая позиция является правильной.
В целом решать данную задачу можно и в вашей формулировке.
Собственно говоря ДЗ разбивается на 2 самостоятельные части :-)
Первая часть это муки выбора куда засунуть регистрацию события изменения цены: либо в модуль проведения (что безусловно легче не не столь красиво) либо в подписку на событие (но подписки мы ещё не проходили).
Ну а вторая часть это “рутина” создания отчета, который быдет уметь читать из журнала регистрации с соответствующими фильтрами и группировками.
Есть какой-то минимальный номер платформы для работы с курсом?
Желательно использовать последнюю версию платформы.
Минимального номер определить сложно, да и нет особого смысла.
Чтобы не наступать на старые грабли, лучше работать на последних релизах. Будем вместе искать новые грабли :)