gifts2017

Методика добавления нового ресурса в регистр накопления 1С 8.1

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

Думаю, что каждый 1С-ник, дорабатывающий конфигурацию под заказчика, рано или поздно сталкивается с задачей доработки существующего отчета, чтобы он выводил какие-то дополнительные данные, которые на момент постановки задачи не хранятся в ресурсах регистров. Далее идет пример задачи, с которой столкнулся я. Вопрос, что такая задача не должна возникать в принципе - это бок при проектировании системы и т.д., опускаем. Все дальнейшие рассуждения есть личное мнение автора, если вы знаете другие пути или считаете, что я неправ в своих рассуждениях-выводах, то прошу писать об этом в комментариях, буду добавлять полезную информацию в статью.

Вводная: регистр ВзаиморасчетыСКонтрагентами, измерения: Фирма, Контрагент, Договор (измерения условные, в данной задаче они не суть важны);

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

При получении подобной задачи на горизонте нарисовывается два пути решения - 1) доработка только отчета и в него запихивать дополнительную логику по расчету валютных сумм 2) доработать регистр, добавить в него измерение и доработать отчет. В каждом из этих путей есть свои плюсы и минусы. В первом решении - плюс, изменения касаются только отчета, зачастую такое решение может выйти менее трудоемким, чем второе, минусы: геморрная реализация, замедление работы отчета. Во втором решении - плюсы: решение легкое, методологически поддерживаемое 1С, позволяет закрыть потенциальные новые задачи по отображеннию данных из нового ресурса в других отчетах; минусы: решение объемное и рисковое с точки зрения количества объектов доработки: нужно добавить в регистр новое измерение, добавить в модуль проведения всех документов, двигающих регистр, алгоритм расчета недостающего ресурса; сложность с первичным заполнением данных регистра - в 1С 7.7 без вариантов, только перепроведение, на это зачастую никто не идет, чтобы не поплыли итоги или нужно прогонять процедуру на тестовой базе и сравнивать обороты до и после. В 8-ке можно гибче решить эту проблему и не "перепроводить" документы, а служебной обработкой заполнить недостающее измерение.

В случае моей задачи, я принял решение идти по второму варианту, так как сразу были предпосылки к тому, что доработка отчета по взаиморасчетам в валюте - это только первая ласточка, дальше, как минимум, еще один отчет по дебиторской задолженности. Ну, и запрос получающий курсы валют на дату операции был небыстрым. Короче, решил попробовать "по науке" и пойти 2-м путем.

Ключевой момент 2-го пути - заполнение регистра без перепроведения документов. В случае регистров сведений, не привязанных к регистратору, все просто и быстро - сформировали запросом таблицу значений и загрузили ее в регистр. В случае регистра накопления такой метод не работает, требуется отбор по регистратору. Во вложении пример обработки, которая позволяет перебрать записи регистра накопления и пересчитать ресурс. Ключевой момент! СПАСИБО Magister: перед началом перебора регистра накопления необходимо отключить использование итогов, а по завершению обработки, включить (см. комментарий №9), иначе обновление регистра идет очень долго! Из-за незнания этого мне пришлось идти по рисковому пути прямых запросов в базу. Дальнейший текст по прямым запросам может представлять разве что академический интерес или же он может понадобиться, если вам нужна сверхскорость  - с отключенным расчетом итогов, пересчет регистра занял бы у меня 5-6 часов, update прошел за 10 минут) или же нет возможности выполнить эту операцию монопольно (во время отключения итогов, всем пользователям базы будут недоступны запросы, использующие виртуальные таблицы Остатки, Обороты и т.д.), что фактически означает, что они не смогут работать).

Не сумев решить быстро эту задачу средствами 1С, пришел к решению воспользоваться прямым запросом на sql сервере (Как заметилlunshttp://v8.1c.ru/predpriyatie/questions_licence.htm 64 вопрос - дальнейшие действия противоречат лицензионной политике 1С, поэтому методика может применяться исключительно "втихаря" и на свой страх и риск - обратиться в службу техподдержки 1С за помощью в решении проблемы, порожденной использованием этого решения, вы не сможете).

Воспользовался обработкой http://infostart.ru/public/16282/ - получил названия полей на сервере.

Маленькое лирическое отступление: язык запросов 1С 8.х синтаксически чистый SQL на русском языке, в данной ситуации нужно сделать update с использованием секции from после которой находится результат select'а (Выбрать). Задача, написать запрос, который получает курсы валюты на дату операций. При последней смене работы мне на двух независимых собеседованиях дали это задание - очень неплохой пример сложного запроса, имеющий простую, понятную формулировку и практическое применение, выкладываю два варианта решения этого задания. Один из них потом использовался в update.

Задание: написать средствами 1С запрос к регистру взаиморасчеты, который бы выводил данные не только по сумме в гривне, но и в валюте, расчитывая ее на основании курсов валют на дату операции. Варианты решений используют временные таблицы для наглядности и упрощения запросов, первый и третий временный запрос одинаковые.

1 вариант с использованием Имеющие (having):

ТекстЗапроса1 = "

//первый врем. запрос - выбираем различные дни операций из взаиморасчетов
    |Выбрать Различные
    |    НАЧАЛОПЕРИОДА(Рег.Период) КАК ПериодДень
    |Поместить тзДни
    |ИЗ
    |    РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Рег
    |ГДЕ Рег.СуммаВзаиморасчетовУпр = 0
    |;

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

    |Выбрать
    |    Рег.Регистратор
    |    ,Максимум(К.Курс) КАК Курс
    |    ,Выразить(Рег.СуммаВзаиморасчетов/К.Курс КАК Число(15, 2)) КАК СуммаВзаиморасчетовУпр1
    |   
    |ИЗ
    |    РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Рег
    |    ЛЕВОЕ СОЕДИНЕНИЕ тзКурсы КАК К
    |    ПО НачалоПериода(Рег.Период) = К.ПериодДень
    |ГДЕ Рег.СуммаВзаиморасчетовУпр = 0
    |    И Рег.СуммаВзаиморасчетов <> 0
    |Сгруппировать ПО Рег.Регистратор
    |";

 

 

Вариант 2 с использованием подзапроса:

ТекстЗапроса2 = "

//аналогично первому варианту решения
    |Выбрать Различные
    |    НАЧАЛОПЕРИОДА(Рег.Период) КАК ПериодДень
    |Поместить тзДни
    |ИЗ
    |    РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Рег
    |ГДЕ Рег.СуммаВзаиморасчетовУпр = 0
    |;

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

 


// аналогично первому варианту решения

  

 |Выбрать
    |    Рег.Регистратор
    |    ,Максимум(К.Курс) КАК Курс
    |    ,Выразить(Рег.СуммаВзаиморасчетов/К.Курс КАК Число(15, 2)) КАК СуммаВзаиморасчетовУпр1
    |   
    |ИЗ
    |    РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Рег
    |    ЛЕВОЕ СОЕДИНЕНИЕ тзКурсы КАК К
    |    ПО НачалоПериода(Рег.Период) = К.ПериодДень
    |ГДЕ Рег.СуммаВзаиморасчетовУпр = 0
    |Сгруппировать ПО Рег.Регистратор";

 

Возвращаемся к нашей первоначальной задаче:

Заходим в mssql management studio, coздаем новый запрос (внимание, все дальнейшие действия делаем на свой страх и риск):

Ниже пример запроса обновления нового ресурса, в нем _AccumReg264 - таблица взаиморасчетов, _Fld268 - ресурс СуммаВзаиморасчетов, _Fld10132 - новый ресурс СуммаВзаиморасчетовУпр (предварительно создается в конфигураторе) в который мы заносим пересчитанные значения, _Reference66 - справочник Валют, из него берем id валюты по наименованию (поле _Description) = 'USD' (можно по коду или любому другому ключевому полю)

_InfoReg69 - регистр сведений Курсы валют, _Fld70RRef - поле Валюта (ссылка на справочник Валюты), _Period - дата курса, _Fld71 - курс валюты.

Перед любым update я сначала запускаю select с таким же окончанием, как у update и полем = формуле update для того, чтобы убедиться, что я собираюсь менять то, что нужно. Потом select комментирую и добавляю шапку update. Процедура сначала делается на тестовой базе, а только потом на боевой. Методику можно применять только тогда, когда вы четко понимаете, что делается, copy-paste здесь не работает. (Update реализован по варианту 2 вышеуказанных запросов 1С - так быстрее)

update
    _AccumReg264
set
    _Fld10132 = Cast(_Fld268/regCurr1._Fld71 as Numeric(15,2))

--select
--    vz._Period
--    ,vz._Fld268 as SumVz
--    ,vz._Fld10132 as SumVzUprDo
--    ,Cast(_Fld268/regCurr1._Fld71 as Numeric(15,2)) as SumVzUpr
--    ,regCurr1._Period as DateKurs
--    ,regCurr1._Fld71 as Kurs

from
    _AccumReg264 as vz
Left Join    
    (select
        Vz1._Period as Период
        ,Max(regCurr._Period) as ДатаКурса
    from _AccumReg264 as Vz1
    Left join _InfoReg69 as regCurr
    on Vz1._Period >= regCurr._Period
    left join
        _Reference66 as Curr
    on regCurr._Fld70RRef = Curr._IDRRef
    Where Curr._Description = 'USD'
    group by Vz1._Period) as Подзапрос1
on vz._Period = Подзапрос1.Период
Left join
    _InfoReg69 as regCurr1
on Подзапрос1.ДатаКурса = regCurr1._Period
    and regCurr1._Fld70RRef in (select _IDRRef
                                from _Reference66 as Curr1
                                Where Curr1._Description = 'USD')

 

Update изменяет базовую таблицу регистра, осталось необходимым пересчитать итоги в виртуальных таблицах (Остатки, Обороты и ОстаткиИОбороты). Так как эту операцию можно сделать средствами 1С, то быстро убегаем из ms sql management studio и в родной стихии запускаем обработку из нескольких строчек:

    Сообщить("Старт пересчета регистра Взаиморасчеты: " + ТекущаяДата());
    МенеджерРегистра = РегистрыНакопления.ВзаиморасчетыСКонтрагентами;
    МенеджерРегистра.ПересчитатьИтоги();
    Сообщить("Окончание пересчета регистра Взаиморасчеты: " + ТекущаяДата());

Пересчет в моем случае длился достаточно долго (около часа), желательно запускать, когда пользователи не работают, иначе заметно медленнее.

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

 

Комментарии к комментариям:

Во вложении обработка, которой я пытался решить проблему средствами 1С с добавлением кода от Magister. Подобный вариант можно использовать для пересчета значений нового ресурса средствами 1С, при этом нет необходимости перепроводить базу.

 

 

 

 

 

 

 

 

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Обработка заполнения нового ресурса регистра Взаиморасчеты средствами 1С
.epf 7,22Kb
15.10.14
68
.epf 7,22Kb 68 Скачать

См. также

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

Комментарии

1. Сергей Лунев (luns) 23.09.10 13:45
Подобный подход к регистру накопления привел к ошибке, что требуется отбор по регистратору

а что помешало регистратор в отбор установить?
что то не пойму в чем сложность.
2. Сергей Ожерельев (Поручик) 23.09.10 13:48
(0) Раскраска кода для профессионального оформления публикации
http://infostart.ru/public/19856/
3. Сергей Лунев (luns) 23.09.10 13:56
Еще кстати хотелось бы добавить, что прямые запросы к SQL не только противоречат стандартам и методикам разработки, но даже и запрещены условиями лицензионного соглашения.
http://v8.1c.ru/predpriyatie/questions_licence.htm 64 вопрос
Alias; w-divin; +2 Ответить 1
4. Андрей Д. (detec) 23.09.10 14:11
Для наглядности было бы неплохо добавить в статью код, которым автор пользовался изначально, с отбором по регистратору.
5. Дмитрий Елисеев (w-divin) 23.09.10 14:31
(3) Спасибо за ссылочку... не знал...
6. Александр (pal_alex) 23.09.10 16:52
7. Александр (pal_alex) 23.09.10 16:53
(1) Речь шла о том, что я выгрузил результат запроса в тз, а потом попытался его загрузить в регистр. Оператор загрузить выдал ошибку, что требуется отбор по регистратору. После этого я начал перебирать регистр по регистраторам, делая на каждый отбор (см. обработку). Этот код работал, проблема была только в том, что долго (по моим расчетам мне потребовалось бы 100 ночей на пересчет)
8. Александр (pal_alex) 23.09.10 17:10
(2) Спасибо за подсказку, завтра скачаю и разукрашу, а то, действительно, плохо читабельно.
9. Misha ⁠ (Magister) 23.09.10 17:33
Средствами 1С добиться более быстрой записи большого регистра (порядка 500 тыс. записей) у меня не получилось

Хм... а не пробовали отключить использование итогов, записать данные, и затем снова включить?
Что-то типа:
РегистрыНакопления.ВзаиморасчетыСКонтрагентами.УстановитьИспользованиеИтогов(Ложь);
-- модификация записей --
РегистрыНакопления.ВзаиморасчетыСКонтрагентами.УстановитьИспользованиеИтогов(Истина);
10. Александр (pal_alex) 23.09.10 17:37
(9) Не пробовал, на тестовой базе проверю, по идее, велика вероятность, что это и есть две магические строчки кода, которые позволят не лезть в кишки системе.
11. Александр (pal_alex) 23.09.10 18:19
(9) Спасибо огромное за подсказку - скорость в разы выше и эту задачу уже можно решать средствами 1С, без использования прямых запросов даже для больших регистров.
12. Erne100 (Erne100) 24.09.10 10:43
С тем что второе решение "методологически поддерживаемое 1С" согласен - даже на обучении рекомендовали так поступать.
Однако практика работы с 1с говорит избегать изменения типовых регистров т.к.
неоднократно были случаи, когда в обработках обновления регистр выгружался в ТЗ (естественно только типовой его состав), потом чистился и заполнялся заново.
Уже многие локти кусают)))
Гугл Вам в помощь!
13. Александр (pal_alex) 24.09.10 14:25
(12) Речь идет о выборке данных регистра запросом, выгрузке результата запроса в тз, а потом полученное тз Загрузить() в набор данных регистра? Так получилось обойтись с регистром сведений, проблем замечено не было. В регистре накопления нужен отбор по регистратору, поэтому подобный алгоритм - это обновление существующих записей, а не удаление и создание. В данной конкретной задаче, даже с учетом моего update, пока проблем тоже не замечено. Понимаю, что это не является гарантией их отсутствия в любом другом случае. Именно поэтому, подобные действия относятся к рисковым, и обязательна предварительная прогонка процедуры на тестовом сервере, а на живом желательно выполнять ее в монопольном режиме с предварительным созданием бэкапа. Но в целом, возможность дописать движения в регистры, не перепроводя, всю базу - это ключевой момент, позволяющий в 8-ке решать подобные задачи по второму пути. В 7-ке, к сожалению, скорее всего, пришлось бы идти по первому пути.
14. WellMaster (WellMaster) 08.12.10 22:27
Прошу прощения, ткните носом на обработку, о которой идет речь в этом предложении:
В 8-ке можно гибче решить эту проблему и не "перепроводить" документы, а служебной обработкой заполнить недостающее измерение.
Сорри, с недосыпа не заметил, что это не просто статья.
15. WellMaster (WellMaster) 09.12.10 10:17
Спасибо за труд. Помогло в решении одного вопроса.
16. Erne100 (Erne100) 29.01.11 15:06
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа