Uploaded by denis.perm

1СПредприятие 8.0. Приемы программирования

advertisement
Всеволод Несвижский
Санкт-Петербург
«БХВ-Петербург»
2007
УДК
ББК
681.3.06
32.973.26-018.2
Н55
Несвижский В.
Н55
1С:Предприятие 8.0. Приемы программирования. — СПб.: БХВ-Петербург,
2007. — 512 с.: ил. + CD-ROM — (Профессиональное программирование)
ISBN 978-5-9775-0089-0
Книга полностью построена на реальных примерах и задачах, решаемых 1Спрограммистами в повседневной работе. Представленные приемы программирования универсальны и применимы в любых существующих конфигурациях системы 1С:Предприятие 8.0.
Рассмотрены наиболее важные и часто используемые объекты конфигурации: документы,
справочники, регистры накопления, регистры сведений, отчеты, макеты и др. Особое
внимание уделено разработке печатных документов, применению построителя отчетов
и анализу данных. Подробно описано, как подключить существующие внешние базы данных SQL к системе 1С:Предприятие 8.0. Отличительной особенностью книги является
большое количество примеров с подробными комментариями. Исходные тексты всех примеров содержатся на прилагаемом компакт-диске.
Для 1С-программистов
УДК 681.3.06
ББК 32.973.26-018.2
Группа подготовки издания:
Главный редактор
Зам. главного редактора
Зав. редакцией
Редактор
Компьютерная верстка
Корректор
Дизайн серии
Оформление обложки
Зав. производством
Екатерина Кондукова
Евгений Рыбаков
Григорий Добин
Елена Кашлакова
Натальи Караваевой
Виктория Пиотровская
Инны Тачиной
Елены Беляевой
Николай Тверских
Лицензия ИД № 02429 от 24.07.00. Подписано в печать 22.06.07.
Формат 70 1001/16. Печать офсетная. Усл. печ. л. 41,28.
Тираж 2500 экз. Заказ №
"БХВ-Петербург", 194354, Санкт-Петербург, ул. Есенина, 5Б.
Санитарно-эпидемиологическое заключение на продукцию
№ 77.99.02.953.Д.006421.11.04 от 11.11.2004 г. выдано Федеральной службой
по надзору в сфере защиты прав потребителей и благополучия человека.
Отпечатано с готовых диапозитивов
в ГУП "Типография "Наука"
199034, Санкт-Петербург, 9 линия, 12
ISBN 978-5-9775-0089-0
© Несвижский В., 2007
© Оформление, издательство "БХВ-Петербург", 2007
Îãëàâëåíèå
Введение .................................................................................................................. 1
Программные требования....................................................................................... 5
ЧАСТЬ I. ПРИЕМЫ ПРОГРАММИРОВАНИЯ ДОКУМЕНТОВ .............. 7
Глава 1. Создание, запись, проведение, блокировка, удаление ................... 9
Создание нового документа ................................................................................. 11
Запись и проведение документа .......................................................................... 13
Блокировка документа .......................................................................................... 14
Удаление документа ............................................................................................. 16
Глава 2. Выбор и поиск ...................................................................................... 17
Глава 3. Получение ссылок ............................................................................... 23
Глава 4. Получение форм .................................................................................. 28
Глава 5. Чтение и установка свойств .............................................................. 35
Глава 6. Заполнение данными .......................................................................... 45
Глава 7. Обработка событий ............................................................................. 49
Глава 8. Использование запросов .................................................................... 55
ЧАСТЬ II. ПРИЕМЫ ПРОГРАММИРОВАНИЯ РЕГИСТРОВ
НАКОПЛЕНИЯ ............................................................................... 95
Глава 9. Получение выборки ............................................................................ 97
Глава 10. Получение остатков ........................................................................ 108
Глава 11. Получение оборотов........................................................................ 115
IV
Îãëàâëåíèå
Глава 12. Получение формы ........................................................................... 122
Глава 13. Получение макета ........................................................................... 126
Глава 14. Получение и установка свойств ................................................... 130
Глава 15. Запись ................................................................................................ 137
Глава 16. Использование запросов ................................................................ 142
ЧАСТЬ III. ПРИЕМЫ ПРОГРАММИРОВАНИЯ РЕГИСТРОВ
СВЕДЕНИЙ ................................................................................... 223
Глава 17. Получение выборки ........................................................................ 225
Глава 18. Получение форм .............................................................................. 235
Глава 19. Получение макета ........................................................................... 240
Глава 20. Получение срезов ............................................................................ 244
Глава 21. Запись ................................................................................................ 251
Глава 22. Использование запросов ................................................................ 256
ЧАСТЬ IV. АНАЛИЗ ДАННЫХ..................................................................... 261
Глава 23. Анализ заказов ................................................................................. 263
Анализ заказов покупателей .............................................................................. 263
Анализ заказов поставщикам ............................................................................. 267
Анализ размещения заказов поставщикам под заказы покупателей ............. 270
Анализ заказов покупателей по оплате ............................................................. 272
Глава 24. Анализ продаж ................................................................................. 278
Анализ объемов продаж за период .................................................................... 278
Анализ продаж в разрезе поступлений ............................................................. 287
Глава 25. Анализ закупок ................................................................................ 293
Анализ объемов закупок за период ................................................................... 293
Анализ оценки работы менеджеров по закупке ............................................... 301
Îãëàâëåíèå
V
Глава 26. Анализ взаиморасчетов ................................................................. 307
Взаиморасчеты с покупателями......................................................................... 315
Взаиморасчеты с поставщиками ........................................................................ 322
ЧАСТЬ V. ОТЧЕТЫ ......................................................................................... 327
Глава 27. Создание и оформление отчетов .................................................. 329
Создание нового отчета на базе существующих .............................................. 330
Использование построителя отчетов ................................................................. 335
Программирование и настройка отчетов .......................................................... 354
Глава 28. Работа со стандартными отчетами .............................................. 361
ЧАСТЬ VI. ДРУГИЕ ОБЪЕКТЫ КОНФИГУРАЦИИ .............................. 381
Глава 29. Справочники .................................................................................... 383
Глава 30. Перечисления ................................................................................... 401
Глава 31. Планы видов характеристик ........................................................ 407
ЧАСТЬ VII. ПЕЧАТНЫЕ ДОКУМЕНТЫ ................................................... 415
Глава 32. Создание и оформление печатных документов......................... 417
Глава 33. Подключение печатных документов к базе ............................... 439
ЧАСТЬ VIII. ВЗАИМОДЕЙСТВИЕ 1С И БАЗ ДАННЫХ ........................ 449
Глава 34. Использование в 1С внешних баз данных ................................. 451
Интерфейс ADO .................................................................................................. 452
Создание процедур и функций SQL .................................................................. 466
Глава 35. Создание базы и подключение к 1С ............................................ 473
Создание новой базы данных ............................................................................. 473
Создание внешней обработки в 1С.................................................................... 485
Подключение базы данных ................................................................................ 488
Приложение. Описание компакт-диска........................................................ 493
Предметный указатель .................................................................................... 495
VI
Îãëàâëåíèå
Введение
Не так давно фирма 1С порадовала многих своих поклонников, выпустив обновленную версию своей, не побоюсь сказать, самой популярной в СНГ программы
для управления предприятием и ведения бухгалтерского учета. Новая система
"1С:Предприятие 8.0" не просто заменила полюбившуюся "1С:Предприятие 7.7",
а намного превзошла ее по своим возможностям. Разработчиками была проделана поистине титаническая работа, в результате которой появилась очень
мощная, настраиваемая, гибкая и многофункциональная система, позволяющая в короткие сроки с нуля создавать готовые решения для автоматизации
различных видов хозяйственной деятельности или использовать уже имеющиеся. Во-первых, было предложено несколько вариантов работы системы:
однопользовательский, файловый и клиент-серверный. Последний наиболее
полно отвечает современным требованиям эффективной работы большого
количества пользователей в распределенной среде. Во-вторых, система может решать широкий круг вопросов, начиная от автоматизации торговой и
производственной деятельности, бухгалтерского учета и заканчивая планированием, бюджетированием и анализом финансовой деятельности. В-третьих,
быстрота и удивительная гибкость разработки новых законченных решений,
имеющих все необходимые средства для сбора, хранения и анализа данных.
Все это послужило основной причиной растущей популярности системы
"1С:Предприятие 8.0", а также создания данной книги.
Для кого написана эта книга? Конечно для разработчиков, уже использующих новую систему, а также для тех, кто собирается переходить на новый
продукт. Книга является сугубо практическим руководством по программированию, построению отчетов и печатных форм, а также взаимодействию
с другими базами данных, написанных на MSSQL (языке запросов фирмы
Microsoft). Не секрет, что встроенная справка и предлагаемая разработчиками документация особо не изобилуют примерами, что иногда приводит в тупик начинающих программистов. В данной книге автор надеется восполнить
2
Введение
этот существенный пробел и помочь читателям научиться быстро и эффективно применять богатые возможности встроенного языка для работы с общими, а главное — прикладными объектами, наиболее востребованными
в условиях современного рынка.
Как уже было сказано, система "1С:Предприятие 8.0" имеет мощный встроенный язык, внешним видом немного напоминающий добрый старый C++.
К основным особенностям нового языка можно отнести:
код полностью объектно-ориентированный;
как минимум, возможность выбора русского или английского языка для
написания кода;
наличие синтакс-помощника, различных конструкторов и шаблонов;
проверка синтаксиса;
отсутствие явной типизации переменных;
удобное автоматическое форматирование процедур, функций, условий и
циклов;
контекстная подсказка.
Для применения всего перечисленного фирма 1С предусмотрела специальное
средство разработки — конфигуратор, который решает не только все эти задачи, но и позволяет создавать или корректировать интерфейс программы,
выполнять административные функции и управлять распределением доступа
к программе. Кроме того, конфигуратор служит единственной точкой входа
для создания и поддержки конфигурации. Конфигурация представляет собой
иерархическую структуру, состоящую из общих и прикладных объектов.
К общим объектам относятся:
подсистемы — позволяют группировать различные объекты конфигурации;
общие модули — предназначены для хранения глобальных процедур,
функций и переменных, доступных из любого объекта конфигурации;
роли — позволяют определить права доступа пользователей к различным
объектам конфигурации;
интерфейсы — помогают установить и распределить взаимодействие
пользователей с программой посредством панелей инструментов, обычных и контекстных меню;
критерии отбора — предназначены для организации отборов данных по
заданным критериям;
общие формы — представляют собой стандартные формы интерфейса,
неоднократно используемые различными объектами конфигурации для
решения однотипных повторяющихся задач;
Введение
3
общие макеты — предназначены для использования в качестве стандарт-
ных печатных форм, которые могут вызываться из различных объектов
конфигурации;
общие картинки — содержат графические файлы для организации краси-
вых и понятных интерфейсов пользователя, например, вместо текстовых
надписей на панели инструментов;
стили — позволяют создавать предопределенные варианты форматирова-
ния и оформления для различных элементов управления;
языки — данные объекты помогают организовать многоязыковую под-
держку конфигурации.
К прикладным объектам можно отнести:
константы — позволяют хранить и использовать в различных объектах
конфигурации определенные постоянные значения;
справочники — позволяют определить общую условно-постоянную ин-
формацию для обращения из других объектов конфигурации;
документы — содержат всевозможные виды документов, необходимых
для решения поставленных задач;
журналы документов — позволяют отбирать и группировать различные
типы документов по определенным признакам;
перечисления — предназначены для хранения наборов значений одного
типа и подстановки в программный код;
отчеты — позволяют создавать удобные для последующего анализа
представления данных;
обработки — удобные объекты, которые могут применяться для решения
всевозможных специфических или сервисных задач, не реализованных
в имеющихся модулях конфигурации;
планы видов характеристик — позволяют назначать дополнительные ха-
рактеристики и свойства для товаров, а также организовать аналитический
учет в бухгалтерии;
планы счетов — предназначены для хранения счетов управленческого
и бухгалтерского учета;
планы видов расчета — предназначены для организации общих правил
расчета, например при пересчете налогов;
регистры сведений — позволяют хранить какую-либо заданную информа-
цию о различных объектах конфигурации, в том числе и с учетом времени;
4
Введение
регистры накопления — предназначены для накопления предопреде-
ленной информации, выраженной числовыми значениями и с учетом
времени;
регистры бухгалтерии — позволяют хранить данные проводок по опреде-
ленным планам счетов.
Существуют и другие объекты, о которых можно получить информацию
в справке 1С. Они не имеют принципиального значения в контексте данной
книги, поэтому здесь не рассматриваются.
На базе перечисленных общих и прикладных объектов строится конфигурация, отвечающая различным задачам управления предприятием и ведением
бухгалтерского учета. Существуют и готовые решения, позволяющие в короткие строки организовать полноценный товарооборот и учет торговых
операций. К ним можно отнести: УПП — управление производственным
предприятием, УТ — управление торговлей, УП — управление персоналом,
УС — управление складом и другие. УПП представляет собой наиболее полное решение на базе "1С:Предприятие 8.0" и включает в себя следующие
подсистемы: управление отношениями с покупателями и поставщиками,
управление оборудованием и ремонтами, управление розничной торговлей,
расчет зарплаты, планирование продаж и закупок, управление производством, управление денежными средствами, бюджетирование, бухгалтерский
и налоговый учет, управление заказами, управление складом, управление
взаиморасчетами, управление персоналом. УПП позволяет в короткие сроки
организовать полноценную программную систему управления и анализа любым торговым или производственным предприятием. Однако в реальной
жизни может потребоваться гораздо более скромный набор возможностей, по
сравнению с имеющимся в УПП. Для этих целей имеет смысл применить более "легкую" конфигурацию. Например, для организации складского учета
вполне достаточным будет установить только систему управления складом.
Этим можно сэкономить финансовые затраты и упростить работу пользователей. Поэтому, выбирая вариант конфигурации, следует заранее определить
решаемый круг задач, которые будут выполняться на данном конкретном
предприятии или участке. Выбранный вариант конфигурации будет включать
в себя только те объекты, которые необходимы, но разработчик имеет возможность добавлять свои собственные в рамках общей структуры, описанной
ранее, а также дорабатывать существующие объекты. При этом возникает
круг вопросов, связанных с поддержкой и обновлением конфигурации, не
рассматриваемый в данной книге. Более подробную информацию об этом
можно получить в службе поддержки фирмы 1С.
Введение
5
Исходя из всего сказанного, можно определить основные темы данного руководства:
1. Приемы программирования документов.
2. Приемы программирования регистров накопления.
3. Приемы программирования регистров сведений.
4. Анализ данных.
5. Отчеты.
6. Объекты конфигурации.
7. Печатные документы.
8. Взаимодействие 1С и баз данных SQL.
Основой упор в книге делается на практическое программирование свойств
и методов различных объектов, написание и оптимизацию запросов. Подразумевается, что читатель знаком с языком 1С, имеет общее представление
о конфигураторе и базовых объектах, умеет пользоваться синтакс-помощником
и встроенной справкой. В качестве языка программирования выбран русский.
Сделано это не случайно, а по причине более удобного восприятия и комфортного написания текстов исходных кодов. Все примеры, рассмотренные
в книге, имеются на прилагаемом компакт-диске.
Программные требования
Для полноценной работы с книгой, необходимо иметь на компьютере
установленную лицензионную программу "1С:Предприятие 8.0" версии не
ниже 8.0.3.16. Кроме этого, рекомендуется иметь одну из следующих конфигураций: управление производственным предприятием, управление производством или управление торговлей. Материалы, рассматриваемые в книге,
целиком содержатся только в первой из перечисленных конфигураций, поэтому не стоит удивляться, не обнаружив какие-либо объекты у себя. Для
работы с примерами, демонстрирующими подключение к внешним базам
данных, следует установить Microsoft SQL Server 2000 или выше.
6
Введение
Часть I
Приемы программирования
документов
8
Часть I. Приемы программирования документов
Глава 1
Создание, запись, проведение,
блокировка, удаление
Документы являются неотъемлемой частью любой конфигурации. Они составляют основу прикладной направленности продукта, его возможности и удобство работы пользователей с предложенным интерфейсом. Грамотно организованный документ позволяет упростить и ускорить процесс формирования
данных в программе, а также помогает провести в последующем более полный
анализ результатов работы, влияющий в целом на результаты работы всего
предприятия.
Готовые конфигурации уже содержат в себе необходимый набор документов,
отвечающий поставленным задачам по торгово-производственным операциям и бухгалтерскому учету. В этой части приводятся приемы программирования различных документов, эффективное использование их свойств, методов и событий. Поскольку в разных конфигурациях могут присутствовать как
одинаковые, так и абсолютно непохожие типы документов, что обусловлено
решением разного круга задач, читателям необходимо учитывать данный
момент. Здесь описываются только наиболее интересные и часто используемые документы, которые могут быть в одной конфигурации (например,
Управление производственным предприятием) и отсутствовать в другой, направленной на какую-то одну область учета (например, Управление персоналом).
Читатели познакомятся с программированием документов посредством
встроенной объектной модели 1С и языка запросов. Первый вариант наиболее приемлем для создания и обновления документов в базе, их проведения
и удаления, получения и установки свойств, обработки событий. Второй
позволяет получать массивы однотипных документов для их последующей
обработки, задействовав при этом меньшие ресурсы системы, что очень актуально для больших баз данных. Хорошим правилом программирования считается применение, где это возможно, языка запросов.
10
Часть I. Приемы программирования документов
Существующая в "1С:Предприятие 8.0" объектная модель предоставляет
программисту базовые объекты для управления документами:
ДокументыМенеджер — предоставляет доступ ко всем имеющимся в кон-
фигурации типам документов;
ДокументОбъект — позволяет получить доступ непосредственно к доку-
менту для его изменения;
ДокументМенеджер — предназначен для получения доступа к определен-
ному виду документа, его формам и макетам;
ДокументСсылка — данный объект позволяет получить ссылку на доку-
мент без возможности изменения последнего;
ДокументСписок — объект необходим для управления списком однотип-
ных документов, отображаемых в табличном виде;
ДокументВыборка — позволяет организовать выборку массива докумен-
тов из базы данных для их последующей обработки.
Каждый из этих объектов имеет свои преимущества и недостатки. Например,
изменить существующий в базе документ можно через объект ДокументОбъект, а получить выборку — через ДокументВыборка. Для использования
ссылки на документ (без модификации самого объекта документа) предпочтительней выбрать ДокументСсылка. ДокументСписок помогает управлять
настройками отборов и упорядочивания отображаемого списка документов,
с которыми работает пользователь.
Любой документ сможет содержать в себе реквизиты, табличные части, формы и макеты. Дата создания документа и номер являются предопределенными реквизитами и назначаются системой автоматически. Структуру документа можно еще представить следующим образом:
Шапка — представляет, как правило, собой общие сведения о документе,
такие как номер, дата создания, наименование организации и контрагента,
номер и дата договора, валюта документа, наименование склада, сумму
документа и НДС (налог на добавленную стоимость);
Список товаров — табличная часть, которая содержит сведения о товарах,
отражаемых в документе. К таким сведениям, в первую очередь, относятся
наименование товара, характеристика, количество, единица измерения,
цена за единицу товара, сумма НДС, стоимость;
Список услуг — дополнительная табличная часть, описывающая различ-
ные услуги, предоставляемые дополнительно к товарной части или самостоятельно. Наиболее распространенным видом услуг являются транспортные расходы на перевозку грузов. К сведениям об услугах относятся:
наименование, количество, единица измерения, количество, стоимость;
Глава 1. Создание, запись, проведение, блокировка, удаление
11
Дополнительные сведения — различные добавочные сведения, в зависи-
мости от вида документа, а также подразделение и имя ответственного за
создание документа.
А теперь на практике рассмотрим приемы программирования документов,
используя перечисленные объекты.
Создание нового документа
Первый документ, который мы сформируем, будет ЗаказПокупателя. Этот вид
документа является отправной точкой в торговых операциях и служит для
формирования заявки покупателя (иначе говоря, контрагента) на приобретение
у нас каких-либо товаров или предоставления ему определенных услуг.
Для создания нового документа достаточно использовать метод объекта ДокументМенеджер СоздатьДокумент, как показано в листинге 1.1.
Листинг 1.1. Создание нового документа
НовыйЗаказПокупателя = Документы.ЗаказПокупателя.СоздатьДокумент();
// Установим текущую дату создания документа
НовыйЗаказПокупателя.Дата = ТекущаяДата();
// Установим свой номер для документа
НовыйЗаказПокупателя.Номер = "Документ 1";
// Определим вид операции
НовыйЗаказПокупателя.ВидОперации =
Перечисления.ВидыОперацийЗаказПокупателя;
// Добавим комментарий
НовыйЗаказПокупателя.Комментарий = "Образец создания нового документа";
В приведенном примере создается новый документ ЗаказПокупателя, где
дате создания присваивается значение текущей даты (встроенная функция
ТекущаяДата). Кроме того, задается свой номер документа, комментарий
и устанавливается вид операции. Вид операции представляет собой предопределенное в конфигурации значение перечисления ВидыОперацийЗаказПокупателя.
Номер документа может быть числовым или строковым типом. Это зависит
от установленных в конфигураторе свойств документа. Строковый тип гораздо предпочтительней, поскольку позволяет вводить буквы и цифры одновременно, что очень важно для предприятий, использующих собственную систему нумерации документов.
12
Часть I. Приемы программирования документов
Как видите, создать новый документ очень просто, но может понадобиться не
только создать документ, но и вывести на экран форму документа, например,
для самостоятельного заполнения ее пользователем. Решение данной задачи
на примере документа ВнутреннийЗаказ показано в листинге 1.2.
Листинг 1.2. Получение и вывод на экран основной формы документа
НовыйВнутреннийЗаказ = Документы.ВнутреннийЗаказ.СоздатьДокумент();
// Установим номер документа, используя встроенную функцию
НовыйВнутреннийЗаказ.УстановитьНовыйНомер();
// Получим основную форму документа, определенную в конфигураторе
ФормаВнутреннийЗаказ =
НовыйВнутреннийЗаказ.ПолучитьФорму("ФормаДокумента");
// Выводим форму документа на экран
ФормаВнутреннийЗаказ.Открыть();
// Последние две строки кода можно заменить одной
ФормаВнутреннийЗаказ =
НовыйВнутреннийЗаказ.ПолучитьФорму("ФормаДокумента").Открыть();
После выполнения кода листинга будет создан и появится на экране монитора новый документ ВнутреннийЗаказ. Пользователь сможет заполнить все
необходимые данные и сохранить документ в базе. Для выбора номера мы
применили доступный для данного объекта метод УстановитьНовыйНомер.
Получение формы реализуется через специальный метод объекта ПолучитьФорму. В качестве аргумента этого метода следует указать имя основной
формы, назначенного этому объекту документа в конфигурации. Кроме того,
метод ПолучитьФорму имеет еще два необязательных параметра: ссылка на
родительскую форму и уникальный ключ для последующего быстрого обращения к форме.
Как правило, у документа есть только одна основная форма, но могут быть
еще несколько дополнительных, например, форма отображения списка данного вида документов, форма выбора и другие. При создании новой конфигурации разработчик сам задает имена основной и дополнительных форм,
а в типовых конфигурациях они уже заданы, поэтому необходимо изучить структуру документа перед его использованием. Более подробно работа
с формами будет рассмотрена в главе 4.
Глава 1. Создание, запись, проведение, блокировка, удаление
13
Запись и проведение документа
После того как создан новый документ, его следует сохранить в базе данных,
а также, если необходимо, провести. Процесс сохранения и проведения документа реализован в одном методе — Записать. Этот метод имеет два
необязательных аргумента. Первый позволяет установить режим записи
(запись, проведение или отмена проведения), а второй определяет режим
проведения (оперативный или не оперативный). Приведем пример сохранения нового документа в листинге 1.3.
Листинг 1.3. Запись документа
НовыйАвансовыйОтчет = Документы.АвансовыйОтчет.СоздатьДокумент();
// Установим текущую дату создания документа
НовыйАвансовыйОтчет.Дата = ТекущаяДата();
// Установим свой номер для документа
НовыйАвансовыйОтчет.Номер = "00004";
// Запишем документ
НовыйАвансовыйОтчет.Записать();
Итак, мы создали новый документ АвансовыйОтчет, установили дату и номер
документа, а затем сохранили его в базе данных, используя метод Записать.
После того как документ записан, может потребоваться его провести. Термин
"проведение" подразумевает выполнение движений документа в системе по
связанным с ним регистрам. Как правило, каждый документ хранит
в себе различные сведения, которые можно использовать впоследствии для
построения отчетов и анализа данных. Чтобы упростить задачу, фирма 1С
разработала так называемые регистры, которые отслеживают и накапливают
информацию об изменениях в документах. Подробнее о назначении и работе
с регистрами будет рассказано в части II книги. Сейчас достаточно понять,
что при проведении документа предопределенные сведения о нем записываются в связанные регистры, заданные на этапе создания конфигурации. Пример проведения документа представлен в листинге 1.4.
Листинг 1.4. Запись и проведение документа
НовыйЗаказПоставщику = Документы.ЗаказПоставщику.СоздатьДокумент();
// Установим текущую дату создания документа
НовыйЗаказПоставщику.Дата = НачалоДня(ТекущаяДата());
// Запишем и проведем документ
14
Часть I. Приемы программирования документов
НовыйЗаказПоставщику.Записать(РежимЗаписиДокумента.Проведение);
// Отменим проведение документа
НовыйЗаказПоставщику.Записать(РежимЗаписиДокумента.ОтменаПроведения);
Сначала мы создали новый документ, затем провели и записали его в базу
данных, а после этого отменили проведение. Проведение документа может
быть выполнено оперативно (оперативный режим подразумевает проведение
документа текущей датой и временем) или "задним числом". Пример оперативного проведения документа представлен в листинге 1.5.
Листинг 1.5. Оперативное проведение документа
НовыйРеализацияТоваров = Документы.РеализацияТоваров.СоздатьДокумент();
// Установим текущую дату создания документа
НовыйРеализацияТоваров.Дата = ТекущаяДата();
// Выберем из справочника валюту
НовыйРеализацияТоваров.ВалютаДокумента =
Справочники.Валюты.НайтиПоНаименованию("RUR");
// Запишем и проведем документ оперативно
НовыйРеализацияТоваров.Записать(РежимЗаписиДокумента.Проведение,
РежимПроведенияДокумента.Оперативный);
Если документ уже существует в базе данных, он будет просто перепроведен.
При создании нового — документ вначале записывается в базу, а потом проводится.
Блокировка документа
Как правило, время от времени в многопользовательской системе возникает
необходимость внести какие-либо изменения в документ. Поскольку один
и тот же документ могут открыть одновременно несколько пользователей,
следует перед внесением изменений попытаться заблокировать его, чтобы
гарантировать сохранение вносимой информации. Заметьте, речь идет о попытке блокировки, поскольку объект уже может быть заблокирован другим
пользователем. Хорошим правилом будет выполнение предварительной проверки состояния блокировки с последующим принятием решения — блокировать документ или подождать, пока он освободится. Рассмотрим пример
блокировки документа в листинге 1.6.
Глава 1. Создание, запись, проведение, блокировка, удаление
15
Листинг 1.6. Управление блокировкой документа
// Создадим новый документ
НовыйПоступлениеТоваров = Документы.ПоступлениеТоваров.СоздатьДокумент();
// Заполним реквизиты документа
НовыйПоступлениеТоваров.Номер = "001";
// . . .
// Сохраним его в базе данных
НовыйПоступлениеТоваров.Записать();
// Теперь получим ссылку на созданный нами документ
ПоступлениеТоваровСсылка =
Документы.ПоступлениеТоваров.НайтиПоНомеру("001");
// Получаем объект документа по ссылке
ПоступлениеТоваровОбъект = ПоступлениеТоваровСсылка.ПолучитьОбъект();
// Блокируем документ
Попытка
ПоступлениеТоваровОбъект.Заблокировать();
// Вносим изменения в документ, например, меняем номер
ПоступлениеТоваровОбъект.Номер = "002";
// Записываем данные в базу
ПоступлениеТоваровОбъект.Записать();
// Разблокируем документ
ПоступлениеТоваровОбъект.Разблокировать();
Исключение
// Выводим сообщение об ошибке
Предупреждение ("Не удалось заблокировать документ!");
КонецПопытки;
Итак, вначале мы создали новый документ, заполнили данными и сохранили
в базе данных. После этого получили ссылку на существующий документ,
воспользовавшись методом НайтиПоНомеру, где в качестве аргумента передали
номер искомого документа. Получив ссылку, мы вызвали метод ПолучитьОбъект,
чтобы внести изменения в документ. Конечно, можно было использовать
созданный вначале объект, а не идти столь длинным путем, но это было сделано умышленно и только в качестве демонстрации работы с блокировкой.
Далее мы создали обработчик ошибок, где и выполнили попытку блокировки. В случае неудачи пользователь получил бы сообщение об ошибке. В случае успешной блокировки мы изменили номер документа, записали обновленный документ в базу и разблокировали его, вызвав метод Разблокировать.
16
Часть I. Приемы программирования документов
Следует заметить, что для проверки наличия блокировки имеется специальный метод Заблокирован, который возвращает истинное или ложное значение, в зависимости от состояния блокировки. Однако, как показывает практика, его применение не всегда дает правильный результат, поэтому
рекомендуется использовать в таких ситуациях обработчик ошибок.
Удаление документа
В "1С:Предприятие 8.0" удаление документа разделено на две составляющие:
пометка объекта на удаление и непосредственное удаление. В первом случае
документ не удаляется из базы, а только устанавливается признак того, что
он больше не нужен. При этом с проведенного документа снимаются все
проводки (движения по регистрам), но документ остается в базе и отображается в общем списке. В любой момент его можно снова восстановить
и провести. Во втором случае документ безвозвратно удаляется из базы без
возможности восстановления. При этом все имеющиеся связи документа
с другими объектами конфигурации сохраняются, что может привести в дальнейшем к многочисленным ошибкам. Рассмотрим пример удаления документа в листинге 1.7.
Листинг 1.7. Удаление документа
// Создадим новый документ
НовыйСписаниеТоваров = Документы.СписаниеТоваров.СоздатьДокумент();
// Заполним дату документа
НовыйСписаниеТоваров.Дата = ТекущаяДата();
// Запишем документ в базу
НовыйСписаниеТоваров.Записать();
// Пометим документ к удалению
Если НЕ НовыйСписаниеТоваров.ПометкаУдаления Тогда
НовыйСписаниеТоваров.УстановитьПометкуУдаления;
КонецЕсли;
// или удалим документ непосредственно
НовыйСписаниеТоваров.Удалить();
Как видно из примера, был создан новый документ и сохранен в базе. После
этого мы вызвали метод УстановитьПометкуУдаления, предварительно проверив наличие признака удаления (свойство ПометкаУдаления). После этого документ все еще находится в базе. Чтобы удалить его окончательно, нужно
применить метод Удалить.
Глава 2
Выбор и поиск
Неотъемлемой частью любой базы данных является возможность выборки
и поиска данных. Поскольку документы интенсивно накапливаются в результате работы пользователей, размер базы увеличивается очень быстро. Для
эффективной работы требуются средства, позволяющие быстро и правильно
находить нужные записи, при этом как можно меньше загружать имеющиеся
процессорные мощности. Любая современная система управления базами
данных в полной мере отвечает этим требованиям. Не стала исключением
и 1С, предоставив разработчикам и конечным пользователям широкие возможности по выбору и поиску в базе отдельных записей. Однако следует
помнить, что без грамотного подхода конечный результат может оказаться не
таким оптимистичным, как предполагается. Связано это, прежде всего, с определенными требованиями, налагаемыми на использование методов поиска
и выборки данных. К ним относятся грамотное применение критериев (условий) поиска. Именно с помощью критериев поиска можно сократить время
получения данных, их конечную значимость и информативность.
В этой главе мы рассмотрим, как можно программно организовать выборку
одного или множества документов, найти строго определенный документ
в базе данных и как правильно использовать различные условия поиска.
Как уже было сказано в главе 1, система предоставляет разработчикам готовые базовые объекты ДокументВыборка и ДокументМенеджер, с помощью
которых можно легко организовать выборку большого количества документов для последующей обработки и модификации. Для начала попробуем выбрать документы, удовлетворяющие определенным условиям, как показано
в листинге 2.1.
18
Часть I. Приемы программирования документов
Листинг 2.1. Выборка документов в заданном диапазоне дат
// Задаем критерии выборки по дате
НачальнаяДата = НачалоДня(ТекущаяДата());
КонечнаяДата
= КонецДня(ТекущаяДата());
// Делаем выборку документов из базы за текущий день
ВыборкаЗаказПокупателя =
Документы.ЗаказПокупателя.Выбрать(НачальнаяДата, КонечнаяДата);
// Организуем цикл для обработки найденных документов
Пока ВыборкаЗаказПокупателя.Следующий() Цикл
// Выводим в окно сообщений номер найденного документа
Сообщить("Номер документа: " + ВыборкаЗаказПокупателя.Номер);
// Выводим в окно сообщений признак проведения
Сообщить("Проведен: " + ВыборкаЗаказПокупателя.Проведен);
// Выводим в окно сообщений признак пометки на удаление
Сообщить("Помечен на удаление: " + ВыборкаЗаказПокупателя.Номер);
// Получаем объект документа
ОбъектЗаказПокупателя = ВыборкаЗаказПокупателя.ПолучитьОбъект();
// Теперь мы можем корректировать документ
Если ОбъектЗаказПокупателя.Проведен Тогда
// Если документ проведен, отменим проведение
ОбъектЗаказПокупателя.Записать(
РежимЗаписиДокумента.ОтменаПроведения);
КонецЕсли;
КонецЦикла;
Итак, мы задали условие выбора документов — диапазон дат в пределах текущего дня. После этого с помощью метода Выбрать делаем выборку документов из базы данных. Первый параметр задает начальную дату поиска,
а второй — конечную дату. Далее организуем цикл для обхода всех найденных записей, предварительно позиционируя курсор специальным методом
Следующий на первую позицию. Теперь нам доступны для чтения номер и дата документа, а также мы можем узнать, проведен ли документ или помечен
на удаление. Кроме этого, можно прочитать значения всех реквизитов и получить доступ к табличным частям документа, если они у него есть. Как вы
заметили, все перечисленные данные о документе мы можем только читать.
Чтобы иметь возможность внести изменения, нам пришлось воспользоваться
методом ПолучитьОбъект, после чего документ стал доступен не только для
чтения, но и для записи. Таким образом, сделав несложный цикл, можно кор-
Глава 2. Выбор и поиск
19
ректировать набор документов одного вида, в данном примере ЗаказПокупателя. Кстати, начальную и конечную даты можно и не задавать, поскольку
это необязательные аргументы. В этом случае мы получим все имеющиеся
в базе документы указанного вида. Сразу замечу, что это не лучшее решение,
поскольку использование объектов конфигурации для выборки данных и без
того существенно замедляет работу всей системы в целом. Чем точнее будут
указанные условия выбора, тем быстрее будет обработан ваш запрос.
Дополнительно в методе Выбрать можно настроить отбор по одному определенному полю, для которого в конфигурации установлено свойство индексирования. Это может быть реквизит документа или табличной части. Условие
отбора задается при помощи третьего аргумента. И, наконец, имеется возможность осуществить упорядочивание по какому-либо полю, для которого
также в конфигурации установлено свойство индексирования. Значение упорядочивания задается в четвертом аргументе. Рассмотрим пример, использующий отбор и упорядочивание, приведенный в листинге 2.2.
Листинг 2.2. Выборка документов с отбором и упорядочиванием
// Задаем условие отбора по виду операции
УсловиеОтбора = Новый Структура("ВидОперации",
Перечисления.ВидыОперацийАвансовыйОтчет.АвансовыйОтчет);
// Задаем упорядочивание по дате создания документа
ПолеУпорядочивания = "Дата Убыв";
// Делаем выборку документов из базы
ВыборкаАвансовыйОтчет =
Документы.АвансовыйОтчет.Выбрать(, , УсловиеОтбора, ПолеУпорядочивания);
// Организуем цикл для обработки найденных документов
Пока ВыборкаАвансовыйОтчет.Следующий() Цикл
// Выводим в окно сообщений номер найденного документа
Сообщить("Номер документа: " + ВыборкаАвансовыйОтчет.Номер);
// Выводим в окно сообщений признак проведения
Сообщить("Проведен: " + ВыборкаАвансовыйОтчет.Проведен);
КонецЦикла;
Как видно из примера, в качестве условия отбора мы выбрали один из видов
операций, заданных в конфигурации для данного типа документа, и передали
его в виде структуры. Также мы определили способ упорядочивания записей — по дате создания документа в порядке убывания, т. е. начиная с самой
последней даты.
20
Часть I. Приемы программирования документов
Как уже говорилось, с помощью метода Выбрать можно получить не только
реквизиты документа, но и реквизиты табличных частей. На примере документа РасходныйОрдерНаТовары попробуем прочитать значения реквизитов
табличной части Товары, заданной для данного документа в конфигурации
(листинг 2.3).
Листинг 2.3. Получение реквизитов табличной части документа
// Задаем упорядочивание по дате создания документа
ПолеУпорядочивания = "Дата Возр";
// Делаем выборку документов из базы
ВыборкаРасходныйОрдерНаТовары =
Документы.РасходныйОрдерНаТовары.Выбрать(, , , ПолеУпорядочивания);
// Организуем цикл для обработки найденных документов
Пока ВыборкаРасходныйОрдерНаТовары.Следующий() Цикл
// Выводим в окно сообщений номер найденного документа
Сообщить("Номер документа: " + ВыборкаРасходныйОрдерНаТовары.Номер);
// Выводим в окно сообщений дату документа
Сообщить("Дата создания: " + ВыборкаРасходныйОрдерНаТовары.Дата);
// Получаем табличную часть Товары
ТабЧастьТовары = ВыборкаРасходныйОрдерНаТовары.Товары;
// Выводим в окно сообщений наименование номенклатуры
Сообщить("Номенклатура: " + ТабЧастьТовары.Номенклатура);
// Выводим в окно сообщений характеристику номенклатуры
Сообщить("Характеристика номенклатуры: " +
ТабЧастьТовары.ХарактеристикаНоменклатуры);
// Выводим в окно сообщений количество товара
Сообщить("Количество: " + ТабЧастьТовары.Количество);
// Выводим в окно сообщений цену товара
Сообщить("Цена: " + ТабЧастьТовары.Цена);
КонецЦикла;
Как видно из примера, мы сделали выборку всех имеющихся в базе документов РасходныйОрдерНаТовары с упорядочиванием по дате создания в порядке возрастания. После этого получили ссылку на табличную часть документа
Товары и вывели в служебное окно сообщений несколько наиболее информативных реквизитов табличной части документа. Как видите, все оказалось
достаточно просто и понятно. Замечу, что данные из табличной части можно
не только читать, но и корректировать. Для этого достаточно получить объект
Глава 2. Выбор и поиск
21
документа (см. листинг 2.1) и далее ссылку на данные табличной части. Думаю, вам не составит труда сделать это самостоятельно.
И последний вопрос, который мы разберем, касается поиска документов
в базе. Имеется два основных метода для организации поиска какого-либо
документа в базе: НайтиПоНомеру и НайтиПоРеквизиту. Первый из них позволяет найти документ по номеру и дате, а второй — по одному из реквизитов
документа, заданных в конфигураторе. В листинге 1.5 уже приводился пример, использующий метод НайтиПоНомеру, где в качестве единственного аргумента передавался номер документа. Если документ существует, мы получим ссылку на него, иначе система возвратит пустую ссылку. Есть еще
второй вариант этого метода, в котором задается не только номер документа,
но и дата. Указанная дата будет связана с определенным диапазоном дат, в
зависимости от установленного в конфигураторе признака контроля уникальности номеров и периодичности повторяемости одинаковых номеров
документов. Периодичность задается в пределах одного дня, месяца, квартала или года. Другими словами, при указании, например, даты 20 мая 2007 г. и
периода в 1 месяц, поиск искомого документа будет выполнен в диапазоне с
1 по 31 мая 2007 г. включительно. Рассмотрим пример, демонстрирующий
сказанное (листинг 2.4).
Листинг 2.4. Поиск документа по номеру и дате
// Пытаемся найти документ и получить на него ссылку
СсылкаПеремещениеТоваров =
Документы.ПеремещениеТоваров.НайтиПоНомеру("00345", ТекущаяДата());
Если СсылкаПеремещениеТоваров.Пустая() Тогда
// Искомый документ не найден в базе
Вопрос("Документ с указанным номером не найден!"
РежимДиалогаВопрос.OK, , , "Поиск");
Иначе
// Используем ссылку на документ для создания копии
НовыйПеремещениеТоваров = СсылкаПеремещениеТоваров.Скопировать();
// Корректируем документ при необходимости
НовыйПеремещениеТоваров.Номер = "00346";
// . . .
// Сохраняем новый документ в базе
НовыйПеремещениеТоваров.Записать();
КонецЕсли;
22
Часть I. Приемы программирования документов
В приведенном примере мы выполнили поиск документа по номеру и текущей дате. Если в конфигураторе свойство периодичности для данного вида
документа установлено как месяц, система будет искать документ с указанным номером в диапазоне дат, начиная с начала месяца и заканчивая последним днем. Номер месяца берется из текущей даты. Если документ не найден,
мы получим пустую ссылку и выведем на экран соответствующее сообщение.
В случае существования искомого документа с помощью метода Скопировать
создаем копию документа, с которой можно работать как с объектом, т. е.
корректировать, записывать, удалять, проводить.
Метод НайтиПоРеквизиту позволяет найти документ, указав какой-либо реквизит, заданный в конфигураторе. В качестве первого аргумента указывается
наименование самого реквизита, а во второй записывается его значение. Посмотрите, как это реализуется на примере листинга 2.5.
Листинг 2.5. Поиск документа по реквизиту
// Пытаемся найти документ и получить на него ссылку
СсылкаСписаниеТоваров =
Документы.СписаниеТоваров.НайтиПоРеквизиту("Ответственный",
"Радкевич О.А.");
Если СсылкаСписаниеТоваров.Пустая() Тогда
// Искомый документ не найден в базе
Вопрос("Документ не найден!", РежимДиалогаВопрос.OK, , , "Поиск");
Иначе
// Используем ссылку на документ для получения объекта
ОбъектСписаниеТоваров = СсылкаСписаниеТоваров.ПолучитьОбъект();
// Выбираем нового ответственного
ОбъектСписаниеТоваров.Ответственный =
Справочники.ФизическиеЛица.НайтиПоКоду("7");
// Сохраняем документ в базе
ОбъектСписаниеТоваров.Записать();
КонецЕсли;
Как видно из примера, вначале мы выполнили поиск документа, задав в качестве реквизита ответственного за создание документа. После этого, получив
объект документа, изменили имя сотрудника и сохранили документ в базе.
В завершение хотелось бы подчеркнуть, что наибольшая эффективность выбора множества документов из базы достигается не с помощью объектов
конфигурации, а посредством запросов, о которых будет подробно рассказано в главе 8. Описанные здесь методы выборки и поиска рекомендуется применять лишь для корректировки одиночных документов.
Глава 3
Получение ссылок
В предыдущих главах мы немного коснулись такого понятия, как ссылка на
документ. Основным предназначением ссылок является получение (чтение)
реквизитов документа и табличных частей, заданных на этапе проектирования в конфигураторе. Для тех читателей, которые знакомы с другими языками программирования (например, C++ или VB), смысл ссылок, думаю, вполне понятен. Для тех же, кто первый опыт программирования приобретает
в системе 1С, поясню, что применение ссылки на какой-либо объект позволяет гораздо эффективнее использовать память и в целом существенно повысить скорость работы программы. Представьте себе, для чего требуется
больше памяти: для получения всего объекта или для единственного адреса
(ссылки) в памяти, который ссылается на этот объект. Конечно же, адрес,
условно представляющий собою числовое значение, будет более правильным
решением. Поскольку в 1С полноценно реализованы принципы объектноориентированного программирования, работа через ссылки является базовым
звеном правильного подхода к программированию различных объектов конфигурации. Удобство ссылок трудно переоценить. Достаточно открыть любой справочник или документ в конфигураторе, чтобы убедиться, что многие
реквизиты представляют собой так называемые ссылочные типы данных (например, реквизит ВалютаДокумента является ссылкой на справочник валют).
Получить ссылку на документ можно посредством следующих базовых
объектов: ДокументВыборка, ДокументМенеджер, ДокументОбъект. Во всех
случаях в результате мы получаем объект ДокументСсылка. Такое разнообразие открывает широкие возможности для получения информации о документе в любой удобный для разработчика момент и в различных ситуациях.
Попытаемся подробно на примерах рассмотреть все эти варианты использования ссылок.
24
Часть I. Приемы программирования документов
Для объекта ДокументВыборка задано свойство Ссылка, которое содержит
ссылку на переданный документ. Через него можно читать значения реквизитов и табличных частей выбранного документа. В листинге 3.1 представлен
пример работы с данным объектом.
Листинг 3.1. Получение ссылки через объект ДокументВыборка
// Делаем выборку документов из базы
ВыборкаВозвратТоваровПоставщикам =
Документы.ВозвратТоваровПоставщикам.Выбрать();
// Организуем обход в цикле для всех полученных документов
Пока ВыборкаВозвратТоваровПоставщикам.Следующий() Цикл
// Получаем ссылку на документ
СсылкаВозвратТоваровПоставщикам =
ВыборкаВозвратТоваровПоставщикам.Ссылка;
// Можем проверить, проведен ли текущий документ
Если НЕ СсылкаВозвратТоваровПоставщикам.Проведен Тогда
Сообщить("Документ проведен.");
КонецЕсли;
// Получаем значение реквизита Комментарий
Описание = СсылкаВозвратТоваровПоставщикам.Комментарий;
// Можем получить объект документа для модификации
ОбъектВозвратТоваровПоставщикам =
СсылкаВозвратТоваровПоставщикам.ПолучитьОбъект();
// Можем получить форму документа
ФормаСпискаВозвратТоваровПоставщикам =
СсылкаВозвратТоваровПоставщикам.ПолучитьФорму("ФормаСписка");
// Можем проверить объект на пустую ссылку
Если СсылкаВозвратТоваровПоставщикам.Пустая() Тогда
Сообщить("Пустая ссылка!");
КонецЕсли;
// Можем создать новую копию существующего документа
СсылкаВозвратТоваровПоставщикам.Скопировать();
// Можем получить уникальный идентификатор объекта ссылки
ГУИД =
СсылкаВозвратТоваровПоставщикам.УникальныйИдентификатор();
КонецЦикла;
Как видно из примера, ссылка на объект позволяет не только прочитать общие сведения о документе и значения реквизитов, но и получить любые
Глава 3. Получение ссылок
25
существующие формы документа (например, основную форму), сам объект
для модификации, создать новую копию существующего документа, а также
определить уникальный идентификатор объекта. Кроме этого, можно обращаться к любым реквизитам документа и табличных частей, признаку пометки на удаление (ПометкаУдаления), объекту описания метаданных документа
(свойство Метаданные).
Базовый объект ДокументМенеджер дает возможность получить ссылку
на объект посредством уникального идентификатора. Данный идентификатор позволяет однозначно идентифицировать любой объект не только в
"1С:Предприятие 8.0", но и на уровне операционной системы Windows.
Рассмотрим пример его использования в листинге 3.2.
Листинг 3.2. Получение ссылки через объект ДокументМенеджер
// Получаем ссылку на документ
СсылкаПлатежноеПоручение = Документы.ПлатежноеПоручение.ПолучитьСсылку();
// Получаем ссылку на документ через известный идентификатор
ГУИД = "00000019-0000-0010-8000-00AA006D2EA4";
СсылкаПлатежноеПоручение =
Документы.ПлатежноеПоручение.ПолучитьСсылку(ГУИД);
// Получаем ссылку через создание нового идентификатора
НовыйГУИД = Новый УникальныйИдентификатор;
СсылкаПлатежноеПоручение =
Документы.ПлатежноеПоручение.ПолучитьСсылку(НовыйГУИД);
Как видно из примера, есть несколько вариантов получения ссылки через
уникальный идентификатор. В первом случае мы вызвали метод Получить
Ссылку без аргументов, в результате чего система сама сформировала и присвоила новый идентификатор объекта. Во втором случае нам был известен
идентификатор, и мы его просто передали методу для формирования ссылки.
И в последнем случае мы создали посредством объекта УникальныйИдентификатор новое уникальное значение идентификатора.
Объект ДокументОбъект также предоставляет свои возможности для управления ссылкой на документ. У него имеется свойство Ссылка и два метода:
ПолучитьСсылкуНового и УстановитьСсылкуНового. Свойство содержит ссылку на документ и доступно только для чтения. Методы позволяют, соответственно, получить и установить ссылку на новый объект документа, который
еще не сохранен (не записан методом Записать) в базе данных. В следующем
листинге 3.3 приводится пример использования свойства Ссылка.
26
Часть I. Приемы программирования документов
Листинг 3.3. Получение ссылки через свойство Ссылка объекта ДокументОбъект
// Делаем выборку документов из базы
ВыборкаПлатежноеПоручение = Документы.ПлатежноеПоручение.Выбрать();
// Организуем обход в цикле для всех полученных документов
Пока ВыборкаПлатежноеПоручение.Следующий() Цикл
// Получаем объект документа
ОбъектПлатежноеПоручение =
ВыборкаПлатежноеПоручение.ПолучитьОбъект();
// Получаем ссылку на объект
СсылкаПлатежноеПоручение =
ОбъектПлатежноеПоручение.ПолучитьОбъект();
КонецЦикла;
Итак, вначале мы организовали выборку документов. После этого с помощью
метода ПолучитьОбъект получили объект и затем, применив свойство Ссылка,
присвоили локальной переменной ссылку на объект документа.
А теперь на примере листинга 3.4 разберем методы ПолучитьСсылкуНового
и УстановитьСсылкуНового для управления ссылкой.
Листинг 3.4. Управление ссылкой через методы объекта ДокументОбъект
// Создаем новый документ
НовыйСчетФактура = Документы.СчетФактура.СоздатьДокумент();
// Формируем новую ссылку для документа
СсылкаСчетФактура = НовыйСчетФактура.ПолучитьСсылку();
// Устанавливаем ссылку для вновь созданного документа
НовыйСчетФактура.УстановитьСсылкуНового(СсылкаСчетФактура);
// Получаем ссылку на новый объект документа, еще не сохраненный в базе
СсылкаСчетФактура = НовыйСчетФактура.ПолучитьСсылкуНового;
Главным условием при работе с методами ПолучитьСсылкуНового и
УстановитьСсылкуНового является их вызов после создания нового документа, но до записи его в базу данных. При этом метод УстановитьСсылкуНового
должен всегда вызываться первым.
И последний очень полезный пример, о котором хотелось бы рассказать, касается проверки типа ссылки при использовании составных типов данных.
Составной тип, как правило, подразумевает выбор нескольких типов для
Глава 3. Получение ссылок
27
описания реквизита. Например, в документе РеализацияТоваровУслуг есть
реквизит составного типа Сделка, который может ссылаться на абсолютно
разные документы конфигурации: ВозвратТоваровОтПокупателя, ЗаказПокупателя, КорректировкаДолга, ПоступлениеДопРасходов, ПоступлениеТоваровУслуг, ПриходныйКассовыйОрдер и др. В таких случаях возникает
проблема правильного определения типа ссылки. Решение данной задачи
представлено в листинге 3.5.
Листинг 3.5. Определение типа ссылки для составных типов
// Создаем процедуру для обработки составного типа
Процедура ОбработатьТипСсылки(ДокументСсылка)
// Проверяем тип ссылки на определенные документы
Если ТипЗнч(ДокументСсылка) =
Тип("ДокументСсылка.ВозвратТоваровОтПокупателя") Тогда
// Выполняем необходимые действия
ИначеЕсли ТипЗнч(ДокументСсылка) =
Тип("ДокументСсылка.ЗаказПокупателя") Тогда
// Выполняем необходимые действия
ИначеЕсли ТипЗнч(ДокументСсылка) =
Тип("ДокументСсылка.КорректировкаДолга") Тогда
// Выполняем необходимые действия
КонецЕсли;
КонецПроцедуры
Как видите, проверка типа ссылки не представляет собой ничего сложного.
С помощью системной функции ТипЗнч мы определяем тип значения и, если
он является ссылкой на нужный тип (системная функция Тип преобразует
строку описания в указанный тип значения) документа, выполняем соответствующую обработку.
Глава 4
Получение форм
Как известно, любое современное приложение, разрабатываемое для конечных
пользователей, содержит в себе различные средства интерактивного взаимодействия с программой, иначе говоря, интерфейс. Система "1С:Предприятие 8.0"
предоставляет разработчикам широкий выбор объектов, позволяющих быстро
создавать полный набор интерфейсных модулей: меню, панелей инструментов, всевозможных элементов управления. К ним также относятся формы,
играющие ключевую роль в формировании удобного интерфейса пользователя. Конфигуратор поддерживает создание общих форм (доступных из любого
объекта конфигурации), форм справочников, форм документов, форм для
отчетов и обработок, форм для журналов документов и регистров, а также
для некоторых других объектов, не рассматриваемых в данной книге.
Документы, как правило, всегда содержат несколько форм. На этапе разработки конфигурации предоставляется возможность задавать для каждого документа различные типы форм. Наиболее часто используются несколько определенных типов форм:
основная форма — позволяет пользователю создавать новый и корректи-
ровать существующий документ, заполнять реквизиты документа и табличных частей, сохранять документ в базе, проводить и отменять проводку;
форма работы со списком — специальная форма, позволяющая отобра-
жать список однотипных документов, в которой доступны различные
средства поиска и отбора, сортировки, создания и удаления, ввода на основании (создание нового документа другого типа на основании выбранного из списка) и многое другое;
форма выбора — предоставляет возможность выбора из списка какого-
либо документа с передачей по ссылке в другую форму.
Глава 4. Получение форм
29
Кроме перечисленных типов форм, имеется возможность создания пользовательских форм произвольного вида. В этом случае разработчик сам определяет предназначение и способы использования данных форм. Как правило,
после формирования всех необходимых форм документа, одну из них назначают основной. Такая форма будет вызываться по умолчанию, если при вызове методов отображения не будет явно задан тип формы.
Здесь мы разберем приемы программирования только базовых и общих
форм, но этого будет вполне достаточно для самостоятельной работы с любыми произвольными типами форм, поскольку для доступа и к тем и другим
используются одни и те же базовые объекты: ДокументМенеджер, Документ
Объект и ДокументСсылка.
В большинстве случаев, при разработке нового типа документа и создании
для него различных форм, в качестве основной выбирают форму документа,
которая содержит реквизиты и табличные части. Элементы управления, входящие в конфигуратор, позволяют в короткие сроки формировать удобный,
красивый и функциональный интерфейс. Пример такого документа представлен на рис. 4.1.
Рис. 4.1. Документ Заказ покупателя
30
Часть I. Приемы программирования документов
Следует отметить, что большинство документов в системе "1С:Предприятие 8.0"
имеют единый вид, что является примером грамотно продуманного интерфейса пользователя. Это дает возможность решить две наиболее важные задачи: удобство обучения и концентрация на самом процессе работы.
Для программного доступа к формам документа, как уже отмечалось ранее,
служат несколько базовых объектов. Разберем их по порядку. Первый из них,
ДокументМенеджер, предоставляет несколько удобных методов для получения
объектов форм. С помощью метода ПолучитьФорму можно получить любую
форму документа, заданную в конфигураторе на этапе проектирования. Посмотрите пример работы с данным методом, представленный в листинге 4.1.
Листинг 4.1. Получение форм документа
// Получаем новую форму документа
ФормаДокумента =
Документы.ЗаказПокупателя.ПолучитьФорму("ФормаДокумента");
// Открываем полученную форму
ФормаДокумента.Открыть();
// Получаем форму выбора
ФормаВыбораДокумента =
Документы.ЗаказПокупателя.ПолучитьФорму("ФормаВыбора");
// Проверяем, если форма уже открыта, просто активизируем ее
Если ФормаВыбораДокумента.Открыта() Тогда
ФормаВыбораДокумента.Активизировать();
Иначе
ФормаВыбораДокумента.ОткрытьМодально();
КонецЕсли;
// Получаем форму списка
ФормаСпискаДокумента =
Документы.ЗаказПокупателя.ПолучитьФорму("ФормаСписка");
// Проверяем, если форма уже открыта, обновляем ее
Если ФормаСпискаДокумента.Открыта() Тогда
ФормаСпискаДокумента.Обновить();
Иначе
ФормаСпискаДокумента.Открыть();
КонецЕсли;
Глава 4. Получение форм
31
Как видно из примера, достаточно указать наименование формы документа,
чтобы получить доступ ко всем свойствам и методам, управляющим поведением и отображением объекта формы. Для открытия формы мы использовали
методы Открыть и ОткрытьМодально. Первый открывает форму и выводит ее
на экран, а второй, кроме того, позволяет задать режим модальности. При
таком режиме пользователь должен прежде закрыть данную форму, чтобы
продолжить работу с другими окнами. Метод Открыть помогает убедиться,
что открываемая форма уже не открыта раньше. Метода Активизировать
делает открытую форму активной (доступной для ввода данных), а метод Обновить — просто обновляет все элементы формы.
Дополнительно, в методе ПолучитьФорму можно задать владельца открываемой формы, так называемую родительскую форму, из которой открывается данная форма. Владелец задается вторым необязательным аргументом. Существует еще и третий необязательный аргумент, представляющий
собой уникальный ключ, по которому можно искать уже открытую форму.
Для наглядности изложенного посмотрите пример, представленный в листинге 4.2.
Листинг 4.2. Дополнительные возможности метода ПолучитьФорму
// Получаем форму списка из другой формы-владельца
ФормаСпискаДокумента =
Документы.ОприходованиеТоваров.ПолучитьФорму("ФормаСписка", Владелец);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаСпискаДокумента.ЗакрыватьПриЗакрытииВладельца = Истина;
// Открываем полученную форму
ФормаСпискаДокумента.Открыть();
// Получаем форму и задаем уникальный ключ
ФормаВыбораДокумента =
Документы.ОприходованиеТоваров.ПолучитьФорму("ФормаВыбора", , "ключ1");
// Открываем полученную форму
ФормаВыбораДокумента.Открыть();
// Получаем форму, используя уникальный ключ
ФормаВыбораДокумента =
Документы.ОприходованиеТоваров.ПолучитьФорму(, , "ключ1");
// Открываем форму в виде модального окна
ФормаВыбораДокумента.ОткрытьМодально();
32
Часть I. Приемы программирования документов
В качестве владельца может выступать любая форма, заданная в конфигурации. Установленный признак ЗакрыватьПриЗакрытииВладельца приводит
к автоматическому закрытию формы при закрытии владельца. Уникальный
ключ, доступный как для чтения, так и для записи, помогает однозначно
идентифицировать объект формы, исключив случайные совпадения имен.
Неповторимое значение ключа можно сформировать с помощью объекта
УникальныйИдентификатор. Использование ключа при открытии формы позволяет реализовать вывод на экран нескольких копий одного документа.
Для упрощения процесса вызова нужного типа формы, можно воспользоваться дополнительными методами объекта ДокументМенеджер:
ПолучитьФормуВыбора — вызывает форму выбора для указанного типа до-
кумента;
ПолучитьФормуСписка — вызывает форму списка документа;
ПолучитьФормуНовогоДокумента — вызывает форму для заполнения ново-
го документа.
Названия этих методов говорят сами за себя. Все они имеют по три необязательных аргумента: наименования формы, владельца формы и уникального
ключа. В отличие от метода ПолучитьФорму, где первый аргумент (наименование формы, заданное в конфигураторе) требуется указывать всегда, дополнительные методы просто получают определенный тип формы. Пример использования этих методов представлен в листинге 4.3.
Листинг 4.3. Дополнительные методы получения форм документа
// Получаем форму списка документа
ФормаСпискаДокумента = Документы.АвансовыйОтчет.ПолучитьФормуСписка();
// Открываем полученную форму
ФормаСпискаДокумента.Открыть();
// Формируем уникальный ключ
ГУИД = Новый УникальныйИдентификатор;
// Получаем форму выбора документа и назначаем ей уникальный ключ
ФормаВыбораДокумента =
Документы.АвансовыйОтчет.ПолучитьФормуВыбора(, , ГУИД);
// Открываем полученную форму
ФормаВыбораДокумента.ОткрытьМодально(10);
// Получаем эту же форму по значению ключа
ФормаВыбораДокументаПоКлючу =
Глава 4. Получение форм
33
Документы.АвансовыйОтчет.ПолучитьФорму(, , ГУИД);
// Если форма уже открыта, активизируем ее
Если ФормаВыбораДокументаПоКлючу.Открыта() Тогда
ФормаВыбораДокументаПоКлючу.Активизировать();
Иначе
ФормаВыбораДокументаПоКлючу.Открыть();
КонецЕсли;
// Получаем форму нового документа
ФормаНовогоДокумента =
Документы.АвансовыйОтчет.ПолучитьФормуНовогоДокумента();
Рассмотренные варианты получения различных типов форм демонстрируют
широкие возможности, доступные разработчику для управления формами
документов.
В базовом объекте ДокументОбъект имеется один метод для получения формы. Называется он ПолучитьФорму, имеет три необязательных аргумента,
а в остальном ничем ни отличается от аналогичного метода объекта
ДокументМенеджер. Для объекта ДокументСсылка также предусмотрен метод ПолучитьФорму. Примеры их использования приведены в листинге 4.4.
Листинг 4.4. Получение форм документа из объектов ДокументОбъект
и ДокументМенеджер
// Делаем выборку документов из базы
ВыборкаПриходныйОрдерНаТовары =
Документы.ПриходныйОрдерНаТовары.Выбрать();
// Организуем цикл для обработки найденных документов
Пока ВыборкаПриходныйОрдерНаТовары.Следующий() Цикл
// Получаем объект документа
ОбъектПриходныйОрдерНаТовары =
ВыборкаПриходныйОрдерНаТовары.ПолучитьОбъект();
// Получаем форму документа
ФормаДокумента = ОбъектПриходныйОрдерНаТовары.ПолучитьФорму();
Если НЕ ФормаВыбораДокументаПоКлючу.Открыта() Тогда
ФормаДокумента.Открыть();
КонецЕсли;
КонецЦикла;
34
Часть I. Приемы программирования документов
// Делаем выборку документов из базы
ВыборкаРеализацияТоваров = Документы.РеализацияТоваров.Выбрать();
// Организуем обход в цикле для всех полученных документов
Пока ВыборкаРеализацияТоваров.Следующий() Цикл
// Получаем ссылку на документ
СсылкаРеализацияТоваров = ВыборкаРеализацияТоваров.Ссылка;
// Получаем форму документа
ФормаДокумента = СсылкаРеализацияТоваров.ПолучитьФорму();
// Открываем полученную форму документа
ФормаДокумента.Открыть();
КонецЦикла;
И последнее, что хотелось бы представить вниманию читателей — это получение так называемых общих форм. Общие формы позволяют использовать
какой-либо стандартный набор действий, доступный из любого объекта конфигурации. Например, чтобы не создавать для каждого документа формы поиска
и отбора по списку, достаточно построить одну общую форму и вызывать по
мере необходимости из разных документов. Для получения любой общей формы предназначен глобальный метод ПолучитьОбщуюФорму. Он имеет три необязательных аргумента, аналогичных рассмотренным ранее для метода
ПолучитьФорму. Пример его использования представлен в листинге 4.5.
Листинг 4.5. Получение общих форм конфигурации
// Получаем стандартную общую форму ФормаЦеныИВалюта
ОбщаяФорма = ПолучитьОбщуюФорму("ФормаЦеныИВалюта");
// Выполняем настройку элементов управления общей формы
// . . .
// Открываем полученную форму
ОбщаяФорма.ОткрытьМодально();
Как видите, получить общую форму достаточно просто. Однако перед тем,
как открыть ее, как правило, требуется выполнить настройку элементов
управления и заполнить начальные параметры использования. Поскольку
настройки даже для стандартных (поставляемых фирмой 1С) общих форм
могут сильно отличаться, здесь мы их приводить не будем.
Глава 5
Чтение и установка свойств
В этой главе мы поговорим о существующих свойствах документов, доступных через базовые объекты конфигурации: ДокументОбъект, ДокументСписок, ДокументВыборка и ДокументСсылка. Свойства позволяют получить
различную информацию о документе: значения реквизитов и табличных частей, определить состояние документа и многое другое. Установить свойства
возможно только через ДокументОбъект, в остальных случаях доступно
лишь чтение значений.
С помощью объекта ДокументОбъект, в первую очередь, можно обратиться
к значениям реквизитов, заданным в конфигураторе для данного документа.
Поскольку реквизиты у разных документов разные, необходимо изучить
структуру каждого перед использованием. Реквизиты хранят различную информацию о документе: валюта, сумма документа, наименование организации и контрагента, вид документа, кратность, ответственный и др. Рассмотрим на примере, представленном в листинге 5.1, работу со свойствами
документов.
Листинг 5.1. Чтение реквизитов документа
// Задаем критерии выборки по дате
НачальнаяДата = НачалоДня(ТекущаяДата());
КонечнаяДата
= КонецДня(ТекущаяДата());
// Делаем выборку документов из базы за текущий день
ВыборкаВнутреннийЗаказ =
Документы.ВнутреннийЗаказ.Выбрать(НачальнаяДата, КонечнаяДата);
// Организуем цикл для обработки найденных документов
Пока ВыборкаВнутреннийЗаказ.Следующий() Цикл
36
Часть I. Приемы программирования документов
// Получаем объект документа
Объект = ВыборкаВнутреннийЗаказ.ПолучитьОбъект();
// Читаем значения реквизитов и выводим в окно сообщений
Сообщить("Номер документа: " + Объект.Номер);
// Валюта документа
Сообщить("Валюта документа: " + Объект.ВалютаДокумента);
// Наименование склада компании
Сообщить("Склад компании: " + Объект.СкладКомпании);
// Признак автоматического резервированиия документа
Сообщить("Авторезервирование: " +
?(Объект.АвтоРезервирование, "истина", "ложь");
// Признак автоматического размещения
Сообщить("Авторазмещение: " +
?(Объект.АвтоРазмещение, "истина", "ложь");
// Наименование организации
Сообщить("Наименование организации: " +
Объект.Организация);
// Ответственный за формирование документа
Сообщить("Ответственный сотрудник: ", Объект.Ответственный);
КонецЦикла;
Как видно из примера, вначале мы организовали выборку документов на текущую дату. Для получения объекта применили метод ПолучитьОбъект. Таким образом, нам стали доступны все реквизиты документа, которые мы успешно прочитали и вывели в окно служебных сообщений 1С.
Установка реквизитов не сильно отличается от чтения, но следует правильно
передавать значения типов, согласно назначенным в конфигураторе. Пример
записи реквизитов представлен в листинге 5.2.
Листинг 5.2. Установка реквизитов документа
// Задаем критерии выборки по дате
НачальнаяДата = НачалоДня(ТекущаяДата());
КонечнаяДата
= КонецДня(ТекущаяДата());
// Делаем выборку документов из базы за текущий день
ВыборкаЗаказПоставщику =
Документы.ЗаказПоставщику.Выбрать(НачальнаяДата, КонечнаяДата);
// Организуем цикл для обработки найденных документов
Глава 5. Чтение и установка свойств
37
СчетчикНомера = 1;
Пока ВыборкаЗаказПоставщику.Следующий() Цикл
// Получаем объект документа
Объект = ВыборкаЗаказПоставщику.ПолучитьОбъект();
// Устанавливаем значения реквизитов
Объект.Номер = "Новый номер " + Строка(СчетчикНомера);
// Получаем ссылку на валюту
СсылкаВалюта = Справочники.Валюты.НайтиПоНаименованию("RUR");
// Устанавливаем выбранную валюту для документа
Объект.ВалютаДокумента = СсылкаВалюта;
// Выбираем новую организацию
СсылкаОрганизация = Справочники.Организации.НайтиПоКоду("0015");
// Устанавливаем новую ссылку на организацию
Объект.Организация = СсылкаОрганизация;
// Выбираем нового ответственного
СсылкаОтветственный =
Справочники.ФизическиеЛица. НайтиПоНаименованию("Иванов И.И.");
// Сохраняем сделанные изменения в базе
Объект.Записать();
СчетчикНомера = СчетчикНомера + 1;
КонецЦикла;
Итак, в рассмотренном примере мы выполнили установку реквизитов документов. Список документов получили через объект ДокументВыборка. Вопервых, мы изменили номера выбранных документов, воспользовавшись числовой переменной СчетчикНомера для формирования уникального значения.
Далее установили новую валюту. Поскольку тип реквизита является ссылкой
на справочник валют, нам понадобилось вначале получить ссылку на выбранную валюту. Для этого был выбран метод НайтиПоНаименованию. Как он
работает, вы уже знаете. Кроме того, используя ссылки, мы скорректировали
в документах организацию и ответственного за создание документа. Чтобы
сохранить сделанные изменения, вызвали метод Записать.
Объект ДокументОбъект позволяет также получить доступ к табличным
частям документа. Имена табличных частей, заданные в конфигураторе,
и являются свойствами документа. Однако, в отличие от реквизитов, данные табличных частей можно только читать. Посмотрите, как это делается
в листинге 5.3.
38
Часть I. Приемы программирования документов
Листинг 5.3. Чтение реквизитов табличных частей документа
// Делаем выборку всех документов из базы
ВыборкаРеализацияТоваровУслуг =
Документы.РеализацияТоваровУслуг.Выбрать();
// Организуем цикл для обработки найденных документов
Пока ВыборкаРеализацияТоваровУслуг.Следующий() Цикл
// Получаем объект документа
Объект = ВыборкаРеализацияТоваровУслуг.ПолучитьОбъект();
// Получаем таблиную часть Товары
ТЧ_Товары = Объект.Товары;
// Выводим наименование номенклатуры
Сообщить("Номенклатура: " + ТЧ_Товары.Номенклатура);
// Выводим характеристику номенклатуры
Сообщить("Характеристика номенклатуры: " +
ТЧ_Товары.ХарактеристикаНоменклатуры);
// Выводим количество товара
Сообщить("Количество: " + Строка(ТЧ_Товары.Количество));
// Выводим серию номенклатуры
Сообщить("Серия номенклатуры: " + ТЧ_Товары.СерияНоменклатуры);
// Получаем табличную часть Услуги
ТЧ_Услуги = Объект.Услуги;
// Получаем наименование услуги
Сообщить("Наименование услуги: " + ТЧ_Услуги.Номенклатура);
// Получаем количество
Сообщить("Количество: " +
Формат(ТЧ_Услуги.Количество, "ЧЦ=15;ЧДЦ=3"));
// Получем единицу измерения
Сообщить("Единица измерения: " +
Строка(ТЧ_Услуги.ЕдиницаИзмерения));
// Получаем табличную часть ВозвратнаяТара
ТЧ_ВозвратнаяТара = Объект.ВозвратнаяТара;
// Получаем наименование возвратной тары
Сообщить("Наименование тары: " + ТЧ_ВозвратнаяТара.Номенклатура);
// Получаем количество
Сообщить("Количество: " +
Формат(ТЧ_ВозвратнаяТара.Количество, "ЧЦ=15;ЧДЦ=3"));
// Получаем цену
Сообщить("Цена: " +
Глава 5. Чтение и установка свойств
39
Формат(ТЧ_ВозвратнаяТара.Цена, "ЧЦ=15;ЧДЦ=2"));
// Получаем значение суммы
Сообщить("Сумма возвратной тары: " +
Формат(ТЧ_ВозвратнаяТара.Сумма, "ЧЦ=15;ЧДЦ=2"));
КонецЦикла;
Как видите, получить значения реквизитов табличных частей достаточно
просто. Поскольку документ РеализацияТоваровУслуг в варианте конфигурации УПП содержит три табличные части, мы получили все три. Однако
в вашей конфигурации может отсутствовать, например, табличная часть
Услуги, поэтому всегда проверяйте структуру документов именно в той конфигурации, в которой вы работаете.
Объект ДокументОбъект позволяет еще управлять следующими значениями
свойств: номер и дата документа, признак проведения и признак удаления.
Эти свойства доступны как для чтения, так и для записи. Пример работы
с ними представлен в листинге 5.4.
Листинг 5.4. Чтение и установка свойств документа
// Делаем выборку всех документов из базы
ВыборкаПоступлениеДопРасходов =
Документы.ПоступлениеДопРасходов.Выбрать();
// Организуем цикл для обработки найденных документов
Пока ВыборкаПоступлениеДопРасходов.Следующий() Цикл
// Получаем объект документа
Объект = ВыборкаПоступлениеДопРасходов.ПолучитьОбъект();
// Получаем и устанавливаем номер документа
НомерДокумента = Объект.Номер;
Объект.Номер = НомерДокумента + " новый";
// Получаем и устанавливаем дату документа
ДатаДокумента = Объект.Дата;
Объект.Дата = ТекущаяДата();
// Определяем признак проведения документа
Если Объект.Проведен Тогда
Объект.Проведен = Ложь;
КонецЕсли;
// Определяем признак пометки на удаление
Если Объект.ПометкаУдаления Тогда
Объект = Ложь;
40
Часть I. Приемы программирования документов
КонецЕсли;
// Сохраняем изменения в базе
Объект.Сохранить();
КонецЦикла;
И последние свойства, о которых хотелось бы здесь упомянуть, доступны
только для чтения и помогают получить сведения о движениях документа,
ссылке на документ и обращения к самому объекту документа.
Движения документа можно прочитать из свойства Движения, которое возвращает коллекцию наборов записей о движениях документа в системе.
На этапе разработки в свойствах документа задаются объекты (регистры),
по которым документ может выполнять движения. Приведем пример получения
движений документа по заданным в конфигураторе регистрам (листинг 5.5).
Листинг 5.5. Получение информации о движениях документа
// Делаем выборку всех документов из базы
ВыборкаПланПродаж = Документы.ПланПродаж.Выбрать();
// Организуем цикл для обработки найденных документов
Пока ВыборкаПланПродаж.Следующий() Цикл
// Получаем объект документа
Объект = ВыборкаПланПродаж.ПолучитьОбъект();
// Получаем коллекцию движений документа
ДвиженияПоРегистрам = Объект.Движения;
// Выбираем движения по регистру сведений ЦеныКонтрагентов
ДвиженияЦеныКонтрагентов = ДвиженияПоРегистрам.ЦеныКонтрагентов;
// Читаем набор записей
ДвиженияЦеныКонтрагентов.Прочитать();
Для Каждого Запись Из ДвиженияЦеныКонтрагентов Цикл
Сообщить(Запись.Цена);
КонецЦикла;
// Выбираем движения по регистру накопления ЗаказыПокупателей
ДвиженияЗаказыПокупателей = ДвиженияПоРегистрам.ЗаказыПокупателей;
// Читаем набор записей
ДвиженияЗаказыПокупателей.Прочитать();
Для Каждого Запись Из ДвиженияЗаказыПокупателей Цикл
Сообщить(Запись.Количество);
КонецЦикла;
Глава 5. Чтение и установка свойств
41
// Выбираем движения по регистру накопления ПартииТоваровКомпании
ДвиженияПартииТоваровКомпании =
ДвиженияПоРегистрам.ПартииТоваровКомпании;
// Читаем набор записей
ДвиженияПартииТоваровКомпании.Прочитать();
Для Каждого Запись Из ДвиженияПартииТоваровКомпании Цикл
Сообщить(Запись.Количество);
Сообщить(Запись.Стоимость);
КонецЦикла;
КонецЦикла;
Итак, сделав выборку из базы, мы получили доступ к объекту документа. После этого, используя свойство Движения, прочитали коллекцию наборов движений документа по регистрам. По каким именно регистрам проходит документ, задается в конфигураторе. В данном случае, мы выбрали данные
о движениях по регистру сведений ЦеныКонтрагентов и двум регистрам накопления — ЗаказыПокупателей и ПартииТоваровКомпании. С помощью
метода Прочитать получаем из регистра набор записей для указанного документа. Далее делаем обход по записям и считываем значения ресурсов регистра.
Свойство Ссылка позволяет получить ссылку на документ, а свойство
ЭтотОбъект возвращает сам объект документа. Его удобно применять в модулях формы или объекта. Пример работы с этими свойствами представлен
в листинге 5.6.
Листинг 5.6. Использование свойств Ссылка и ЭтотОбъект
// Делаем выборку всех документов из базы
ВыборкаЗаказПоставщику = Документы.ЗаказПоставщику.Выбрать();
// Организуем цикл для обработки найденных документов
Пока ВыборкаЗаказПоставщику.Следующий() Цикл
// Получаем объект документа
Объект = ВыборкаЗаказПоставщику.ПолучитьОбъект();
// Получаем ссылку на документ
СсылкаЗаказПоставщику = Объект.Ссылка;
КонецЦикла;
// Если мы находимся в модуле объекта или формы документа
// Получаем ссылку на документ без метода ПолучитьОбъект
СсылкаЗаказПоставщику = ЭтотОбъект.Ссылка;
42
Часть I. Приемы программирования документов
Объект ДокументСписок также имеет несколько полезных свойств, о которых мы сейчас поговорим. К слову, создать данный объект программно не
получится. Он создается системой автоматически на этапе создания формы
списка документов или явном добавлении разработчиком на закладке реквизитов формы. С помощью него организуется отбор, сортировка и размещение
колонок в таблице списка документов. Через свойства этого объекта можно
управлять размещением и отображением колонок в табличной части, работать с отборами и сортировкой. Отборы нужны для наложения ограничений
(по дате, организации и т. д.) на отображаемую в таблице информацию. Сортировки помогают упорядочить вывод данных по порядку (например, по дате
создания в порядке убывания). Посмотрите в листинге 5.7, как можно применять свойства объекта ДокументСписок для вывода информации об установленных полях отбора.
Листинг 5.7. Получение данных об установленных отборах
// Прочитаем отборы, установленные для списка документов формы
Для Каждого ЭлементОтбора Из ЭтаФорма.ДокументСписок.Отбор Цикл
// Проверяем использование поля в отборе
Если ЭлементОтбора.Использование Тогда
// Выводим имя и представление элемента отбора
Сообщить(ЭлементОтбора.Имя + ЭлементОтбора.Представление);
// Выводим вид сравнения
Сообщить(ЭлементОтбора.ВидСравнения);
// Выводим значение отбора
Сообщить(ЭлементОтбора.Значение);
// Выводим диапазон значений
Сообщить(ЭлементОтбора.ЗначениеС + " " +
ЭлементОтбора.ЗначениеПо);
КонецЕсли;
КонецЦикла;
Для получения данных полей отбора мы применили цикл, в котором выбрали
с помощью объекта Отбор имена и представления всех используемых (свойство Использование) отборов, а также вид сравнения и выбранный период.
Чтобы установить отбор по определенному полю таблицы, достаточно через
объект Отбор присвоить значение поля отбора и установить для него свойство Использование в значение Истина. Пример установки отбора по реквизитам документа представлен в листинге 5.8.
Глава 5. Чтение и установка свойств
43
Листинг 5.8. Установка отбора по реквизиту документа
// Устанавливаем отбор по организации
ЭтаФорма.ДокументСписок.Отбор.Организация.ВидСравнения =
ВидСравнения.Равно;
ЭтаФорма.ДокументСписок.Отбор.Организация.Значение = Организация;
ЭтаФорма.ДокументСписок.Отбор.Организация.Использование = Истина;
// Устанавливаем отбор по ответственному
ЭтаФорма.ДокументСписок.Отбор.Организация.ВидСравнения =
ВидСравнения.Равно;
ЭтаФорма.ДокументСписок.Отбор.Ответственный.Значение = Ответственный;
ЭтаФорма.ДокументСписок.Отбор.Ответственный.Использование = Истина;
Объект Отбор имеет набор элементов отбора, совпадающий с заданными для
документа реквизитами. В свою очередь, для каждого элемента отбора доступны стандартные свойства: вид сравнения (устанавливает вид операции
сравнения), значение (для реквизитов, не использующих период), значения
периода, имя, представление, использование (указывает, используется ли
данный элемент в отборе) и др.
Свойство Порядок (объект Порядок) позволяет настроить сортировку списка по
возрастанию или убыванию. Как правило, такая необходимость возникает при
отображении данных с учетом даты создания, суммы документа или других
критериев. Пример работы со свойством Порядок представлен в листинге 5.9.
Листинг 5.9. Получение данных об установленных сортировках
// Прочитаем сортировки, установленные для списка документов формы
Для Каждого ЭлементПорядка Из ЭтаФорма.ДокументСписок.Порядок Цикл
// Выводим имя и представление элемента сортировки
Сообщить(ЭлементПорядка.Имя + ЭлементПорядка.Представление);
// Выводим направление сортировки
Сообщить(ЭлементПорядка.Направление);
КонецЦикла;
И последнее свойство, доступное для объекта ДокументСписок, называется
Колонки. Оно позволяет получить сведения обо всех колонках текущего списка документов. Количество колонок зависит от количества реквизитов документа, заданных в конфигураторе. Для получения сведений об используемых в списке колонках можно написать код, как в листинге 5.10.
44
Часть I. Приемы программирования документов
Листинг 5.10. Получение сведений о колонках списка документов
// Прочитаем имена колонок
Для Каждого КолонкаСписка Из ЭтаФорма.ДокументСписок.Колонки Цикл
// Выводим имя колонки списка
Сообщить(КолонкаСписка.Имя);
// Выводим признак автоудаления
Сообщить(КолонкаСписка.АвтоУдаление);
КонецЦикла;
Из примера видно, что, воспользовавшись циклом, мы перебрали все доступные колонки списка документа и вывели в окно служебных сообщений имя и
признак автоудаления для каждой колонки. Признак автоудаления позволяет
определить признак удаления колонки из списка при удалении соответствующей колонки из таблицы. Хочу заметить, что объект списка документов
и табличное поле документа, отображающее колонки данных, являются разными объектами в отношении установки отбора, сортировки или управления
колонками. Первый из них содержит данные запроса, а второй выводит информацию на экран.
И последний объект, о свойствах которого хотелось упомянуть — это
ДокументСсылка. Его доступные свойства можно лишь читать, и они не отличаются от свойств объекта ДокументОбъект. Для работы с ними достаточно получить ссылку на документ. Получение ссылок подробно рассмотрено
в главе 3.
Глава 6
Заполнение данными
В этой главе мы поговорим о том, как программно заполнять данными различные документы. Как правило, необходимость в этом возникает при пакетной обработке большого числа документов или при автоматическом формировании новых документов на основании других.
В общем случае, наша задача сводится к двум основным действиям: заполнение требуемых реквизитов и заполнение табличной части документа. Чтобы
сформировать новый вид документа на основании другого, можно воспользоваться методом Заполнить объекта ДокументОбъект. Он принимает один
аргумент, который может быть ссылкой на другой документ, на основании
которого формируется новый документ. Пример использования данного метода представлен в листинге 6.1.
Листинг 6.1. Использование метода Заполнить
// Получаем ссылку на нужный документ
СсылкаЗаказПоставщику = Документы.ЗаказПоставщику.НайтиПоНомеру("001");
// Проверяем существование документа
Если НЕ СсылкаЗаказПоставщику.Пустая() Тогда
// Создаем новый документ на основании заказа поставщику
НовыйПоступлениеТоваров =
Документы.ПоступлениеТоваров.СоздатьДокумент();
// Заполняем новое поступление на основании заказа
НовыйПоступлениеТоваров.Заполнить(СсылкаЗаказПоставщику);
// Записываем новый документ в базу
НовыйПоступлениеТоваров.Записать();
// или открываем форму документа для редактирования
46
Часть I. Приемы программирования документов
// ФормаДокумента =
//
НовыйПоступлениеТоваров.ПолучитьФорму("ФормаДокумента");
// Открываем форму документа
// ФормаДокумента.Открыть();
КонецЕсли;
Как видно из примера, вначале мы получили ссылку на документ заказа, на
основании которого необходимо создать новый документ поступления. После этого с помощью метода СоздатьДокумент сформировали новый пустой
документ. Далее вызываем метод Заполнить и заполняем реквизиты и табличную часть нового документа. В итоге вновь созданный документ мы можем либо записать в базу, либо открыть для редактирования пользователем.
Как видите, все достаточно просто и красиво.
Рассмотренный алгоритм удобно применять, когда в конфигураторе заданы
документы, вводимые на основании исходного. Данная операция выполняется при разработке новой конфигурации. В свойствах любого документа можно
с помощью специального конструктора настроить процедуру ввода на основании. В готовой конфигурации следует проверить, какие виды документов
заданы для ввода на основании исходного. Например, на основании документа
ЗаказПокупателя можно сформировать документы: РеализацияТоваровУслуг,
ВозвратТоваровОтПокупателя, КорректировкаЗаказаПокупателя, Приходный
КассовыйОрдер и др.
Когда же возникает необходимость сформировать новый документ на основании другого, и он не задан явно в конфигураторе, приходится самостоятельно заполнять необходимые реквизиты и табличную часть документа. Посмотрите на пример такого кода, представленный в листинге 6.2.
Листинг 6.2. Использование метода Заполнить
// Получаем ссылку на нужный документ
СсылкаЗаказПокупателя = Документы.ЗаказПокупателя.НайтиПоНомеру("005");
// Проверяем существование документа
Если НЕ СсылкаЗаказПокупателя.Пустая() Тогда
// Создаем новый документ
НовыйАвансовыйОтчет = Документы.АвансовыйОтчет.СоздатьДокумент();
// Заполняем реквизиты
НовыйАвансовыйОтчет.Номер = СсылкаЗаказПокупателя.Номер;
НовыйАвансовыйОтчет.Дата = СсылкаЗаказПокупателя.Дата;
НовыйАвансовыйОтчет.ВидОперации =
Глава 6. Заполнение данными
Перечисления.ВидОперацийАвансовыйОтчет.АвансовыйОтчет;
НовыйАвансовыйОтчет.КурсДокумента =
СсылкаЗаказПокупателя.КурсДокумента;
НовыйАвансовыйОтчет.ВалютаДокумента =
СсылкаЗаказПокупателя.ВалютаДокументаДокумента;
НовыйАвансовыйОтчет.КратностьДокумента =
СсылкаЗаказПокупателя.КратностьДокумента;
НовыйАвансовыйОтчет.Организация =
СсылкаЗаказПокупателя.Организация;
НовыйАвансовыйОтчет.Ответственный =
СсылкаЗаказПокупателя.Ответственный;
НовыйАвансовыйОтчет.Подразделение =
СсылкаЗаказПокупателя.Подразделение;
НовыйАвансовыйОтчет.Склад = СсылкаЗаказПокупателя.Склад;
НовыйАвансовыйОтчет.ТипЦен = СсылкаЗаказПокупателя.ТипЦен;
НовыйАвансовыйОтчет.СуммаДокумента =
СсылкаЗаказПокупателя.СуммаДокумента;
// Заполняем табличную часть Товары
Для Каждого СтрокаТЧЗаказ Из СсылкаЗаказПокупателя.Товары Цикл
СтрокаТЧАвансовыйОтчет =
СтрокаТЧАвансовыйОтчет.Товары.Добавить();
СтрокаТЧАвансовыйОтчет.Номенклатура =
СтрокаТЧЗаказ.Номенклатура;
СтрокаТЧАвансовыйОтчет.Количество =
СтрокаТЧЗаказ.Количество;
СтрокаТЧАвансовыйОтчет.ЕдиницаИзмерения =
СтрокаТЧЗаказ.ЕдиницаИзмерения;
СтрокаТЧАвансовыйОтчет.Коэффициент =
СтрокаТЧЗаказ.Коэффициент;
СтрокаТЧАвансовыйОтчет.ХарактеристикаНоменклатуры =
СтрокаТЧЗаказ.ХарактеристикаНоменклатуры;
СтрокаТЧАвансовыйОтчет.Цена =
СтрокаТЧЗаказ.Цена;
СтрокаТЧАвансовыйОтчет.Сумма =
СтрокаТЧЗаказ.Сумма;
СтрокаТЧАвансовыйОтчет.СтавкаНДС =
СтрокаТЧЗаказ.СтавкаНДС;
СтрокаТЧАвансовыйОтчет.СуммаНДС =
СтрокаТЧЗаказ.СуммаНДС;
47
48
Часть I. Приемы программирования документов
КонецЦикла;
// Открываем форму документа для редактирования
// ФормаДокумента =
//
НовыйАвансовыйОтчет.ПолучитьФорму("ФормаДокумента");
// Открываем форму документа
// ФормаДокумента.Открыть();
КонецЕсли;
Приведенный пример чисто условный, поскольку в реальном документообороте не создается новый авансовый отчет на основании заказ покупателя.
Основной целью ставилось показать, как программно передавать данные из
одного документа в другой.
Итак, мы создали новый документ. После этого заполнили реквизиты, скопировав их из исходного документа ЗаказПокупателя. Чтобы сформировать
табличную часть, организовали цикл, в котором последовательно выбрали
все записи из товарной части заказа. С помощью метода Добавить создали
новые записи в табличной части нового документа. После копирования всех
данных открыли форму документа для редактирования.
Подобным способом можно передавать различные значения реквизитов
и табличных частей между документами, когда в этом возникает необходимость.
Глава 7
Обработка событий
Важной составляющей программирования документов в 1С являются события. Они позволяют динамически, во время выполнения программы, выполнять разнообразные действия по управлению документом через модули объектов и форм.
Для модуля объекта определены несколько базовых событий, названия которых должны строго совпадать с именами, заданными в ДокументОбъект.
К ним относятся:
ПриЗаписи — позволяет выполнить какие-либо действия после записи
документа в базу данных, но до подтверждения открытой транзакции на
запись;
ПриКопировании — происходит при создании нового документа путем ко-
пирования;
ПриУстановкеНовогоНомера — возникает при корректировке номера до-
кумента;
ПередЗаписью — происходит перед записью документа в базу, но после
открытия транзакции на запись;
ПередУдалением — происходит перед удалением объекта документа из
базы данных;
ОбработкаЗаполнения — происходит при вызове метода Заполнить, а так-
же ввода документа на основании, если это задано в конфигураторе;
ОбработкаПроведения — происходит в момент проведения документа;
ОбработкаУдаленияПроведения — вызывается при отмене проведения до-
кумента.
50
Часть I. Приемы программирования документов
Предварительно эти события должны быть добавлены в модуль объекта документа в виде процедуры. Рассмотрим подробнее использование данных
событий.
Событие ПриЗаписи позволяет отменить транзакцию записи, и в результате
документ не будет записан в базу данных. Единственный аргумент управляет
результатом операции: при установке его в значение Истина транзакция записи будет отменена, иначе документ сохранится в базе. Пример работы с этим
событием представлен в листинге 7.1.
Листинг 7.1. Использование события ПриЗаписи
// Описываем в модуле объекта событие
Процедура ПриЗаписи(Отказ)
// Выполняем проверку даты сохраняемого документа
Если ЭтотОбъект.Дата < ТекущаяДата() Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
В приведенном примере мы проверяем дату записываемого документа. Если
она меньше текущей даты, отменяем процесс (транзакцию) записи в базу
данных. Следует помнить, что данные документа уже записаны в базу, поэтому проверка значений реквизитов здесь будет не совсем корректной.
Следующее событие ПриКопировании позволяет отследить процесс создания
нового документа путем копирования. Процедура события имеет один аргумент — объект исходного документа. В этом событии можно выполнить проверку передаваемых данных, их полноту и соответствие ожидаемым типам.
Пример работы с событием показан в листинге 7.2.
Листинг 7.2. Использование события ПриКопировании
// Описываем в модуле объекта документа ЗаказПокупателя событие
Процедура ПриКопировании(ОбъектКопирования)
// Меняем вид операции для нового документа
ЭтотОбъект.ВидОперации =
Перечисления.ВидыОперацийЗаказПокупателя.СчетНаОплату;
// Устанавливаем текущую дату для нового документа
ЭтотОбъект.Дата = ТекущаяДата();
КонецПроцедуры
Глава 7. Обработка событий
51
Событие ПриУстановкеНовогоНомера позволяет задать собственный способ
формирования номера документа. Процедура события имеет два аргумента:
признак обработки по умолчанию и строковое значение префикса для получения нового номера. При установке признака в значение Истина будет выполнен стандартный способ нумерации. Пример использования этого события представлен в листинге 7.3.
Листинг 7.3. Использование события ПриУстановкеНовогоНомера
// Описываем в модуле объекта документа событие
Процедура ПриУстановкеНовогоНомера(Стандартнаяобработка, Префикс)
// Устанавливаем собственный генератор нового номера
СтандартнаяОбработка = Ложь;
ЭтоОбъект.Номер = "Номер " +
Формат(ТекущаяДата(),"ДФ=HHmmss;ДЛФ=T")
КонецПроцедуры
Событие ПередЗаписью позволяет выполнить различные проверки перед записью документа в базу данных. Процедура содержит три аргумента: признак отказа, режим записи и режим проведения. Установка первого аргумента в значение Истина приведет к отмене операции записи. Второй аргумент
позволяет управлять режимом записи документа (запись, проведение или отмена проведения). Последний аргумент относится к режиму проведения
(оперативный или неоперативный) текущего документа. Пример использования этого события показан в листинге 7.4.
Листинг 7.4. Использование события ПередЗаписью
// Описываем в модуле объекта документа событие
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем режим записи документа
Если РежимЗаписи.Запись Тогда
Сообщить("Выполняется запись документа " +
ЭтотОбъект.Номер + " в базу");
ИначеЕсли РежимЗаписи.Проведение Тогда
Сообщить("Выполняется проведение документа " +
ЭтотОбъект.Номер);
Иначе
Сообщить("Выполняется отмена проведения документа " +
ЭтотОбъект.Номер);
52
Часть I. Приемы программирования документов
КонецЕсли;
// Проверяем режим проведения
Если РежимПроведения.Неоперативный Тогда
Сообщить("Выполняется неоперативное проведение документа!";
КонецЕсли;
КонецПроцедуры
Событие ПередУдалением позволяет отследить, нужен ли еще удаляемый документ, и, при необходимости, отменить операцию удаления. Для этого
единственный аргумент процедуры события должен быть установлен в значение Истина. Простой пример работы с данным событием представлен
в листинге 7.5.
Листинг 7.5. Использование события ПередУдалением
// Описываем в модуле объекта документа событие
Процедура ПередУдалением(Отказ)
// Если дата документа меньше текущей, отменяем операцию удаления
Если ЭтотОбъект.Дата < ТекущаяДата() Тогда
// Отменяем удаление документа
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Следующее событие ОбработкаЗаполнения позволяет отследить правильность передачи данных при выполнении метода Заполнить объекта Документ
Объект или ввода нового документа на основании другого. Единственный
аргумент процедуры события имеет произвольный тип и может содержать
как объект всего документа, так и отдельных его реквизитов. Пример использования события представлен в листинге 7.6. Проверим операцию ввода нового документа РеализацияТоваровУслуг на основании других документов,
заданных в конфигураторе.
Листинг 7.6. Использование события ОбработкаЗаполнения
// Описываем в модуле объекта документа событие
Процедура ОбработкаЗаполнения(Основание)
// Проверяем тип исходного документа
Глава 7. Обработка событий
53
Если ТипЗнч(Основание) =
Тип("ДокументСсылка.ЗаказПокупателя") Тогда
// Выполняем какие-либо свои действия по заполнению
ИначеЕсли ТипЗнч(Основание) =
Тип("ДокументСсылка.ПоступлениеТоваровУслуг") Тогда
// Выполняем какие-либо свои действия по заполнению
КонецЕсли;
КонецПроцедуры
Как видно из примера, создать новый документ РеализацияТоваровУслуг
можно на основании двух документов конфигурации — заказа покупателя и
реализации товаров и услуг. В процедуре события мы проверяем тип документа-основания и выполняем, если необходимо, различные действия.
Событие ОбработкаПроведения позволяет в момент проведения документа
выполнить какие-либо операции по регистрации движений документа. Процедура события содержит два аргумента: признак отказа проведения и режим
проведения документа (оперативный или неоперативный). Установка первого аргумента в значение Истина отменяет процесс проведения документа.
Пример использования данного события представлен в листинге 7.7.
Листинг 7.7. Использование события ОбработкаПроведения
// Описываем в модуле объекта документа событие
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Запрещаем неоперативное проведение
Если РежимПроведения.Неоперативный Тогда
Отказ = Истина;
КонецЕсли;
// Определяем движения по регистру накопления ЗакаПокупателя
Для Каждого СтрокаТовары Из Товары Цикл
Движение = Движения.ЗаказыПокупателей.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Регистратор = Ссылка;
Движение.Номенклатура = СтрокаТовары.Номенклатура;
Движение.ХарактеристикаНоменклатуры =
СтрокаТовары.ХарактеристикаНоменклатуры;
Движение.Количество = СтрокаТовары.Количество;
Движение.ПодразделениеКомпании = Подразделение;
54
Часть I. Приемы программирования документов
Движение.СуммаУпрУчета = СуммаДокумента;
КонецЦикла;
// записываем движения регистров
Движения.ЗаказыПокупателей.Записать();
КонецПроцедуры
В примере мы вначале проверяем режим проведения и, если документ проводится не оперативно, отменяем операцию. Далее формируем движение по
регистру накопления ЗаказыПокупателей. Совсем не обязательно вручную
писать движения документа по регистрам. Для этого в конфигураторе существует конструктор движений. После его запуска и настройки всех движений
создается (или перезаписывается существующая) процедура события
ОбработкаПроведения.
Событие ОбработкаУдаленияПроведения вызывается в том случае, когда
пользователь отменяет проведение документа. Содержит один аргумент, позволяющий отменить операцию. Простой пример работы с данным событием
представлен в листинге 7.8.
Листинг 7.8. Использование события ОбработкаУдаленияПроведения
// Описываем в модуле объекта документа событие
Процедура ОбработкаУдаленияПроведения(Отказ)
// Запрещаем отмену проведения
Если ЭтотОбъект.Дата < ТекущаяДата() Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Приведенный пример проверяет, чтобы пользователь не мог отменить проведение документа, если его дата меньше текущей.
Вот и все события, с которыми хотелось познакомить читателей. Существует
еще много разных событий для модуля формы документа, но здесь они не
рассматриваются.
Глава 8
Использование запросов
В предыдущих главах книги читатель познакомился с всевозможными приемами программирования документов как объектов конфигурации. Как вы
уже знаете, документы можно читать из базы и сохранять в базу. Для доступа к объекту документа применяется ДокументОбъект, а для выборки — ДокументМенеджер. При создании нового документа или обновлении существующего, в любом случае, приходится применять базовые объекты
ДокументМенеджер и ДокументОбъект. И тут других вариантов нет. Но вот
для выборки набора документов из базы наиболее эффективным будет не использование методов объекта ДокументМенеджер, а написание запросов на
встроенном в 1С языке. Поэтому настоятельно рекомендую всем читателям
придерживаться следующего правила: при необходимости прочитать один
или несколько документов — можно вызвать метод Выбрать объекта
ДокументМенеджер, в остальных же случаях работать только через запросы.
О том, как с помощью запросов читать различные виды документов из базы
данных, мы и поговорим в этой главе.
Формирование запроса можно выполнить двумя способами: c помощью
встроенного конструктора запросов или вручную. У каждого из них есть свои
достоинства и недостатки, но поскольку данная книга является практическим
руководством по программированию, основное внимание будет уделено
именно самостоятельному написанию запросов, а с конструктором мы познакомимся в самом конце главы.
Основным объектом для работы с запросами служит Запрос. Он содержит
одно свойство — Текст (строка, заключенная в двойные кавычки), куда записывается исходный текст запроса и несколько методов:
Выполнить — запускает запрос на выполнение и возвращает в результате
набор данных;
56
Часть I. Приемы программирования документов
УстановитьПараметр — необходим для передачи в запрос значений пере-
менных или объектов;
НайтиПараметры — позволяет получить сведения о параметрах, передан-
ных в запрос.
Рассмотрим простой пример запроса к базе данных, представленный в листинге 8.1.
Листинг 8.1. Создание запроса к базе данных
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер,
|
Дата,
|
Организация,
|
ВалютаДокумента КАК Валюта,
|
Ответственный
КАК Сотрудник,
|
СуммаДокумента
КАК Сумма
|ИЗ
|
Документ.АвансовыйОтчет
|УПОРЯДОЧИТЬ ПО
|
Дата УБЫВ";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
Итак, мы создали новый объект Запрос и сформировали текст. После этого
вызвали метод Выполнить и сохранили выборку в переменную ДанныеВыборки
(объект РезультатЗапроса). Это простой пример читает из базы все документы АвансовыйОтчет и после упорядочивания их по дате создания в порядке
убывания возвращает клиенту. Поскольку обычно требуется выбрать не все
документы, а только удовлетворяющие определенному условию, построим
запрос, получающий данные из базы за определенный период. Как это сделать, показано в листинге 8.2.
Глава 8. Использование запросов
57
Листинг 8.2. Получение данных за указанный период
// Формируем текст запроса
ТекстЗапроса = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Организация,
|
ВалютаДокумента
КАК Валюта,
|
Ответственный
КАК Сотрудник,
|
Авторезервирование КАК ПризнакРезерв,
|
Авторазмещение
КАК ПризнакРазмещение,
|
ДатаОтгрузки
КАК ДатаОтгрузки
|ИЗ
|
Документ.ВнутреннийЗаказ
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная
|УПОРЯДОЧИТЬ ПО
|
Дата УБЫВ";
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос(ТекстЗапроса);
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
Чтобы указать в запросе период выборки, мы воспользовались методом
УстановитьПараметр. Он передает два аргумента: имя параметра и его значение. В самом тексте запроса добавили условие выборки данных (ключевое
слово ГДЕ) и с помощью ключевого слова МЕЖДУ установили диапазон по дате
создания документа. Как вы заметили, параметры, передаваемые в запрос,
должны обязательно начинаться с символа & (амперсанд). В результате выполнения запроса переменная ДанныеВыборки будет содержать все документы, созданные за текущий день.
После того как запрос выполнен, необходимо обработать результат — выборку данных. Для этого используются методы объекта РезультатЗапроса:
Выбрать или Выгрузить. Кроме того, если запрос не вернул ожидаемые данные, можно в этом убедиться посредством метода Пустой. Рекомендуется
58
Часть I. Приемы программирования документов
всегда вызывать его перед обработкой выборки. Метод Выгрузить копирует
полученные данные в таблицу значений (или дерево значений, если выборка
делается по иерархии), а метод Выбрать создает выборку записей, которые
можно обработать в цикле. Рассмотрим пример работы метода Выбрать, приведенный в листинге 8.3.
Листинг 8.3. Использование метода Выбрать для обработки результата запроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Продавец,
|
Контрагент
КАК Покупатель,
|
ВалютаДокумента КАК Валюта,
|
Ответственный
КАК Сотрудник,
|
Сделка
КАК ЗаказПокупателя,
|
СуммаДокумента
КАК Сумма
|ИЗ
|
Документ.ВозвратТоваровОтПокупателя
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Сделка ССЫЛКА Документ.ЗаказПокупателя
|УПОРЯДОЧИТЬ ПО
|
Дата УБЫВ";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные
Данные = ДанныеВыборки.Выбрать();
Пока Данные.Следующий() Цикл
Сообщить("Номер: " + Данные.НомерДокумента);
Глава 8. Использование запросов
59
Сообщить("Дата: " + Данные.ДатаДокумента);
Сообщить("Продавец: " + Данные.Продавец);
Сообщить("Покупатель: " + Данные.Покупатель);
Сообщить("Валюта: " + Данные.Валюта);
Сообщить("Сотрудник: " + Данные.Сотрудник);
Сообщить("Заказ: " + Данные.ЗаказПокупателя);
Сообщить("Сумма документа: " + Данные.Сумма);
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
После того как мы сформировали и выполнили запрос, нам необходимо проверить наличие данных, что и было сделано с помощью метода Пустой. Если
данные получены, вызываем метод Выбрать. Он возвращает результат запроса (объект ВыборкаИзРезультатаЗапроса). По условию запрос нам возвратит
все документы в заданном диапазоне дат, у которых в качестве документасделки указан заказ покупателя. Далее организуем цикл и посредством метода Следующий получаем все записи выборки. Замечу, что данный метод необходимо всегда вызывать первым для установки курсора на первую запись.
Последующие вызовы позволяют по очереди перебрать все записи выборки.
Метод Выбрать имеет три необязательных аргумента. Первый задает вариант
обхода записей, второй определяет группировки полей для обхода и третий
определяет список используемых группировок, заданных во втором аргументе. Варианты обхода делятся на: прямой, по группировкам и по группировкам с иерархией. По умолчанию выбирается прямой вариант, при котором
записи из запроса идут одна за одной, в том виде, в котором они были переданы. В приведенных примерах применяется именно такой способ. Обход по
группировкам позволяет получить данные, сгруппированные по одному,
нескольким или всем полям выборки, с агрегатными функциями и без них.
Как это делается, показано в листинге 8.4.
Листинг 8.4. Использование метода Выбрать с группировкой
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
60
Часть I. Приемы программирования документов
|
Дата
|
ВалютаДокумента КАК Валюта,
КАК ДатаДокумента,
|
Организация
КАК Покупатель,
|
Контрагент
КАК Поставшик,
|
Ответственный
КАК Менеджер,
|
Сделка
КАК Заказ,
|
СуммаДокумента
КАК Сумма
|ИЗ
|
Документ.ВозвратТоваровПоставщику
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Сделка ССЫЛКА Документ.ЗаказПоставщику
|УПОРЯДОЧИТЬ ПО
|
Дата
|ИТОГИ ПО
|
Контрагент";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные с группировкой по полю Поставщик
Данные =
ДанныеВыборки.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока Данные.Следующий() Цикл
Сообщить("Поставщик: " + Данные.Поставщик);
// Получаем остальные реквизиты документа
Сведения
= ДанныеВыборки.Выбрать();
Пока Сведения.Следующий() Цикл
Сообщить("Номер: " + Сведения.НомерДокумента);
Сообщить("Дата: " + Сведения.ДатаДокумента);
Сообщить("Покупатель: " + Сведения.Покупатель);
Сообщить("Валюта: " + Сведения.Валюта);
Сообщить("Сотрудник: " + Сведения.Сотрудник);
Сообщить("Заказ: " + Сведения.Заказ);
Сообщить("Сумма документа: " + Сведения.Сумма);
КонецЦикла;
Глава 8. Использование запросов
61
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
Итак, мы сформировали запрос по документу ВозвратТоваровПоставшику
с группировкой по полю Поставщик (ключевое слово ИТОГИ ПО). В результате
полученные данные были сгруппированы по поставщику. Последующий вызов в цикле метода Выбрать с установленным обходом по группировкам возвратит нам всех поставщиков из выбранных документов, упорядоченных по
дате создания. Чтобы получить остальные поля выборки, мы повторно выполняем метод Выбрать и помещаем результат в переменную Сведения. Далее, как обычно, организуем цикл и перебираем оставшиеся поля.
Рассмотрим теперь последний вариант выборки с группировками по иерархии.
Понятие иерархии подразумевает наличие главных и подчиненных (вложенных) записей. Такой принцип построения свойственен, в первую очередь, справочникам. Например, такие товары, как электрочайник, миксер, соковыжималка, можно внести в общую группу Бытовая техника, которая будет находиться
на более высоком уровне по отношению к ним, а молоток, отвертку и ножовку — определить в группу Инструмент. В результате запроса мы получим две
записи: Бытовая техника и Инструмент. Для получения подчиненных записей
необходимо повторно вызвать метод Выбрать. Посмотрите пример написания
запроса с выборкой по иерархии, представленный в листинге 8.5.
Листинг 8.5. Использование метода Выбрать с группировкой по иерархии
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
Количество
КАК Количество,
|
Цена
КАК Цена,
|
Сумма
КАК Сумма,
|
СтавкаНДС
КАК СтавкаНДС,
|
СуммаНДС
КАК СуммаНДС
|ИЗ
|
|ГДЕ
Документ.ЗаказПокупателя.Товары
62
Часть I. Приемы программирования документов
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Ссылка.Проведен
|УПОРЯДОЧИТЬ ПО
|
Номенклатура
|ИТОГИ СУММА(Количество), СУММА(Сумма) ПО
|
Номенклатура,
|
Номенклатура ИЕРАРХИЯ";
// Передаем в запрос параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные с группировкой по полю Поставщик
Данные = ДанныеВыборки.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока Данные.Следующий() Цикл
// Получаем номенклатурные группы и количество
Сообщить("Номенклатурная группа: " + Данные.Номенклатура);
Сообщить("Количество: " + Данные.Количество);
// Получаем остальные поля
Сведения
= ДанныеВыборки.Выбрать();
Пока Сведения.Следующий() Цикл
Сообщить("Номенклатура: " +
Сведения.Номенклатура);
Сообщить("Количество: " + Сведения.Количество);
Сообщить("Цена: " + Сведения.Цена);
Сообщить("Сумма: " + Сведения.Сумма);
Сообщить("Ставка НДС: " + Сведения.СтавкаНДС);
Сообщить("Сумма НДС: " + Сведения.СуммаНДС);
КонецЦикла;
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
В приведенном примере мы создали запрос к табличной части документа
Товары. Для указания группировки по иерархии в итогах применили ключе-
Глава 8. Использование запросов
63
вое слово ИЕРАРХИЯ. В условии запроса установили: период выборки за текущий месяц (встроенная функция НачалоМесяца) не позднее текущего дня
и получение только проведенных документов. После организовали цикл по
записям верхнего уровня с наименованием и суммарным количеством. Чтобы
получить более подробные сведения о каждом товаре, входящем в группу,
повторно вызвали метод Выбрать и опять прошли по циклу, но уже для подчиненных записей. Поскольку структура справочников может иметь несколько вложенных групп, имеет смысл написать отдельную процедуру и вызывать ее рекурсивно.
Существует еще один полезный метод для объекта выборки данных из результата запроса. Он называется СледующийПоЗначениюПоля и позволяет прочитать данные только по определенному полю. Необходимость в этом может
возникнуть, когда понадобится проанализировать результат одного поля выборки, чтобы по результатам анализа выполнить какую-либо обработку над
другими полями. Посмотрите пример, приведенный в листинге 8.6.
Листинг 8.6. Использование метода СледующийПоЗначениюПоля
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Подразделение
КАК Подразделение,
|
Ответственный
КАК Сотрудник,
|
Организация.Наименование КАК Организация,
|
ВалютаДокумента
КАК Валюта,
|
СуммаДокумента
КАК Сумма
|ИЗ
|
Документ.СписаниеТоваров
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Проведен
|УПОРЯДОЧИТЬ ПО
|
Организация";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
64
Часть I. Приемы программирования документов
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если в запросе есть хотя бы одна запись, продолжаем обработку
Если ДанныеВыборки.Количество() > 0 Тогда
// Выбираем данные
Данные = ДанныеВыборки.Выбрать();
Пока Данные.СледующийПоЗначениюПоля("Сотрудник") Цикл
// Получаем ответственного за документ
Сообщить("Сотрудник: " + Данные.Сотрудник);
Сообщить("Отдел: " + Данные.Подразделение);
Сообщить("Организация: " + Данные.Организация);
// Получаем документы по выбранному сотруднику
Пока Данные.СледующийПоЗначениюПоля("Номер") Цикл
Сообщить("Номер: " + Данные.НомерДокумента);
Сообщить("Дата: " + Данные.ДатаДокумента);
Сообщить("Валюта: " + Данные.ВалютаДокумента);
Сообщить("Сумма: " + Данные.Сумма);
КонецЦикла;
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
В этом примере мы написали запрос, который выбирает из базы все проведенные документы за текущий год (встроенная функция НачалоГода). После
получения результата запроса вызываем метод СледующийПоЗначениюПоля
с группировкой по сотруднику. Нам будут возвращены документы по каждому сотруднику, и, чтобы проанализировать их, мы повторно используем метод СледующийПоЗначениюПоля, но уже с группировкой по номеру документа.
Результаты выводим в окно служебных сообщений.
Как видите, метод Выбрать дает возможность посредством объекта
ВыборкаИзРезультатаЗапроса манипулировать данными по своему усмотрению. Мы еще будем возвращаться к нему, а сейчас поговорим о втором методе получения данных, который называется Выгрузить. В отличие от первого, он возвращает таблицу (или дерево) значений. При прямом обходе
выборки данные копируются в таблицу, при использовании группировок —
в дерево значений. Таблица значений является объектом и поддерживает различные методы для управления выбранными строками. Посмотрите пример,
приведенный в листинге 8.7.
Глава 8. Использование запросов
65
Листинг 8.7. Использование метода Выгрузить для получения выборки
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Продавец,
|
Контрагент
КАК Покупатель,
|
ВалютаДокумента КАК Валюта,
|
СуммаДокумента КАК Сумма
|ИЗ
|
Документ.РеализацияТоваровУслуг
|ГДЕ
|
Дата > &ДатаНачальная И
|
Проведен
|УПОРЯДОЧИТЬ ПО
|
Дата";
// Передаем параметры начальной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выгружаем данные в таблицу значений
ДанныеТабЗначений = ДанныеВыборки.Выгрузить();
// Проверяем, есть ли в данные
Если ДанныеТабЗначений.Количество() > 0 Тогда
// Организуем цикл для перебора значений
Для Каждого ДанныеСтрока Из ДанныеТабЗначений Цикл
// Получаем значения полей выборки
Сообщить("Номер: " + ДанныеСтрока.НомерДокумента);
Сообщить("Дата: " + ДанныеСтрока.ДатаДокумента);
Сообщить("Продавец: " + ДанныеСтрока.Продавец);
Сообщить("Покупатель: " + ДанныеСтрока.Покупатель);
Сообщить("Валюта: " + ДанныеСтрока.Валюта);
Сообщить("Сумма документа: " + ДанныеСтрока.Сумма);
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
66
Часть I. Приемы программирования документов
На примере документа РеализацияТоваровУслуг мы посмотрели, как применяется метод Выгрузить. Так же, как и раньше, создается цикл, в котором,
в зависимости от установленного типа обхода, можно прочитать данные результата запроса. Кроме того, полученные данные можно непосредственно
выгрузить в табличную часть документа, минуя промежуточные обработки.
Единственное условие — имена полей выборки и колонок табличной части
должны совпадать. Как это делается, показано в листинге 8.8.
Листинг 8.8. Выгрузка результата запроса в табличную часть документа
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура,
|
Количество,
|
ЕдиницаИзмерения,
|
Коэффициент,
|
Цена,
|
ХарактеристикаНоменклатуры,
|
СерияНоменклатуры,
|
ЗаказПокупателя,
|
СпособСписанияТоваров
|ИЗ
|
Документ.ПеремещениеТоваров.Товары
|ГДЕ
|
Дата > &ДатаНачальная И
|
Проведен";
// Передаем параметры начальной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выгружаем данные в табличную часть Товары
Товары = ДанныеВыборки.Выгрузить();
В этом примере мы напрямую выгрузили результат запроса в табличную
часть документа.
Глава 8. Использование запросов
67
Объект ТаблицаЗначений позволяет получить из запроса выборочно любую
колонку. Для этого служит метод ВыгрузитьКолонку. В качестве единственного аргумента ему можно передать номер или имя поля выборки. Посмотрите пример в листинге 8.9.
Листинг 8.9. Выгрузка одиночной колонки из результата запроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура,
|
Количество,
|
ЕдиницаИзмерения,
|
Коэффициент,
|
Цена,
|
ХарактеристикаНоменклатуры,
|
СерияНоменклатуры,
|
ЗаказПокупателя
|ИЗ
|
Документ.ПеремещениеТоваров.Товары
|ГДЕ
|
Дата > &ДатаНачальная И
|
Проведен";
// Передаем параметры начальной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выгружаем данные в таблицу значений
ДанныеТабЗначений = ДанныеВыборки.Выгрузить();
// Проверяем, есть ли в данные
Если ДанныеТабЗначений.Количество() > 0 Тогда
// Выгружаем колонку Номенклатура
МассивНоменклатура =
ДанныеТабЗначений.ВыгрузитьКолонку("Номенклатура");
// Выгружаем колонку Количество
МассивКоличество =
ДанныеТабЗначений.ВыгрузитьКолонку("Количество");
68
Часть I. Приемы программирования документов
// Выгружаем колонку ЗаказПокупателя
МассивЗаказыПокупателя =
ДанныеТабЗначений.ВыгрузитьКолонку("ЗаказПокупателя");
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
Как видите, после получения результата запроса мы с помощью метода
ВыгрузитьКолонку считали в переменные три поля: номенклатура, количество
и заказ.
Часто возникает необходимость создать во время выполнения программы
свою таблицу значений и загрузить туда результат запроса. Как это реализовать, показано в листинге 8.10.
Листинг 8.10. Создание новой таблицы значений для выгрузки результата
запроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Продавец,
|
Контрагент
КАК Покупатель,
|
ВалютаДокумента КАК Валюта,
|
СуммаДокумента
КАК Сумма
|ИЗ
|
Документ.РеализацияТоваровУслуг
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Проведен";
// Передаем параметры начальной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Формируем свою таблицу значений
Глава 8. Использование запросов
69
ТабЗначений = Новый ТаблицаЗначений;
ТабЗначений.Колонки.Добавить("НомерДокумента");
ТабЗначений.Колонки.Добавить("ДатаДокумента");
ТабЗначений.Колонки.Добавить("Продавец");
ТабЗначений.Колонки.Добавить("Покупатель");
ТабЗначений.Колонки.Добавить("Валюта");
ТабЗначений.Колонки.Добавить("Сумма");
// Выгружаем данные в таблицу значений
ТабЗначений = ДанныеВыборки.Выгрузить();
// Сортируем данные по дате документа в порядке убывания
ТабЗначений.Сортировать("ДатаДокумента Убыв");
После того как был сформирован и выполнен запрос, мы создали новую таблицу значений. Добавили необходимые колонки и методом Выгрузить скопировали в нее результат запроса. И, наконец, вызвали метод Сортировать для
упорядочивания данных по дате документа в порядке убывания. В этом методе можно указать и несколько полей, по которым будет выполнена сортировка.
Объект ТаблицаЗначений имеет еще один полезный метод — Свернуть,
который позволяет свернуть (сгруппировать с пересчетом числовых значений) выбранные колонки. Он имеет два параметра: строка с наименованиями колонок (через запятую) для группировки и строка для суммируемых
полей. Пример, демонстрирующий работу данного метода, представлен
в листинге 8.11.
Листинг 8.11. Использование метода Свернуть
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
Количество
КАК Количество,
|
ЕдиницаИзмерения
КАК ЕдиницаИзмерения,
|
Коэффициент
КАК Коэффициент,
|
Цена
КАК Цена,
|
ХарактеристикаНоменклатуры КАК Характеристика,
|
СуммаНДС
КАК СуммаНДС,
70
|
Часть I. Приемы программирования документов
Сумма
КАК Сумма
|ИЗ
|
Документ.РеализацияТоваровУслуг.Товары
|ГДЕ
|
Дата > &ДатаНачальная И
|
Проведен";
// Передаем параметры начальной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выгружаем данные в таблицу значений
Товары = ДанныеВыборки.Выгрузить();
// Проверяем, есть ли в данные
Если Товары.Количество() > 0 Тогда
// Сворачиваем по номенклатуре, количеству и сумме
Товары.Свернуть("Номенклатура, ХарактеристикаНоменклатуры",
"Количество, Сумма");
// Выводим результаты группировки в окно служебных сообщений
Для Каждого Товар Из Товары Цикл
Сообщить("Номенклатура: ", Товар.Номенклатура);
Сообщить("Характеристика номенклатуры: ",
Товар.ХарактеристикаНоменклатуры);
// Суммарное количество по текущей номенклатуре
Сообщить("Количество: ", Товар.Количество);
// Суммарная стоимость по текущей номенклатуре
Сообщить("Сумма: ", Товар.Сумма);
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
В этом примере после получения результата запроса, мы свернули данные по
колонкам номенклатуры и характеристике, а также просуммировали количество и сумму по каждому товару. Следует заметить, что поля выборки, не
указанные в методе Свернуть, будут удалены. В результате мы получим общие значения количества и суммы по каждому товару с учетом характеристики.
Теперь, когда мы познакомились с основными методами обработки запросов,
поговорим о способах формирования запросов по различным видам докумен-
Глава 8. Использование запросов
71
тов и их структуре. Как вы уже успели заметить, в запросе можно получить
не только общие реквизиты документа, но и табличные части. Сделать это
можно двумя отдельными запросами, а лучше применить один общий запрос,
как это показано в листинге 8.12.
Листинг 8.12. Получение основных реквизитов и табличных частей документа
в одном запросе
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЕдиницаИзмерения
КАК ЕдиницаИзмерения,
|
Количество
КАК Количество,
|
Номенклатура
КАК Номенклатура,
|
СтавкаНДС
КАК СтавкаНДС,
|
Сумма
КАК Сумма,
|
СуммаНДС
КАК СуммаНДС,
|
ХарактеристикаНоменклатуры КАК ХарктеристикаНоменклатуры,
|
Цена
КАК Цена,
|
Ссылка.АвтоРазмещение
КАК ПризнакАвторазмещение,
|
Ссылка.АвтоРезервирование КАК ПризнакАвтоРезервирование,
|
Ссылка.ВалютаДокумента
КАК Валюта,
|
Ссылка.Дата
КАК ДатаДокумента,
|
Ссылка.ДатаОтгрузки
КАК ДатаОтгрузки,
|
Ссылка.Контрагент
КАК Покупатель,
|
Ссылка.Номер
КАК НомерДокумента,
|
Ссылка.Организация
КАК Продавец,
|
Ссылка.Ответственный
КАК Сотрудник,
|
Ссылка.СуммаДокумента
КАК СуммаДокумента
|ИЗ
|
Документ.ЗаказПокупателя.Товары
|ГДЕ
|
Ссылка.Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Ссылка.Проведен";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
72
Часть I. Приемы программирования документов
ДанныеВыборки = Запрос.Выполнить();
// Выгружаем данные в таблицу значений
ДанныеТабЗначений = ДанныеВыборки.Выгрузить();
// Проверяем, есть ли в данные
Если ДанныеТабЗначений.Количество() > 0 Тогда
// Обрабатываем здесь результат запроса
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
Из текста запроса вы видите, что мы обратились к табличной части Товары
документа ЗаказПокупателя. Чтобы дополнительно получить основные реквизиты, использовали поле ссылки (Ссылка) на документ и через него вышли
на общие сведения. Такой вид запроса может пригодиться при создании печатных форм документов, где требуются и данные реквизитов и данные табличных частей.
В языке запросов существует возможность ограничить данные результата
запроса с помощью ключевого слова РАЗРЕШЕННЫЕ. Если оно указано, будут
возвращены только те записи, которые доступны текущему пользователю,
согласно назначенным ему правам доступа. Посмотрите пример, представленный в листинге 8.13.
Листинг 8.13. Использование в запросе ключевого слова РАЗРЕШЕННЫЕ
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ РАЗРЕШЕННЫЕ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Покупатель,
|
Контрагент
КАК Поставшик,
|
СуммаДокумента КАК Сумма
|ИЗ
|
Документ.ВозвратТоваровПоставщику
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная
|УПОРЯДОЧИТЬ ПО
|
Дата
Глава 8. Использование запросов
73
|ИТОГИ ПО
|
Контрагент";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
Запрос с ограничением по правам вернет только доступные пользователю
данные. Права доступа и роли устанавливаются в конфигураторе для каждого
пользователя или групп пользователей.
Как уже говорилось ранее, в 1С активно используются ссылки, поэтому хорошим правилом будет передача в запрос не интервала дат, а ссылки на выбранный пользователем документ. Как это делается, показано в листинге 8.14.
Листинг 8.14. Получение информации по документу через ссылку
Процедура ПолучитьСведенияОДокументе(ДокументСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Покупатель,
|
Контрагент
КАК Поставшик,
|
СуммаДокумента
КАК Сумма
|ИЗ
|
Документ.ПоступлениеТоваровУслуг
|ГДЕ
|
Ссылка = &Ссылка";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выполняем обработку результата запроса
// . . .
КонецПроцедуры
74
Часть I. Приемы программирования документов
Данную процедуру можно применить при выборе пользователем определенного документа из списка. Через аргумент ДокументСсылка мы передаем
в запрос ссылку на выбранный документ и получаем в результате необходимые данные по реквизитам или табличным частям.
Многие документы конфигурации содержат не одну, а несколько табличных
частей (Товары, Услуги, Возвратная тара и др.). Чтобы прочитать одновременно сведения по разным табличным частям, можно воспользоваться объединением таблиц. Посмотрите пример, представленный в листинге 8.15.
Листинг 8.15. Получение выборки из объединения всех табличных частей
документа
Процедура ПолучитьТабличныеЧастиДокумента(ДокументСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ЕдиницаИзмерения
КАК ЕдиницаИзмерения,
|
СтавкаНДС
КАК СтавкаНДС,
|
Цена
КАК Цена,
|
СУММА(Количество)
КАК Количество,
|
СУММА(Сумма)
КАК Сумма,
|
СУММА(СуммаНДС)
КАК СуммаНДС,
|
МИНИМУМ(НомерСтроки) КАК НомерСтроки,
|
0
КАК Метка
|ИЗ
|
Документ.РеализацияТоваровУслуг.Товары
|ГДЕ
|
Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ЕдиницаИзмерения,
|
СтавкаНДС,
|
Цена
// Объединяем с табличной частью Услуги
|ОБЪЕДИНИТЬ ВСЕ
|
Глава 8. Использование запросов
75
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
|
СтавкаНДС
КАК СтавкаНДС,
|
Цена
КАК Цена,
|
Количество
КАК Количество,
|
Сумма
КАК Сумма,
|
СуммаНДС
КАК СуммаНДС,
|
НомерСтроки
КАК НомерСтроки,
|
1
КАК Метка
|ИЗ
|
Документ.РеализацияТоваровУслуг.Услуги
|ГДЕ
|
Ссылка = &Ссылка
// Объединяем с табличной частью ВозвратнаяТара
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|
Номенклатура
|
Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
КАК Номенклатура,
|
""Без НДС""
КАК СтавкаНДС,
|
Цена
КАК Цена,
|
Количество
КАК Количество,
|
Сумма
КАК Сумма,
|
0
КАК СуммаНДС,
|
НомерСтроки
КАК НомерСтроки,
|
2
КАК Метка
|ИЗ
|
Документ.РеализацияТоваровУслуг.ВозвратнаяТара
|ГДЕ
|
Ссылка = &Ссылка
|УПОРЯДОЧИТЬ ПО Метка ВОЗР, НомерСтроки ВОЗР";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выполняем обработку результата запроса
// . . .
КонецПроцедуры
76
Часть I. Приемы программирования документов
Для объединения запросов по табличным частям документа мы применили
ключевое слово ОБЪЕДИНИТЬ. С его помощью результаты всех запросов были
помещены в одну таблицу. Названия результирующих полей при этом берутся из первого запроса. Самое главное, количество и очередность полей во
всех запросах должны строго совпадать. Тип полей может отличаться, тогда
результирующее поле будет иметь составной тип, что допустимо в 1С. Одинаковые строки из разных запросов будут заменены одной, если не указать
дополнительное ключевое слово ВСЕ. Поле НомерСтроки (порядковый номер)
позволяет сохранить очередность записей, как задано в табличной части документа.
Как вы уже поняли, в предыдущем примере мы объединили данные из разных табличных частей документа в одну таблицу. Однако если понадобится
получить сведения по каждой табличной части отдельно, следует изменить
запрос, исключив из него объединения. Пример такого запроса представлен
в листинге 8.16.
Листинг 8.16. Получение сведений из разных табличных частей документа
Процедура ПолучитьТабличныеЧастиДокумента(ДокументСсылкаЗаказПоставщику)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Дата
КАК ДатаДокумента,
|
Номер
КАК НомерДокумента,
|
Организация
КАК Покупатель,
|
Контрагент
КАК Продавец,
|
Ответственный
КАК Сотрудник,
|
Товары.(
|
Номенклатура
КАК ТоварНоменклатура,
|
Количество
КАК ТоварКоличество,
|
ЕдиницаИзмерения КАК ТоварЕдИзм,
|
СтавкаНДС
КАК ТоварСтавкаНДС,
|
СуммаНДС
КАК ТоварСуммаНДС,
|
Сумма
КАК ТоварСумма,
|
Цена
КАК ТоварЦена
|
),
|
Услуги.(
Глава 8. Использование запросов
77
|
Содержание
КАК УслугаНаименование,
|
Количество
КАК УслугаКоличество,
|
Цена
КАК УслугаЦена,
|
СтавкаНДС
КАК УслугаСтавкаНДС,
|
СуммаНДС
КАК УслугаСуммаНДС,
|
Сумма
КАК УслугаСумма
|
)
|ИЗ
|
Документ.ПоступлениеТоваров
|ГДЕ
|
Сделка = &Ссылка И Проведен
|УПОРЯДОЧИТЬ ПО Дата УБЫВ";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка",
ДокументСсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Пока ДанныеВыборки.Следующий() Цикл
// Получаем общие сведения по документу
Сообщить("Дата: ", ДанныеВыборки.ДатаДокумента);
Сообщить("Номер: ", ДанныеВыборки.НомерДокумента);
Сообщить("Покупатель: ", ДанныеВыборки.Покупатель);
Сообщить("Продавец: ", ДанныеВыборки.Продавец);
Сообщить("Ответственный: ", ДанныеВыборки.Сотрудник);
// Получаем товарную часть документа
Товары = ДанныеВыборки.Товары.Выбрать();
// Обрабатываем товарную часть
Пока Товары.Следующий() Цикл
Сообщить("Номенклатура: ",
Товары.ТоварНоменклатура);
Сообщить("Количество: ", Товары.ТоварКоличество);
Сообщить("Единица измерения: ", Товары.ТоварЕдИзм);
Сообщить("Ставка НДС: ", Товары.ТоварСтавкаНДС);
Сообщить("Сумма НДС : ", Товары.ТоварСуммаНДС);
Сообщить("Сумма: ", Товары.ТоварСумма);
Сообщить("Цена: ", Товары.ТоварЦена);
КонецЦикла;
// Получаем табличную часть Услуги
Услуги = ДанныеВыборки.Услуги.Выбрать();
78
Часть I. Приемы программирования документов
// Обрабатываем данные об услугах
// . . .
КонецЦикла;
КонецПроцедуры
В этом запросе мы выбираем все проведенные поступления, у которых в качестве сделки выбрана ссылка на документ ЗаказПоставщика. Кроме общей
информации, дополнительно получаем сведения по товарной части и услугам. Как вы видите, чтобы прочитать данные по табличным частям, мы делаем дополнительную выборку (методом Выбрать) из результата запроса. После
этого, как обычно, обрабатываем данные в цикле. Если в качестве параметра
запроса передать ссылку на текущий документ, то циклы можно опустить,
поскольку запрос возвратит нам только один-единственный документ.
Существует еще одна полезная возможность выборки одного или нескольких
документов по рейтингу в контексте суммы, количества или иного значения
реквизита. Например, нужно получить десять реализаций за текущий месяц,
имеющих наибольшую сумму продажи. Для этого имеет смысл применить
ключевое слово ПЕРВЫЕ, а следом за ним указать количество выбираемых документов. Посмотрите пример, представленный в листинге 8.17.
Листинг 8.17. Получение документов по рейтингу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ ПЕРВЫЕ 10
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Продавец,
|
Контрагент
КАК Покупатель,
|
СуммаДокумента КАК СуммаДокумента
|ИЗ
|
Документ.РеализацияТоваровУслуг
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Проведен = ИСТИНА
|УПОРЯДОЧИТЬ ПО
|
СуммаДокумента УБЫВ";
Глава 8. Использование запросов
79
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
В результате выполнения этого запроса мы получим десять проведенных документов за текущий месяц с наибольшими значениями суммы продаж. Этот
эффект достигается упорядочиванием (ключевое слово УПОРЯДОЧИТЬ ПО) результата запроса по сумме документа в порядке убывания. Другими словами,
запрос выберет все документы за указанный период (в данном случае за текущий месяц), затем выполнит сортировку по полю суммы и в завершение
возвратит первые десять записей, которые и будут иметь максимальные значения по сумме документа. Не правда ли, просто и красиво.
Кроме рейтинга по сумме документа, можно применить слово ПЕРВЫЕ для определения даты первого созданного документа за указанный период или наибольшего количества проданного (закупленного по документу поступления)
товара. В определенной мере, ключевое слово ПЕРВЫЕ помогает быстро и
удобно реализовать простой отчет на базе одного вида документа, без объединения и связывания таблиц.
Теперь поговорим о применении в запросах очень полезного оператора В.
С его помощью можно проверить, совпадает ли значение поля, указанного
слева, со значениями полей, перечисленных справа от оператора. Правое выражение может быть списком (массивом) значений или подзапросом. Посмотрите пример работы с этим оператором, представленный в листинге 8.18.
Листинг 8.18. Использование оператора В с массивом
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
Количество
КАК Количество,
|
ЕдиницаИзмерения КАК ЕдиницаИзмерения,
|
Цена
КАК Цена
|ИЗ
|
Документ.ВнутреннийЗаказ.Товары
80
Часть I. Приемы программирования документов
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная
|
И Проведен = ИСТИНА
|
И Номенклатура В (&СписокНоменклатуры)
|УПОРЯДОЧИТЬ ПО
|
Ссылка.Дата УБЫВ";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Создаем новый массив
МассивНоменклатура = Новый Массив;
// Добавляем в массив элементы
МассивНоменклатура.Добавить("Телевизор");
МассивНоменклатура.Добавить("Видеомагнитофон");
МассивНоменклатура.Добавить("Видеокамера");
// Передаем в запрос массив
Запрос.УстановитьПараметр("СписокНоменклатуры", МассивНоменклатура);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
В рассмотренном примере мы выбрали из базы данных проведенные документы за указанный период, у которых наименование номенклатуры в табличной части Товары совпадает хотя бы с одним значением из массива. Массив мы заполнили различными товарами и передали в качестве параметра
в запрос. Если перед оператором В указать логический оператор НЕ, то результат будет противоположным: запрос возвратит все документы, у которых
в товарной части не указаны перечисленные значения из массива.
Вместо массива или списка элементов можно передать подзапрос. В этом
случае будет выполнена проверка совпадения левого от оператора значения
с результатом подзапроса. Пример использования подзапроса в операторе В
показан в листинге 8.19.
Листинг 8.19. Использование оператора В с подзапросом
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
Глава 8. Использование запросов
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Ответственный КАК Ответственный
81
|ИЗ
|
Документ.ВнутреннийЗаказ
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная
|
И Проведен = ИСТИНА
|
И Подразделение В
|
(
|
ВЫБРАТЬ
|
Ссылка
|
ИЗ
|
Справочник.Подразделения
|
ГДЕ
|
|
Наименование ПОДОБНО &ШаблонПоиска
)
|УПОРЯДОЧИТЬ ПО
|
Дата УБЫВ";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ШаблонПоиска", "%закупка%");
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
В данном примере мы делаем выборку проведенных за определенный период
документов, у которых в наименовании подразделения присутствует слово
"закупка". Оператор В сравнивает значения результата подзапроса с указанным в документе, и если одно из них совпадает, условие выполняется. Оператор ПОДОБНО позволяет задать маску для поиска значений в базе данных.
Конечно, представленный пример не имеет практического значения, хотя бы
потому, что здесь можно обойтись и без подзапроса, задав условие поиска по
маске в основном запросе. Основная цель примера — показать, как используется оператор В с подзапросом.
Не секрет, что различные документы в конфигурации логически связаны между собой. Например, документ Поступление можно создать на основании
заказа поставщику, а документ Реализация — на основании заказа покупателя. Эти связи реализованы в виде ссылок, позволяя легко обратиться из ос-
82
Часть I. Приемы программирования документов
новного документа к связанному с ним документу. В запросах можно воспользоваться такой возможностью, чтобы прочитать сведения о связанных документах. Рассмотрим пример, реализующий похожую задачу (листинг 8.20).
Листинг 8.20. Получение информации о связанных документах
Процедура ОпределитьТипСвязанногоДокумента(ДокументСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Покупатель,
|
Контрагент
КАК Поставшик,
|
СуммаДокумента
КАК Сумма,
|
Сделка.Ссылка
КАК СвязанныйДокумент
|ИЗ
|
Документ.ПоступлениеТоваровУслуг
|ГДЕ
|
Ссылка = &Ссылка";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Выполняем запрос
Данные = Запрос.Выполнить().Выбрать();
// Выполняем обработку результата запроса
Пока Данные.Следующий() Цикл
// Получаем общие данные по документу
Сообщить("Номер : " + Данные.НомерДокумента);
Сообщить("Дата : " + Данные.ДатаДокумента);
Сообщить("Номер связанного документа: " +
Данные.СвязанныйДокумент.Номер);
// Определяем тип связанного документа
Если ТипЗнч(Данные.СвязанныйДокумент) =
Тип("ДокументСсылка.АвансовыйОтчет") Тогда
Сообщить("Тип связанного документа:
|АвансовыйОтчет");
Глава 8. Использование запросов
83
ИначеЕсли ТипЗнч(Данные.СвязанныйДокумент) =
Тип("ДокументСсылка.ВозвратТоваровПоставщику") Тогда
Сообщить("Тип связанного документа:
|ВозвратТоваровПоставщику");
ИначеЕсли ТипЗнч(Данные.СвязанныйДокумент) =
Тип("ДокументСсылка.ЗаказПоставщику") Тогда
Сообщить("Тип связанного документа:
|ЗаказПоставщику");
ИначеЕсли ТипЗнч(Данные.СвязанныйДокумент) =
Тип("ДокументСсылка.КорректировкаДолга") Тогда
Сообщить("Тип связанного документа:
|КорректировкаДолга");
ИначеЕсли ТипЗнч(Данные.СвязанныйДокумент) =
Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
Сообщить("Тип связанного документа:
|РеализацияТоваровУслуг");
ИначеЕсли ТипЗнч(Данные.СвязанныйДокумент) =
Неопределено Тогда
Сообщить("Связанный документ не задан!");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В этом примере мы выполнили запрос по текущему документу, чтобы определить тип связанного документа, указанного в поле сделка. В конфигураторе данное поле имеет составной тип, поскольку ссылается на разные виды
документов. После получения результата запроса, прежде чем работать со
связанным документом, необходимо проверить его тип. Задача решается посредством встроенной функции ТипЗнч, определяющей тип значения переменной. Если не выбран ни один связанный документ, она возвратит нам
значение Неопределено. После того как тип определен, можно обращаться
к его реквизитам и табличным частям. В примере мы прочитали значение
номера связанного документа раньше, потому что номер и дата документа
имеются у всех документов конфигурации по умолчанию.
При обращении в запросе к табличной части документа можно указать несколько колонок сразу и поместить их в одну вложенную таблицу или раздельно, инициировав тем самым создание своих вложенных таблиц по каждой колонке. Первый вариант показан в листинге 8.21.
84
Часть I. Приемы программирования документов
Листинг 8.21. Запрос к группе колонок табличной части
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Ответственный КАК Ответственный,
|
Товары.(Номенклатура, Количество, Цена) КАК Товар,
|
ВозвратнаяТара.(Номенклатура, Количество, Цена) КАК Тара
|ИЗ
|
Документ.ВнутреннийЗаказ";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате запроса к табличным частям документа мы получим по одной
вложенной таблице для частей Товары и ВозвратнаяТара.
Однако можно разделить данные колонок по отдельным таблицам, как показано в листинге 8.22.
Листинг 8.22. Запрос к отдельным колонкам табличной части
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Ответственный
КАК Ответственный,
|
Товары.Номенклатура
КАК ТоварНоменклатура,
|
Товары.Количество
КАК ТоварКоличество,
|
Товары.Цена
КАК ТоварЦена,
|
ВозвратнаяТара.Номенклатура КАК ТараНоменклатура,
|
ВозвратнаяТара.Количество
КАК ТараКоличество,
|
ВозвратнаяТара.Цена
КАК ТараЦена
Глава 8. Использование запросов
85
|ИЗ
|
Документ.ВнутреннийЗаказ";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В этом случае для каждой колонки табличной части создается своя вложенная таблица.
Часто возникает ситуация, когда в табличной части документа присутствуют
одинаковые записи, например, повторяется номенклатура. Перед нами стоит
задача получить из товарной части документа все неповторяющиеся наименования товаров для какого-нибудь отчета. Реализовать эту возможность
можно с помощью ключевого слова РАЗЛИЧНЫЕ. Оно помогает отсеять повторяющиеся записи, передав в результат запроса уникальные значения, в данном случае номенклатуры. Пример работы со словом РАЗЛИЧНЫЕ представлен
в листинге 8.23.
Листинг 8.23. Получение неповторяющихся записей
Процедура ПолучитьРазличныеТовары(ДокументСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ РАЗЛИЧНЫЕ
|
Ссылка.Номер
КАК НомерДокумента,
|
Ссылка.Дата
КАК ДатаДокумента,
|
Ссылка.Ответственный КАК Ответственный,
|
Номенклатура
КАК Номенклатура,
|
Количество
КАК Количество,
|
Цена
КАК Цена
|ИЗ
|
Документ.ВнутреннийЗаказ.Товары
|ГДЕ
|
Ссылка = &Ссылка";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
КонецПроцедуры
86
Часть I. Приемы программирования документов
Ключевое слово РАЗЛИЧНЫЕ можно применять не только к реквизитам табличных частей, но и к основным реквизитам документа. Выбор неповторяющихся значений чем-то похож на группировку, но имеется одно существенное отличие. При группировках одинаковые записи объединяются в одну
с пересчетом числовых полей. Здесь же выбираются только различные записи, а остальные просто опускаются.
Пользуясь тем, что запрос представляет собой строковую переменную, можно
написать различные универсальные процедуры и функции, изменяющие текст
запроса в зависимости от ситуации. Рассмотрим пример простой функции, которая позволяет выбрать товарную часть для любого документа конфигурации,
поддерживающего работу с таблицей номенклатуры (листинг 8.24).
Листинг 8.24. Получение товарной части для заданного типа документа
Функция ПолучитьТовары(ДокументСсылка, ТипВыбранногоДокумента = "")
// Проверяем аргумент функции
Если ТипВыбранногоДокумента = "" Тогда
Возврат Неопределено;
КонецЕсли;
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ЕдиницаИзмерения
КАК ЕдиницаИзмерения,
|
Количество
КАК Количество,
|
Цена
КАК Цена
|ИЗ
|
Документ. " + ТипВыбранногоДокумента + ".Товары
|ГДЕ
|
Ссылка = &Ссылка";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Выполняем запрос
Возврат Запрос.Выполнить().Выбрать();
КонецФункции
// Вызываем функцию для документа ЗаказПокупателя
ДанныеВыборка = ПолучитьТовары(ЭтотОбъект.Ссылка, ЗаказПокупателя);
Глава 8. Использование запросов
87
В нашей функции тип документа передается через аргумент как строковое
наименование. Текст запроса формируется путем конкатенации строк, что
и позволяет унифицировать функцию под различные документы. Однако
структура каждого документа имеет свои специфические отличия, поэтому
имеет смысл еще более ее усложнить. Достигается это применением различных условий, циклов и перечислений, как в коде функции, так и в тексте запроса. Посмотрите пример, представленный в листинге 8.25.
Листинг 8.25. Получение информации по указанному документу
Функция ПолучитьСведения(ДокументСсылка, ТипВыбранногоДокумента = "",
ПомеченНаУдаление = Ложь, ТолькоПроведенные = Ложь)
// Проверяем аргумент функции
Если ТипВыбранногоДокумента = "" Тогда
Возврат Неопределено;
КонецЕсли;
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса
ТекстЗапроса = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ЕдиницаИзмерения
КАК ЕдиницаИзмерения,
|
Количество
КАК Количество,
|
Цена
КАК Цена
|ИЗ
|
Документ. " + ТипВыбранногоДокумента + ".Товары
|ГДЕ
|
Ссылка = &Ссылка";
// Надо ли выбирать документы, помеченные для удаления
Если ПомеченНаУдаление Тогда
Запрос.УстановитьПараметр("ПомеченНаУдаление",
ПомеченНаУдаление);
// Добавляем условие в запрос
ТекстЗапроса = ТекстЗапроса + " И ПометкаУдаления";
КонецЕсли;
// Надо ли выбирать только проведенные документы
Если ПомеченНаУдаление Тогда
Запрос.УстановитьПараметр("ТолькоПроведенные",
88
Часть I. Приемы программирования документов
ТолькоПроведенные);
// Добавляем условие в запрос
ТекстЗапроса = ТекстЗапроса + " И Проведен";
КонецЕсли;
// Завершаем текст запроса строкой сортировки по дате
ТекстЗапроса = ТекстЗапроса + " УПОРЯДОЧИТЬ ПО Дата";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Передаем текст запроса
Запрос.Текст = ТекстЗапроса;
// Выполняем запрос
Возврат Запрос.Выполнить().Выбрать();
КонецФункции
Итак, наша функция выбирает документ из базы по ссылке. Если указать
признак пометки на удаление, документ будет выбран только при таком условии. То же самое касается и признака проведения. Как видите, текст запроса мы по ходу проверки условий расширяем путем конкатенации строк. После того как запрос сформирован, передаем его в объект запроса.
В языке запросов 1С, как и в других аналогичных языках, имеется возможность связывать несколько таблиц вместе для получения общего результата.
При работе с документами такая необходимость возникает редко, поэтому
рассмотрим просто для примера вариант связывания таблицы документа
и справочника. Как это делается, показано в листинге 8.26.
Листинг 8.26. Связывание документа со справочником
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказ.Номер
КАК НомерДокумента,
|
Заказ.Дата
КАК ДатаДокумента,
|
Заказ.ДатаОплаты
КАК ДатаОплаты,
|
Заказ.ДатаОтгрузки
КАК ДатаОтгрузки,
|
Заказ.Контрагент
КАК Контрагент,
|
Заказ.Организация
КАК Организация,
|
Заказ.Ответственный
КАК Ответственный,
Глава 8. Использование запросов
89
|
Заказ.Склад
КАК Склад,
|
Контрагенты.Представление КАК ПредставлениеКонтрагента,
|
Контрагенты.Код
КАК КодКонтрагента
|ИЗ
|
Документ.ЗаказПокупателя КАК Заказ
|
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
|
ПО Заказ.Контрагент = Контрагенты.Ссылка";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В приведенном примере мы с помощью ключевого слова СОЕДИНЕНИЕ подключили в таблице документов таблицу контрагентов. Условие связывания
таблиц мы установили с помощью слова ПО и выбрали в качестве поля ссылку на контрагента. Ключевое слово ЛЕВОЕ позволяет задать тип соединения,
при котором из таблицы документа выбираются все данные, а из таблицы
справочника — только удовлетворяющие условию связи. Данный пример не
имеет практического значения и предложен вниманию читателей только для
ознакомления. Кроме того, если вы заметили, в запросе используются псевдонимы для таблиц. Объясняется все просто: когда в отдельном блоке запроса указана всего одна таблица, псевдоним не обязателен; когда таблиц две
и более, компилятор запроса должен точно определить, какое поле к какой
таблице относится.
Многие документы в системе имеют реквизиты составного типа, ссылающиеся на разные объекты. Мы уже говорили, что с помощью встроенной
функции ТипЗнч можно проверить тип ссылки, передаваемой в запрос. Однако есть возможность и в самом запросе указать, документ какого типа нам
нужен. Для этого применяется ключевое слово ВЫРАЗИТЬ. Пример кода, демонстрирующий сказанное, представлен в листинге 8.27.
Листинг 8.27. Использование ключевого слова ВЫРАЗИТЬ
Процедура ОпределитьТипСвязанногоДокумента(ДокументСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Покупатель,
90
Часть I. Приемы программирования документов
|
Контрагент
КАК Поставщик,
|
СуммаДокумента КАК Сумма,
|
ВЫРАЗИТЬ (Сделка
|
КАК Документ.АвансовыйОтчет) КАК СвязанныйДокумент
|ИЗ
|
Документ.ПоступлениеТоваровУслуг
|ГДЕ
|
Ссылка = &Ссылка
|
И Проведен
|
И Сделка ССЫЛКА Документ.АвансовыйОтчет";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
// Выполняем запрос
Данные = Запрос.Выполнить().Выбрать();
// Выполняем обработку результата запроса
// . . .
КонецПроцедуры
В данном запросе мы выбираем проведенный документ, имеющий в поле
сделки ссылку на объект АвансовыйОтчет. С помощью ключевого слова
ВЫРАЗИТЬ явно задаем тип колонки сделки принадлежащим документу АвансовыйОтчет. Далее выполняем запрос и обрабатываем соответствующим образом.
Теперь поговорим о пересчете итогов в запросах. Если возникает необходимость в пересчете каких-то числовых параметров по документам, можно воспользоваться группировкой с итогами. Делается это посредством ключевого
слова ИТОГИ. Пример расчета итоговых значений по сумме реализаций представлен в листинге 8.28.
Листинг 8.28. Получение итоговых значений полей
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК
|
ХарактеристикаНоменклатуры КАК
|
Количество
КАК
|
Сумма
в объект
Номенклатура,
ХарактеристикаНоменклатуры,
Количество,
КАК Сумма
Глава 8. Использование запросов
91
|ИЗ
|
Документ.РеализацияТоваровУслуг.Товары
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Проведен
|
|ИТОГИ СУММА(Количество), СУММА(Сумма) ПО
|
ОБЩИЕ,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
// Выполняем запрос
Данные = Запрос.Выполнить().Выбрать();
В результате запроса мы получим общие итоги по всем найденным документам: суммарное количество и общую сумму продаж по всей номенклатуре.
В завершение темы хотелось бы познакомить читателей с таким удобным
средством создания запросов, как конструктор запросов. Визуально он выглядит так, как показано на рис. 8.1.
Рис. 8.1. Конструктор запросов
92
Часть I. Приемы программирования документов
Он состоит из отдельных логически связанных частей, расположенных на
разных закладках. На основной закладке находится дерево таблиц из разных
объектов конфигурации. Одна закладка отвечает за группировку данных
в запросе. Другая — задает условия выборки. Есть отдельные закладки для
настройки сортировки, итогов, связей между таблицами. Конструктор является мощным инструментом, позволяющим создавать простые и очень сложные запросы, подзапросы с различной степенью вложенности и многое другое. Тем, кто всегда писал тексты запросов вручную, на первом этапе будет
непривычно работать с конструктором, но период адаптации проходит быстро и почти безболезненно. Зато в результате вы получаете удобное средство
для быстрого и правильного (есть проверка синтаксиса) формирования новых
запросов. Сразу оговорюсь, что при всех достоинствах конструктор имеет
один недостаток — слабо оптимизированный код, избыточность подзапросов
и дополнительных таблиц.
Попробуем написать запрос с помощью конструктора. Вначале откроем конструктор и на первой закладке выберем документ ВнутреннийЗаказ. Перенесем в окно таблиц выбранный документ и добавим в запрос следующие поля:
номер, дата, дата отгрузки, организация, ответственный, подразделение
и склад. Далее перейдем на закладку условий и введем новое значение. Для
этого добавим произвольное условие "ВнутреннийЗаказ.Проведен". Чтобы во
время формирования запроса посмотреть на исходный код, достаточно нажать кнопку Запрос. После этого перейдем на закладку объединений и псевдонимов, чтобы задать свои имена для полей выборки. И, наконец, на закладке сортировки установим сортировку по дате документа в порядке убывания.
Чтобы сохранить запрос, осталось нажать кнопку OK. При этом исходный
текст запроса будет добавлен в код объекта модуля или формы. Если все
сделано правильно, ваш запрос должен выглядеть примерно так, как в листинге 8.29.
Листинг 8.29. Пример запроса, сформированного конструктором запросов
// Текст запроса
ТекстЗапроса = "
|ВЫБРАТЬ
|
ВнутреннийЗаказ.Номер КАК НомерДокумента,
|
ВнутреннийЗаказ.Дата КАК ДатаДокумента,
|
ВнутреннийЗаказ.ДатаОтгрузки,
|
ВнутреннийЗаказ.Организация,
|
ВнутреннийЗаказ.Подразделение КАК Подразделение,
|
ВнутреннийЗаказ.Склад КАК Склад
Глава 8. Использование запросов
93
|ИЗ
|
Документ.ВнутреннийЗаказ КАК ВнутреннийЗаказ
|
|ГДЕ
|
(ВнутреннийЗаказ.Проведен)
|
|УПОРЯДОЧИТЬ ПО
|
ДатаДокумента УБЫВ
|";
Как видите, полученный результат немного отличается от запроса, сделанного
руками. Во-первых, конструктор задал псевдоним для таблицы, хотя в данном случае его можно было опустить. Во-вторых — мелкие отличия в форматировании, отступах, указании скобок для условия и сортировка по имени
псевдонима, а не исходного поля. В общем, запрос получился простым и понятным.
Но ситуация поменяется, если появятся связанные таблицы и подзапросы.
В этом случае оптимизация кода оставляет желать лучшего. Исходя из сказанного, наилучшим вариантом будет написание нового запроса в конструкторе с последующей корректировкой вручную для достижения оптимального кода.
94
Часть I. Приемы программирования документов
Часть II
Приемы программирования
регистров накопления
96
Часть I. Приемы программирования документов
Глава 9
Получение выборки
Важной составляющей системы "1С:Предприятие 8.0" являются регистры
накопления. Именно с их помощью происходит накопление информации по
движению товаров, основных и денежных средств. Вся накапливаемая информация в результате хозяйственной деятельности предприятия постоянно
заносится в регистры и образует мощную базу данных, которую можно применять для дальнейшего анализа, построения отчетов и диаграмм.
Движение по регистрам осуществляется в момент проведения документов.
Каждый документ конфигурации, как правило, осуществляет движения по
одному или нескольким регистрам накопления. Выбор регистров, по которым будет проходить документ, задается в регистраторе на этапе разработки
конфигурации.
Регистр накопления является прикладным объектом конфигурации и состоит
из ресурсов, измерений и реквизитов. Он также может иметь собственные
формы и макеты. Измерения позволяют обозначить объекты, по которым будет отслеживаться информация, а ресурсы являются значениями, накапливающими эту информацию. Реквизиты помогают предоставить дополнительные сведения по каждой записи. Существуют системные (заданные по
умолчанию и не изменяемые) и пользовательские (задаваемые пользователем
на этапе разработки) реквизиты. К системным относятся такие, как период,
регистратор, момент времени и др.
Кроме того, существует два основных типа регистров накопления: остатков
и оборотов. Регистры остатков позволяют проанализировать данные на заданный момент времени. Регистры оборотов хранят различную суммовую и
количественную информацию в разрезе измерений.
В этой главе мы поговорим о том, как сделать выборку из регистра накопления и правильно обработать полученные данные. Для этого предназначен
98
Часть II. Приемы программирования регистров накопления
общий объект РегистрНакопленияМенеджер, который в качестве результата
возвращает объект РегистрНакопленияВыборка. В общем случае выборка
задается с помощью метода Выбрать. Он имеет четыре необязательных параметра, через которые можно установить период выборки, условия и способ
сортировки записей. Рассмотрим базовый пример получения данных из регистра накопления, представленный в листинге 9.1.
Листинг 9.1. Выборка данных из регистра накопления
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Делаем выборку записей из регистра
ДанныеВыборка = РегистрЗаказыПокупателей.Выбрать();
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
// Выводим в окно сообщений номенклатуру
Сообщить("Номенклатура: " + СокрП(ДанныеВыборка.Номенклатура));
// Выводим в окно сообщений количество номенклатуры
Сообщить("Количество: " + ДанныеВыборка.Количество);
КонецЦикла;
В примере мы сделали запрос по регистру ЗаказыПокупателей. Запрос вернул
все записи регистра, обход по которым мы организовали с помощью цикла.
В результате в окно служебных сообщений вывели наименование регистратора, номенклатуру и количество. Регистратором является документ, осуществляющий движение по регистру. Номенклатура и количество описывают
значения из товарной части регистратора. Если в документе указано три позиции номенклатуры, в регистре будет три записи с одинаковым регистратором по каждому товару.
Как правило, выборка записей из регистра осуществляется за определенный
период, чтобы минимизировать затраты и ускорить процесс получения данных. Для этого можно в методе Выбрать задать начальную и конечную даты
периода. Кроме того, в качестве периода разрешено использовать объекты
МоментВремени и Граница. Как это делается, показано в листинге 9.2.
Глава 9. Получение выборки
99
Листинг 9.2. Выборка данных из регистра накопления за определенный период
// Задаем период выборки с 17 марта по 20 мая 2008 года
НачальнаяДата = Дата(2008, 3, 17);
КонечнаяДата = ТекущаяДата(2008, 4, 20);
// Задаем регистр накопления
РегистрЗаказыПоставщикам = РегистрыНакопления.ЗаказыПоставщикам;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрЗаказыПоставщикам.Выбрать(НачальнаяДата, КонечнаяДата);
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
// Теперь в качестве периода установим момент времени
ДатаВыборки = ЭтотОбъект.МоментВремени();
// Задаем регистр накопления
РегистрЗаказыПоставщикам = РегистрыНакопления.ЗаказыПоставщикам;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрЗаказыПоставщикам.Выбрать(ДатаВыборки);
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
// Теперь используем объект Граница для задания периода выборки
НачальнаяДата = НачалоГода(ТекущаяДата());
КонечнаяДата = Новый Граница(ТекущаяДата(), ВидГраницы.Включая);
// Задаем регистр накопления
РегистрЗаказыПоставщикам = РегистрыНакопления.ЗаказыПоставщикам;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрЗаказыПоставщикам.Выбрать(НачальнаяДата, КонечнаяДата);
// Организуем цикл для обработки записей регистра
100
Часть II. Приемы программирования регистров накопления
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
Итак, в примере мы разобрали три варианта установки периода выборки.
В первом случае мы явно указали начальную и завершающую даты. Во втором — использовали объект МоментВремени, который задает момент времени по ссылке на документ. В третьем случае воспользовались объектом Граница, чтобы установить завершающую дату для выборки. Конструктор этого
объекта состоит из значения даты и параметра описания, который позволяет
включать граничное значение даты (как в примере) или нет.
И наконец, метод Выбрать дает возможность отобрать записи регистра по условию и упорядочить результат запроса. Посмотрите пример, представленный в листинге 9.3.
Листинг 9.3. Выборка данных из регистра накопления с отбором и сортировкой
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоМесяца(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("Регистратор");
УсловиеОтбора.Регистратор = ЭтотОбъект.Ссылка;
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрПартииТоваровНаСкладах.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Номенклатура Убыв");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
В приведенном примере мы установили отбор по документу-регистратору
и сортировку по номенклатуре в порядке убывания. Отбор представляет
собой структуру, где ключом служит описание поля, а значением — значение
Глава 9. Получение выборки
101
отбора. Для полей отбора можно выбирать измерения и реквизиты регистра,
для которых в конфигураторе установлено свойство индексирования. Это
важный момент, и о нем следует всегда помнить. Параметр сортировки является строковым значением, содержащим имена измерений или реквизитов,
а также направление упорядочивания — по возрастанию или убыванию.
Следующий метод, с которым хотелось бы познакомить читателей, называется ВыбратьПоРегистратору. Он содержит всего один параметр, определяющий
ссылку на заданный для данного регистра документ-регистратор. Например,
для регистра накопления ПартииТоваровНаСкладах регистраторами могут
являться следующие документы: АвансовыйОтчет, ПеремещениеТоваров,
ВозвратТоваровОтПокупателя, ВозвратТоваровПоставщику, КорректировкаСерийИХарактеристикТоваров, ПоступлениеТоваровУслуг и некоторые
другие. Каждый из них при проведении выполняет движение по этому регистру. Рассмотрим пример использования метода ВыбратьПоРегистратору,
представленный в листинге 9.4.
Листинг 9.4. Выборка данных из регистра накопления с помощью метода
ВыбратьПоРегистратору
// Задаем ссылку на документ
ДокументСсылка = ЭтотОбъект.Ссылка;
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрПартииТоваровНаСкладах.ВыбратьПоРегистратору(ДокументСсылка);
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщения вид движения
Сообщить("Вид движения: " + ?(ДанныеВыборка.ВидДвижения =
ВидДвиженияНакопления.Приход, "Приход", "Расход"));
// Выводим в окно сообщений номер документа-регистратора
Сообщить("Номер: " + ДанныеВыборка.Регистратор.Номер);
// Выводим в окно сообщений дату документа-регистратора
Сообщить("Дата: " + ДанныеВыборка.Регистратор.Дата);
// Выводим в окно сообщений сотрудника
Сообщить("Ответственный: " +
ДанныеВыборка.Регистратор.Ответственный);
КонецЦикла;
102
Часть II. Приемы программирования регистров накопления
Как видите, использовать данный метод достаточно просто. Вначале передаем ссылку на документ, который может выполнять движения по данному
регистру накопления, а затем обрабатываем, как и раньше, в цикле результат выборки. Дополнительно проверяем значение системного реквизита
ВидДвижения, который может быть либо приходным, либо расходным. Направление движения задается в конфигураторе с помощью конструктора
движений.
А теперь подробнее поговорим о программировании знакомого нам объекта
РегистрНакопленияВыборка. С его единственным методом Следующий мы
уже работали. Он выполняет переход к следующей записи результата выборки. Кроме того, требуется в начале обработки как минимум один раз его вызвать для установки курсора на первую запись. Каждый последующий вызов
передвигает позицию курсора на одну запись вперед, пока не будет достигнут конец выборки и метод не возвратит значение ИСТИНА.
После того как мы познакомились с основными правилами выборки записей из
регистров накопления, подробнее остановимся на некоторых наиболее популярных примерах получения данных и последующей обработке результатов.
Регистр накопления ЗаказыПокупателей позволяет отследить все движения
документа ЗаказПокупателя по системе. Когда менеджер фирмы оформляет
и проводит новый заказ, в регистр записывается определенное число записей,
равное количеству позиций номенклатуры в товарной части документа. В качестве регистратора выступает сам заказ с видом движения "приход". После
того как товар оплачен покупателем, на основании заказа оформляется документ реализации (расходная накладная), где указываются все или отдельные позиции номенклатуры. При проведении реализации в регистр Заказы
Покупателей добавляются записи с видом движения "расход". Как вы уже
догадались, число записей соответствует количеству позиций номенклатуры
в расходной накладной. Кроме этих двух основных документов, движения по
регистру могут выполняться и другими документами. Например, если заказ
корректируется (замена, добавление, удаление номенклатуры или изменение
количества), то движения выполняет документ КорректировкаЗаказаПокупателя. Если товар перед реализацией перемещается по складам, в регистр добавляются движения по документу ПеремещениеТоваров. Одним словом,
в качестве регистратора могут выступать все документы, которые заданы
в конфигураторе. Одни из них выполняют только приходные записи, другие — только расходные, а некоторые (например, ПеремещениеТоваров) —
и расходные, и приходные одновременно. Рассмотрим пример, в котором по
результату выборки анализируются все движения по заказу (листинг 9.5).
Глава 9. Получение выборки
103
Листинг 9.5. Анализ движения документа по регистру накопления
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоМесяца(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("Регистратор");
// Передаем ссылку на заказ покупателя
УсловиеОтбора.Регистратор = ЗаказПокупателяСсылка;
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрЗаказыПокупателей.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Период Возр");
// Определяем переменную для типа документа-регистратора
ТипДокумента = "";
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Проверяем тип документа-регистратора
Если ТипЗнч(ДанныеВыборка.Регистратор) =
Тип("ДокументСсылка.ЗаказПокупателя") Тогда
ТипДокумента = "Заказ покупателя";
ИначеЕсли ТипЗнч(ДанныеВыборка.Регистратор) =
Тип("ДокументСсылка.ЗакрытиеЗаказовПокупателей") Тогда
ТипДокумента = "Закрытие заказа покупателя";
ИначеЕсли ТипЗнч(ДанныеВыборка.Регистратор) =
Тип("ДокументСсылка.КорректировкаЗаказаПокупателя") Тогда
ТипДокумента = "Корректировка заказа покупателя";
ИначеЕсли ТипЗнч(ДанныеВыборка.Регистратор) =
Тип("ДокументСсылка.ПеремещениеТоваров") Тогда
ТипДокумента = "Перемещение товаров";
ИначеЕсли ТипЗнч(ДанныеВыборка.Регистратор) =
Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
ТипДокумента = "Реализация товаров";
КонецЕсли;
// Выводим период записи регистра
Сообщить("Период: " + ДанныеВыборка.Период);
// Выводим вид движения по регистру
Сообщить("Вид движения: " + ?(ДанныеВыборка.ВидДвижения =
104
Часть II. Приемы программирования регистров накопления
ВидДвиженияНакопления.Приход, "Приход", "Расход"));
Сообщить(ТипДокумента + " номер " +
ДанныеВыборка.Регистратор.Номер + " от " +
ДанныеВыборка.Регистратор.Дата);
КонецЦикла;
В результате запроса мы получим движения указанного заказа по регистру
накопления за текущий месяц. С помощью этого примера вы сможете лучше
представить формирование записей регистра накопления.
Если необходимо проанализировать сведения о товарах и их количестве по заказу покупателя, достаточно выполнить код, представленный в листинге 9.6.
Листинг 9.6. Выборка номенклатуры и количества из регистра накопления
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоКвартала(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("Регистратор");
// Передаем ссылеку на заказ покупателя
УсловиеОтбора.Регистратор = ЭтотОбъект.Ссылка;
// Задаем регистр накопления
РегистрЗаказыПокупателей =
РегистрыНакопления.ЗаказыПокупателей;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрЗаказыПокупателей.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Номенклатура Возр");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
// Выводим вид движения по регистру
Сообщить("Вид движения: " + ?(ДанныеВыборка.ВидДвижения =
ВидДвиженияНакопления.Приход, "Приход", "Расход"));
// Выводим в окно сообщений номенклатуру
Сообщить("Номенклатура: " + ДанныеВыборка.Номенклатура);
// Выводим в окно сообщений количество
Сообщить("Количество: " + ДанныеВыборка.Количество);
КонецЦикла;
Глава 9. Получение выборки
105
Следующий документ, который позволяет оформить заказ на закупку продукции, называется ЗаказПоставщику. Соответственно, регистр накопления — ЗаказыПостащикам. В качестве регистратора по этому регистру также
могут выступать различные документы системы, заданные в конфигураторе.
К наиболее важным относятся ПоступлениеТоваровУслуг (приходная накладная), ЗаказПокупателя, КорректировкаЗаказаПоставщику и, конечно, сам
документ ЗаказПоставщику. Регистр дает возможность отследить все изменения в системе, влияющие на заказ поставщику. Кроме того, регистр содержит измерение ЗаказПокупателя, что может помочь при анализе связанных
с заказом покупателя заказов поставщикам. Посмотрите пример, представленный в листинге 9.7.
Листинг 9.7. Выборка из регистра накопления ЗаказыПоставщикам
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоМесяца(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("ЗаказПокупателя");
// Передаем ссылеку на заказ покупателя
УсловиеОтбора.ЗаказПокупателя = ЗаказПокупателяСсылка;
// Задаем регистр накопления
РегистрЗаказыПоставщикам =
РегистрыНакопления.ЗаказыПоставщикам;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрЗаказыПоставщикам.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Период Возр");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
// Выводим вид движения по регистру
Сообщить("Вид движения: " + ?(ДанныеВыборка.ВидДвижения =
ВидДвиженияНакопления.Приход, "Приход", "Расход"));
// Выводим в окно сообщений связанный заказ покупателя
Сообщить("Заказ поставщику: " + ДанныеВыборка.ЗаказПоставщику);
КонецЦикла;
В результате выполнения запроса с отбором по заказу покупателя мы получим все записи регистра в указанном периоде, связанные с этим заказом.
106
Часть II. Приемы программирования регистров накопления
Важное место в торговых операциях занимает регистр ПартииТоваровНа
Складах (название может изменяться в разных конфигурациях), с помощью
которого можно отслеживать движения партий товаров на складах (партионный учет) в разрезе документов оприходования, заказов, перемещений и реализаций. Он позволяет получить остатки и стоимость товара на складах или
конкретном складе. Например, чтобы узнать историю движения какого-либо
товара по системе, можно воспользоваться следующим примером, представленным в листинге 9.8.
Листинг 9.8. Выборка из регистра накопления ПартииТоваровНаСкладах
// Задаем период выборки за текущий год
НачальнаяДата = НачалоГода(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("Номенклатура");
// Передаем ссылеку на заказ покупателя
УсловиеОтбора.Номенклатура = НоменклатураСсылка;
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Делаем выборку записей из регистра
ДанныеВыборка =
РегистрПартииТоваровНаСкладах.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Период Возр");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Номенклатура: " + ДанныеВыборка.Номенклатура);
// Выводим характеристику номенклатуры
Сообщить("Характеристика: " +
ДанныеВыборка.ХарактеристикаНоменклатуры);
// Выводим серию номенклатуры
Сообщить("Серия номенклатуры: " + ДанныеВыборка.СерияНомеклатуры);
// Выводим вид движения по регистру
Сообщить("Вид движения: " + ?(ДанныеВыборка.ВидДвижения =
ВидДвиженияНакопления.Приход, "Приход", "Расход"));
// Выводим в окно сообщений заказ покупателя
Сообщить("Заказ покупателя: " + ДанныеВыборка.Заказ);
Глава 9. Получение выборки
107
// Выводим в окно сообщений документ оприходования
Сообщить("Документ оприходования: " +
ДанныеВыборка.ДокументПоставки);
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
// Выводим в окно сообщений количество номенклатуры
Сообщить("Количество: " + ДанныеВыборка.Количество);
// Выводим в окно сообщений стоимость номенклатуры
Сообщить("Стоимость: " + ДанныеВыборка.Стоимость);
КонецЦикла;
В примере мы устанавливаем в отборе ссылку на товар. После выполнения
запроса нам становятся доступны все документы, связанные с движением
этого товара по системе, начиная от заказа и оприходования, до реализации
покупателю.
На этом мы завершим описание методов выборки из регистров накопления
и перейдем к знакомству с двумя основными разновидностями регистра накопления: остаткам и оборотам. Регистр остатков дает возможность получить
значение остатка (например, номенклатуры) на определенный момент времени, а регистр оборотов дает представление о полной картине движения по
установленному признаку (например, по складу) за какой-либо период.
Глава 10
Получение остатков
Регистр остатков является разновидностью регистра накопления и позволяет
оперативно получить информацию о наличии средств на определенный момент времени. Он образуется двумя таблицами: движений и итогов. Первая
строится на базе движения (проведения) документов, а вторая содержит значения итогов по остаткам в разрезе заданных в конфигураторе измерений.
Таблица регистра остатков представляет собой виртуальный объект, структуру которого нельзя открыть и скорректировать в конфигураторе. Чтобы просмотреть доступные измерения, ресурсы и реквизиты такой таблицы, следует
воспользоваться конструктором запросов, где вместе с основной таблицей
регистра представлены все виртуальные.
Для получения остатков регистра накопления предназначен метод Остатки.
Он имеет четыре необязательных параметра: момент времени, отбор, измерения и ресурсы. Результат выполнения записывается в таблицу значений. Рассмотрим базовый пример работы с регистром остатков, показанный в листинге 10.1.
Листинг 10.1. Выборка данных из регистра остатков
// Задаем регистр накопления
РегистрЗаказыПоставщикамОстатки = РегистрыНакопления.ЗаказыПоставщикам;
// Делаем выборку остатков по всем измерениям и ресурсам регистра
ДанныеОстатки = РегистрЗаказыПоставщикамОстатки.Остатки();
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
ОбщееКоличествоТоваров = ДанныеОстатки.Итог("КоличествоОстаток");
КонецЕсли;
Глава 10. Получение остатков
109
В данном примере мы не указали ограничений, поэтому в результате получили текущие остатки товаров по заказам, рассчитанные на максимальную дату
движений регистра. Кроме того, воспользовавшись методом Итог, мы рассчитали общее количество номенклатуры. Единственный аргумент метода
принимает наименование колонки из таблицы значений, по которой будет
подсчитан итог.
Теперь попробуем установить момент времени, на который нам требуется
пересчитать остатки номенклатуры. В качестве первого параметра можно
передать и значение даты. В этом случае данные будут получены на начало
дня. Посмотрите пример, представленный в листинге 10.2.
Листинг 10.2. Получение остатков номенклатуры на указанную дату
// Задаем регистр накопления
РегистрЗаказыПоставщикамОстатки = РегистрыНакопления.ЗаказыПоставщикам;
// Задаем дату, на которую будут рассчитаны остатки
ДатаОстатков = ТекущаяДата();
// Делаем выборку остатков
ДанныеОстатки = РегистрЗаказыПоставщикамОстатки.Остатки(ДатаОстатков);
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем количество заказанных товаров
Сообщить("Количество: " + ДанныеОстатки[0].Количество);
КонецЕсли;
В результате выполнения запроса мы получим рассчитанные остатки товаров
на начало дня текущей даты.
Отбор по регистру остатков можно установить только для измерений. Он
представляет собой структуру, где ключом служит измерение, а значением —
значение измерения регистра. Например, зададим условие отбора по заказу
поставщику, как это сделано в листинге 10.3.
Листинг 10.3. Установка отбора по регистру остатков
// Задаем регистр накопления
РегистрЗаказыПоставщикамОстатки = РегистрыНакопления.ЗаказыПоставщикам;
// Задаем дату, на которую будут рассчитаны остатки
ДатаОстатков = ТекущаяДата();
// Задаем отбор по заказу
110
Часть II. Приемы программирования регистров накопления
ОтборПоЗаказу = Новый Структура();
ОтборПоЗаказу.Вставить("ЗаказПоставщику", СсылкаЗаказПоставщику);
// Делаем выборку остатков
ДанныеОстатки = РегистрЗаказыПоставщикамОстатки.Остатки
(ДатаОстатков, ОтборПоЗаказу);
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем количество всех заказанных товаров
КоличествоТоваров = ДанныеОстатки[0].Количество;
// Выводим дату расчета остатков
Сообщить("Дата расчета: " + ДатаОстатков);
// Выводим в окно сообщений результат
Сообщить("Количество: " + КоличествоТоваров);
КонецЕсли;
Здесь мы указали не только дату выборки, но и условие отбора. В результате
мы получим остатки товаров по указанному заказу поставщику на начало дня
текущей даты. При необходимости рассчитать текущие остатки по определенной номенклатуре можно применить код, показанный в листинге 10.4.
Листинг 10.4. Расчет текущих остатков номенклатуры
// Задаем регистр накопления
РегистрЗаказыПоставщикамОстатки = РегистрыНакопления.ЗаказыПоставщикам;
// Задаем отбор по номенклатуре
ОтборПоТовару = Новый Структура();
ОтборПоТовару.Вставить("Номенклатура", СсылкаНоменклатура);
// Делаем выборку остатков
ДанныеОстатки = РегистрЗаказыПоставщикамОстатки.Остатки(, ОтборПоТовару);
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем остаток по номенклатуре
КоличествоТовара = ДанныеОстатки.Итог("КоличествоОстаток");
// Выводим в окно сообщений результат
Сообщить("Остаток номенклатуры: " + КоличествоТовара);
КонецЕсли;
В результате выполнения запроса нам будет возвращено значение текущего
остатка по указанному товару. Кроме того, можно явно задать измерения,
Глава 10. Получение остатков
111
в разрезе которых необходимо получить остатки. Например, рассчитаем количество оставшейся номенклатуры по связанным заказам покупателей (листинг 10.5).
Листинг 10.5. Расчет текущих остатков номенклатуры по заказам покупателей
// Задаем регистр накопления
РегистрЗаказыПоставщикамОстатки = РегистрыНакопления.ЗаказыПоставщикам;
// Задаем отбор по заказу поставщику
ОтборПоЗаказу = Новый Структура();
ОтборПоЗаказу.Вставить("ЗаказПоставщику", СсылкаЗаказПоставщику);
// Задаем список измерений
СписокИзмерений = "Номенклатура, ЗаказПокупателя";
// Делаем выборку остатков
ДанныеОстатки = РегистрЗаказыПоставщикамОстатки.Остатки
(, ОтборПоЗаказу, СписокИзмерений);
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем количество заказанных товаров
КоличествоТовара = ДанныеОстатки[0].Количество;
// Выводим в окно сообщений результат
Сообщить("Количество: " + КоличествоТовара);
КонецЕсли;
После выполнения данного примера результат будет хранить значения остатков всех товаров по связанным заказам покупателей для заданного заказа поставщику.
Кроме перечисленных возможностей, метод Остатки позволяет задать имена
ресурсов, по которым будут рассчитаны остатки. Например, регистр ПартииТоваровНаСкладах содержит несколько ресурсов, как количественных, так и
суммовых. Если нам требуется рассчитать только сумму, можно явно это задать в четвертом параметре метода. Как это делается, показано в листинге 10.6.
Листинг 10.6. Расчет остаточной стоимости номенклатуры по заказу покупателя
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
112
Часть II. Приемы программирования регистров накопления
// Задаем отбор по заказу покупателя
ОтборПоЗаказу = Новый Структура();
ОтборПоЗаказу.Вставить("ЗаказПокупателя", СсылкаЗаказПокупателя);
// Задаем список измерений
СписокИзмерений = "Номенклатура";
// Делаем выборку остатков
ДанныеОстатки = РегистрПартииТоваровНаСкладах.Остатки
(, ОтборПоЗаказу, СписокИзмерений, "СтоимостьОстаток");
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем итоговую стоимость заказанных товаров
Сообщить("Общая стоимость: " + ДанныеОстатки[0].СтоимостьОстаток);
// Или через таблицу значений
// Получаем итоговую стоимость заказанных товаров
ОбщаяСтоимость = ДанныеОстатки.Итог("СтоимостьОстаток");
Сообщить("Общая стоимость: " + ОбщаяСтоимость);
КонецЕсли;
В данном примере мы пересчитали остаточную стоимость каждого имеющегося товара по заданному заказу покупателя.
Поскольку все параметры метода Остатки являются необязательными, можно
выбирать всевозможные их комбинации для получения остатков в разрезе
различных измерений.
А теперь попробуем определить остатки товаров на складах в разрезе заказа покупателя. Для этого достаточно передать в запрос ссылку на указанные документы. Пример, представленный в листинге 10.7, демонстрирует
сказанное.
Листинг 10.7. Получение остатков номенклатуры в разрезе заказа
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Задаем отбор по заказу покупателя
ОтборПоЗаказу = Новый Структура();
ОтборПоЗаказу.Вставить("ЗаказПокупателя", СсылкаЗаказПокупателя);
// Задаем список измерений
Глава 10. Получение остатков
113
СписокИзмерений = "Номенклатура, Склад";
// Делаем выборку остатков
ДанныеОстатки = РегистрПартииТоваровНаСкладах.Остатки
(, ОтборПоЗаказу, СписокИзмерений, "Количество");
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем остатки товаров по заказу
ОстатокТовара = ДанныеОстатки[0].Количество;
// Выводим в окно сообщений результат
Сообщить("Остаток: " + ОстатокТоваров);
КонецЕсли;
В результате запроса к регистру мы получили таблицу значений по остаткам
номенклатуры в разрезе заказа покупателя. Далее в цикле поочередно вывели
в окно сообщений все позиции номенклатуры, оставшиеся на складах.
Имеет смысл также получить остатки товаров на всех складах, независимо от
документов поступлений и оприходований. Посмотрите пример, представленный в листинге 10.8.
Листинг 10.8. Получение остатков номенклатуры по всем складам
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Задаем отбор по заказу покупателя
ОтборПоНоменклатуре = Новый Структура();
ОтборПоНоменклатуре.Вставить("Номенклатура", СсылкаНоменклатура);
// Задаем список измерений
СписокИзмерений = "Склад";
// Делаем выборку остатков
ДанныеОстатки = РегистрПартииТоваровНаСкладах.Остатки
(, ОтборПоНоменклатуре, СписокИзмерений, "Количество");
// Проверяем наличие записей
Если ДанныеОстатки.Количество() > 0 Тогда
// Получаем общий остаток номенклатуры на складах
ОбщийОстаток = ДанныеОстатки.Итог("КоличествоОстаток");
Сообщить("Остаток на складах: " + ОбщийОстаток);
КонецЕсли;
114
Часть II. Приемы программирования регистров накопления
В приведенном примере мы рассчитали остатки выбранной номенклатуры по
каждому складу, а также получили общий остаток на всех складах.
Как видите, регистры остатков дают возможность получать различную актуальную информацию, что немаловажно для оперативной работы любого
предприятия.
В следующей главе мы познакомимся с еще одним типом регистра накопления — оборотным, по которому можно проследить всю историю движения
товаров в системе.
Глава 11
Получение оборотов
В системе 1С существует еще один тип регистра накопления. Он называется
оборотным и содержит различную информацию, которую можно получить за
определенный период времени. Например, движение денежных средств или
продажи товаров. По нему можно проследить историю движения любого товара по системе, начиная от оприходования на склад до реализации конечному покупателю. Тип регистра задается на этапе разработки в конфигураторе.
Важным отличием от регистра остатков является возможность задавать начальную и конечную дату периода, за который требуется рассчитать обороты. На его основе реализуются всевозможные отчеты и обработки.
Для получения оборотов посредством объекта РегистрНакопленияМенеджер
существует метод Обороты. Он имеет пять необязательных параметров: начальную и конечную даты периода, условия отбора, список измерений и список ресурсов. Если не указать начало периода, регистр возвратит данные
с момента начала работы базы данных. При отсутствии конечной даты будут
получены данные до момента последнего движения документов. Если не задать список измерений, результат запроса отразит обороты по всем измерениям, указанным в конфигураторе для выбранного вида регистра. То же самое относится и к ресурсам. Рассмотрим пример запроса к регистру оборотов
Продажи, представленный в листинге 11.1.
Листинг 11.1. Выборка данных из регистра оборотов
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Делаем выборку остатков по всем измерениям и ресурсам регистра
ДанныеОбороты = РегистрПродажиОбороты.Обороты();
// Проверяем наличие записей
116
Часть II. Приемы программирования регистров накопления
Если ДанныеОбороты.Количество() > 0 Тогда
// Получаем общее количество проданных товаров
Сообщить("Количество: " + ДанныеОбороты[0].КоличествоОборот);
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
КонецЕсли;
Итак, мы вызвали метод Обороты без параметров. В результате выполнения
регистр вернул общее количество проданных товаров за весь период существования базы данных и, соответственно, общую сумму продаж. Выборка
представляет собой таблицу значений, где каждому измерению соответствует
своя колонка, а также по две колонки для каждого ресурса.
А сейчас попробуем получить сумму продаж за текущий месяц по указанному документу поступления. Для этого явно укажем период выборки, а в качестве отбора передадим ссылку на документ поступления. Как это сделать,
показано в листинге 11.2.
Листинг 11.2. Получение количества и суммы продаж за период по документу
поступления
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Задаем начальную дату периода
ДатаНачальная = НачалоМесяца(ТекущаяДата());
// Задаем конечную дату периода
ДатаКонечная = ТекущаяДата();
// Задаем отбор по документу поступления
ОтборПоДокументуПоступления = Новый Структура();
ОтборПоДокументуПоступления.Вставить
("ДокументПоставки", СсылкаДокументПоступления);
// Делаем выборку
ДанныеОбороты = РегистрПродажиОбороты.Обороты
(ДатаНачальная, ДатаКонечная, ОтборПоДокументуПоступления);
// Проверяем наличие записей
Если ДанныеОбороты.Количество() > 0 Тогда
// Получаем общее количество проданных товаров
Сообщить("Количество: " + ДанныеОбороты[0].КоличествоОборот);
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
КонецЕсли;
Глава 11. Получение оборотов
117
В результате запроса мы сможем оценить, сколько и на какую сумму было
продано товаров, оформленных по определенному документу поступления.
Можно еще дополнительно определить, сколько продано продукции в разрезе ответственного сотрудника, оформившего документ поступления. Реализовать это можно с помощью добавления еще одного критерия отбора, как
показано в листинге 11.3.
Листинг 11.3. Получение количества и суммы продаж за период по документу
поступления и ответственному
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Задаем начальную дату периода
ДатаНачальная = НачалоМесяца(ТекущаяДата());
// Задаем конечную дату периода
ДатаКонечная = ТекущаяДата();
// Задаем отбор по документу поступления
ОтборПоДокументуПоступления = Новый Структура();
ОтборПоДокументуПоступления.Вставить
("ДокументПоставки", СсылкаДокументПоступления);
// Задаем ответственного за создание документа
ОтборПоДокументуПоступления.Вставить
("ДокументПоставки.Ответственный", СсылкаОтветственный);
// Делаем выборку
ДанныеОбороты = РегистрПродажиОбороты.Обороты
(ДатаНачальная, ДатаКонечная, ОтборПоДокументуПоступления);
// Проверяем наличие записей
Если ДанныеОбороты.Количество() > 0 Тогда
// Получаем общее количество проданных товаров
Сообщить("Количество: " + ДанныеОбороты[0].КоличествоОборот);
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
КонецЕсли;
В рассмотренных примерах выборка делалась по всем измерениям регистра,
что не совсем правильно. Попробуем исправить это и задать только те измерения, которые нас интересуют. Посмотрите, как задаются измерения для
оборотного регистра, в следующем примере (листинг 11.4).
118
Часть II. Приемы программирования регистров накопления
Листинг 11.4. Получение количества и суммы продаж за период по заказу
покупателя и подразделению
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Задаем начальную дату периода
ДатаНачальная = НачалоМесяца(ТекущаяДата());
// Задаем конечную дату периода
ДатаКонечная = ТекущаяДата();
// Задаем отбор по заказу и подразделению
УсловиеОтбора = Новый Структура();
УсловиеОтбора.Вставить("ЗаказПокупателя", СсылкаЗаказПокупателя);
УсловиеОтбора.Вставить("Подразделение", СсылкаПодразделение);
// Задаем необходимые измерения
СписокИзмерений = "ЗаказПокупателя, Подразделение";
// Делаем выборку
ДанныеОбороты = РегистрПродажиОбороты.Обороты
(ДатаНачальная, ДатаКонечная, УсловиеОтбора, СписокИзмерений);
// Проверяем наличие записей
Если ДанныеОбороты.Количество() > 0 Тогда
// Получаем общее количество проданных товаров
Сообщить("Количество: " + ДанныеОбороты[0].КоличествоОборот);
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
КонецЕсли;
В результате выполнения запроса мы получим общее количество и сумму
продаж за текущий месяц по заказу покупателя и подразделению.
Последний параметр метода Обороты позволяет указать наименования ресурсов, которые нужно получить, иначе будут возвращены рассчитанные итоги
по всем ресурсам. Как задаются имена ресурсов, показано в листинге 11.5.
Листинг 11.5. Получение суммы продаж за период по документу продажи
и подразделению
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Задаем начальную дату периода
ДатаНачальная = НачалоМесяца(ТекущаяДата());
Глава 11. Получение оборотов
119
// Задаем конечную дату периода
ДатаКонечная = ТекущаяДата();
// Задаем отбор по заказу и подразделению
УсловиеОтбора = Новый Структура();
УсловиеОтбора.Вставить("ДокументПродажи", СсылкаДокументПродажи);
УсловиеОтбора.Вставить("Подразделение", СсылкаПодразделение);
// Задаем необходимые измерения
СписокИзмерений = "ДокументПродажи, Подразделение";
// Задаем ресурсы регистра, по которым нужно получить итог
СписокРесурсов = "СуммаПродажиОборот";
// Делаем выборку
ДанныеОбороты = РегистрПродажиОбороты.Обороты
(ДатаНачальная, ДатаКонечная, УсловиеОтбора, СписокИзмерений,
СписокРесурсов);
// Проверяем наличие записей
Если ДанныеОбороты.Количество() > 0 Тогда
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
КонецЕсли;
В приведенном примере мы явно указали, что нас интересуют итоги только
по сумме продаж, задав соответствующее имя ресурса в методе Обороты.
Чтобы дополнительно получить сведения по каждой реализованной номенклатуре, можно использовать вариант кода, представленный в листинге 11.6.
Листинг 11.6. Получение развернутой информации по реализованной
номенклатуре
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Задаем начальную дату периода
ДатаНачальная = НачалоМесяца(ТекущаяДата());
// Задаем конечную дату периода
ДатаКонечная = ТекущаяДата();
// Задаем необходимые измерения
СписокИзмерений =
"Номенклатура, ЗаказПокупателя, ДокументПоставки, ДокументПродажи";
// Задаем ресурсы регистра, по которым нужно получить итог
СписокРесурсов = "КоличествоОборот, СуммаПродажиОборот";
120
Часть II. Приемы программирования регистров накопления
// Делаем выборку
ДанныеОбороты = РегистрПродажиОбороты.Обороты
(ДатаНачальная, ДатаКонечная, , СписокИзмерений, СписокРесурсов);
// Проверяем наличие записей
Если ДанныеОбороты.Количество() > 0 Тогда
// Получаем общее количество проданных товаров
Сообщить("Количество проданных товаров: " +
ДанныеОбороты[0].КоличествоОборот);
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
// Получаем развернутую информацию по номенклатуре
Для Каждого Товар Из ДанныеОбороты Цикл
Сообщить("Номенклатура: " + Товар.Номенклатура);
Сообщить("Заказ покупателя: " + Товар.ЗаказПокупателя);
Сообщить("Документ оприходования: " +
Товар.ДокументПоставки);
Сообщить("Документ продажи: " + Товар.ДокументПродажи);
Сообщить("Количество: " + Товар.КоличествоОборот);
Сообщить("Сумма продажи: " + Товар.СуммаПродажиОборот);
КонецЦикла;
КонецЕсли;
В этом примере мы получили подробное описание по каждой реализованной
номенклатуре за период с указанием заказа покупателя, документа оприходования и документа продажи.
Поскольку результатом метода Обороты является таблица значений, имеет
смысл воспользоваться ее методами для управления данными. Например,
чтобы обработать только одну колонку, можно выгрузить ее в отдельный
массив посредством метода ВыгрузитьКолонку. Посмотрите, как это делается,
на примере из листинга 11.7.
Листинг 11.7. Использование метода ВыгрузитьКолонку
// Задаем регистр накопления
РегистрПродажиОбороты = РегистрыНакопления.Продажи;
// Делаем выборку остатков по всем измерениям и ресурсам регистра
ДанныеОбороты = РегистрПродажиОбороты.Обороты();
// Проверяем наличие записей
Если ДанныеОбороты.Количество() > 0 Тогда
Глава 11. Получение оборотов
121
// Получаем общее количество проданных товаров
Сообщить("Количество: " + ДанныеОбороты[0].КоличествоОборот);
// Получаем общую сумму продаж
Сообщить("Сумма продаж: " + ДанныеОбороты[0].СуммаПродажиОборот);
// Выгружаем сведения о номенклатуре в массив
Товары = Новый Массив;
Товары = ДанныеОбороты.ВыгрузитьКолонку("Номенклатура");
// Обрабатываем результат
Для Каждого Товар Из Товары Цикл
Сообщить("Наименование: " + Товар.Наименование);
Сообщить("Номер ГТД: " + Товар.НомерГТД);
Сообщить("Страна происхождения: " +
Товар.СтранаПроисхождения);
Сообщить("Единица измерения: " +
Товар.БазоваяЕдиницаИзмерения);
КонецЦикла;
КонецЕсли;
В рассмотренном примере мы вывели общие обороты по регистру продаж
и дополнительно выгрузили из таблицы значений колонку с номенклатурой
для дальнейшего анализа. Вы можете самостоятельно поэкспериментировать
с другими измерениями регистра.
На этом, думаю, можно остановиться, поскольку представленного в этой главе материала вполне достаточно для работы с оборотными регистрами. Кроме того, наиболее эффективными методами работы с регистрами, как остатков, так и оборотов, являются все-таки запросы, а не объекты конфигурации.
Глава 12
Получение формы
Как и у многих других объектов конфигурации, регистры накопления поддерживают использование форм для визуального анализа и управления записями.
Во время разработки конфигурации в свойствах регистра можно сформировать
стандартные (форма списка и форма набора записей) и собственные (произвольные) формы. При наличии нескольких типов форм следует одну выбрать
основной. Это позволяет всегда отображать основную форму, если при обращении явно не будет задан ее тип.
Для доступа к формам регистров накопления используются два метода: Полуи ПолучитьФормуСписка. Первый имеет три параметра. Первый
обязательный параметр содержит имя формы, как оно задано в конфигураторе. Второй необязательный параметр дает возможность указать родительскую форму, являющуюся владельцем вызываемой формы. Последний параметр задает уникальный ключ, по которому можно найти форму среди уже
открытых. Это особенно актуально, когда создается несколько копий однотипных форм.
читьФорму
Рассмотрим простой пример получения формы набора записей регистра,
представленный в листинге 12.1.
Листинг 12.1. Получение формы регистра накопления
// Получаем форму набора записей регистра накопления
ФормаНабораЗаписей =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФорму("ФормаНабораЗаписей");
// Отображаем форму на экране
ФормаНабораЗаписей.Открыть();
Глава 12. Получение формы
123
После выполнения примера на экран будет выведена форма набора записей
регистра накопления. Если для формы задан владелец, то его следует указать,
чтобы сохранялась связь между ними. Посмотрите пример из листинга 12.2.
Листинг 12.2. Использование владельца формы
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФорму
("ФормаСписка", ФормаВладелец);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаСпискаРегистра.ЗакрыватьПриЗакрытииВладельца = Истина;
// Отображаем форму на экране
ФормаСпискаРегистра.Открыть();
Чтобы однозначно идентифицировать открытую форму (копию формы) среди других, необходимо задать значение ключа уникальности. Как это делается, показано в листинге 12.3.
Листинг 12.3. Использование ключа уникальности
// Формируем уникальный ключ
ГУИД = Новый УникальныйИдентификатор;
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФорму
("ФормаСписка", ФормаВладелец, ГУИД);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаСпискаРегистра.ЗакрыватьПриЗакрытииВладельца = Истина;
// Отображаем форму на экране
ФормаСпискаРегистра.ОткрытьМодально();
// Получаем эту же форму по значению ключа
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФорму
("ФормаСписка", ФормаВладелец, ГУИД);
// Если форма уже открыта, активизируем ее
Если ФормаСпискаРегистра.Открыта() Тогда
ФормаСпискаРегистра.Активизировать();
Иначе
ФормаСпискаРегистра.Открыть();
КонецЕсли;
124
Часть II. Приемы программирования регистров накопления
Перед вызовом формы мы создали и передали уникальный ключ. Когда форма будет открыта, ее легко можно будет найти среди однотипных форм посредством ключа.
Получив форму списка регистра накопления, можно сразу же установить отбор по регистратору. Посмотрите пример, представленный в листинге 12.4.
Листинг 12.4. Использование отбора по регистратору для формы списка
// Задаем отбор по регистратору
ДокументСсылка = Документы.ЗаказыПокупателей.НайтиПоНомеру
("0001", ТекущаяДата());
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФорму("ФормаСписка");
// Устанавливаем отбор
ФормаСпискаРегистра.ПараметрОтборПоРегистратору = ДокументСсылка;
// Отображаем форму на экране
ФормаСпискаРегистра.Открыть();
Итак, вначале мы получили ссылку на документ, который является регистратором для нашего регистра накопления. Затем получили форму списка и через свойство расширения формы списка ПараметрОтборПоРегистратору указали условие отбора по документу.
Второй метод получения формы ПолучитьФормуСписка похож на предыдущий,
но не содержит обязательных параметров. Если тип вызываемой формы не указан, будет отображена основная форма, заданная в конфигураторе. Как правило, у регистров накопления в качестве основной формы служит форма списка.
Рассмотрим пример работы с данным методом, показанный в листинге 12.5.
Листинг 12.5. Использование метода ПолучитьФормуСписка
// Формируем уникальный ключ
ГУИД = Новый УникальныйИдентификатор;
// Получаем основную форму регистра накопления
ФормаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФормуСписка
( , ФормаВладелец, ГУИД);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаРегистра.ЗакрыватьПриЗакрытииВладельца = Истина;
Глава 12. Получение формы
125
// Отображаем форму на экране
ФормаРегистра.Открыть();
// Получаем эту же форму по значению ключа
ФормаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФормуСписка
( , ФормаВладелец, ГУИД);
// Если форма уже открыта, активизируем ее
Если ФормаРегистра.Открыта() Тогда
ФормаРегистра.Активизировать();
Иначе
ФормаРегистра.Открыть();
КонецЕсли;
В результате выполнения на экране появится основная форма регистра, даже
если она не является формой списка. Как видно из примера, метод ПолучитьФормуСписка поддерживает указание родительской формы, а также ключ
уникальности.
Вы заметили, что оба метода практически одинаковы и различаются лишь
способом задания типа формы. В первом случае эта процедура необходима,
а во втором ее можно опустить, что приведет к загрузке основной формы регистра накопления, как задано в конфигураторе. В конечном итоге, выбор
метода остается за вами.
Глава 13
Получение макета
Регистры накопления, кроме объектов форм, поддерживают еще макеты.
Макет представляет собой шаблон, с помощью которого можно считывать
и устанавливать всевозможные настройки отборов, группировок и других
параметров. Макеты активно применяются при построении отчетов и создании печатных форм. Физически он представляет собой табличный документ
с заданным форматированием.
Для регистров накопления макеты, как и формы, используются далеко не
всегда. Основной причиной служит, как правило, дальнейшее построение
отчетов на базе этих регистров. При этом все необходимые настройки для
отчетов берутся из макета регистра. Здесь мы поговорим, как получать макеты регистров накопления и обрабатывать полученные данные.
Для обращения к макету вначале следует его вызвать с помощью метода
ПолучитьМакет. Он имеет один обязательный параметр, описывающий макет.
Описание макета задается на этапе разработки в конфигураторе. В результате
своего выполнения метод возвращает объект ТабличныйДокумент. Этот объект поддерживает широкие возможности по настройке и отображению разных типов данных. Посмотрите пример получения макета регистра, представленный в листинге 13.1.
Листинг 13.1. Получение макета регистра накопления
// Получаем макет регистра накопления
Макет =
РегистрыНакопления.ЗаказыПоставщикам.ПолучитьМакет("НастройкиОтчета");
В результате мы получим макет регистра ЗаказыПоставщикам с именем
"НастройкиОтчета". После этого мы можем читать или записывать значения
Глава 13. Получение макета
127
настроек для формирования отчета или построения печатного документа. Более подробно о структуре макета рассказано в главе 32, а здесь мы поговорим
о непосредственной работе с макетом. Скажу только, что макет может состоять из логически связанных областей, к которым можно обращаться через
метод ПолучитьОбласть. В качестве параметра метода передается наименование одной области, пересечения областей или адрес ячеек табличного документа. Рассмотрим пример получения области макета, показанный в листинге 13.2.
Листинг 13.2. Получение области группировок макета регистра накопления
// Получаем макет регистра накопления
Макет =
РегистрыНакопления.Продажи.ПолучитьМакет("НастройкиОтчета");
// Получаем область группировок
ОбластьГруппировок = Макет.ПолучитьОбласть("Группировки");
// Определяем количество строк полученной области
КоличествоСтрок = ОбластьГруппировок.ВысотаТаблицы;
// Читаем настройки группировок из области макета
Для НомерСтроки = 1 По КоличествоСтрок Цикл
// Получаем имя поля группировки
Сообщить("Имя поля: ",
СокрЛП(ОбластьГруппировок.Область(НомерСтроки, 1).Текст);
// Получаем представление поля
Сообщить("Представление поля: ",
СокрЛП(ОбластьГруппировок.Область(НомерСтроки, 2).Текст);
// Получаем описание поля
Сообщить("Описание поля: ",
СокрЛП(ОбластьГруппировок.Область(НомерСтроки, 3).Текст);
// Получаем признак использования по умолчанию
Сообщить("Используется по умолчанию: ",
Булево(СокрЛП(ОбластьГруппировок.Область(НомерСтроки, 5).Текст));
// Признак вывода итогов по иерархии
Сообщить("Использовать в итогах по иерархии: ",
Булево(СокрЛП(ОбластьГруппировок.Область(НомерСтроки, 7).Текст));
// Признак вывода пустых значений группировок
Сообщить("Выводить пустые значения: ",
Булево(СокрЛП(ОбластьГруппировок.Область(НомерСтроки, 11).Текст));
КонецЦикла;
128
Часть II. Приемы программирования регистров накопления
Итак, мы получили макет регистра для настройки отчета. После этого
мы определили количество строк в области, воспользовавшись свойством
ВысотаТаблицы объекта ТабличныйДокумент. Далее с помощью метода
ПолучитьОбласть считали настройки полей, размещенные в макете. При этом
в каждой строке описывается только одно поле. Значения настроек поля расположены в отдельных колонках. Для нахождения нужного значения мы
применили метод Область, передав ему адрес ячейки таблицы, представляющий собой пересечение номера строки и столбца. Найденное значение помещается в объект ОбластьЯчеекТабличногоДокумента и читается через свойство Текст.
Кроме группировок, в отчет, как правило, передают настройки показателей,
выражающих количественные и суммовые значения регистра накопления
(ресурсы). Как это делается, показано в листинге 13.3.
Листинг 13.3. Получение области показателей макета регистра накопления
// Получаем макет регистра накопления
Макет =
РегистрыНакопления.Продажи.ПолучитьМакет("НастройкиОтчета");
// Получаем область группировок
ОбластьПоказателей = Макет.ПолучитьОбласть("Показатели");
// Определяем количество строк полученной области
КоличествоСтрок = ОбластьПоказателей.ВысотаТаблицы;
// Читаем настройки группировок из области макета
Для НомерСтроки = 1 По КоличествоСтрок Цикл
// Получаем имя показателя
Сообщить("Имя показателя: ",
СокрЛП(ОбластьПоказателей.Область(НомерСтроки, 1).Текст);
// Получаем представление показателя
Сообщить("Представление показателя: ",
СокрЛП(ОбластьПоказателей.Область(НомерСтроки, 2).Текст);
// Получаем описание показателя
Сообщить("Описание показателя: ",
СокрЛП(ОбластьПоказателей.Область(НомерСтроки, 3).Текст);
// Получаем признак расчета по реквизиту регистра
Сообщить("Расчет по реквизиту регистра: ",
Булево(СокрЛП(ОбластьПоказателей.Область(НомерСтроки, 4).Текст));
// Признак использования по умолчанию
Сообщить("Использовать по умолчанию: ",
Глава 13. Получение макета
129
Булево(СокрЛП(ОбластьПоказателей.Область(НомерСтроки, 5).Текст));
// Строка форматирования
Сообщить("Строка форматирования: ",
Булево(СокрЛП(ОбластьПоказателей.Область(НомерСтроки, 6).Текст));
КонецЦикла;
Принцип получения значений настроек показателей ничем не отличается от
предыдущего. Результат можно использовать для настройки форм отчетов.
Кроме того, достаточно разработать всего один общий макет для различных
типов регистров накопления или логических групп, вызывая его по мере необходимости и считывая параметры настроек по соответствующему регистру. Поскольку макет является статическим объектом, он может с успехом
применяться любыми регистрами для собственных нужд. Там можно хранить
всевозможные параметры настроек и просто постоянные значения. Единственным правилом считается то, что макет в таком случае должен содержать
как общие настройки группировок, показателей, одинаковые для всех регистров, так и характерные только для определенных типов. Думаю, читателям
не составит труда самостоятельно продолжить работу с макетами, а с тем,
как их создавать, мы еще познакомимся.
Глава 14
Получение и установка свойств
В предыдущих главах мы рассмотрели различные методы работы с регистрами накопления, позволяющие формировать запросы, получать формы
и макеты. Здесь мы остановимся на использовании свойств базовых
объектов, доступных разработчикам. Это РегистрНакопленияВыборка, возвращаемый методом Выбрать, а также РегистрНакопленияСписок (метод
ПолучитьФормуСписка). С некоторыми из свойств этих объектов мы уже сталкивались (имена измерений, реквизитов, ресурсов), поэтому на них останавливаться не будем.
Итак, после того, как с помощью метода Выбрать мы получаем выборку данных, становятся доступны все свойства объекта РегистрНакопленияВыборка.
В первую очередь, имеется возможность определить, является ли запись регистра активной, другими словами, принимает ли она участие в расчетах итогов. Для этого служит свойство Активность. Если значение его истинно, значит, запись активна и участвует в расчетах итогов регистра. Данное свойство
доступно только для чтения. Чтобы установить признак активности, используется метод УстановитьАктивность, доступный из объекта РегистрНакопленияНаборЗаписей. Делается это в момент формирования новых записей регистра. Замечу, что в выборку данных по регистру попадают как активные, так
и неактивные записи. Посмотрите пример чтения признака активности, представленный в листинге 14.1.
Листинг 14.1. Использование свойства Активность
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Делаем выборку записей из регистра
ДанныеВыборка = РегистрЗаказыПокупателей.Выбрать();
Глава 14. Получение и установка свойств
131
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
Сообщить("Признак активности: " +
?(ДанныеВыборка.Активность = Истина, "активная", "неактивная"));
КонецЦикла;
Приведенный пример позволит явно определить активные и неактивные
записи регистра. Можно также выполнить запрос, указав в условии признак
активности, что позволит вернуть только активные записи. Сделать это можно так, как показано в листинге 14.2.
Листинг 14.2. Получение активных записей регистра накопления
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоМесяца(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по признаку активности
УсловиеОтбора = Новый Структура("Активность");
УсловиеОтбора.Активность = Истина;
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Делаем выборку записей из регистра
ДанныеВыборка = РегистрПартииТоваровНаСкладах.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Регистратор Убыв");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
Выполнив запрос, мы в результате получим только активные записи, влияющие на итоги регистра.
Следующее полезное свойство называется ВидДвижения и указывает тип
записи регистра: приход или расход. Свойство доступно только для чтения
и применяется в контексте регистров остатков. Посмотрите пример работы
с этим свойством, показанный в листинге 14.3.
132
Часть II. Приемы программирования регистров накопления
Листинг 14.3. Использование свойства ВидДвижения
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоМесяца(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по виду движения
УсловиеОтбора = Новый Структура("ВидДвижения");
УсловиеОтбора.Движения = ВидДвиженияНакопления.Приход;
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
РегистрыНакопления.ПартииТоваровНаСкладах;
// Делаем выборку записей из регистра
ДанныеВыборка = РегистрПартииТоваровНаСкладах.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Регистратор Возр");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
В рассмотренном примере мы получили из регистра выборку только приходных записей, поставив условие отбора по виду движения. Следует помнить,
что свойство ВидДвижения доступно только для регистров остатков и в оборотных регистрах не используется.
Следующее свойство называется НомерСтроки и содержит уникальное значение для каждой записи из списка записей по регистратору. Оно доступно
только для чтения. Приведем пример, демонстрирующий работу данного
свойства (листинг 14.4).
Листинг 14.4. Использование свойства НомерСтроки
// Задаем период выборки за текущий месяц
НачальнаяДата = НачалоМесяца(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("Регистратор");
УсловиеОтбора.Регистратор = ДокументСсылка;
// Задаем регистр накопления
РегистрПартииТоваровНаСкладах =
Глава 14. Получение и установка свойств
133
РегистрыНакопления.ПартииТоваровНаСкладах;
// Делаем выборку записей из регистра
ДанныеВыборка = РегистрПартииТоваровНаСкладах.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "НомерСтроки Возр");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим номер строки
Сообщить("Номер строки: " + ДанныеВыборка.НомерСтроки);
// Выводим в окно сообщений документ-регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
// Выводим в окно сообщений номенклатуру
Сообщить("Номенклатура: " + ДанныеВыборка.Номенклатура);
КонецЦикла;
В примере мы выполняем запрос к регистру по регистратору (передаем ссылку на документ-регистратор). Задаем сортировку по номеру строки в порядке
возрастания (признак возрастания можно не указывать, поскольку он используется по умолчанию). В результате все записи по указанному регистратору
выводятся в окно сообщений в порядке, заданном номерами строк записей.
Следующее свойство регистра называется Период. Оно служит для хранения
даты и времени для каждой записи регистра. Рассмотрим пример использования этого свойства, как показано в листинге 14.5.
Листинг 14.5. Использование свойства Период
// Задаем период выборки за текущий год
НачальнаяДата = НачалоГода(ТекущаяДата());
КонечнаяДата = ТекущаяДата();
// Формируем условие отбора по регистратору
УсловиеОтбора = Новый Структура("Регистратор");
УсловиеОтбора.Регистратор = ДокументСсылка;
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Делаем выборку записей из регистра
ДанныеВыборка = РегистрЗаказыПокупателей.Выбрать
(НачальнаяДата, КонечнаяДата, УсловиеОтбора, "Период Убыв");
// Организуем цикл для обработки записей регистра
Пока ДанныеВыборка.Следующий() Цикл
// Выводим номер строки
134
Часть II. Приемы программирования регистров накопления
Сообщить("Период: " + ДанныеВыборка.Период);
// Выводим в окно сообщений регистратор
Сообщить("Регистратор: " + ДанныеВыборка.Регистратор);
КонецЦикла;
Как вы догадались, мы выбрали из базы документы по определенному регистратору за весь текущий год и отсортировали их в порядке убывания периода записей.
Последнее свойство называется Регистратор и содержит ссылку на документ
движения, инициировавший запись в регистр накопления. Поскольку мы уже
сталкивались с данным свойством неоднократно, приводить примеры работы
с ним не будем, а перейдем к описанию свойств следующего объекта.
Базовый объект РегистрНакопленияСписок предоставляет в распоряжение
разработчика три свойства: Отбор, Порядок и Колонки. Рассмотрим их подробнее. Свойство Отбор позволяет поставить условия отбора (фильтр) записей регистра. Следует помнить, что отбор можно установить только по измерениям, регистратору, периоду, номеру строки, признаку активности и виду
движения. Посмотрите пример работы с отбором, представленный в листинге 14.6.
Листинг 14.6. Использование свойства Отбор
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФормуСписка("ФормаСписка");
// Получаем ссылку на документ-регистратор
Ссылка = ДокументЗаказПокупателяСсылка;
// Устанавливаем отбор по регистратору
ФормаСпискаРегистра.СписокРегистра.Отбор.Регистратор.Установить(Ссылка);
// Отображаем форму на экране
ФормаСпискаРегистра.Открыть();
В приведенном примере мы получили форму списка регистра ЗаказыПокупателей. После этого передали ссылку на документ заказа, который является
регистратором. Далее установили для табличного поля (здесь СписокРегистра)
отбор по регистратору. В завершение отображаем форму списка на экране.
В табличное поле попадут только записи с указанным регистратором. Таким
же образом можно строить всевозможные варианты отбора для отображения
данных в форме списка.
Глава 14. Получение и установка свойств
135
Следующее свойство называется Порядок. Оно управляет упорядочиванием
результирующих записей регистра по одному или нескольким критериям.
В качестве критериев могут выступать измерения, ресурсы, реквизиты,
а также период, регистратор, номер строки и признак активности записи.
Пример работы с данным свойством представлен в листинге 14.7.
Листинг 14.7. Использование свойства Порядок
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФормуСписка("ФормаСписка");
// Получаем подразделение
Ссылка = ПодразделениеСсылка;
// Устанавливаем сортировку по подразделению
ФормаСпискаРегистра.СписокРегистра.Порядок.Регистратор.Установить
("Ссылка Возр");
// Отображаем форму на экране
ФормаСпискаРегистра.Открыть();
Пример устанавливает для формы списка регистра сортировку по реквизиту
Подразделение в порядке убывания. Чтобы прочитать порядок, заданный для
формы списка, можно воспользоваться кодом, представленным в листинге 14.8.
Листинг 14.8. Получение сведений об установленном порядке
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФормуСписка("ФормаСписка");
// Получаем установленный порядок
ФормаСпискаПорядок = ФормаСпискаРегистра.СписокРегистра.Порядок;
// Получаем настройки
Для Каждого Элемент Из ФормаСпискаПорядок Цикл
Сообщить("Имя элемента: " + Элемент.Имя);
Сообщить("Представление: " + Элемент.Представление);
Сообщить("Направление сортировки: " + Элемент.Направление);
Сообщить("Данные: " + Элемент.Данные);
Сообщить("Путь к данным: " + Элемент.ПутьКДанным);
КонецЦикла;
136
Часть II. Приемы программирования регистров накопления
В приведенном примере мы считываем текущие настройки сортировки и выводим в окно служебных сообщений. Для этого вначале получаем объект Порядок формы списка, а затем в цикле делаем обход по всем элементам коллекции и получаем значения всех свойств.
Последнее свойство называется Колонки и позволяет получить информацию
обо всех колонках табличного поля формы списка. Пример работы с данным
свойством показан в листинге 14.9.
Листинг 14.9. Использование свойства Колонки
// Получаем форму списка регистра накопления
ФормаСпискаРегистра =
РегистрыНакопления.ЗаказыПокупателей.ПолучитьФормуСписка("ФормаСписка");
// Получаем колонки
ФормаСпискаКолонки = ФормаСпискаРегистра.СписокРегистра.Колонки;
// Читаем имена колонок
Для Каждого Элемент Из ФормаСпискаКолонки Цикл
Сообщить("Имя колонки: " + Элемент.Имя);
КонецЦикла;
Как видно из примера, мы получаем коллекцию колонок табличного поля,
расположенного на форме списка регистра накопления. После этого организуем обход всех элементов и выводим в окно сообщений имена колонок таблицы.
Вот мы и познакомились со свойствами базовых объектов регистра накопления. Думаю, самостоятельная работа с ними не вызовет у вас больших трудностей, а мы перейдем к рассмотрению не менее важной части, связанной
с добавлением новых записей в регистр накопления.
Глава 15
Запись
В этой главе мы познакомимся с вопросами управления записями регистров
накопления, их считыванием и сохранением в базу данных. Для поддержки
этих возможностей в системе 1С предусмотрен специальный объект, который
называется РегистрНакопленияНаборЗаписей. Из названия понятно, что он
может оперировать наборами записей регистра: читать из базы, добавлять
новые или изменять существующие. Только с помощью данного объекта
можно записывать данные в регистр. Связь документов и регистров накопления устанавливается через объект ДокументОбъект. У него имеется специальное свойство Движения, которое служит для получения доступа к набору
записей движений документа. При проведении документа данные вначале
попадают в набор записей, а уже потом сохраняются в базе.
Процесс записи данных в регистр накопления организуется следующим образом. Вначале с помощью метода СоздатьНаборЗаписей (принадлежит базовому объекту РегистрНакопленияМенеджер) создается пустой набор записей.
После этого устанавливается отбор по документу-регистратору, который будет выполнять движения по регистру. Затем заполняются требуемые значения измерений, ресурсов и реквизитов регистра. В завершение вызывается
метод Записать, который сохраняет набор записей в базу данных. Рассмотрим пример, демонстрирующий сказанное (листинг 15.1).
Листинг 15.1. Запись в регистр накопления
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Создаем набор записей
НаборЗаписейЗаказ = РегистрЗаказыПокупателей.СоздатьНаборЗаписей();
// Устанавливаем отбор по ссылке на текущий документ
138
Часть II. Приемы программирования регистров накопления
НаборЗаписейЗаказ.Отбор.Регистратор.Значение = ЭтотОбъект.Ссылка;
// Заполняем измерения и ресурсы регистра
Движение = НаборЗаписейЗаказ.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = ТекущаяДата();
Движение.Регистратор = ЭтотОбъект.Ссылка;
Движение.Номенклатура = ТоварСсылка;
Движение.ХарактеристикаНоменклатуры = ХарактеристикаНоменклатурыСсылка;
Движение.ДоговорВзаиморасчетов = ДоговорВзаиморасчетов.Ссылка;
Движение.Количество = ТоварКоличество;
// Добавляем данные в регистр накопления
НаборЗаписейЗаказ.Записать(Ложь);
Как видите, все достаточно понятно и просто. Создали пустой набор, установили отбор, заполнили набор данными и сохранили данные в регистре.
Метод Записать имеет один необязательный параметр. При установке его
в значение Истина будет выполнено замещение данных, а при установке
в значение Ложь — добавление новых записей. По умолчанию задано значение Истина, поэтому нужно очень внимательно и осторожно пользоваться
методом записи. Если вам необходимо только добавить новые записи, следует явно установить параметр метода в значение Ложь. В случае, когда документ уже проведен, можно предварительно очистить все записи по документу движения. Как это сделать, показано в листинге 15.2.
Листинг 15.2. Удаление движений проведенного документа из регистра
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Создаем набор записей
НаборЗаписейЗаказ = РегистрЗаказыПокупателей.СоздатьНаборЗаписей();
// Устанавливаем отбор по ссылке на текущий документ
НаборЗаписейЗаказ.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
// Получаем все записи из регистра по установленному отбору
НаборЗаписейЗаказ.Прочитать();
// Удаляем все записи из набора
НаборЗаписейЗаказ.Очистить();
// Записываем изменения в базу данных
НаборЗаписейЗаказ.Записать();
Глава 15. Запись
139
В рассмотренном примере мы очистили движения документа по регистру,
подготовив основу для новой операции проведения. Напомню, что исходный
документ уже был проведен ранее. Чтобы получить записи регистра по документу, мы установили отбор и вызвали метод Получить. После этого с помощью метода Очистить мы удалили все записи из регистра по нашему
документу и записали результат операции методом Записать. Естественно,
после этого нужно вновь сформировать все движения и сохранить в регистр.
Хочу обратить ваше внимание на принципиально важный момент. Система
1С формирует все движения документов по регистрам самостоятельно. Вмешательство в этот процесс может повлечь нарушение структурной целостности всей базы, поскольку операция проведения имеет очень сложную и четко
обозначенную последовательность действий. Рекомендуется после удаления
движений проведенного документа использовать метод Записать (объект ДокументОбъект) для безопасной записи всех движений документа по регистрам системы.
А теперь немного поговорим о некоторых других возможностях объекта РегистрНакопленияНаборЗаписей. С его помощью можно прочитать записи
регистра. Как это делается, показано в листинге 15.3.
Листинг 15.3. Чтение записей из регистра накопления
// Задаем регистр накопления
РегистрЗаказыПоставщикам = РегистрыНакопления.ЗаказыПоставщикам;
// Создаем набор записей
НаборЗаписейЗаказ = РегистрЗаказыПоставщикам.СоздатьНаборЗаписей();
// Устанавливаем отбор по ссылке на текущий документ
НаборЗаписейЗаказ.Отбор.Регистратор.Установить(ЭтотОбъект.Ссылка);
// Получаем все записи из регистра по установленному отбору
НаборЗаписейЗаказ.Прочитать();
// Организуем цикл для перебора полученных записей
Для Каждого Движение Из НаборЗаписейЗаказ Цикл
Сообщить("Период: " + Движение.Период);
Сообщить("Регистратор: " + Движение.Регистратор);
Сообщить("Заказ покупателя: " + Движение.ЗаказПокупателя);
Сообщить("Номенклатура: " + Движение.Номенклатура);
Сообщить("Характеристика: " +
Движение.ХарактеристикаНоменклатуры);
Сообщить("Подразделение: " + Движение.Подразделение);
Сообщить("Количество: " + Движение.Количество);
КонецЦикла;
140
Часть II. Приемы программирования регистров накопления
В данном листинге продемонстрирован способ чтения записей регистра по
указанному документу. Так же, как и раньше, создается набор записей и передается ссылка на документ-регистратор. После чего методом Получить считываются все соответствующие записи регистра. Далее организуется обход
записей в цикле, где извлекаются значения измерений, ресурсов и реквизитов. Как видите, все очень просто.
Записи регистра можно выгружать непосредственно в таблицу значений. Для
этого предназначен метод Выгрузить. При этом результат будет точно соответствовать структуре набора записей. Посмотрите пример, представленный
в листинге 15.4.
Листинг 15.4. Считывание набора записей регистра в таблицу значений
// Задаем регистр накопления
РегистрЗаказыПоставщикам = РегистрыНакопления.ЗаказыПоставщикам;
// Создаем набор записей
НаборЗаписейЗаказ = РегистрЗаказыПоставщикам.СоздатьНаборЗаписей();
// Устанавливаем отбор по ссылке на текущий документ
НаборЗаписейЗаказ.Отбор.Регистратор.Значение = ЭтотОбъект.Ссылка;
// Выгружаем все записи в таблицу значений
ТаблицаЗначенийЗаказ = НаборЗаписейЗаказ.Выгрузить();
Как видите, ничего сложного в этом нет. Кроме того, можно выполнить обратную операцию, т. е. загрузить данные из таблицы значений в регистр. Это
реализуется методом Загрузить. В качестве параметра метода следует передать сформированную таблицу значений.
Объект РегистрНакопленияНаборЗаписей поддерживает два метода, с помощью которых можно задать вид движения по регистру: ДобавитьПриход
и ДобавитьРасход. Названия этих методов говорят сами за себя. Рассмотрим
пример работы с этими методами, показанный в листинге 15.5.
Листинг 15.5. Установка вида движения по регистру накопления
// Задаем регистр накопления
РегистрЗаказыПокупателей = РегистрыНакопления.ЗаказыПокупателей;
// Создаем набор записей
НаборЗаписейЗаказ = РегистрЗаказыПокупателей.СоздатьНаборЗаписей();
// Устанавливаем отбор по ссылке на текущий документ
НаборЗаписейЗаказ.Отбор.Регистратор.Значение = ЭтотОбъект.Ссылка;
Глава 15. Запись
141
// Заполняем измерения и ресурсы регистра
Движение = НаборЗаписейЗаказ.ДобавитьПриход();
Движение.Период = ЭтотОбъект.Дата;
Движение.Регистратор = ЭтотОбъект.Ссылка;
Движение.Номенклатура = ТоварСсылка;
Движение.ХарактеристикаНоменклатуры = ХарактеристикаНоменклатурыСсылка;
Движение.Количество = ТоварКоличество;
// Добавляем данные в регистр накопления
НаборЗаписейЗаказ.Записать(Ложь);
В рассмотренном примере вид движения по регистру задается методом
ДобавитьПриход. Второй метод работает аналогично, поэтому мы его разбирать не будем.
На этом хотелось бы завершить описание объекта РегистрНакопленияНаборЗаписей. Надеюсь, что материал, представленный здесь, поможет вам правильно читать и записывать данные в любые регистры накопления. Однако
еще раз хочу повторить, что не стоит явно создавать и записывать данные
в регистр, особенно в готовых конфигурациях. Используйте проведение документов для сохранения данных и правильной организации всех движений
по регистрам системы. Это тот случай, когда можно воспользоваться услугами 1С, не вникая во все тонкости достаточно сложного и трудоемкого процесса.
Глава 16
Использование запросов
Встроенный в систему 1С язык запросов представляет собой мощное и наиболее оптимальное средство для работы с регистрами, в частности, с регистрами накопления. Используйте его, где это только возможно: для чтения данных из базы, формирования запросов, при создании обработок и печатных
форм. Кроме выигрыша в скорости, вы получаете гибкий инструмент для обработки и анализа больших массивов данных. Программисты, знакомые
с Transact-SQL, без особых трудностей смогут перейти на язык запросов 1С.
Он предоставляет поддержку английского и русского языка и обладает в определенных моментах весьма продвинутыми возможностями.
Сразу хочу заметить, что гораздо удобнее работать именно с русской версией
языка запросов, поскольку не придется постоянно смешивать в коде два языка, да и в дальнейшем легче будет поддерживать готовый продукт. Имена
команд в русском варианте легко сопоставлять с их англоязычными аналогами. Время, потраченное на изучение русской версии, много раз окупится
в дальнейшем. Надеюсь, что убедил читателей в преимуществах написания
запросов именно на русском языке.
В этой главе мы поговорим о том, как правильно создавать запросы для чтения данных и построении итогов с помощью регистров накопления. Как уже
говорилось в предыдущих главах, существует два основных типа регистра
накопления: остатки и обороты. Тип регистра задается на стадии разработки
программы в конфигураторе. По регистрам остатков можно получать остатки
каких-либо сущностей (товаров, денежных или основных средств) на определенный момент времени. Регистры оборотов позволяют проследить движения сущностей за любой период (с момента работы базы данных) по различным документам движения. Мы обсудим запросы не только к отдельным
регистрам, но и к объединениям нескольких регистров между собой, документам и справочникам.
Глава 16. Использование запросов
143
Для работы с языком запросов предназначен объект Запрос, на котором мы
уже подробно останавливались в главе 8, поэтому приступим непосредственно к самому процессу программирования. Вначале сформируем простой запрос по регистру ЗаказыПокупателей и обработаем результат, как показано
в листинге 16.1.
Листинг 16.1. Формирование запроса к регистру ЗаказыПокупателей
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Активность
КАК Активность,
|
ВидДвижения
КАК ВидДвижения,
|
Регистратор
КАК Регистратор,
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Подразделение
КАК Подразделение,
|
ДоговорВзаиморасчетов
КАК Договор,
|
Количество
КАК Количество
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей
|
|УПОРЯДОЧИТЬ ПО
|
Период";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные
Данные = ДанныеВыборки.Выбрать();
Пока Данные.Следующий() Цикл
Сообщить("Период: " + Данные.Период);
Сообщить("Признак активности: " + Данные.Активность);
Сообщить("Вид движения: " +
?(Данные.ВидДвижения = ВидДвиженияНакопления.Приход, "Приход", "Расход");
Сообщить("Документ движения: " + Данные.Регистратор);
144
Часть II. Приемы программирования регистров накопления
Сообщить("Номенклатура: " + Данные.Номенклатура);
Сообщить("ХарактеристикаНоменклатуры: " +
Данные.ХарактеристикаНоменклатуры);
Сообщить("Подразделение: " + Данные.Подразделение);
Сообщить("Количество номенклатуры: " + Данные.Количество);
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
Итак, мы создали запрос по регистру и получили в результате набор записей
за весь период существования базы данных. Для справки, документ ЗаказПокупателя позволяет оформить намерения покупателя приобрести у нас какойлибо товар. Не факт, что свои намерения покупатель выполнит или что его
устроит качество полученного товара, поэтому существуют дополнительные
документы работы с заказом. К ним можно отнести ЗакрытиеЗаказовПокупателей, КорректировкаЗаказаПокупателя и ВозвратТоваровОтПокупателя. Поскольку в книге не ставится целью изучать структуру документов, перейдем
к следующему практическому примеру. Чтобы выбрать данные только по
одному заказу, следует передать в параметре запроса ссылку на документ,
как это сделано в листинге 16.2.
Листинг 16.2. Формирование запроса к регистру по ссылке на ЗаказПокупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Регистратор
КАК Регистратор,
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Подразделение
КАК Подразделение,
|
ДоговорВзаиморасчетов
КАК Договор,
|
Количество
КАК Количество
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей
|ГДЕ
|
ЗаказПокупателя = &Заказ
Глава 16. Использование запросов
145
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("Заказ", ЭтотОбъект.Ссылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные
Данные = ДанныеВыборки.Выбрать();
Пока Данные.Следующий() Цикл
Сообщить("Документ движения: " + Данные.Регистратор);
Сообщить("Номенклатура: " + Данные.Номенклатура);
Сообщить("Подразделение: " + Данные.Подразделение);
Сообщить("Договор: " + Данные.Договор);
Сообщить("Количество номенклатуры: " + Данные.Количество);
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
В результате выполнения запроса мы получили данные по одному заказу покупателя. Здесь предполагается, что запрос формируется в модуле формы
документа ЗаказПокупателя (ЭтотОбъект.Ссылка), иначе следует получить
ссылку на интересующий документ каким-либо другим способом.
А теперь попробуем получить остатки номенклатуры по заказу покупателя,
воспользовавшись регистром остатков. Пример показан в листинге 16.3.
Листинг 16.3. Получение текущих остатков номенклатуры по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
СУММА(КоличествоОстаток)
КАК КоличествоОстаток
146
Часть II. Приемы программирования регистров накопления
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки
|ГДЕ
|
ЗаказПокупателя = &Заказ
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("Заказ", ЭтотОбъект.Ссылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные
Данные = ДанныеВыборки.Выбрать();
Пока Данные.Следующий() Цикл
Сообщить("Номенклатура: " + Данные.Номенклатура);
Сообщить("Характеристика номенклатуры: " +
Данные.ХарактеристикаНоменклатуры);
Сообщить("Количество: " + Данные.Количество);
КонецЦикла;
Иначе
Сообщить("По Вашему запросу не найдено ни одной записи!");
КонецЕсли;
Как видно из примера, в результате запроса мы получили остатки товаров по
заказу покупателя. Для этого был использован регистр остатков и группировка по номенклатуре и характеристике, чтобы учесть одинаковые товары
в заказе.
Регистр остатков поддерживает два необязательных параметра: период выборки и условие. Период определяет дату (а также момент времени или границу), на которую рассчитываются остатки, а условие позволяет задать отбор
записей внутри регистра, до того как они будут обработаны внешними командами. Этот прием существенно улучшает скорость выполнения запроса,
Глава 16. Использование запросов
147
и имеет смысл применять его, где это только возможно. Посмотрите пример,
приведенный в листинге 16.4.
Листинг 16.4. Установка условия выборки для регистра остатков
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
СУММА(КоличествоОстаток)
|
ЗаказПокупателя.Контрагент КАК Покупатель
КАК КоличествоОстаток,
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(&Период,
|
ЗаказПокупателя ССЫЛКА Документ.ЗаказПокупателя И
|
ЗаказПокупателя.Контрагент = &Покупатель)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПокупателя.Контрагент
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Покупатель", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В приведенном запросе мы задали условие отбора документов по контрагенту и виду документа на текущую дату. Вначале будет выполнен отбор документов по условию в регистре, а уже затем результирующий набор записей
будет обработан в запросе (группировка и упорядочивание). В итоге мы получим остатки товаров по заказам покупателей в разрезе контрагента. Ссылку на контрагента мы передали через параметр запроса. Кроме того, мы явно
задали вид документа, поскольку в этом регистре измерение ЗаказПокупателя
148
Часть II. Приемы программирования регистров накопления
имеет составной тип и может выступать как документ ЗаказПокупателя или
ВнутреннийЗаказ. Ключевое слово ССЫЛКА полностью решает данную проблему, отбирая только документы нужного вида.
Документ ЗаказПокупателя содержит очень важный для торговых операций
реквизит, как дата отгрузки. Она служит для примерного задания срока, не
позднее которого следует отгрузить товар покупателю. Чтобы проанализировать просроченные заказы, можно воспользоваться кодом, представленным
в листинге 16.5.
Листинг 16.5. Анализ сроков отгрузки товаров по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказПокупателя
КАК Заказ,
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры
КАК ХарактеристикаНоменклатуры,
|
ЗаказПокупателя.Контрагент
КАК Покупатель,
|
ЗаказПокупателя.ДатаОтгрузки КАК ДатаОтгрузки,
|
СУММА(КоличествоОстаток)
КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(&Период,
|
ЗаказПокупателя ССЫЛКА Документ.ЗаказПокупателя И
|
ЗаказПокупателя.Ссылка.ДатаОтгрузки <= &СрокОтгрузки И
|
ЗаказПокупателя.Контрагент = &Покупатель)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("СрокОтгрузки", СрокОтгрузки);
Запрос.УстановитьПараметр("Покупатель", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В качестве предельного срока отгрузки мы задаем заранее определенную дату,
не позднее которой необходимо отгрузить товары покупателю. Результатом
Глава 16. Использование запросов
149
выполнения запроса будут все заказы покупателей на текущую дату, у которых дата отгрузки меньше установленной и которые, по сути, являются просроченными.
А теперь попробуем получить сведения об остатках товаров на складах по
заказам покупателей. Для этого нам потребуется не только регистр ЗаказыПокупателей, но и специальный регистр по размещенным на складах товарам, который в конфигурации УПП (Управление Производственным Предприятием) называется ТоварыНаСкладах. Как это реализовать, показано
в листинге 16.6.
Листинг 16.6. Получение остатков номенклатуры по заказам покупателей
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказыПокупателейОстатки.Номенклатура КАК Номенклатура,
|
ЗаказыПокупателейОстатки.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
|
ЗаказыПокупателейОстатки.КоличествоОстаток КАК Количество,
|
ТоварыНаСкладахОстатки.Склад КАК Склад,
|
ТоварыНаСкладахОстатки.КоличествоОстаток КАК Размещено
|ИЗ
|
|
РегистрНакопления.ЗаказыПокупателей.Остатки(,
ЗаказПокупателя = &Заказ) КАК ЗаказыПокупателейОстатки
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыНаСкладах.Остатки(,
|
Заказ = &Заказ И Склад = &Склад) КАК ТоварыНаСкладахОстатки
|ПО ЗаказыПокупателейОстатки.Номенклатура =
|
ТоварыНаСкладахОстатки.Номенклатура И |
ЗаказыПокупателейОстатки.ХарактеристикаНоменклатуры =
|
ТоварыНаСкладахОстатки.ХарактеристикаНоменклатуры
|
|ГДЕ
|
ЗаказыПокупателейОстатки.ЗаказПокупателя.Дата = &Период
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
150
Часть II. Приемы программирования регистров накопления
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Итак, мы построили запрос на базе левого соединения двух регистров накопления: по заказам покупателей и товарам на складах. При таком соединении
выбираются все записи, удовлетворяющие условию из таблицы, расположенной слева от команды, и соответствующие им записи из правой таблицы.
Кроме того, перед этим выполняются отборы по условиям, заданным в самих
регистрах остатков. Для регистра ЗаказыПокупателей отбираются записи по
заданному заказу, а для регистра ТоварыНаСкладах — по заказу и складу
размещения. В результате мы получим остатки товаров на указанном складе,
размещенные по заказу покупателя. Заметьте, поскольку в запросе используются две таблицы, следует указывать для них псевдонимы, чтобы явно установить принадлежность полей своим виртуальным таблицам.
Следующий пример, который мы рассмотрим, поможет нам получить количество реализованных товаров по заказу покупателя. Посмотрите код, представленный в листинге 16.7.
Листинг 16.7. Получение количества и суммы реализованных товаров
по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Количество
КАК ПроданоКоличество,
|
Сумма
КАК ПроданоСумма,
|
ЗаказПокупателя
КАК ЗаказПокупателя
|ИЗ
|
РегистрНакопления.Продажи
|
|ГДЕ
|
Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваров И
Глава 16. Использование запросов
|
Продажи.ЗаказПокупателя = &Заказ И
|
Продажи.Регистратор.Проведен И
|
НЕ Продажи.Номенклатура.Услуга
151
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В данном случае мы обратились к регистру Продажи, в котором ведется учет
проданных и возвращенных покупателем товаров. Предполагая, что все заказанные покупателем товары успешно проданы, получаем количество и сумму
продажи по каждой номенклатуре. Отбор записей задаем по нескольким условиям: регистратор содержит проведенный документ реализации, ссылка
указывает на выбранный заказ, и номенклатура не является услугой.
Документ ЗаказПокупателя поддерживает такие функции, как автоматическое размещение и резервирование товаров, когда товар резервируется на
определенных складах под конкретный заказ покупателя. Для того чтобы
можно было проанализировать, какое количество товара по заказу зарезервировано и размещено на складах, необходимо использовать три регистра накопления (для УПП): ЗаказыПокупателей, РазмещениеЗаказовПокупателей
и ТоварыВРезервеНаСкладах. Как это делается, показано в листинге 16.8.
Листинг 16.8. Анализ размещения и резервирования товаров по заказу
покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказыОстатки.Номенклатура
|
ЗаказыОстатки.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
|
ЗаказыОстатки.КоличествоОстаток
|
ВЫБОР
|
КАК Номенклатура,
КАК ЗаказаноКоличество,
КОГДА РезервОстатки.КоличествоОстаток ЕСТЬ NULL
152
Часть II. Приемы программирования регистров накопления
|
ТОГДА 0
|
ИНАЧЕ РезервОстатки.КоличествоОстаток
|
КОНЕЦ КАК ЗарезервированоКоличество,
|
ВЫБОР
|
КОГДА РазмещениеОстатки.КоличествоОстаток ЕСТЬ NULL
|
ТОГДА 0
|
|
ИНАЧЕ РазмещениеОстатки.КоличествоОстаток
КОНЕЦ КАК РазмещеноКоличество
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(,
|
ЗаказПокупателя = &Заказ) КАК ЗаказыОстатки
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(,
|
ДокументРезерва = &Заказ) КАК РезервОстатки
|ПО
|
ЗаказыОстатки.Номенклатура = РезервОстатки.Номенклатура И
|
ЗаказыОстатки.ХарактеристикаНоменклатуры =
|
РезервОстатки.ХарактеристикаНоменклатуры
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.РазмещениеЗаказовПокупателей.Остатки(,
|
ЗаказПокупателя = &Заказ) КАК РазмещениеОстатки
|ПО ЗаказыОстатки.Номенклатура = РазмещениеОстатки.Номенклатура И
|
ЗаказыОстатки.ХарактеристикаНоменклатуры =
|
РазмещениеОстатки.ХарактеристикаНоменклатуры";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Несмотря на пугающий вид запроса, он представляет собой всего лишь соединение трех таблиц регистров остатков. В качестве отбора по регистрам
передается ссылка на заказ покупателя. В результате мы получаем заказанное
количество товара, а также количество размещенного и зарезервированного
товара на складах под заказ покупателя.
Следующий регистр, о котором мы поговорим, называется ЗаказыПоставщикам. Как вы догадались по названию, он предназначен для сбора и анализа
информации по документам ЗаказПоставщику. Сам документ предназначен
для оформления наших намерений приобрести какой-либо товар у поставщика. В ходе процесса могут меняться заказываемые товары или количество, мы
Глава 16. Использование запросов
153
можем по каким-либо причинам отказаться от покупки или возвратить некачественный товар, а также предъявить претензии в случае его недопоставки.
Все эти действия регистрируются различными дополнительными документами:
ВозвратТоваровПоставщику, ЗакрытиеЗаказовПоставщикам и КорректировкаЗаказаПоставщику. Регистр ЗаказыПоставщикам позволяет накапливать все
движения заказов по этим документам. В результате мы можем проследить остатки и обороты товаров, взаиморасчеты с контрагентами и многое другое. Например, чтобы узнать, сколько заказано номенклатуры под определенный заказ
покупателя, можно написать код, представленный в листинге 16.9.
Листинг 16.9. Получение остатков заказанных поставщику товаров под заказ
покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
ДоговорВзаиморасчетов
КАК Договор,
|
ЗаказПоставщику
КАК ЗаказПоставщику,
|
КоличествоОстаток
КАК Заказано
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(&Период,
|
ЗаказПокупателя
= &Заказ)
|ГДЕ
|
ЗаказПоставщику.Проведен
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В этом примере мы делаем запрос к регистру остатков, передав в виде параметров дату выборки и ссылку на заказ покупателя. В результате получим
154
Часть II. Приемы программирования регистров накопления
остатки номенклатуры по всем проведенным заказам, которые связаны с заказом покупателя на конец дня текущей даты.
Документ ЗаказПоставщику содержит такой реквизит, как ДатаПоступления,
который задает ожидаемую дату поставки заказанного товара от поставщика.
На основании этой даты можно узнать, какие заказы покупателей срываются
(реквизит ДатаОтгрузки) по вине поставщика. Посмотрите код, представленный в листинге 16.10.
Листинг 16.10. Получение заказов покупателя, по которым срываются сроки
отгрузки
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказПокупателя
КАК ЗаказПокупателя,
|
ЗаказПоставщику
КАК ЗаказПоставщику,
|
ЗаказПоставщику.ДатаПоступления КАК ДатаПоставки,
|
ЗаказПокупателя.ДатаОтгрузки
КАК ДатаОтгрузки,
|
ЗаказПоставщику.Контрагент
КАК Поставщик,
|
Подразделение
КАК Подразделение
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам
|
|ГДЕ
|
ЗаказПоставщику.ДатаПоступления > ЗаказПокупателя.ДатаОтгрузки И
|
ЗаказПоставщику.Проведен";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим все заказы покупателей, у которых дата отгрузки меньше даты поступления по заказу поставщику. Другими словами, все заказы, по которым срываются сроки отгрузки по вине поставщика или по причине неточно запланированной даты, а также
возможных ошибок при оформлении документов.
Часто возникает необходимость оценить остатки товаров по заказу поставщику с учетом выполненных корректировок. Для этого можно воспользоваться примером, показанным в листинге 16.11.
Глава 16. Использование запросов
155
Листинг 16.11. Анализ остатков номенклатуры по заказу поставщику с учетом
корректировок
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура КАК Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
МАКСИМУМ(Остатки.КоличествоОстаток) КАК КоличествоОстатокПоЗаказу,
|
СУММА(Заказы.КоличествоПоЗаказу) КАК Заказано,
|
Остатки.ЗаказПокупателя КАК ЗаказПокупателя
|ИЗ
|(ВЫБРАТЬ
|
Заказ.Номенклатура КАК Номенклатура,
|
Заказ.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказ.Количество * Заказ.Коэффициент
|
Заказ.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент
|
КАК КоличествоПоЗаказу
|ИЗ
|
Документ.ЗаказПоставщику.Товары КАК Заказ
|
|ГДЕ
|
ЗаказПоставщику.Ссылка = &Заказ
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|
Корректировка.Номенклатура,
|
Корректировка.ХарактеристикаНоменклатуры,
|
Корректировка.Количество * Корректировка.Коэффициент
|
Корректировка.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент
|ИЗ
|
Документ.КорректировкаЗаказаПоставщику.Товары КАК Корректировка
|
|ГДЕ
|
Корректировка.Ссылка.ЗаказПоставщику = &Заказ И
|
Корректировка.Ссылка.Проведен = ИСТИНА)
|
КАК Заказы
156
Часть II. Приемы программирования регистров накопления
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(,
|
ЗаказПоставщику = &Заказ) КАК Остатки
|ПО Заказы.Номенклатура = Остатки.Номенклатура И
| Заказы.ХарактеристикаНоменклатуры = Остатки.ХарактеристикаНоменклатуры
|ГДЕ
|
Остатки.КоличествоОстаток > 0
|
|СГРУППИРОВАТЬ ПО
|
Заказы.Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры,
|
Остатки.ЗаказПокупателя
|
|ИТОГИ МАКСИМУМ(КоличествоОстатокПоЗаказу) ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПокупателя";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ЗаказПоставщикуСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В приведенном примере мы формируем запрос на базе двух документов
и регистра остатков. Запросы по документам мы объединяем по выбранным
полям и вкладываем в подзапрос, к которому посредством левого соединением добавляем таблицу остатков регистра накопления (она создается при условии ненулевого остатка). После этого группируем поля вложенного запроса по документам и соответствующие поля таблицы регистра и вычисляем
итоговые остатки по номенклатуре и заказу покупателя.
И в последнем примере работы с регистром ЗаказыПоставщикам мы получим
количество проданных товаров по заказам поставщикам. Код представлен
в листинге 16.12.
Листинг 16.12. Получение количества и суммы реализованных товаров
по заказам поставщиков
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
Глава 16. Использование запросов
157
|ВЫБРАТЬ
|
Продажи.Номенклатура
КАК Номенклатура,
|
Продажи.ХарактеристикаНоменклатуры КАК Характеристика,
|
Продажи.ДокументПоставки
КАК ЗаказПоставщику,
|
Продажи.Количество
КАК ПроданоКоличество,
|
Продажи.СуммаПродажи
КАК СуммаПродажи,
|
Продажи.Регистратор
КАК ДокументПродажи
|ИЗ
|
РегистрНакопления.Продажи КАК Продажи
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыПоставщикам КАК Заказы
|
ПО Продажи.Номенклатура = Заказы.Номенклатура И
|
Продажи.ХарактеристикаНоменклатуры =
|
Заказы.ХарактеристикаНоменклатуры И
|
ВЫРАЗИТЬ(Продажи.ДокументПоставки КАК Документ.ЗаказПоставщику) =
|
Заказы.ЗаказПоставщику)
|
|ГДЕ
|
Заказы.ЗаказПоставщику.Проведен И
|
Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваров И
|
НЕ Продажи.Номенклатура.Услуга";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Выполнив запрос, мы получим количество и сумму проданных товаров по
проведенным заказам поставщикам. Чтобы ограничить выборку, можно задать ссылку на конкретный заказ или установить ограничение по дате.
А теперь поговорим о регистре накопления, по которому можно определить
взаимную задолженность между нашим предприятием и поставщиками (или
покупателями). В УПП он называется ВзаиморасчетыСКонтрагентами, а в УП —
КонтрагентыВзаиморасчетыКомпании. Данный регистр поддерживает получение данных, как по остаткам, так и по оборотам. Например, чтобы получить сумму задолженности по документу РеализацияТоваровУслуг в разрезе
договора взаиморасчетов, можно использовать код, представленный в листинге 16.13.
Листинг 16.13. Получение суммы задолженности по документу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
158
Часть II. Приемы программирования регистров накопления
Запрос.Текст = "
"ВЫБРАТЬ
|
ДоговорВзаиморасчетов КАК Договор,
|
Сделка
КАК Сделка,
|
СуммаОстаток
КАК СуммаВзаисморасчетовОстаток
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(,
|
ДоговорВзаиморасчетов = &Договор И Сделка = &Сделка)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Сделка", РеализацияТоваровУслугСсылка);
Запрос.УстановитьПараметр("Договор", ДоговорВзаиморасчетовСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим сумму остатка по взаиморасчетам с контрагентом в разрезе документа. Данный запрос подойдет для любых документов, указанных в конфигураторе по измерению Сделка. Имеет
смысл вызывать подобный код в момент проведения документа, чтобы определить текущую сумму задолженности контрагента по договору. Если она
существует, следует отменить проведение, пока задолженность не будет погашена (например, оформлено входящее платежное поручение, погашающее
сумму долга).
Чтобы проследить историю взаиморасчетов по какому-либо документу, можно применить оборотный регистр. Посмотрите пример, показанный в листинге 16.14.
Листинг 16.14. Анализ взаиморасчетов по документам движения
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка.Дата
КАК
|
Сделка.ДатаОплаты
КАК
|
Сделка.ДатаОтгрузки
КАК
|
Сделка.Номер
КАК
|
Сделка.Организация
КАК
|
Сделка.Контрагент
КАК
|
Сделка.Ответственный
в объект
ДатаДокументСделка,
ДатаОплатыДокументСделка,
ДатаОтгрузкиДокументСделка,
НомерДокументСделка,
ОрганизацияДокументСделка,
КонтрагентДокументСделка,
КАК СотрудникДокументСделка,
Глава 16. Использование запросов
159
|
Сделка.СуммаДокумента
КАК СуммаДокументСделка,
|
Сделка.ВалютаДокумента
КАК ВалютаДокументСделка,
|
СуммаПриход
КАК СуммаРасход,
|
СуммаРасход
КАК СуммаПриход,
|
СуммаОборот
КАК СуммаОборот,
|
Регистратор
КАК ДокументДвижения,
|
Регистратор.Дата
КАК ДатаДокументаДвижения,
|
Регистратор.Номер
КАК НомерДокументаДвижения,
|
Регистратор.СуммаДокумента КАК СуммаПоДокументуДвижения
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Обороты(
|
&НачальнаяДата, &КонечнаяДата, Регистратор, Сделка = &Сделка)
|";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Сделка", РеализацияТоваровУслугСсылка);
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата());
Запрос.УстановитьПараметр("КонечнаяДата", КонецДня(ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В качестве сделки задаем ссылку на документ ЗаказПокупателя. Период выборки устанавливаем в пределах текущего месяца. Кроме того, в третьем параметре регистра оборотов назначаем периодичность разворота итогов, т. е.
за какие отрезки времени будут сформированы данные. Периодичность может задаваться как день, неделя, месяц, квартал, год, а может соотноситься
с документами-регистраторами, что мы и сделали в нашем случае. Результатом выборки будут все приходные и расходные документы по заказу, формирующие взаиморасчеты с контрагентом. Все приходные движения представлены со знаком плюс, расходные — со знаком минус, а обороты содержат
сумму приходных и расходных значений. В конфигурации существует специальное перечисление, позволяющее устанавливать периодичность разворота оборотов. Для этого следует в запрос добавить еще один параметр и передавать в него требуемое значение. В листинге 16.15 представлены возможные
варианты периодичности.
Листинг 16.15. Передача в запрос значения периодичности
// Анализ для выбора периодичности
Если Периодичность = Перечисления.Периодичность.День Тогда
// обороты разворачиваются по дням
160
Часть II. Приемы программирования регистров накопления
ИначеЕсли Периодичность = Перечисления.Периодичность.Неделя Тогда
// обороты разворачиваются по неделям
ИначеЕсли Периодичность = Перечисления.Периодичность.Месяц Тогда
// обороты разворачиваются по месяцам
ИначеЕсли Периодичность = Перечисления.Периодичность.Квартал Тогда
// обороты разворачиваются по кварталам
ИначеЕсли Периодичность = Перечисления.Периодичность.Год Тогда
// обороты разворачиваются по годам
ИначеЕсли Периодичность = Регистратор Тогда
// обороты разворачиваются по регистратору
ИначеЕсли Периодичность = Запись Тогда
// обороты разворачиваются по записям
ИначеЕсли Периодичность = Запись Тогда
// обороты за период
КонецЕсли;
// . . .
Запрос.УстановитьПараметр("Периодичность", Периодичность);
Как видите, вариантов достаточно много, что позволяет выбирать представление данных по оборотному регистру накопления.
Следующий регистр, который мы разберем, называется ДвижениеДенежныхСредств (в УП — ДенежныеСтредстваКомпании). Данный регистр позволяет проследить движение денежных потоков по банковским счетам, кассам предприятия и различным платежным документам. Например, чтобы
получить остаток средств на банковских счетах, можно выполнить код, представленный в листинге 16.16.
Листинг 16.16. Получение остатков денежных средств на банковском счете
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СтруктурнаяЕдиница.ВидСчета
КАК ВидСчета,
|
СтруктурнаяЕдиница.ДатаОткрытия
КАК ДатаОткрытия,
|
СтруктурнаяЕдиница.ДатаЗакрытия
КАК ДатаЗакрытия,
|
СтруктурнаяЕдиница.Банк
КАК Банк,
|
СтруктурнаяЕдиница.БанкДляРасчетов
КАК БанкДляРасчетов,
Глава 16. Использование запросов
161
|
СтруктурнаяЕдиница.ВалютаДенежныхСредств
|
СтруктурнаяЕдиница.Наименование КАК
|
КАК Валюта,
СтруктурнаяЕдиницаНаименование,
|
СтруктурнаяЕдиница.НомерСчета
КАК НомерСчета,
|
СуммаОстаток
КАК СуммаОстаток,
|
СуммаУпрОстаток
КАК СуммаУпрОстаток
|ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании.Остатки
|
|ГДЕ
|
СтруктурнаяЕдиница.ДатаЗакрытия > &ДатаРасчетная И
|
НомерСчета = &НомерСчета И
|
СтруктурнаяЕдиница ССЫЛКА Справочник.КассыКомпании
|
|УПОРЯДОЧИТЬ ПО
|
СтруктурнаяЕдиницаНаименование";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("НомерСчета", НомерСчета);
Запрос.УстановитьПараметр("ДатаРасчетная", КонецДня(ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В рассмотренном примере мы воспользовались регистром остатков для получения остатков денежных средств на заданном счете. В качестве параметров
задали номер счета и расчетную дату для проверки того, что счет является
действующим. Результатом запроса стали суммы остатков денежных средств
в управленческой валюте и валюте документов.
Данный регистр, как и предыдущие, поддерживает регистр оборотов, по которому можно проследить движение денег в разрезе документов. Как это
сделать, показано в листинге 16.17.
Листинг 16.17. Анализ регистра оборотов по движению денежных средств
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
162
|
|
|
|
|
|
|
|
|
Часть II. Приемы программирования регистров накопления
Регистратор
Регистратор.Дата
СтруктурнаяЕдиница.Наименование
СуммаПриход
СуммаРасход
СуммаОборот
СуммаУпрПриход
СуммаУпрРасход
СуммаУпрОборот
КАК
КАК
КАК
КАК
КАК
КАК
КАК
КАК
КАК
ДокументДвижения,
ДокументДвиженияДата,
Касса,
СуммаПриход,
СуммаРасход,
СуммаОборот,
СуммаУпрПриход,
СуммаУпрРасход,
СуммаУпрОборот
|ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании.Обороты(&ДатаНачальная,
|
&ДатаКонечная, Регистратор, )
|
|ГДЕ
|
СтруктурнаяЕдиница.Код = &Код И
|
СтруктурнаяЕдиница ССЫЛКА Справочник.КассыКомпании";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Код", КодКассыКомпании);
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата());
Запрос.УстановитьПараметр("КонечнаяДата", КонецДня(ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Итак, мы сформировали запрос к оборотному регистру, где указали период
выборки и код кассы. После выполнения мы получим развернутую по регистрам информацию о движении денежных средств по выбранной кассе компании, а также суммы в валюте документов и управленческой. Чтобы получить полную информацию по банковским счетам и кассам, можно применить
код, представленный в листинге 16.18.
Листинг 16.18. Анализ оборотов по счетам и кассам предприятия
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СУММА( ВЫБОР
Глава 16. Использование запросов
163
|
КОГДА СтруктурнаяЕдиница ССЫЛКА Справочник.КассыКомпании
|
ТОГДА СуммаУпрПриход
|
ИНАЧЕ 0
|
КОНЕЦ) КАК СуммаПриходаПоКассе,
|
СУММА( ВЫБОР
|
КОГДА СтруктурнаяЕдиница ССЫЛКА Справочник.БанковскиеСчета
|
ТОГДА СуммаУпрПриход
|
ИНАЧЕ 0
|
КОНЕЦ) КАК СуммаПриходаПоСчету,
|
СУММА(ВЫБОР
|
КОГДА СтруктурнаяЕдиница ССЫЛКА Справочник.КассыКомпании
|
ТОГДА СуммаУпрРасход
|
ИНАЧЕ 0
|
КОНЕЦ) КАК СуммаРасходаПоКассе,
|
СУММА(ВЫБОР
|
КОГДА СтруктурнаяЕдиница ССЫЛКА Справочник.БанковскиеСчета
|
ТОГДА СуммаУпрРасход
|
ИНАЧЕ 0
|
КОНЕЦ) КАК СуммаРасходаПоСчету,
|
СУММА(ВЫБОР
|
КОГДА СтруктурнаяЕдиница ССЫЛКА Справочник.КассыКомпании
|
ТОГДА СуммаУпрОборот
|
ИНАЧЕ 0
|
КОНЕЦ) КАК СуммаОборотПоКассе,
|
СУММА(ВЫБОР
|
КОГДА СтруктурнаяЕдиница ССЫЛКА Справочник.БанковскиеСчета
|
ТОГДА СуммаУпрОборот
|
ИНАЧЕ 0
|
КОНЕЦ) КАК СуммаОборотПоСчету
|ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании.Обороты(
|
&ДатаНачальная, &ДатаКонечная)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата());
Запрос.УстановитьПараметр("КонечнаяДата", КонецДня(ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Во время запроса мы проверяем тип справочника по составному типу ссылки.
В завершение суммируем результаты ресурсов регистра. После выполнения
164
Часть II. Приемы программирования регистров накопления
выборка будет содержать все суммы (по счетам и кассам) в управленческой
валюте.
Бывает, что необходимо оценить продажи товаров и поступления денежных
средств (платежи) по ним. Для этого имеет смысл воспользоваться двумя регистрами: Продажи и ДвижениеДенежныхСредств. Посмотрите внимательно
пример в листинге 16.19.
Листинг 16.19. Анализ оборотов по счетам и кассам предприятия
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Движение.Период КАК Период,
|
СУММА(Движение.СуммаПродажи)
|
СУММА(Движение.СуммаПоступления) КАК СуммаПоступило
КАК СуммаПродано,
|ИЗ
|
(ВЫБРАТЬ
|
Продажи.Период
|
Продажи.СуммаПродажиОборот КАК СуммаПродажи,
|
0
|
КАК Период,
КАК СуммаПоступления
ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты(
|
&ДатаНачальная, &ДатаКонечная, Неделя) КАК Продажи
|
|ОБЪЕДИНИТЬ ВСЕ
|
|
ВЫБРАТЬ
|
ДенежныеСредства.Период,
|
0,
|
|
ДенежныеСредства.СуммаУпрПриход
ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании.Обороты(
|
ва
&ДатаНачальная, &ДатаКонечная, Неделя) КАК ДенежныеСредст-
|
) КАК Движение
|
|СГРУППИРОВАТЬ ПО
Глава 16. Использование запросов
|
165
Движение.Период
|
|УПОРЯДОЧИТЬ ПО
|
Период";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата());
Запрос.УстановитьПараметр("КонечнаяДата", КонецДня(ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В представленном примере мы объединили два запроса в один. Из регистра
продаж получили сумму продаж за период, а сумму платежей — из регистра
ДенежныеСредстваКомпании. В результате выполнения запроса мы смогли
оценить разницу между продажами товаров и реальной оплатой этих товаров
покупателями за определенный период. Дополнительно развернули данные
с периодичностью, равной неделе. Самостоятельно вы можете добавить в запросы документы движения, договоры взаиморасчетов и другую полезную
информацию. Главное, не забывайте, что при объединении двух или более
таблиц следует следить за тем, чтобы поля имели одинаковый тип или допустимое пустое значение, а их количество в обоих запросах совпадало.
Важной составляющей торговых операций является учет и анализ товаров на
складах предприятия. В УПП поддерживаются несколько регистров для такого учета: ПартииТоваровНаСкладах (в конфигурации УП — ПартииТоваровКомпании), ТоварыВРезервеНаСкладах, ТоварыНаСкладах (ОстаткиТоваровКомпании) и ТоварыОрганизаций. Кроме того, есть специальные
регистры, ТоварыКПередачеСоСкладов и ТоварыКПолучениюНаСклады,
которые задействуются, как правило, при минусовых отгрузках (оформление
отгрузочных документов до реального оприходования товаров на склад) посредством документов ПриходныйОрдерНаТовары и РасходныйОрдерНаТовары. Мы рассмотрим особенности работы с представленными регистрами
с помощью запросов.
Регистр ПартииТоваровНаСкладах позволяет вести учет по партиям номенклатуры. В качестве регистраторов выступают документы поступления, оприходования, перемещения и реализации. Важно помнить, что учет партий ведется только в случае, когда для номенклатуры в справочнике установлен
соответствующий признак. Чтобы получить данные по регистру в разрезе
документа поступления, можно использовать код, представленный в листинге 16.20.
166
Часть II. Приемы программирования регистров накопления
Листинг 16.20. Получение партии товаров по документу поступления
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Регистратор
КАК Регистратор,
|
НомерСтроки
КАК НомерСтроки,
|
Активность
КАК ПризнакАктивности,
|
Номенклатура
КАК Номенклатура,
|
Склад
КАК СкладПоступления,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СерияНоменклатуры
КАК СерияНоменклатуры,
|
ДокументОприходования
КАК ДокументОприходования,
|
СтатусПартии
КАК СтатусПартии,
|
Заказ
КАК Заказ,
|
Количество
КАК Количество,
|
Стоимость
КАК Стоимость,
|
ДокументДвижения
КАК ДокументДвижения
|ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах
|
|ГДЕ
|
Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг И
|
Регистратор = &Регистратор
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Регистратор", ДокументПоступленияСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате запроса мы получим сведения о партиях номенклатуры на складе, оприходованных документом ПоступлениеТоваровУслуг. Часто бывает
необходимо оценить остатки номенклатуры в разрезе партий на определенном складе. Выполнить это можно с помощью запроса по регистру остатков,
как показано в листинге 16.21.
Глава 16. Использование запросов
167
Листинг 16.21. Получение остатков номенклатуры в разрезе партий
по складу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Регистратор
КАК Регистратор,
|
НомерСтроки
КАК НомерСтроки,
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Склад
КАК СкладПоступления,
|
СерияНоменклатуры
КАК СерияНоменклатуры,
|
ДокументОприходования
КАК ДокументОприходования,
|
СтатусПартии
КАК СтатусПартии,
|
Заказ
КАК Заказ,
|
КоличествоОстаток
КАК КоличествоОстаток,
|
СтоимостьОстаток
КАК СтоимостьОстаток,
|
ДокументДвижения
КАК ДокументДвижения
|ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах.Остатки
|
|ГДЕ
|
Склад = &Склад
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в качестве параметра запроса ссылку на склад, мы получим остатки
номенклатуры в разрезе партий. Дополнительно имеет смысл указать определенную номенклатуру, по которой нужно оценить остатки. Посмотрите,
как это делается в листинге 16.22.
168
Часть II. Приемы программирования регистров накопления
Листинг 16.22. Получение остатков партий номенклатуры по складу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Склад
КАК СкладПоступления,
|
СерияНоменклатуры
КАК СерияНоменклатуры,
|
СУММА(КоличествоОстаток)
КАК КоличествоОстаток,
|
СУММА(СтоимостьОстаток)
КАК СтоимостьОстаток
|ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах.Остатки
|
|ГДЕ
|
Склад = &Склад И
|
Номенклатура = &Номенклатура
|СГРУППИРОВАТЬ ПО
|
Номенклатура, ХарактеристикаНомеклатуры, Склад, СерияНомеклатуры
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Здесь мы передали в качестве параметра ссылку на товар и склад размещения
и сгруппировали данные для получения суммарного остатка и стоимости
в разрезе складов и серий.
Часто бывают такие ситуации, когда проданный товар по различным причинам покупатель возвращает обратно. При этом в системе оформляется документ возврата (ВозвратТоваровОтПокупателя). Для оценки возвращенного
товара в разрезе партионного учета необходимо соединить запрос к документу возврата и регистру остатков. Как это сделать, показано в листинге 16.23.
Глава 16. Использование запросов
169
Листинг 16.23. Анализ возвращенной покупателем номенклатуры в разрезе
партий
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Партии.Номенклатура,
|
Партии.ХарактеристикаНоменклатуры,
|
Партии.СерияНоменклатуры,
|
Партии.Количество,
|
Партии.Стоимость
|ИЗ
|
Документ.ВозвратТоваровОтПокупателя КАК Возврат
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ПартииТоваровНаСкладах
|
КАК Партии
|
ПО Партии.Регистратор = Возврат.Ссылка
|ГДЕ
|
Возврат.Товары.ДокументПартии = &ДокументПартии";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДокументПартии", ДокументРеализацияСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В рассмотренном примере мы через левое соединение связали таблицу документов и регистра накопления по документу партии, в данном случае реализации.
Результатом выборки явился список номенклатуры, которая была возвращена на
наш склад, а также ее серийные номера, количество и суммы.
При заполнении документов (товарной части) часто требуется получить не только остатки товаров на складе, но и номера серий для ведения учета по партиям.
Реализовать это можно также с помощью регистра ПартииТоваровНаСкладах.
Посмотрите пример, представленный в листинге 16.24.
Листинг 16.24. Получение остатков номенклатуры на складе с учетом серий
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
170
Часть II. Приемы программирования регистров накопления
Запрос.Текст = "
|ВЫБРАТЬ
|
Товар.Код
КАК Код,
|
Товар.Ссылка
КАК Номенклатура,
|
Товар.ЕдиницаХраненияОстатков
КАК ЕдиницаИзмерения,
|
СУММА(Остатки.КоличествоОстаток) КАК Остаток,
|
Остатки.ХарактеристикаНоменклатуры
|
КАК ХарактеристикаНоменклатуры,
|
Остатки.СерияНоменклатуры
КАК Серия
|ИЗ
|
Справочник.Номенклатура КАК Товар
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ПартииТоваровКомпании.Остатки(
|
&Период, Склад = &Склад) КАК Остатки
|ПО
|
Товар.Ссылка = Остатки.Номенклатура
|ГДЕ
|
КоличествоОстаток > 0
|СГРУППИРОВАТЬ ПО
|
Товар.Ссылка,
|
Остатки.ХарактеристикаНоменклатуры,
|
Остатки.СерияНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Товар.Наименование";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Как видите, сначала мы отобрали записи регистра по дате и указанному
складу. Затем выполнили соединение со справочником Номенклатура, чтобы
получить информацию о товарах. Кроме того, удалили из выборки товары,
которых нет на складе (количество меньше или равно нулю) и выполнили
группировку по номенклатуре. После выполнения запроса в выборку попали
остатки товаров по складу, которыми можно заполнить табличную часть документа.
Глава 16. Использование запросов
171
Как вы уже знаете, большая часть товаров, как правило, резервируется на
складе под заказы. Для этого при оформлении заказа указывают признак резервирования под будущие поставки (заказ покупателя) или для своих нужд
(внутренний заказ). Когда на склад поступает заказанный товар, он автоматически распределяется из свободного остатка и резервируется под заказ.
Чтобы получить остатки товаров, зарезервированных под заказы на определенную дату и с учетом партий, можно применить код, представленный
в листинге 16.25.
Листинг 16.25. Получение остатков номенклатуры, зарезервированной
под заказы
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ОстаткиТоваров.Номенклатура
КАК Товар,
|
ОстаткиТоваров.СкладКомпании
КАК Склад,
|
ОстаткиТоваров.Заказ
КАК ЗаказПокупателя,
|
СУММА(ОстаткиТоваров.КоличествоОстаток) КАК Количество
|ИЗ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки
|
(&Период, НЕ(Заказ.Ссылка ЕСТЬ NULL )) КАК ОстаткиТоваров
|
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
|
(ВЫБРАТЬ
|
ПартииТоваров.Номенклатура КАК Номенклатура,
|
ПартииТоваров.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
|
ПартииТоваров.КоличествоОстаток КАК
|
|
КоличествоОстаток
ИЗ
|
РегистрНакопления.ПартииТоваровКомпании.Остатки
|
(&Период) КАК ПартииТоваров
|
|
|
ГДЕ
(ПартииТоваров.КоличествоОстаток <> 0)
|
) КАК ПартииОстаток
|
ПО ОстаткиТоваров.Номенклатура = ПартииОстаток.Номенклатура
172
Часть II. Приемы программирования регистров накопления
|
И ОстаткиТоваров.ХарактеристикаНоменклатуры =
|
ПартииОстаток.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ОстаткиТоваров.Заказ,
|
ОстаткиТоваров.Номенклатура,
|
ОстаткиТоваров.СкладКомпании
|
|ИТОГИ ПО
|
ОБЩИЕ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Итак, мы обратились в запросе к регистру ОстаткиТоваровКомпании, чтобы
получить актуальные остатки товаров на складах. Поскольку основной нашей
целью было получить зарезервированную под заказы номенклатуру, по которой ведется учет партий, мы через внутреннее соединение подключили регистр ПартииТоваровКомпании. При таком соединении выбираются только
те записи из обеих таблиц регистров, которые удовлетворяют условию (в нашем случае по номенклатуре и характеристике номенклатуры). Полученный
в результате соединения запрос мы сгруппировали и рассчитали итоги. Ключевое слово ОБЩИЕ обозначает, что итоги будут подсчитаны по всем значениям полей таблицы, для которых заданы агрегатные функции.
В предыдущем примере мы получили остатки товаров на складах, зарезервированные под заказы покупателей (внутренние заказы). Теперь мы сделаем
противоположный расчет: подсчитаем остатки товаров на складах, лежащих
в свободном остатке. Как это сделать, показано в листинге 16.26.
Листинг 16.26. Получение номенклатуры, находящейся в свободном остатке
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ОстаткиТоваров.Номенклатура
КАК Товар,
|
ОстаткиТоваров.СкладКомпании
КАК Склад,
Глава 16. Использование запросов
|
173
СУММА(ОстаткиТоваров.КоличествоОстаток) КАК Количество
|ИЗ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки
|
(&Период, Заказ.Ссылка ЕСТЬ NULL) КАК ОстаткиТоваров
|
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
|
(ВЫБРАТЬ
|
ПартииТоваров.Номенклатура КАК Номенклатура,
|
ПартииТоваров.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
|
ПартииТоваров.КоличествоОстаток КАК
|
КоличествоОстаток
|
ИЗ
|
РегистрНакопления.ПартииТоваровКомпании.Остатки
|
(&Период) КАК ПартииТоваров
|
|
ГДЕ
|
(ПартииТоваров.КоличествоОстаток <> 0)
|
) КАК ПартииОстаток
|
ПО ОстаткиТоваров.Номенклатура = ПартииОстаток.Номенклатура
|
И ОстаткиТоваров.ХарактеристикаНоменклатуры =
|
ПартииОстаток.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ОстаткиТоваров.Номенклатура,
|
ОстаткиТоваров.СкладКомпании
|
|ИТОГИ СУММА(Количество) ПО
|
ОБЩИЕ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Данный запрос похож на предыдущий. Однако если раньше мы выбирали из
регистра только те записи, в которых присутствовала ссылка на заказ покупателя, то теперь делаем все наоборот. Записи регистра без указания заказа относятся к свободному остатку и могут распределяться по любым новым или
уже созданным документам.
174
Часть II. Приемы программирования регистров накопления
В конфигурации УПП получить остатки зарезервированных на складах товаров можно посредством регистра ТоварыВРезервеНаСкладах. Например,
чтобы получить остатки резерва по внутренним заказам, используйте код,
подобный приведенному в листинге 16.27.
Листинг 16.27. Получение остатков номенклатуры на складе,
зарезервированной под внутренний заказ
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура
КАК Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказы.ВнутреннийЗаказ
КАК Заказ
|
СУММА(Заказы.КоличествоОстаток)
КАК ОстатокПоЗаказу,
|
СУММА(Резерв.КоличествоОстаток)
КАК ЗарезервированоПоЗаказу
|
|ИЗ
|
РегистрНакопления.ВнутренниеЗаказы.Остатки
|
(&ДатаКонечная, ВнутреннийЗаказ = &Заказ) КАК Заказы
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки
|
(&ДатаКонечная, ДокументРезерва = &Заказ И
|
Склад = &Склад) КАК Резерв
|
ПО Заказы.Номенклатура = Резерв.Номенклатура
|
И Заказы.ХарактеристикаНоменклатуры =
|
Резерв.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
Заказы.Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры,
|
Заказы.ВнутреннийЗаказ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ВнутреннийЗаказСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 16. Использование запросов
175
В запросе мы использовали два регистра накопления: ВнутренниеЗаказы
и ТоварыВРезервеНаСкладах. В первом запросе мы выбрали все записи по
заказу на текущую дату. Во втором получили зарезервированные остатки номенклатуры на текущую дату под заказ на указанном складе. Далее выполнили левое соединение двух таблиц по номенклатуре и характеристике номенклатуры. Полученный результат сгруппировали и рассчитали остаток по
заказу, а также зарезервированный остаток под заказ. Чтобы получить остаток резерва по заказу покупателя, придется немного изменить код, как это
сделано в листинге 16.28.
Листинг 16.28. Получение остатков номенклатуры на складе,
зарезервированной под заказ покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура
КАК Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказы.ВнутреннийЗаказ
КАК Заказ
|
СУММА(Заказы.КоличествоОстаток)
КАК ОстатокПоЗаказу,
|
СУММА(Резерв.КоличествоОстаток)
КАК ЗарезервированоПоЗаказу
|
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки
|
(&ДатаКонечная, ЗаказПокупателя = &Заказ) КАК Заказы
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки
|
(&ДатаКонечная,
|
ВЫРАЗИТЬ(ДокументРезерва КАК Документ.ЗаказПокупателя) = &Заказ И
|
Склад = &Склад) КАК Резерв
|
ПО Заказы.Номенклатура = Резерв.Номенклатура
|
И Заказы.ХарактеристикаНоменклатуры =
|
Резерв.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
Заказы.Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры,
|
Заказы.ВнутреннийЗаказ";
176
Часть II. Приемы программирования регистров накопления
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В этом примере мы применили регистр остатков ЗаказыПокупателей. Кроме
того, поскольку измерение ДокументРезерва из регистра ТоварыВРезервеНаСкладах является составным типом, мы применили ключевое слово
ВЫРАЗИТЬ для явного преобразования составной ссылки в ссылку на заказ покупателя.
Следующий пример также может использоваться в конфигурации УПП
и служит для получения свободного остатка номенклатуры на указанном
складе. Посмотрите код, представленный в листинге 16.29.
Листинг 16.29. Получение свободного остатка номенклатуры на складе
с учетом движений по ордерам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ТоварыНаСкладах.Номенклатура
КАК Номенклатура,
|
ТоварыНаСкладах.ХарактеристикаНоменклатуры КАК ХарНоменклатуры,
|
ТоварыНаСкладах.Склад
КАК Склад,
|
ТоварыНаСкладах.КоличествоОстаток
КАК ОстатокНаСкладе,
|
ТоварыВРезерве.КоличествоОстаток
КАК Размещено,
|
ТоварыКПередаче.КоличествоОстаток
КАК КПередаче
|ИЗ
|
РегистрНакопления.ТоварыНаСкладах.Остатки(,
|
Номенклатура = &Номенклатура И Склад = &Склад)
|
КАК ТоварыНаСкладах
|
// получаем остаток из резерва
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(,
|
Номенклатура = &Номенклатура И
|
ДокументРезерва <> &ДокументРезерва) КАК ТоварыВРезерве
Глава 16. Использование запросов
177
|ПО ТоварыНаСкладах.Номенклатура = ТоварыВРезерве.Номенклатура И
|
ТоварыНаСкладах.ХарактеристикаНоменклатуры =
|
ТоварыВРезерве.ХарактеристикаНоменклатуры И
|
ТоварыНаСкладах.Склад = ТоварыВРезерве.Склад
|
// получаем остаток по ордерам
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки(,
|
Номенклатура = &Номенклатура) КАК ТоварыКПередаче
|ПО ТоварыНаСкладах.Номенклатура = ТоварыВРезерве.Номенклатура И
|
ТоварыНаСкладах.ХарактеристикаНоменклатуры =
|
ТоварыКПередаче.ХарактеристикаНоменклатуры И
|
ТоварыНаСкладах.Склад = ТоварыКПередаче.Склад";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("ДокументРезерва", ДокументРезерваСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В запросе мы выбираем свободный остаток товара на складе, используя данные из трех регистров. Первый возвращает остаток товара, второй — товар,
не зарезервированный для указанного документа резерва, а третий получает
товар, подготовленный к передаче со склада. Регистр ТоварыКПередачеСоСкладов применяется при использовании ордерной схемы оприходования
и списания товаров со склада. Данная схема может применяться при минусовых отгрузках и оформляется следующими документами движения: ПриходныйОрдерНаТовары и РасходныйОрдерНаТовары.
А теперь поговорим о том, как в УПП получить остатки номенклатуры на
складах по заказу покупателя с учетом зарезервированных товаров, размещенных в заказах поставщикам и сформированных в виде комплектов. Для этого
нам понадобятся три регистра накопления и регистр сведений КомплектующиеНоменклатуры. Посмотрите пример, показанный в листинге 16.30.
Листинг 16.30. Получение остатков по заказу покупателей с учетом резерва,
размещения и комплектации номенклатуры
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
178
Часть II. Приемы программирования регистров накопления
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура
КАК Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказы.КоличествоОстаток
КАК КоличествоПоЗаказу,
|
ВЫБОР
|
КОГДА Резерв.КоличествоОстаток ЕСТЬ NULL ТОГДА 0
|
ИНАЧЕ Резерв.КоличествоОстаток
|
КОНЕЦ КАК КоличествоРезерв,
|
ВЫБОР
|
КОГДА Размещение.КоличествоОстаток ЕСТЬ NULL ТОГДА 0
|
ИНАЧЕ Размещение.КоличествоОстаток
|
КОНЕЦ КАК КоличествоРазмещено
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(,
|
ЗаказПокупателя = &Заказ) КАК Заказы
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(,
|
ДокументРезерва = &Заказ) КАК Резерв
|
ПО Заказы.Номенклатура = Резерв.Номенклатура И
|
Заказы.ХарактеристикаНоменклатуры =
|
Резерв.ХарактеристикаНоменклатуры
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.РазмещениеЗаказовПокупателей.Остатки(,
|
ЗаказПокупателя = &Заказ) КАК Размещение
|
ПО Заказы.Номенклатура = Размещение.Номенклатура И
|
Заказы.ХарактеристикаНоменклатуры =
|
Размещение.ХарактеристикаНоменклатуры
|
СОЕДИНЕНИЕ РегистрСведений.КомплектующиеНоменклатуры КАК
|
Комплектующие
|
ПО Заказы.Номенклатура = Комплектующие.Номенклатура И
|
Заказы.ХарактеристикаНоменклатуры =
|
Комплектующие.ХарактеристикаНоменклатуры
|
|ИТОГИ СУММА(КоличествоПоЗаказу), СУММА(КоличествоРезерв),
|
СУММА(КоличествоРазмещено)
|ПО Номенклатура, ХарактеристикаНоменклатуры";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 16. Использование запросов
179
Вначале мы получаем остатки по заказу покупателя. Далее через левое соединение выбираем зарезервированные товары по этому же заказу. На следующем шаге выбираем все товары из заказа, размещенные в заказах поставщикам. После этого посредством полного соединения с регистром
сведений КомплектующиеНоменклатуры отбираем комплекты, собранные на
базе номенклатуры из заказа покупателя. На регистрах сведений мы подробно остановимся в части III книги. И, наконец, рассчитываем итоги по всем
остаткам с учетом номенклатуры и характеристики.
В реальной практике часто стоит задача получить остатки не по одному заказу, а сразу по нескольким. Например, чтобы посчитать остатки зарезервированной на складе номенклатуры по всем заказам покупателей из табличной
части документа, можно воспользоваться кодом, представленным в листинге 16.31.
Листинг 16.31. Получение остатков по группе заказов покупателей
// Выгружаем заказы из табличной части документа
ГруппаЗаказов = ТЧЗаказы.ВыгрузитьКолонку("ЗаказПокупателя");
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
Склад
КАК Склад,
|
КоличествоОстаток
КАК КоличествоВРезерве
|ИЗ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(
|
&Период, ДокументРезерва В (&ГруппаЗаказов))";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("ГруппаЗаказов", ГруппаЗаказов);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Вначале мы выгружаем в список из табличной части документа колонку
с заказами покупателей (метод ВыгрузитьКолонку). После этого делаем запрос к регистру остатков, задав период и условие отбора. Условие формируем
180
Часть II. Приемы программирования регистров накопления
при помощи оператора В, который проверяет соответствие значения в левой
части одному из группы значений в правой. Другими словами, когда запись
регистра содержит документ резерва (в данном случае заказ покупателя),
присутствующий в группе заказов, она попадает в результирующую выборку.
Теперь разберем, как можно получить остатки номенклатуры, зарезервированной на определенном складе. Для этого нам опять понадобится обратиться к регистру накопления ТоварыВРезервеНаСкладах. Посмотрите пример,
показанный в листинге 16.32.
Листинг 16.32. Получение остатков для комплекта номенклатуры по заказу покупателя
// Выгружаем номенклатуру из табличной части документа
ГруппаТоваров = Товары.ВыгрузитьКолонку("Номенклатура");
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Склад
КАК Склад,
|
КоличествоОстаток
КАК КоличествоВРезерве
|ИЗ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(,
|
ДокументРезерва = &Заказ И
|
Номенклатура В (&ГруппаТоваров) И
|
Склад = &Склад)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
Запрос.УстановитьПараметр("ГруппаТоваров", ГруппаТоваров);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Так же, как и в предыдущем примере, мы выгрузили из табличной части колонку с номенклатурой и передали в запрос в качестве параметра. Дополнительно указали ссылку на заказ и склад размещения. Результатом запроса будет остаток зарезервированной группы номенклатуры на складе по заказу
покупателя.
Глава 16. Использование запросов
181
На следующем шаге мы поговорим о том, как можно в конфигурации УП
проанализировать количество проданных (или списанных) товаров, принятых
на комиссию от комитента. Для этих целей предназначен регистр РеализованныеТовары. По нему строится анализ в разрезе документов оприходования и реализации, номенклатуре и серии (учет по партиям), а также договора
взаиморасчетов. Например, чтобы получить оставшееся количество товаров
в разрезе договора по документу поступления, достаточно написать запрос,
как показано в листинге 16.33.
Листинг 16.33. Получение остатка номенклатуры в разрезе договора
взаиморасчетов по документу поступления
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
СерияНоменклатуры,
|
ДокументПоставки,
|
Количество,
|
ДоговорВзаиморасчетов
|ИЗ
|
РегистрНакопления.РеализованныеТовары
|
|ГДЕ
|
ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов И
|
ДокументПоставки = &ДокументПоставки И
|
ДокументПоставки ССЫЛКА Документ.ПоступлениеТоваров
|
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
ДоговорСсылка);
Запрос.УстановитьПараметр("ДокументПоставки", ПоступлениеТоваровСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
182
Часть II. Приемы программирования регистров накопления
Передаем в запрос ссылку на договор и документ поступления и получаем
количество оставшейся номенклатуры по документу поставки. Можно также
по этому регистру получить обороты за период по определенной номенклатуре. Для этого следует выполнить запрос к оборотному регистру, как показано в листинге 16.34.
Листинг 16.34. Получение оборотов за период по реализованной номенклатуре
комитента
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СерияНоменклатуры
КАК Серия,
|
ДоговорВзаиморасчетов
КАК Договор,
|
ДокументПоставки
КАК ДокументПокупки,
|
ДокументПродажи
КАК ДокументПродажи,
|
Регистратор
КАК ДокументДвижения,
|
КоличествоПриход
КАК КоличествоПриход,
|
КоличествоРасход
КАК КоличествоРасход,
|
КоличествоОборот
КАК КоличествоОборот
|ИЗ
|
РегистрНакопления.РеализованныеТовары.Обороты(
|
&ДатаНачальная, &ДатаКонечная, Регистратор,
|
Номенклатура = &Номенклатура)
|
|УПОРЯДОЧИТЬ ПО
|
Период,
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 16. Использование запросов
183
В результате выполнения запроса мы получим обороты по заданной номенклатуре за период, развернутые по регистратору. Другими словами, мы сможем оценить все движения номенклатуры по документам: по каким был приход, по каким — реализация, суммарные обороты по каждой сделке.
А теперь получим текущие остатки номенклатуры с учетом серий и количество списанных товаров в разрезе документа продажи. Для этого используем
три регистра накопления: РеализованныеТовары, ПартииТоваровКомпании
и ПартииТоваровКомпанииОтданные. Посмотрите пример, представленный
в листинге 16.35.
Листинг 16.35. Получение остатков и списанных товаров с учетом серий
номенклатуры
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ПродажиОстатки.Номенклатура
|
ПродажиОстатки.ХарактеристикаНоменклатуры КАК
КАК Номенклатура,
|
ХарактеристикаНомеклатуры,
|
ПродажиОстатки.СерияНоменклатуры,
КАК Серия
|
ПродажиОстатки.ДокументПродажи
КАК ДокументПродажи,
|
ПродажиОстатки.ДокументПродажи.ВалютаДокумента КАК Валюта,
|
ПродажиОстатки.ДокументПоставки
КАК ДокументПоставки,
|
ПродажиОстатки.КоличествоОстаток
КАК ОстатокКоличество,
|
ДвиженияПоПартиям.Количество
КАК СписаноКоличество,
|
ДвиженияПоПартиям.Стоимость
КАК СписаноСтоимость
|ИЗ
|
РегистрНакопления.РеализованныеТовары.Остатки(,
|
ДоговорВзаиморасчетов = &Договор
|
И ДокументПоставки = &ДокументПоставки) КАК ПродажиОстатки
|
|ЛЕВОЕ СОЕДИНЕНИЕ
|
(ВЫБРАТЬ
|
ПартииТоваров.Номенклатура
|
ПартииТоваров.ХарактеристикаНоменклатуры КАК
КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ПартииТоваров.СерияНоменклатуры КАК Серия,
184
Часть II. Приемы программирования регистров накопления
|
ПартииТоваров.ДокументПоставки
КАК ДокументПоставки,
|
ПартииТоваров.Регистратор
КАК Регистратор,
|
ПартииТоваров.Количество
КАК Количество,
|
ПартииТоваров.Стоимость
КАК Стоимость
|
ИЗ
|
РегистрНакопления.ПартииТоваровКомпании КАК ПартииТоваров
|
|
ГДЕ
|
ПартииТоваров.Регистратор = &ДокументПродажи И
|
ПартииТоваров.ВидДвижения = &ВидДвижения
|
|
ОБЪЕДИНИТЬ ВСЕ
|
|
ВЫБРАТЬ
|
ПартииТоваровОтданные.Номенклатура,
|
ПартииТоваровОтданные.ХарактеристикаНоменклатуры,
|
ПартииТоваровОтданные.СерияНоменклатуры,
|
ПартииТоваровОтданные.ДокументПоставки,
|
ПартииТоваровОтданные.Регистратор,
|
ПартииТоваровОтданные.Количество,
|
ПартииТоваровОтданные.Стоимость
|
ИЗ
|
РегистрНакопления.ПартииТоваровКомпанииОтданные КАК
|
ПартииТоваровОтданные
|
|
ГДЕ
|
ПартииТоваровОтданные.Регистратор = &ДокументПродажи И
|
ПартииТоваровОтданные.ВидДвижения = &ВидДвижения)
|
КАК ДвиженияПоПартиям
|
|ПО ПродажиОстатки.Номенклатура = ДвиженияПоПартиям.Номенклатура
|
И ПродажиОстатки.ХарактеристикаНоменклатуры =
|
ДвиженияПоПартиям.ХарактеристикаНоменклатуры
|
И ПродажиОстатки.СерияНоменклатуры =
|
ДвиженияПоПартиям.СерияНоменклатуры
|
И ПродажиОстатки.ДокументПоставки =
|
ДвиженияПоПартиям.ДокументПоставки
|
И ПродажиОстатки.ДокументПродажи = ДвиженияПоПартиям.Регистратор";
// Устанавливаем параметры запроса
Глава 16. Использование запросов
185
Запрос.УстановитьПараметр("ДокументПоставки", ПоступлениеСсылка);
Запрос.УстановитьПараметр("Договор", ДоговорВзаиморасчетовСсылка);
Запрос.УстановитьПараметр("ДокументПродажи", ДокумментПродажиСсылка);
Запрос.УстановитьПараметр("ДокументВидДвижения",
ВидДвиженияНакопления.Расход);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В нашем примере актуальные остатки номенклатуры мы получили из регистра остатков РеализованныеТовары. Чтобы определить количество и сумму
списанных товаров, объединили два запроса по партиям товаров на складах
и партиях товаров, отданных в подзапрос, и объединили через левое соединение с таблицей первого регистра. Для учета партий добавили в условия соединения внешнего и внутреннего запросов серию номенклатуры.
В конфигурации УПП существует специальный регистр, где ведется учет товаров в разрезе организаций. Когда товары приходуются на склад, в соответствующих документах поступления (ОприходованиеТоваров или ПоступлениеТоваровУслуг) выбирается организация, под которую они регистрируются
в системе. Для анализа товаров в разрезе организаций в конфигурации предусмотрен регистр накопления ТоварыОрганизаций. Например, чтобы получить
остатки товаров по определенной организации, можно написать код, показанный в листинге 16.36.
Листинг 16.36. Получение остатков номенклатуры по выбранной организации
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
ДоговорВзаиморасчетов
КАК Договор,
|
Регистратор
КАК ДокументДвижения,
|
КоличествоОстаток
КАК ОстатокПоОрганизации
|ИЗ
|
РегистрНакопления.ТоварыОрганизаций.Остатки(
|
&Период, Организация = &Организация)
|
186
Часть II. Приемы программирования регистров накопления
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Организация", ОрганизацияСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим актуальные остатки номенклатуры для указанной организации. Если дополнительно необходимо учесть
номенклатуру и договор взаиморасчетов, наш пример следует написать так,
как показано в листинге 16.37.
Листинг 16.37. Получение остатков по выбранной номенклатуре организации
в разрезе договора взаиморасчетов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
ДоговорВзаиморасчетов
КАК Договор,
|
Регистратор
КАК ДокументДвижения,
|
КоличествоОстаток
КАК ОстатокПоОрганизации
|ИЗ
|
РегистрНакопления.ТоварыОрганизаций.Остатки(
|
&Период, Организация = &Организация И
|
Номенклатура = &Номенклатура И
|
ДоговорВзаиморасчетов = &Договор)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Организация", ОрганизацияСсылка);
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Договор", ДоговорСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 16. Использование запросов
187
Теперь запрос стал более гибким и позволяет узнать остаток выбранной номенклатуры организации в разрезе договора взаиморасчетов. Поскольку данный регистр поддерживает учет по партиям, можно узнать остатки номенклатуры в разрезе партий. Дополнительно нам придется проанализировать
регистр ТоварыНаСкладах, чтобы получить текущие остатки номенклатуры
на складах. Посмотрите код, представленный в листинге 16.38.
Листинг 16.38. Получение остатков по организации в разрезе серий
номенклатуры
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СерияНоменклатуры КАК Серия,
|
КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период,
|
Номенклатура = &Номенклатура)
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|
СерияНоменклатуры КАК Серия,
|
КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ТоварыОрганизаций.Остатки(&Период,
|
Организация = &Организация
|
|
И Номенклатура = &Номенклатура
)
|ИТОГИ МИНИМУМ(КоличествоОстаток) ПО
|
Серия";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Организация", ОрганизацияСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
188
Часть II. Приемы программирования регистров накопления
Для решения задачи мы применили объединение двух регистров остатков.
В первом запросе мы получаем актуальные остатки выбранной номенклатуры по всем складам в разрезе серий. Во втором — остатки номенклатуры по
организации в разрезе серий. После этого выполняем объединение таблиц
регистров и рассчитываем минимальные итоговые количества оставшейся
номенклатуры по каждой имеющейся серии.
Получить остатки или обороты партий номенклатуры на складах можно
с помощью регистра ПартииТоваровНаСкладах. Мы уже вкратце останавливались на данном регистре, а сейчас поговорим более подробно, поскольку он
играет ключевую роль в учете товаров по партиям. Для начала получим количество оприходованной номенклатуры в разрезе партий на указанном
складе. Посмотрите код, представленный в листинге 16.39.
Листинг 16.39. Получение партий номенклатуры по выбранному складу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
СерияНоменклатуры
КАК СерияНоменклатуры,
|
Склад
КАК Склад,
|
ДокументОприходования
КАК ДокументОприходования,
|
СтатусПартии
КАК СтатусПартии,
|
Заказ
КАК Заказ,
|
Регистратор
КАК ДокументДвижения,
|
СУММА(Количество)
КАК Количество,
|
СУММА(Стоимость)
КАК Стоимость
|ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах
|
|ГДЕ
|
Период МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Номенклатура = &Номенклатура И
|
Склад = &Склад
|
|СГРУППИРОВАТЬ ПО
Глава 16. Использование запросов
189
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
СерияНоменклатуры,
|
Склад,
|
ДокументОприходования,
|
СтатусПартии,
|
Заказ,
|
Регистратор";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Через параметры запроса мы установили период выборки и передали ссылки
на номенклатуру и склад. В результате выполнения запроса мы получим общее количество и сумму по выбранной номенклатуре, имеющейся на указанном складе. Поскольку в запросе используется группировка, в результирующей таблице будут отражены все поля группировки.
Для получения оперативной информации удобнее пользоваться регистром
остатков. Он предоставляет возможность увидеть актуальные остатки товаров по различным измерениям, таким как серия, склад, документ оприходования и заказ. Построим, например, запрос, получающий остатки номенклатуры с определенной серией (листинг 16.40).
Листинг 16.40. Получение остатков номенклатуры определенной серии
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СерияНоменклатуры
КАК СерияНоменклатуры,
|
Склад
КАК Склад,
|
ДокументОприходования
КАК ДокументОприходования,
190
Часть II. Приемы программирования регистров накопления
|
Заказ
КАК Заказ,
|
Регистратор
КАК ДокументДвижения,
|
СУММА(КоличествоОстаток)
КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&ДатаКонечная,
|
Номенклатура = &Номенклатура И
|
СерияНоменклатуры = &Серия)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
СерияНоменклатуры,
|
Склад,
|
ДокументОприходования,
|
Заказ,
|
Регистратор";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Серия", СерияСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Как видите, задача решается просто. Устанавливаем ограничение выборки по
дате и задаем в качестве условия отбора ссылку на номенклатуру и серию.
Для того чтобы получить развернутую информацию по номенклатуре, следует воспользоваться регистром оборотов или совмещенным по остаткам
и оборотам. Посмотрите пример, представленный в листинге 16.41.
Листинг 16.41. Получение движений по выбранной номенклатуре
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры
КАК ХарактеристикаНоменклатуры,
|
СерияНоменклатуры
КАК СерияНоменклатуры,
Глава 16. Использование запросов
191
|
Склад
КАК Склад,
|
Заказ
КАК Заказ,
|
Регистратор
КАК ДокументДвижения,
|
СУММА(КоличествоНачальныйОстаток) КАК Поступило,
|
СУММА(КоличествоКонечныйОстаток)
КАК Осталось,
|
СУММА(КоличествоПриход)
КАК КоличествоПриход,
|
СУММА(КоличествоРасход)
КАК КоличествоРасход,
|
СУММА(КоличествоОборот)
КАК КоличествоОборот
|
|ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(
|
&ДатаНачальная, &ДатаКонечная, Регистратор, Движения,
|
Номенклатура = &Номенклатура)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
СерияНоменклатуры,
|
Склад,
|
Заказ,
|
Регистратор
|ИТОГИ СУММА(Поступило), СУММА(Осталось), СУММА(КоличествоПриход),
|
СУММА(КоличествоРасход), СУММА(КоличествоОборот) ПО
|
ОБЩИЕ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Как видно из примера, мы воспользовались регистром остатков и оборотов,
чтобы выбрать наиболее полную информацию о движениях выбранной номенклатуры. Кроме того, результат выборки развернули по регистратору (документам, выполняющим движения по регистру) и подсчитали общие итоги
по всем ресурсам.
Чтобы выбрать номенклатуру, находящуюся в свободном остатке, следует
добавить запрос к регистру ТоварыНаСкладах, как это сделано в листинге 16.42.
192
Часть II. Приемы программирования регистров накопления
Листинг 16.42. Получение номенклатуры из свободного остатка
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ТоварыНаСкладах.Номенклатура
КАК Номенклатура,
|
ТоварыНаСкладах.Склад
КАК Склад,
|
ТоварыНаСкладах.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
|
ПартииОстаток.Серия
КАК Серия,
|
СУММА(ТоварыНаСкладах.КоличествоОстаток)
КАК Количество
|ИЗ
|
РегистрНакопления.ТоварыНаСкладах.Остатки
|
(&Период, (Заказ.Ссылка ЕСТЬ NULL) И
|
Номенклатура = &Номенклатура) КАК ТоварыНаСкладах
|
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
|
(ВЫБРАТЬ
|
ПартииТоваров.Номенклатура КАК Номенклатура,
|
ПартииТоваров.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
|
ПартииТоваров.СерияНоменклатуры КАК Серия,
|
ПартииТоваров.КоличествоОстаток КАК
|
КоличествоОстаток
|
ИЗ
|
РегистрНакопления.ПартииТоваровНаСкладах.Остатки
|
(&Период) КАК ПартииТоваров
|
|
ГДЕ
|
(ПартииТоваров.КоличествоОстаток <> 0)
|
) КАК ПартииОстаток
|
ПО ТоварыНаСкладах.Номенклатура =
|
ПартииОстаток.Номенклатура
|
И ТоварыНаСкладах.ХарактеристикаНоменклатуры =
|
ПартииОстаток.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ТоварыНаСкладах.Номенклатура,
Глава 16. Использование запросов
193
|
ТоварыНаСкладах.Склад,
|
ТоварыНаСкладах.ХарактеристикаНоменклатуры,
|
ПартииОстаток.Серия
|
|ИТОГИ СУММА(Количество) ПО
|
ОБЩИЕ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате запроса мы получим количество выбранной номенклатуры, размещенной в свободном остатке. А теперь выберем остатки номенклатуры,
зарезервированной под определенный заказ покупателя. Как это сделать, показано в листинге 16.43.
Листинг 16.43. Получение остатка номенклатуры, зарезервированной под заказ
покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ТоварыНаСкладах.Номенклатура
КАК Товар,
|
ТоварыНаСкладах.Склад
КАК Склад,
|
ТоварыНаСкладах.Заказ
КАК ЗаказПокупателя,
|
СУММА(ТоварыНаСкладах.КоличествоОстаток) КАК Количество
|ИЗ
|
РегистрНакопления.ТоварыНаСкладах.Остатки
|
(&Период, НЕ(Заказ.Ссылка ЕСТЬ NULL ) И
|
Заказ = &Заказ И
|
Номенклатура = &Номенклатура) КАК ТоварыНаСкладах
|
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
|
(ВЫБРАТЬ
|
ПартииТоваров.Номенклатура КАК Номенклатура,
|
ПартииТоваров.ХарактеристикаНоменклатуры КАК
|
ХарактеристикаНоменклатуры,
194
Часть II. Приемы программирования регистров накопления
|
ПартииТоваров.КоличествоОстаток КАК
|
КоличествоОстаток
|
ИЗ
|
РегистрНакопления.ПартииТоваровКомпании.Остатки
|
(&Период) КАК ПартииТоваров
|
|
ГДЕ
|
(ПартииТоваров.КоличествоОстаток <> 0)
|
) КАК ПартииОстаток
|
ПО ТоварыНаСкладах.Номенклатура =
|
ПартииОстаток.Номенклатура
|
И ТоварыНаСкладах.ХарактеристикаНоменклатуры =
|
ТоварыНаСкладах.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ТоварыНаСкладах.Заказ,
|
ТоварыНаСкладах.Номенклатура,
|
ТоварыНаСкладах.Склад
|
|ИТОГИ ПО
|
ОБЩИЕ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом запроса будут остатки выбранной номенклатуры, зарезервированной под конкретный заказ покупателя.
Хочу заметить, что регистр накопления ПартииТоваровНаСкладах следует
всегда использовать в том случае, если ведется учет номенклатуры по партиям, а также для получения движений номенклатуры по документам. Если
требуется лишь узнать остатки номенклатуры на складе, вполне достаточно
будет данных из регистра ТоварыНаСкладах.
Следующий регистр, о котором мы поговорим, называется ВнутренниеЗаказы. Как видно из названия, он предназначен для накапливания информации
о движении документов ВнутреннийЗаказ. Данный документ предназначен,
Глава 16. Использование запросов
195
в первую очередь, для резервирования товаров из будущих поставок или свободного остатка под внутренние нужды предприятия.
Теперь попробуем написать запрос для получения остатков номенклатуры по
выбранному внутреннему заказу (листинг 16.44).
Листинг 16.44. Получение остатков номенклатуры по внутреннему заказу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказчик
КАК Заказчик,
|
СтатусПартии
КАК СтатусПартии,
|
ВнутреннийЗаказ
КАК ВнутреннийЗаказ,
|
СУММА(КоличествоОстаток)
КАК КоличествоОстаток
|
|ИЗ
|
РегистрНакопления.ВнутренниеЗаказы.Остатки(&ДатаКонечная,
|
ВнутреннийЗаказ = &Заказ)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
Заказчик,
|
СтатусПартии,
|
ВнутреннийЗаказ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ВнутреннийЗаказСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Здесь мы сделали запрос к регистру, выбрав остатки номенклатуры на текущую дату по внутреннему заказу, ссылку на который передали в виде параметра запроса. Чтобы получить остатки по конкретному товару, следует передать в запрос ссылку на выбранную номенклатуру. Как это сделать,
показано в листинге 16.45.
196
Часть II. Приемы программирования регистров накопления
Листинг 16.45. Получение остатка выбранной номенклатуры по внутреннему
заказу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
ВнутреннийЗаказ
КАК ВнутреннийЗаказ,
|
СУММА(КоличествоОстаток)
КАК КоличествоОстаток
|
|ИЗ
|
РегистрНакопления.ВнутренниеЗаказы.Остатки(&ДатаКонечная,
|
Номенклатура = &Номенклатура И
|
ВнутреннийЗаказ = &Заказ)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ВнутреннийЗаказ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Заказ", ВнутреннийЗаказСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Добавив в условие выборки ссылку на товар, мы смогли получить остаток
конкретной номенклатуры на текущую дату. В следующем примере мы получим зарезервированную под внутренний заказ номенклатуру, размещенную
на выбранном складе. Посмотрите пример, представленный в листинге 16.46.
Листинг 16.46. Получение остатков зарезервированной номенклатуры по складу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Глава 16. Использование запросов
197
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура КАК Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказы.Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
|
Заказы.СтатусПартии КАК СтатусПартии,
|
Заказы.ВнутреннийЗаказ КАК ВнутреннийЗаказ,
|
СУММА(Заказы.КоличествоОстаток) КАК КоличествоОстаток,
|
СУММА(ТоварыВРезерве.КоличествоОстаток) КАК КоличествоРезерв
|ИЗ
|
РегистрНакопления.ВнутренниеЗаказы.Остатки(&ДатаКонечная,
|
ВнутреннийЗаказ = &Заказ) КАК Заказы
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(&ДатаКонечная,
|
Склад = &Склад И
|
ДокументРезерва = &Заказ И
|
ДокументРезерва ССЫЛКА Документ.ВнутреннийЗаказ)
|
КАК ТоварыВРезерве
|
ПО Заказы.Номенклатура = ТоварыВРезерве.Номенклатура И
|
Заказы.ХарактеристикаНоменклатуры =
|
ТоварыВРезерве.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
Заказы.Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры,
|
Заказы.СтатусПартии,
|
Заказы.ВнутреннийЗаказ";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ВнутреннийЗаказСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В рассмотренном примере мы используем два регистра: ВнутренниеЗаказы и
ТоварыВРезервеНаСкладах. Из первого получаем текущий остаток по заказу,
а из второго остатки номенклатуры, зарезервированные на складе под выбранный внутренний заказ.
198
Часть II. Приемы программирования регистров накопления
Как мы уже говорили, при оформлении внутреннего заказа можно указать
размещение номенклатуры в существующем заказе поставщику. Построим
запрос для получения остатков номенклатуры, размещенной в таком заказе.
Пример кода показан в листинге 16.47.
Листинг 16.47. Получение остатков номенклатуры, размещенной в заказе
поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура КАК Номенклатура,
|
Заказы.Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаХранения,
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказы.ВнутреннийЗаказ,
|
Заказы.КоличествоОстаток КАК Заказано,
|
РазмещеноВЗаказахПоставщикам.КоличествоОстаток КАК
|
РазмещеноВЗаказеПоставщику
|ИЗ
|
РегистрНакопления.ВнутренниеЗаказы.Остатки(,
|
Номенклатура В (
|
ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура
|
ИЗ Документ.ВнутреннийЗаказ.Товары
|
ГДЕ Документ.ВнутреннийЗаказ.Товары.Ссылка =
|
&Заказ)) И
|
ВнутреннийЗаказ = &Заказ) КАК Заказы
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.РазмещениеЗаказовПокупателей.Остатки(,
|
Номенклатура В (
|
ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура
|
ИЗ Документ.ВнутреннийЗаказ.Товары
|
ГДЕ Документ.ВнутреннийЗаказ.Товары.Ссылка =
|
&Заказ) И
|
ВЫРАЗИТЬ(
|
ЗаказПокупателя КАК Документ.ВнутреннийЗаказ) = &Заказ)
|
КАК РазмещеноВЗаказахПоставщикам
|
ПО Заказы.Номенклатура = РазмещеноВЗаказахПоставщикам.Номенклатура
|
И Заказы.ХарактеристикаНоменклатуры =
Глава 16. Использование запросов
|
199
РазмещеноВЗаказахПоставщикам.ХарактеристикаНоменклатуры";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ВнутреннийЗаказСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Для решения задачи мы выполнили левое соединение двух регистров остатков. В регистре ВнутренниеЗаказы установили отбор записей по уникальной
номенклатуре (ключевое слово РАЗЛИЧНЫЕ) из товарной части внутреннего
заказа и ссылке на выбранный заказ. То же самое проделали со вторым регистром РазмещениеЗаказовПокупателей. В качестве условия соединения передали поля номенклатуры и характеристики. После выполнения запроса получили остатки номенклатуры по документу ВнутреннийЗаказ и количество
в заказе поставщику.
Поскольку все торговые операции так или иначе связаны с взаиморасчетами
между поставщиками и покупателями, поговорим еще немного о регистре
накопления ВзаиморасчетыСКонтрагентами. Он позволяет получить сумму
задолженности одного из участников операции перед другим в разрезе договоров взаиморасчетов. Данные договора между контрагентом и организацией
содержат различные льготы и ограничения. Например, проверенному контрагенту можно отгружать товар в долг под обещанные платежи. При этом по
договору контролируется сумма задолженности, при превышении которой
документ отгрузки нельзя будет провести. Предельные значения максимальной суммы задолженности в каждой организации устанавливаются исходя из
собственных расчетов. Кроме того, контроль задолженности может быть отключен вовсе. Получить сумму текущей задолженности по контрагенту можно так, как показано в листинге 16.48.
Листинг 16.48. Получение остатков номенклатуры, размещенной в заказе
поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Взаиморасчеты.ДоговорКонтрагента
КАК Договор,
|
Взаиморасчеты.Сделка
КАК Сделка,
|
Взаиморасчеты.СуммаВзаиморасчетовОстаток КАК СуммаОстаток,
|
Взаиморасчеты.ДоговорКонтрагента.ВалютаВзаиморасчетов,
200
Часть II. Приемы программирования регистров накопления
|
КурсыВалютСрезПоследних.Курс
КАК Курс,
|
КурсыВалютСрезПоследних.Кратность
КАК Кратность
|
|ИЗ
|
РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(
|
&ДатаКонечная, ДоговорКонтрагента.Организация = &Организация И
|
ДоговорКонтрагента.Владелец = &Контрагент) КАК Взаиморасчеты
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрСведений.КурсыВалют.СрезПоследних(&ДатаКонечная)
|
КАК КурсыВалют
|
ПО Взаиморасчеты.ДоговорКонтрагента.ВалютаВзаиморасчетов =
|
КурсыВалют.Валюта";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Организация", ОрганизацияСсылка);
Запрос.УстановитьПараметр("Контрагент", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В рассмотренном примере мы выбираем записи из регистра остатков на текущую дату по выбранной организации и контрагенту в разрезе договора
взаиморасчетов. Чтобы получить текущий курс валюты, подключаем регистр
сведений КурсыВалют посредством левого соединения. Объединяем оба регистра по валюте. Результатом запроса будет сумма взаимной задолженности
между организацией и контрагентом в разрезе договора.
Имеется возможность развернуть историю взаиморасчетов по документам
движения. Для этого следует написать запрос к регистру оборотов. Как это
сделать, показано в листинге 16.49.
Листинг 16.49. Получение истории взаиморасчетов по выбранному контрагенту
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Взаиморасчеты.ДоговорКонтрагента
КАК Договор,
|
Взаиморасчеты.Сделка
КАК Сделка,
|
Взаиморасчеты.ДоговорКонтрагента.ВалютаВзаиморасчетов,
Глава 16. Использование запросов
201
|
Взаиморасчеты.СуммаВзаиморасчетовПриход КАК СуммаПриход,
|
Взаиморасчеты.СуммаВзаиморасчетовРасход КАК СуммаРасход,
|
Взаиморасчеты.СуммаВзаиморасчетовОборот КАК СуммаОборот
|
|ИЗ
|
РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(
|
&ДатаНачальная, &ДатаКонечная, Регистратор,
|
ДоговорКонтрагента.Владелец = &Контрагент) КАК Взаиморасчеты";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Как видите, все достаточно просто. Задали период выборки, разворот данных
по регистратору и ссылку на выбранного контрагента. Чтобы получить сумму
взаиморасчетов по сделке, например, заказу покупателя, добавьте в условие
отбора по регистру ссылку на заказ, как это сделано в листинге 16.50.
Листинг 16.50. Получение истории взаиморасчетов по сделке
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Взаиморасчеты.ДоговорКонтрагента
КАК Договор,
|
Взаиморасчеты.Сделка
КАК ЗаказПокупателя,
|
Взаиморасчеты.ДоговорКонтрагента.ВалютаВзаиморасчетов,
|
Взаиморасчеты.СуммаВзаиморасчетовПриход КАК СуммаПриход,
|
Взаиморасчеты.СуммаВзаиморасчетовРасход КАК СуммаРасход,
|
Взаиморасчеты.СуммаВзаиморасчетовОборот КАК СуммаОборот
|
|ИЗ
|
РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(
|
&ДатаНачальная, &ДатаКонечная, Регистратор,
|
Сделка = &Заказ) КАК Взаиморасчеты";
// Устанавливаем параметры запроса
202
Часть II. Приемы программирования регистров накопления
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Таким же образом можно получить сумму взаиморасчетов по документам
поступления и реализации. По регистру взаиморасчетов, как уже было сказано, можно проконтролировать сумму задолженности контрагента. Например,
чтобы проверить превышение суммы долга, можно написать код, как в листинге 16.51.
Листинг 16.51. Получение списка контрагентов, имеющих задолженность
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка.Контрагент
КАК Контрагент,
|
СУММА(СуммаОстаток)
КАК СуммаДолга,
|
СУММА(СуммаВалОстаток) КАК СуммаДолгаВВалюте
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки
|
|ГДЕ
|
Сделка.Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная
|
|СГРУППИРОВАТЬ ПО
|
Сделка.Контрагент
|
|ИМЕЮЩИЕ
|
СУММА(СуммаОстаток) >= &ЛимитЗадолженности ИЛИ
|
СУММА(СуммаВалОстаток) >= &ЛимитЗадолженностиВВалюте";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("ЛимитЗадолженности", ЛимитЗадолженности);
Запрос.УстановитьПараметр("ЛимитЗадолженностиВВалюте",
Глава 16. Использование запросов
203
ЛимитЗадолженностиВВалюте);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Вначале мы выбрали из регистра остатков все записи по выбранному периоду и рассчитали суммы долга по каждому. Затем с помощью ключевого слова
ИМЕЮЩИЕ отобрали только тех контрагентов, у которых сумма долга превышает
заданный лимит. Для справки поясню, что слово ИМЕЮЩИЕ позволяет установить условия отбора по полям группировки. Чтобы получить сумму задолженности в разрезе документа (сделки), можно применить код, представленный в листинге 16.52.
Листинг 16.52. Получение задолженности контрагента в разрезе сделки
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка
КАК Сделка,
|
СУММА(СуммаОстаток)
КАК СуммаДолга,
|
СУММА(СуммаВалОстаток) КАК СуммаДолгаВВалюте
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки
|
|ГДЕ
|
Сделка.Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная
|
|СГРУППИРОВАТЬ ПО
|
Сделка
|
|ИМЕЮЩИЕ
|
СУММА(СуммаОстаток) >= &ЛимитЗадолженности ИЛИ
|
СУММА(СуммаВалОстаток) >= &ЛимитЗадолженностиВВалюте";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("ЛимитЗадолженности", ЛимитЗадолженности);
Запрос.УстановитьПараметр("ЛимитЗадолженностиВВалюте",
204
Часть II. Приемы программирования регистров накопления
ЛимитЗадолженностиВВалюте);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В данном случае мы получим сумму задолженности контрагента по сделке.
Для анализа задолженности контрагента за период в разрезе документов
движения можно написать запрос к регистру остатков и оборотов, как показано в листинге 16.53.
Листинг 16.53. Анализ задолженности контрагента за период
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка.Контрагент
КАК Контрагент,
|
Сделка
КАК Сделка,
|
ДоговорВзаиморасчетов
КАК Договор,
|
СуммаНачальныйОстаток
КАК НачальнаяСуммаДолга,
|
СуммаКонечныйОстаток
КАК ОстатокСуммыДолга,
|
СуммаПриход
КАК СуммаУменьшенияДолга,
|
СуммаРасход
КАК СуммаУвеличенияДолга,
|
СуммаОборот
КАК СуммаОбротов,
|
СуммаВалНачальныйОстаток КАК НачальнаяСуммаДолгаВВалюте,
|
СуммаВалКонечныйОстаток
КАК КонечнаяСуммаДолгаВВалюте,
|
СуммаВалПриход
КАК СуммаУменьшенияДолгаВВалюте,
|
СуммаВалРасход
КАК СуммаУвеличенияДолгаВВалюте,
|
СуммаВалОборот
КАК СуммаОборотовВВалюте
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.ОстаткиИОбороты
|
(&ДатаНачальная, &ДатаКонечная, Регистратор, Движения,
|
Сделка.Контрагент = &Контрагент)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Контрагент", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 16. Использование запросов
205
Воспользовавшись регистром остатков и оборотов, мы получили все движения по выбранному контрагенту за период с момента формирования задолженности (с учетом периода выборки) и до текущего момента.
Кроме рассмотренных примеров использования регистра взаиморасчетов,
можно еще узнать, присутствуют ли в регистре ссылки на определенные документы или договора. Например, чтобы убедиться в движении выбранного
документа ПриходныйКассовыйОрдер с определенным контрагентом, следует сделать запрос, как показано в листинге 16.54.
Листинг 16.54. Проверка движения документа по регистру
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ ПЕРВЫЕ 1
|
Период
|
Регистратор.Плательщик КАК Контрагент,
КАК Период,
|
Регистратор
КАК ДокументДвижения
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании
|
|ГДЕ
|
Регистратор = &Регистратор И
|
Регистратор.Плательщик = &Плательщик И
|
(Регистратор ССЫЛКА Документ.ПриходныйКассовыйОрдер)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Регистратор", ПриходныйКассовыйордерСсылка);
Запрос.УстановитьПараметр("Плательщик", КонтрагентСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос выбирает только одну запись (ключевое слово ПЕРВЫЕ) из регистра,
которая удовлетворяет условиям выборки. В качестве условий мы передаем
ссылку на документ движения и контрагента. Для выполнения проверки по
нескольким регистрам имеет смысл сделать объединение запросов.
Следующий пример демонстрирует проверку наличия движений номенклатуры по регистрам накопления (листинг 16.55).
206
Часть II. Приемы программирования регистров накопления
Листинг 16.55. Проверка движения номенклатуры по регистрам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ВнутренниеЗаказы.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ЗаказыПокупателей.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ЗаказыПоставщикам.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.Закупки.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ПартииТоваровНаСкладах.Номенклатура
|ГДЕ
|
|
Номенклатура = &Номенклатура
Глава 16. Использование запросов
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.Продажи.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ПродажиСебестоимость.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.РазмещениеЗаказовПокупателей.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ТоварыВрезервеНаСкладах.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ТоварыКПередачеОрганизаций.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ТоварыКПередачеСоСкладов.Номенклатура
207
208
Часть II. Приемы программирования регистров накопления
|ГДЕ
|
Номенклатура = &Номенклатура
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ТоварыНаСкладах.Номенклатура
|ГДЕ
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ ПЕРВЫЕ 1
|
РегистрНакопления.ТоварыКПолучениюНаСклады.Номенклатура
|ГДЕ
|
Номенклатура = &Номенклатура";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
// Выполняем запрос
Если Запрос.Выполнить().Пустой();
Сообщить("Движений номенклатуры " + НоменклатураСсылка +
"не найдено!");
КонецЕсли;
Запрос проверяет наличие ссылок на выбранную номенклатуру по всем потенциальным регистрам накопления. Чтобы проверить движения по определенному складу, воспользуйтесь кодом, представленным в листинге 16.56.
Листинг 16.56. Проверка движения по складу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
|
Регистратор КАК ДокументДвижения
|ИЗ
|
РегистрНакопления.ТоварыВРезервеНаСкладах
|ГДЕ
|
Склад = &Склад";
Глава 16. Использование запросов
209
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим все документы из регистра, по
которым выполнял движения выбранный склад. Чтобы проверить движения
по другим регистрам, просто укажите в запросе требуемое имя, например,
как это сделано в листинге 16.57.
Листинг 16.57. Проверка движения по складу через выбор регистра накопления
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
|
Регистратор КАК ДокументДвижения
|ИЗ
|
РегистрНакопления." + ИмяРегистра + "
|ГДЕ
|
Склад = &Склад";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Склад", СкладСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Как вы догадались, в переменную ИмяРегистра следует записать наименование нужного регистра накопления.
Практически на любом предприятии существует система выделения наличных денежных средств сотрудникам (подотчетным лицам) для различных
нужд, связанных с рабочим процессом: командировочные расходы, закупка
товаров, оплата услуг, оплата товаров поставщику, расходы на проезд, расходы на бензин и прочее. Для учета этих операций предназначен документ
АвансовыйОтчет. Учет ведется в валюте сделки и валюте взаиморасчетов
подотчетного лица. На его основании могут формироваться документы поступления. Все движения, связанные с этим документом, отражаются в регистре ПодотчетныеЛицаВзаиморасчетыКомпании. Построим запрос, позволяющий получить текущие остатки по сумме взаиморасчетов подотчетного
лица и организации (листинг 16.58).
210
Часть II. Приемы программирования регистров накопления
Листинг 16.58. Получение остатков по взаиморасчетам с подотчетным
лицом
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ФизЛицо КАК ПодотчетноеЛицо,
|
Валюта КАК Валюта,
|
ДокументОтчета,
|
СУММА(СуммаОстаток) КАК СуммаОстаток,
|
СУММА(СуммаВалОстаток) КАК СуммаВалОстаток
|ИЗ
|
РегистрНакопления.ПодотчетныеЛицаВзаиморасчетыКомпании.Остатки(
|
&ДатаКонечная, ФизЛицо = &ПодотчетноеЛицо)
|
|СГРУППИРОВАТЬ ПО
|
ФизЛицо,
|
Валюта,
|
ДокументОтчета
|
|ИТОГИ СУММА(СуммаОстаток), СУММА(СуммаВалОстаток) ПО
|
ФизЛицо,
|
Валюта";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("ПодотчетноеЛицо", ПодотчетноеЛицоСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим рассчитанные итоги по взаиморасчетам подотчетного лица и организации на текущую дату. С документом АвансовыйОтчет тесно связан другой документ — РасходныйКассовыйордер, поскольку с его помощью оформляется выдача денежных средств
подотчетному лицу. Чтобы получить сумму выданного сотруднику аванса,
можно написать код, как показано в листинге 16.59.
Глава 16. Использование запросов
211
Листинг 16.59. Получение остатка суммы аванса по выбранной валюте
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ДокументОтчета
КАК РасходныйОрдер,
|
СуммаВалОстаток КАК СуммаДенежныхСредствВВалюте
|ИЗ
|
РегистрНакопления.ПодотчетныеЛицаВзаиморасчетыКомпании.Остатки(,
|
ДокументОтчета ССЫЛКА Документ.РасходныйКассовыйОрдер И
|
ФизЛицо
|
Валюта = &Валюта)";
= &ПодотчетноеЛицо И
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Валюта", ВалютаСсылка);
Запрос.УстановитьПараметр("ПодотчетноеЛицо", ПодотчетноеЛицоСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом запроса будет сумма, выданная подотчетному лицу по расходному кассовому ордеру.
А теперь кратко остановимся на регистре, который учитывает суммы сделок
по заказам покупателей и поставщиков. Называется он СуммыЗаказов и имеет всего одно измерение (ссылка на заказ) и два ресурса: сумма оплаты
и сумма заказа. По нему можно получить сумму оборотов в разрезе заказов
и сумму оплаты по заказам. Например, чтобы определить сумму оплаты и
документы движения по заказу покупателя, выполните код, представленный
в листинге 16.60.
Листинг 16.60. Получение суммы оплаты по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Регистратор
КАК ДокументДвижения,
212
|
Часть II. Приемы программирования регистров накопления
СУММА(СуммаОплаты) КАК СуммаОплаты
|ИЗ
|
РегистрНакопления.СуммыЗаказов
|
|ГДЕ
|
(Заказ ССЫЛКА Документ.ЗаказПокупателя) И
|
Заказ = &Заказ
|
|СГРУППИРОВАТЬ ПО
|
Период,
|
Регистратор
|
|УПОРЯДОЧИТЬ ПО
|
Период";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Заказ", ЗаказПокупателяСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом запроса будет сумма оплаты заказа покупателя по документам
движения. Для получения оборотов по заказу поставщику в разрезе документов движения обратимся к оборотному регистру, как показано в листинге 16.61.
Листинг 16.61. Получение оборотов по заказу поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Регистратор
КАК ДокументДвижения,
|
СуммаЗаказаОборот
КАК ОборотыПоЗаказу,
|
СуммаОплатыОборот
КАК ОборотыПоОплате,
|
СуммаЗаказаОборот – СуммаОплатыОборот КАК НепроплаченнаяСумма
|ИЗ
|
РегистрНакопления.СуммыЗаказов.Обороты(
|
&ДатаНачальная, &ДатаКонечная, Регистратор, Заказ = &Заказ)";
Глава 16. Использование запросов
213
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("Заказ", ЗаказПоставщикуСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
По результатам запроса мы получили сумму оборотов и сумму оплаты по выбранному заказу, а также неоплаченный остаток.
Соединив регистр СуммаЗаказов с регистром ОстаткиТоваровКомпании,
можно получить заказы с зарезервированными на складах товарами, по которым не поступила оплата (предоплата). Как это сделать, смотрите в листинге 16.62.
Листинг 16.62. Выборка неоплаченных заказов покупателей
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ РАЗЛИЧНЫЕ
|
Остатки.Заказ
|
Остатки.Заказ.ДоговорВзаиморасчетов.ЧислоДнейРезерваБезОплаты
|
КАК ЗаказПокупателя,
КАК ДержатьРезервБезОплатыДней,
|
Остатки.Заказ.Дата
КАК Дата,
|
Остатки.Заказ.ДатаОплаты
КАК ДатаОплаты,
|
Остатки.Заказ.Отгрузки
КАК ДатаОтгрузки,
|
СуммаЗаказов.СуммаОплатыОборот КАК СуммаОплаты
|ИЗ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки(
|
&Период, ) КАК Остатки
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.СуммыЗаказов.Обороты(,
|
&Период) КАК СуммаЗаказов
|ПО
|
Остатки.Заказ = СуммаЗаказов.Заказ
|
|ГДЕ
|Остатки.Заказ.ДоговорВзаиморасчетов.ДержатьРезервБезОплатыОграниченноеВремя";
// Устанавливаем параметры запроса
214
Часть II. Приемы программирования регистров накопления
Запрос.УстановитьПараметр("Период", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим заказы покупателей, по которым не внесена оплата (предоплата) и в договоре взаиморасчетов установлен
признак ограничения времени резервирования товаров для закупщика.
Как правило, наибольшая эффективность запроса достигается при соединении (объединении) нескольких регистров между собой. Это позволяет получить подробную и разностороннюю информацию, отвечающую требованиям
заказчиков. Мы уже рассматривали похожие запросы, где фигурировали регистры и документы одновременно. Несмотря на это, хочу привести классический пример запроса, позволяющего получить наиболее полную информацию по документам ЗаказПокупателя в разрезе номенклатуры: резервы,
остатки на складе, свободные остатки, ожидаемые поставки по заказу поставщику. Все эти данные можно взять по отдельно взятым регистрам, но
гораздо лучше будет прочитать их одним-единственным запросом. Работая
в системе 1С, вы поймете, что построить хороший и полномасштабный отчет
можно только таким образом. Посмотрите пример, представленный в листинге 16.63.
Листинг 16.63. Выборка неоплаченных заказов покупателей
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказы.Номенклатура
|
Заказы.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
Заказы.ЗаказПокупателя
КАК Заказ,
|
Заказы.ДоговорВзаиморасчетов
КАК Договор,
|
СУММА(Заказы.КоличествоОстаток)
КАК КоличествоЗаказано,
|
СУММА(Свободно.КоличествоОстаток) КАК КоличествоСвободно,
|
СУММА(ВЫБОР
|
КОГДА ЗарезервированоНаСкладе.СкладКомпании = &Склад
|
ТОГДА ЗарезервированоНаСкладе.КоличествоОстаток
|
|
ИНАЧЕ 0
КОНЕЦ) КАК РезервНаСкладе,
Глава 16. Использование запросов
|
215
СУММА(ВЫБОР
|
КОГДА (Ожидается.КоличествоОстаток) ЕСТЬ NULL
|
ТОГДА 0
ИНАЧЕ Ожидается.КоличествоОстаток
|
КОНЕЦ) КАК КоличествоОжидается,
|
СУММА(ВЫБОР
|
КОГДА (Зарезервировано.КоличествоОстаток) ЕСТЬ NULL
|
ТОГДА 0
|
|
ИНАЧЕ Зарезервировано.КоличествоОстаток
КОНЕЦ) КАК РезервВсего
|
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(,
|
ЗаказПокупателя ССЫЛКА Документ.ЗаказПокупателя) КАК Заказы
|
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки(,
|
|
НЕ(Заказ = НЕОПРЕДЕЛЕНО)) КАК ЗарезервированоНаСкладе
ПО
|
ЗарезервированоНаСкладе.ХарактеристикаНоменклатуры =
|
Заказы.ХарактеристикаНоменклатуры И
|
ЗарезервированоНаСкладе.Номенклатура = Заказы.Номенклатура
|
И ЗарезервированоНаСкладе.Заказ = Заказы.ЗаказПокупателя
|
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки(,
|
|
НЕ(Заказ = НЕОПРЕДЕЛЕНО)) КАК Зарезервировано
ПО
|
Зарезервировано.ХарактеристикаНоменклатуры =
|
Заказы.ХарактеристикаНоменклатуры И
|
Зарезервировано.Номенклатура = Заказы.Номенклатура И
|
Зарезервировано.Заказ = Заказы.ЗаказПокупателя
|
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки(,
|
Заказ = НЕОПРЕДЕЛЕНО И СкладКомпании = &Склад) КАК Свободно
|
ПО
|
Свободно.ХарактеристикаНоменклатуры =
|
Заказы.ХарактеристикаНоменклатуры И
|
Свободно.Номенклатура = Заказы.Номенклатура
216
Часть II. Приемы программирования регистров накопления
|
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(,
|
НЕ(ЗаказПокупателя = НЕОПРЕДЕЛЕНО)) КАК Ожидается
|
ПО Ожидается.ХарактеристикаНоменклатуры =
|
Заказы.ХарактеристикаНоменклатуры И
|
Ожидается.Номенклатура = Заказы.Номенклатура И
|
Ожидается.ЗаказПокупателя = Заказы.ЗаказПокупателя
|
|ГДЕ
|
Заказы.ЗаказПокупателя.ДатаОтгрузки <= &ПредельнаяДатаОтгрузки
|
|СГРУППИРОВАТЬ ПО
|
Заказы.Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры,
|
Заказы.ЗаказПокупателя,
|
Заказы.ДоговорВзаиморасчетов,
|
|
ИМЕЮЩИЕ
|
СУММА(
|
ВЫБОР
|
КОГДА ЗарезервированоНаСкладе.СкладКомпании =
&Склад
|
ТОГДА ЗарезервированоНаСкладе.КоличествоОстаток
|
ИНАЧЕ 0
|
КОНЕЦ) > 0
|
|
УПОРЯДОЧИТЬ ПО
|
Заказы.Номенклатура.Наименование,
|
Заказы.ХарактеристикаНоменклатуры.Наименование
|
|
ИТОГИ ПО
|
Заказы.Номенклатура,
|
Заказы.ХарактеристикаНоменклатуры,
|
Заказы.ЗаказПокупателя";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("Склад", СкладСсылка);
Запрос.УстановитьПараметр("ПредельнаяДатаОтгрузки", ЛимитДатаОтгрузки);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 16. Использование запросов
217
В качестве основного мы выбрали регистр остатков ЗаказыПокупателей.
По нему мы рассчитали заказанное количество номенклатуры и прочитали
сведения о самом заказе. Затем посредством левого соединения добавили регистр ОстаткиТоваровКомпании, чтобы вычислить зарезервированные остатки номенклатуры в разрезе заказа покупателя. Следующим соединением получили общее количество зарезервированной номенклатуры по выбранному
складу. Далее выбрали свободные остатки и, наконец, ожидаемое количество
по заказам поставщикам. Кроме того, мы поставили условие отбора по дате
отгрузки (все заказы, у которых дата отгрузки меньше установленного предела) и наложили условие на поля группировки (остатки должны быть положительными). Как видите, запрос получился большим и трудным для восприятия. Это не значит, что он будет медленно работать, он лишь затруднит
внесение корректировок. Поэтому рекомендую, хотя бы на этапе изучения,
сложные запросы строить и проверять с помощью конструктора запросов.
Не секрет, что многие объекты в системе 1С поддерживают иерархию.
В первую очередь это относится к справочникам. С помощью иерархии организуется хорошо структурированное представление данных, удобное для
восприятия. Запросы позволяют использовать иерархию (по итоговым полям)
при обращении к соответствующим данным. Рассмотрим пример, демонстрирующий формирование запроса по иерархии (листинг 16.64).
Листинг 16.64. Использование иерархии в запросах
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
Номенклатура.Представление КАК Представление,
КАК Номенклатура,
|
КоличествоПриход
КАК КоличествоПриход
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Обороты
|
|ИТОГИ СУММА(КоличествоПриход) ПО
|
Номенклатура ИЕРАРХИЯ";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
218
Часть II. Приемы программирования регистров накопления
В приведенном примере мы рассчитали итоги по заказанной номенклатуре.
Результатом запроса будут итоговые записи по каждой номенклатуре плюс
итоги по каждой группе номенклатуры в соответствии со структурой справочника. Для указания расчета по иерархии мы воспользовались ключевым
словом ИЕРАРХИЯ. Если нужны итоги только по группам номенклатуры, следует составить запрос так, как это сделано в листинге 16.65.
Листинг 16.65. Получение итогов по группам номенклатуры
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
Номенклатура.Представление КАК Представление,
КАК Номенклатура,
|
КоличествоПриход
КАК КоличествоПриход
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Обороты
|
|ИТОГИ СУММА(КоличествоПриход) ПО
|
Номенклатура ТОЛЬКО ИЕРАРХИЯ";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом запроса будут итоги по группам номенклатуры. Ключевое слово
ИЕРАРХИЯ может быть использовано и в условии запроса для передачи ссылки
на группу, а не на отдельный товар. Посмотрите пример, представленный
в листинге 16.66.
Листинг 16.66. Получение номенклатурной группы из запроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
Номенклатура.Представление КАК Представление,
Глава 16. Использование запросов
219
|
КоличествоПриход
КАК КоличествоПриход,
|
КоличествоРасход
КАК КоличествоРасход,
|
КоличествоОборот
КАК КоличествоОборот
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Обороты
|
|ГДЕ
|
ЗаказыПоставщикамОбороты.Номенклатура.Ссылка
|
В ИЕРАРХИИ (&ГруппаНоменклатуры)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ГруппаНоменклатуры", ГруппаСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В данном запросе в качестве параметра мы передали ссылку на номенклатурную группу. Результат запроса возвратит переданную группу и все подчиненные группы с элементами из справочника номенклатуры.
В завершение данной главы мы поговорим о том, как из результата запроса
заполнить сводную таблицу и диаграмму, а также познакомимся с записью
значений регистров накопления без проведения документов.
Сводная таблица позволяет представлять сложные структуры данных в разрезе измерений регистров. Перед применением сводную таблицу следует
разместить в виде встроенной таблицы табличного документа. Для выборки
результата запроса в сводную таблицу необходимо назначить свойству
ИсточникДанных ссылку на полученную выборку. Рассмотрим пример, приведенный в листинге 16.67.
Листинг 16.67. Формирование сводной таблицы на основании запроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СкладКомпании КАК СкладКомпании,
|
Номенклатура
КАК Номенклатура,
|
Количество
КАК Количество
|ИЗ
|
РегистрНакопления.ОстаткиТоваровКомпании
220
Часть II. Приемы программирования регистров накопления
|
|ИТОГИ СУММА(Количество) ПО
|
Номенклатура ИЕРАРХИЯ,
|
СкладКомпании ИЕРАРХИЯ";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Заполняем существующую сводную таблицу данными запроса
СводнаяТаблицаОстаткиТоваров.ИсточникДанных = ДанныеВыборки;
// Отображаем окна и поля таблицы
СводнаяТаблицаОстаткиТоваров.ОтображатьПоля = Истина;
// Запрещаем обновление
СводнаяТаблицаОстаткиТоваров.Обновление = Ложь;
// Добавляем колонки
СводнаяТаблицаОстаткиТоваров.Колонки.Добавить(
СводнаяТаблицаОстаткиТоваров.Поля.СкладКомпании;
// Добавляем строки
СводнаяТаблицаОстаткиТоваров.Строки.Добавить(
СводнаяТаблицаОстаткиТоваров.Поля.Номенклатура;
// Добавляем данные
СводнаяТаблицаОстаткиТоваров.Данные.Добавить(
СводнаяТаблицаОстаткиТоваров.Поля.Количество;
// Разрешаем обновление
СводнаяТаблицаОстаткиТоваров.Обновление = Истина;
В этом примере мы продемонстрировали способ заполнения сводной таблицы из результата запроса. После получения выборки назначили источник
данных, а затем добавили необходимые колонки, строки и данные. Вид созданной таблицы отражает количество номенклатуры по всем складам компании. Сводная таблица имеет много свойств и методов программного управления, изучение которых выходит за рамки данной книги.
Следующий элемент визуального представления запросов, о котором мы поговорим, это диаграмма. Главным достоинством ее является графическое
отображение данных, что намного улучшает восприятие результатов отчетов
и всевозможных аналитик. Основными составляющими диаграммы в 1С
служат точка (положение на оси абсцисс для установки значений серии)
и серия (последовательный набор значений по измерению регистра). Для
формирования диаграммы из запроса предназначено свойство ИсточникДанных,
в которое передается результат выборки. Рассмотрим пример программного
заполнения диаграммы, представленный в листинге 16.68.
Глава 16. Использование запросов
221
Листинг 16.68. Формирование диаграммы на основании запроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СкладКомпании КАК СкладКомпании,
|
Номенклатура КАК Номенклатура,
|
Количество
КАК Количество
|ИЗ
|
РегистрНакопления.ОстаткиТоваровКомпании";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
// Заполняем существующую сводную таблицу данными запроса
ДиаграммаОстаткиТоваров.ИсточникДанных = ДанныеВыборки;
// Запрещаем обновление
ДиаграммаОстаткиТоваров.Обновление = Ложь;
// Заполняем диаграмму данными из запроса
Пока ДанныеВыборка.Следующий() Цикл
Серия =
ДиаграммаОстаткиТоваров.УстановитьСерию(ДанныеВыборка.Номенклатура);
Точка =
ДиаграммаОстаткиТоваров.УстановитьТочку(ДанныеВыборка.СкладКомпании);
ДиаграммаОстаткиТоваров.УстановитьЗначение(
Точка, Серия, ДанныеВыборка.Количество);
КонецЦикла;
// Разрешаем обновление
ДиаграммаОстаткиТоваров.Обновление = Истина;
Как видите, построить диаграмму по запросу достаточно просто. Вначале
назначаем источнику данных результат выборки. Затем в цикле формируем
точки и серии, добавляя для каждой отметки значение ресурса регистра накопления. Более подробную информацию по свойствам и методам объекта
диаграммы можно получить во встроенной справке 1С.
И последнее, с чем мы познакомимся — это изменение значений реквизитов
регистра накопления. Данный способ подразумевает корректировку полей
записи без проведения документа. Главным условием при таком методе является сохранение ссылки на документ. Как вы уже знаете, запись (обновление)
222
Часть II. Приемы программирования регистров накопления
данных производится через набор записей. Рассмотрим пример обновления
значений реквизитов в записях регистра накопления в листинге 16.69.
Листинг 16.69. Изменение значений реквизитов в записях регистра
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Регистратор
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей
|
|ГДЕ
|
ЗаказыПокупателей.ЗаказПокупателя.Дата МЕЖДУ
|
&ДатаНачальная И &ДатаКонечная";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаНачальная", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
// Обрабатываем записи
Пока ДанныеВыборка.Следующий() Цикл
Записи =
РегистрыНакопления.ЗаказыПокупателей.СоздатьНаборЗаписей();
Записи.Отбор.Регистратор.Значение = ДанныеВыборки.Регистратор;
Записи.Прочитать();
Для Каждого Запись Из Записи Цикл
Запись.ПодразделениеКомпании = НовоеПодразделение;
КонецЦикла;
Записи.Записать();
КонецЦикла;
В приведенном примере мы сформировали запрос по регистру ЗаказыПокупателей, получив все записи за текущий год. После этого создали пустой набор
записей и, установив отбор по регистратору, прочитали в него записи регистра.
Затем заменили в каждой записи ссылку на подразделение компании и сохранили результат обратно в регистр. Как видите, все достаточно просто.
На этом мы завершим тему программирования запросов по регистрам накопления и перейдем к следующему типу регистров — сведений.
Часть III
Приемы программирования
регистров сведений
224
Часть I. Приемы программирования документов
Глава 17
Получение выборки
В этой главе мы обсудим вопросы программирования регистров сведений.
Они предназначены для хранения различной постоянной и развернутой (по
измерениям) во времени информации. Отсюда вытекает, что существует два
типа таких регистров: периодический (присутствует измерение Период) и
непериодический. Для первого типа в конфигураторе можно задавать периодичность от секунды до года. Наиболее известные из регистров — КурсыВалют, ЦеныКомпании. Регистры второго типа хранят постоянные данные, необходимые другим объектам конфигурации. К ним, в первую очередь,
относятся ЗначенияСвойствОбъектов, КатегорииОбъектов, КомплектующиеНоменклатуры, НазначенияСвойствОбъектов и другие.
Регистр сведений состоит из измерений, ресурсов и реквизитов. Измерения
и реквизиты задают описание хранимой информации, а ресурсы, соответственно, значения. Добавление данных в регистр сведений возможно как с помощью документов, так и вручную. Это зависит от установки в конфигураторе режима записи (Независимый или Посредством регистраторов). Для
работы с регистрами сведений предусмотрены следующие базовые объекты:
РегистрСведенийМенеджер — управляет поиском, выборкой и созданием
записей регистра;
РегистрСведенийВыборка — позволяет получать и обрабатывать записи
регистра;
РегистрСведенийЗапись — предоставляет доступ к записям регистра;
РегистрСведенийНаборЗаписей — управляет получением, удалением и
записью в регистр набора записей;
РегистрСведенийСписок — управляет отображением записей регистра
в табличном поле.
226
Часть III. Приемы программирования регистров сведений
В этой главе мы поговорим о том, как программно осуществить выборку
записей из регистров сведений для последующего анализа и обработки. Базовый объект РегистрСведенийМенеджер предоставляет несколько методов,
позволяющих считывать записи регистра из базы данных. В первую очередь,
это метод Выбрать. Имеется две разновидности данного метода — для периодических и непериодических регистров.
При обращении к периодическому регистру можно передавать четыре необязательных параметра: начало и конец периода, условие отбора и способ упорядочивания записей. Рассмотрим пример использования периодического
регистра, представленный в листинге 17.1.
Листинг 17.1. Выборка данных из периодического регистра сведений
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.Выбрать();
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные.Период);
// Получаем валюту
Сообщить("Валюта: " + ВыборкаДанные.Валюта);
// Получаем полное наименование валюты
Сообщить("Полное название: " + ВыборкаДанные.НаименованиеПолное);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные.Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные.Курс);
КонецЦикла;
Итак, разберем подробнее приведенный пример. Вначале мы получили базовый объект РегистрСведенийМенеджер. Далее посредством метода Выбрать
сделали запрос по регистру КурсыВалют. В результате мы получили все
имеющиеся записи, независимо от периода и типа валюты. С помощью цикла
организовали последовательный перебор всех записей и вывели в окно сообщений доступные измерения и ресурсы регистра.
Поскольку регистр является периодическим, попробуем выбрать курсы валют за текущий месяц. Посмотрите, как это сделано в листинге 17.2.
Глава 17. Получение выборки
227
Листинг 17.2. Получение курсов валют за текущий месяц
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Устанавливаем период выборки
ДатаНачальная = НачалоМесяца(ТекущаяДата());
ДатаКонечная = ТекущаяДата();
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.Выбрать(ДатаНачальная, ДатаКонечная);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные.Период);
// Получаем валюту
Сообщить("Валюта: " + ВыборкаДанные.Валюта);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные.Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные.Курс);
КонецЦикла;
В примере мы установили период выборки, передав начальную и конечную
даты в метод Выбрать. Кроме даты, можно задавать момент времени (объект
МоментВремени) и границу интервала (объект Граница). В результате выполнения запроса мы получим все курсы по всем валютам за текущий месяц.
Согласитесь, что на практике требуется узнать курс по какой-то одной валюте, поэтому воспользуемся третьим параметром метода Выбрать для установки отбора по валюте. Посмотрите пример из листинга 17.3.
Листинг 17.3. Получение курсов по выбранной валюте
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Делаем отбор по выбранной валюте
ВалютаОтбор = Новый Структура("Валюта");
// Передаем ссылку на выбранную валюту
ВалютаОтбор.Валюта = ВыбраннаяВалютаСсылка;
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.Выбрать(, , ВалютаОтбор);
228
Часть III. Приемы программирования регистров сведений
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные.Период);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные.Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные.Курс);
КонецЦикла;
В качестве отбора мы сформировали и передали структуру, содержащую
один элемент. В результате запрос нам вернул все доступные курсы по выбранной валюте. Но и такой вариант может нас не устроить, когда нужно получить сразу самое последнее значение курса. Для решения этой проблемы
следует дополнительно задать упорядочивание по периоду в порядке убывания. Как это сделать, показано в листинге 17.4.
Листинг 17.4. Получение курсов валют с упорядочиванием по периоду
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Делаем отбор по выбранной валюте
ВалютаОтбор = Новый Структура("Валюта");
// Передаем ссылку на выбранную валюту
ВалютаОтбор.Валюта = ВыбраннаяВалютаСсылка;
// Задаем сортировку
СтрокаПорядок = "Период Убыв";
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.Выбрать(, , ВалютаОтбор,
СтрокаПорядок);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные.Период);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные.Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные.Курс);
КонецЦикла;
Глава 17. Получение выборки
229
Для сортировки по периоду мы передали в четвертый параметр метода
строку описания. Однако и в этом случае в результате запроса присутствуют
записи, которые нам не нужны — это набор записей предыдущих значений
курсов. Решить задачу возможно, но потребуется применить другой метод,
который называется ПолучитьПоследнее. С его помощью можно вывести самое последнее значение курса на указанную дату. Он содержит два параметра: конечную дату выборки и условие отбора. Посмотрите пример работы
с данным методом, представленный в листинге 17.5.
Листинг 17.5. Получение курса валюты на выбранную дату
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Делаем отбор по выбранной валюте
ВалютаОтбор = Новый Структура("Валюта");
// Передаем ссылку на выбранную валюту
ВалютаОтбор.Валюта = ВыбраннаяВалютаСсылка;
// Делаем выборку
ТекущийКурс = РегистрКурсыВалют.ПолучитьПоследнее(ТекущаяДата(),
ВалютаОтбор);
// Выводим значение курса в окно сообщений
Сообщить("Текущий курс: " + ТекущийКурс.Курс);
Как видите, нам удалось получить актуальное значение курса валюты на текущую дату. В противоположность методу ПолучитьПоследнее, существует
метод ПолучитьПервое, позволяющий прочитать из регистра самую первую
запись на указанную дату. Поскольку он ничем больше не отличается от рассмотренного метода, вы сможете самостоятельно разобраться в его работе.
А теперь поговорим о непериодических регистрах сведений и получении выборки для них. Как уже отмечалось, для чтения записей из такого регистра
можно применять метод Выбрать. При этом будут доступны только два
необязательных параметра: условия отбора и вид сортировки. Посмотрите
пример, представленный в листинге 17.6.
Листинг 17.6. Выборка данных из непериодического регистра сведений
// Задаем регистр сведений
РегистрКомплектующие = РегистрыСведений.КомплектующиеНоменклатуры;
// Делаем отбор по выбранной валюте
НоменклатураОтбор = Новый Структура("Номенклатура");
230
Часть III. Приемы программирования регистров сведений
// Передаем ссылку на выбранную валюту
НоменклатураОтбор.Номенклатура = ВыбраннаяНоменклатураСсылка;
// Задаем сортировку
СтрокаПорядок = "Номенклатура";
// Делаем выборку
ВыборкаДанные = РегистрКомплектующие.Выбрать(НоменклатураОтбор,
СтрокаПорядок);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Номенклатура
Сообщить("Номенклатура: " + ВыборкаДанные.Номенклатура);
// Получаем характеристику номенклатуры
Сообщить("Характеристика: " +
ВыборкаДанные.ХарактеристикаНоменклатуры);
// Получаем единицу измерения
Сообщить("Единица измерения: " + ВыборкаДанные.ЕдиницаИзмерения);
// Получаем долю стоимости номенклатуры в комплекте
Сообщить("Доля стоимости: " + ВыборкаДанные.ДоляСтоимости);
// Получаем количество
Сообщить("Количество: " + ВыборкаДанные.Количество);
КонецЦикла;
В примере мы выполнили запрос к непериодическому регистру сведений
КомплектующиеНоменклатуры. Данный регистр хранит сведения о номенклатуре, входящей в комплекты. В качестве условия выбрали отбор по ссылке
на указанную номенклатуру, а также установили сортировку записей по товарам в порядке возрастания.
В практически любой стандартной конфигурации существует регистр сведений ЗначенияСвойствОбъектов, который хранит всевозможные значения
свойств для справочников и документов. Регистр состоит из двух измерений
(объект и свойство) и ресурса (значение). Объектом может быть ссылка на
справочник или документ. Свойство задается из объекта СвойстваОбъектов
(относится к прикладному объекту ПланыВидовХарактеристик). Попробуем,
например, получить все свойства и их значения для документа ЗаказПокупателя (листинг 17.7).
Листинг 17.7. Получение значений свойств для документа ЗаказПокупателя
// Задаем регистр сведений
РегистрСвойства = РегистрыСведений.ЗначенияСвойствОбъектов;
// Делаем отбор
Глава 17. Получение выборки
231
ОбъектОтбор = Новый Структура("Объект");
// Передаем ссылку на заказ
ОбъектОтбор.Объект = ЗаказПокупателяСсылка;
// Задаем сортировку
СтрокаПорядок = "Свойство";
// Делаем выборку
ВыборкаДанные = РегистрСвойства.Выбрать(ОбъектОтбор, СтрокаПорядок);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Читаем свойство
Сообщить("Свойство: " + ВыборкаДанные.Свойство);
// Читаем значение свойства
Сообщить("Значение: " + ВыборкаДанные.Значение);
КонецЦикла;
Для получения списка свойств по документу мы установили отбор по ссылке
на выбранный заказ. В результате выполнения запрос возвратил нам все доступные свойства и их значения по заказу покупателя.
Следующий метод, позволяющий прочитать записи регистра сведений, называется ВыбратьПоРегистратору. Его следует применять только для тех регистров, у которых в конфигураторе задан режим записи по регистратору. Метод имеет один обязательный параметр — ссылку на документ, являющийся
регистратором. Посмотрите пример, представленный в листинге 17.8.
Листинг 17.8. Выборка записей из регистра сведений с помощью метода
ВыбратьПоРегистратору
// Задаем регистр сведений
РегистрЦеныКомпании = РегистрыСведений.ЦеныКомпании;
// Задаем регистратор
ДокументСсылка = ИзменениеЦенКомпанииСсылка;
// Делаем выборку
ВыборкаДанные =
РегистрЦеныКомпании.ВыбратьПоРегистратору(ДокументСсылка);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Дата документа
Сообщить("Дата документа: " + ВыборкаДанные.Регистратор.Дата);
// Номер документа
232
Часть III. Приемы программирования регистров сведений
Сообщить("Номер документа: " + ВыборкаДанные.Регистратор.Номер);
// Номенклатура
Сообщить("Номенклатура: " + ВыборкаДанные.Номенклатура);
// Единица измерения
Сообщить("Единица измерения: " + ВыборкаДанные.ЕдиницаИзмерения);
// Валюта
Сообщить("Валюта: " + ВыборкаДанные.Валюта.Наименование);
// Процент скидки или наценки
Сообщить("Процент: " + ВыборкаДанные.ПроцентСкидкиНаценки);
// Цена
Сообщить("Цена: " + ВыборкаДанные.Цена);
КонецЦикла;
Передав ссылку на документ, мы получили все записи регистра, по которым
он делал движения.
И последний метод, с которым мы познакомимся, называется Получить.
В отличие от всех предыдущих, он дает возможность прочитать только значения ресурсов регистра сведений. Существуют два варианта применения
метода, для периодических и непериодических регистров. В первом случае
задаются два параметра: период (обязательный) и условие отбора (обязателен
для регистров, имеющих измерения). Во втором — только условие отбора.
Для примера попробуем получить курсы валют из регистра КурсыВалют на
текущую дату (листинг 17.9).
Листинг 17.9. Выборка записей из регистра сведений с помощью метода
Получить
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Получаем ссылку на валюту
ВалютаСсылка = Справочники.Валюты.НайтиПоНаименованию("EUR");
ОтборВалюта = Новый Структура("Валюта", ВалютаСсылка);
// Делаем выборку
СтруктураДанные = РегистрЦеныКомпании.Получить(
ТекущаяДата(), ОтборВалюта);
// Выводим в окно сообщений полученные данные
Сообщить("Кратность: " + СтруктураДанные.Кратность);
Сообщить("Курс: " + СтруктураДанные.Курс);
Глава 17. Получение выборки
233
Передав в метод текущую дату и ссылку на выбранную валюту, мы получили
структуру, содержащую значения ресурсов регистра сведений. А теперь попробуем определить по регистру ОтветственныеЛицаКомпании сотрудников,
отвечающих за определенные участки работы. Посмотрите пример, представленный в листинге 17.10.
Листинг 17.10. Выборка записей из регистра ОтветственныеЛицаКомпании
// Задаем регистр сведений
РегистрСотрудники = РегистрыСведений.ОтветственныеЛицаКомпании;
// Получаем ссылку на склад компании
СкладСсылка = Справочники.СкладыКомпании.НайтиПоНаименованию("Главный");
ОтборСклад = Новый Структура("СтруктурнаяЕдиница", СкладСсылка);
// Делаем выборку
СтруктураДанные = РегистрСотрудники.Получить(
ТекущаяДата(), ОтборСклад);
// Выводим в окно сообщений полученные данные
Сообщить("Ответственный по главному складу компании: " +
СтруктураДанные.ФизическоеЛицо);
// Получаем ссылку на кассу компании
КассаСсылка = Справочники.КассыКомпании.НайтиПоНаименованию("Основная");
ОтборКасса = Новый Структура("СтруктурнаяЕдиница", КассаСсылка);
// Делаем выборку
СтруктураДанные = РегистрСотрудники.Получить(
ТекущаяДата(), ОтборКасса);
// Выводим в окно сообщений полученные данные
Сообщить("Ответственный за основную кассу: " +
СтруктураДанные.ФизическоеЛицо);
Сообщить("Телефоны: " + СтруктураДанные.ФизическоеЛицо.Телефоны);
Вначале мы получили ответственного сотрудника за выбранный склад. Для
этого было достаточно установить отбор по единственному измерению регистра СтруктурнаяЕдиница. В результате запрос вернул нам сведения о сотруднике компании, ответственном за выбранный склад. То же самое мы
проделали повторно, но уже — чтобы определить ответственного по кассе
компании.
Рассмотрим еще один пример работы для получения паспортных данных выбранного физического лица. Для этого нам следует обратиться к регистру
сведений, который называется ПаспортныеДанныеФизЛиц. В качестве изме-
234
Часть III. Приемы программирования регистров сведений
рения имеется ссылка на физическое лицо, а ресурсы описывают реквизиты
документа (листинг 17.11).
Листинг 17.11. Получение паспортных данных физического лица
// Задаем регистр сведений
РегистрПаспортныеДанные = РегистрыСведений.ПаспортныеДанныеФизЛиц;
// Получаем ссылку на физическое лицо
ФизЛицоСсылка =
Справочники.ФизическиеЛица.НайтиПоНаименованию("Луговой С.В.");
// Задаем условие отбора
ФизЛицоОтбор = Новый Структура("ФизЛицо", ФизЛицоСсылка);
// Делаем выборку
СтруктураДанные = РегистрПаспортныеДанные.ПолучитьПоследнее(
ТекущаяДата(), ФизЛицоСсылка);
// Получаем вид документа
Сообщить("Вид документа: " + СтруктураДанные.ДокументВид);
// Получаем серию
Сообщить("Серия документа: " + СтруктураДанные.ДокументСерия);
// Получаем номер
Сообщить("Номер документа: " + СтруктураДанные.ДокументНомер);
// Получаем дату выдачи
Сообщить("Дата выдачи: " + СтруктураДанные.ДокументДатаВыдачи);
// Получаем наименование органа, выдавшего документ
Сообщить("Выдан: " + СтруктураДанные.ДокументКемВыдан);
// Получаем код подразделения
Сообщить("Код подразделения: " +
СтруктураДанные.ДокументКодПодразделения);
Передав в качестве отбора ссылку на физическое лицо (справочник ФизическиеЛица), мы выбрали из регистра все паспортные данные на текущую дату.
Как видите, работать с регистрами сведений достаточно просто. Кроме того,
система 1С предоставляет для этого гораздо больше средств, чем нужно. Как
и другие объекты, регистры сведений поддерживают использование форм,
с методами программирования которых мы познакомимся в следующей главе.
Глава 18
Получение форм
Регистры сведений поддерживают использование различных типов форм для
визуального отображения и управления записями. К ним относятся форма
списка (для просмотра и отбора записей) и форма записи (для корректировки
отдельной записи). Можно также установить способ редактирования отдельной записи: в списке, в диалоге или обоими способами. Кроме того, одну из
форм следует назначить основной — она будет вызываться по умолчанию,
если не будет явно задано имя в методах отображения форм.
Объект РегистрСведенийМенеджер содержит три метода для работы с формами:
ПолучитьФорму — получает любую доступную форму регистра, заданную
по имени;
ПолучитьФормуСписка — позволяет получить форму списка;
ПолучитьФормуРедактированияЗаписи — выводит на экран форму редак-
тирования отдельной записи регистра.
Первый из этих методов позволяет отобразить любую форму регистра, заданную в конфигураторе. Это может быть и форма списка, и форма редактирования записи или любая другая. Метод имеет три аргумента: первый (обязательный) описывает форму, второй (необязательный) задает владельца
формы и третий (необязательный) дает возможность установить для формы
уникальный идентификатор. В качестве возвращаемого значения служит
объект Форма. Посмотрите пример кода, представленный в листинге 18.1.
Листинг 18.1. Получение формы регистра сведений
// Получаем форму списка регистра сведений
ФормаСписка = РегистрыСведений.КурсыВалют.ПолучитьФорму("ФормаСписка");
// Отображаем форму на экране
ФормаСписка.Открыть();
236
Часть III. Приемы программирования регистров сведений
В примере мы получили форму списка для регистра КурсыВалют и отобразили
ее на экране. Если форма регистра вызывается из другой формы, можно установить параметр владельца, чтобы явно указать, какой форме она будет принадлежать. Рассмотрим пример, демонстрирующий сказанное (листинг 18.2).
Листинг 18.2. Использование владельца формы
// Получаем форму записи регистра сведений
ФормаЗаписиРегистра =
РегистрыСведений.ПаспортныеДанныеФизЛиц.ПолучитьФорму
("ФормаЗаписи", ФормаВладелецСсылка);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаЗаписиРегистра.ЗакрыватьПриЗакрытииВладельца = Истина;
// Отображаем форму на экране
ФормаЗаписиРегистра.Открыть();
Теперь мы передали в метод ПолучитьФорму ссылку на форму владельца.
Кроме того, установили свойство объекта Форма (ЗакрыватьПриЗакрытииВладельца), позволяющее автоматически закрывать форму регистра при закрытии формы владельца.
Третий параметр метода дает возможность назначить уникальный ключ для
формы, что позволит всегда найти ее среди множества открытых форм,
а также отобразить несколько копий одной и той же формы. Как установить
уникальный ключ для формы, показано в листинге 18.3.
Листинг 18.3. Использование ключа уникальности
// Формируем уникальный ключ
ГУИД = Новый УникальныйИдентификатор;
// Получаем форму записи регистра сведений
ФормаЗаписиРегистра =
РегистрыНакопления.ПаспортныеДанныеФизЛиц.ПолучитьФорму
("ФормаЗаписи", ФормаВладелецСсылка, ГУИД);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаЗаписиРегистра.ЗакрыватьПриЗакрытииВладельца = Истина;
// Отображаем форму на экране
ФормаЗаписиРегистра.ОткрытьМодально();
// Получаем эту же форму по значению ключа
ФормаЗаписиРегистра =
РегистрыНакопления.ПаспортныеДанныеФизЛиц.ПолучитьФорму
Глава 18. Получение форм
237
("ФормаЗаписи", ФормаВладелецСсылка, ГУИД);
// Если форма уже открыта, активизируем ее
Если ФормаЗаписиРегистра.Открыта() Тогда
ФормаЗаписиРегистра.Активизировать();
Иначе
ФормаЗаписиРегистра.Открыть();
КонецЕсли;
Вначале мы сформировали уникальное значение для ключа посредством объекта УникальныйИдентификатор. После этого получили форму записи и вывели ее на экран. Далее попытались еще раз вызвать форму записи, передав
созданный ранее ключ уникальности. Поскольку форма уже на экране (дополнительно проверили это с помощью метода Открыта), мы просто активизировали ее. Чтобы получить новый экземпляр формы, следует создать новый уникальный идентификатор и передать в параметр ПолучитьФорму. Все
просто и не требует дополнительных пояснений.
Существующее расширение для формы списка дает возможность сразу установить отбор по регистратору или измерению регистра. Например, для регистра
КурсыВалют, имеющего единственное измерение Валюта, можно выполнить
отбор по определенной валюте. Как это сделать, показано в листинге 18.4.
Листинг 18.4. Установка отбора для формы списка по измерению регистра
// Получаем ссылку на валюту
ВалютаСсылка = Справочники.Валюты.НайтиПоНаименованию("RUR");
// Получаем форму списка регистра сведений
ФормаСпискаРегистра =
РегистрыСведений.КурсыВалют.ПолучитьФорму("ФормаСписка");
// Устанавливаем отбор
ВалютаОтбор = Новый Структура("Валюта");
ВалютаОтбор.Валюта = ВалютаСсылка;
ФормаСпискаРегистра.ПараметрОтборПоИзмерению = ВалютаОтбор;
// Отображаем форму на экране
ФормаСпискаРегистра.Открыть();
Как видите, перед тем, как отобразить форму на экране, мы выполнили отбор
по измерению, передав параметру расширения структуру отбора. В результате в списке будут присутствовать только записи курсов российского рубля.
Аналогично работает расширение отбора записей по регистратору. Использовать его имеет смысл только для регистров, у которых в конфигураторе
238
Часть III. Приемы программирования регистров сведений
установлен режим записи по регистратору. Посмотрите пример, показанный
в листинге 18.5.
Листинг 18.5. Использование отбора по регистратору для формы списка
// Задаем отбор по регистратору
ДокументСсылка = Документы.ИзменениеЦенКомпании.НайтиПоНомеру
("0001", ТекущаяДата());
// Получаем форму списка регистра сведений
ФормаСпискаРегистра =
РегистрыСведений.ЦеныКомпании.ПолучитьФорму("ФормаСписка");
// Устанавливаем отбор
ФормаСпискаРегистра.ПараметрОтборПоРегистратору = ДокументСсылка;
// Отображаем форму на экране
ФормаСпискаРегистра.Открыть();
Вначале мы получили ссылку на документ движения. Затем передали ее
в параметр расширения формы списка записей и вывели на экран эту форму.
При этом все записи в списке будут иметь в качестве регистратора переданную нами ссылку на документ.
Следующий метод отображения формы регистра сведений, о котором мы поговорим, называется ПолучитьФормуРедактированияЗаписи. Он имеет три необязательных параметра: описание формы, ссылку на владельца и уникальный ключ. Если первый параметр не задан, будет выбрана основная форма
регистра, заданная в конфигураторе. Посмотрите пример работы с данным
методом, представленный в листинге 18.6.
Листинг 18.6. Использование метода ПолучитьФормуРедактированияЗаписи
// Формируем уникальный ключ
ГУИД = Новый УникальныйИдентификатор;
// Получаем форму редактирования записи
ФормаЗаписи =
РегистрыСведений.ПаспортныеДанныеФизЛиц.ПолучитьФормуРедактированияЗаписи
("ФормаЗаписи", ФормаВладелецСсылка, ГУИД);
// Устанавливаем свойство закрытия при закрытии владельца формы
ФормаЗаписи.ЗакрыватьПриЗакрытииВладельца = Истина;
// Отображаем форму на экране
ФормаЗаписи.Открыть();
// Получаем эту же форму по значению ключа
Глава 18. Получение форм
239
ФормаЗаписи =
РегистрыСведений.ПаспортныеДанныеФизЛиц.ПолучитьФормуРедактированияЗаписи
("ФормаЗаписи", ФормаВладелецСсылка, ГУИД);
// Если форма уже открыта, активизируем ее
Если ФормаЗаписи.Открыта() Тогда
ФормаЗаписи.Активизировать();
Иначе
ФормаЗаписи.Открыть();
КонецЕсли;
В приведенном примере мы назначили уникальный ключ и получили форму
записи регистра ПаспортныеДанныеФизЛиц. После этого сделали повторный
запрос и, в зависимости от состояния формы (открыта или нет), активизировали существующую форму или открыли повторно. Если указать новый уникальный ключ, будет создан новый экземпляр формы записи.
И последний метод, который мы изучим, называется ПолучитьФормуСписка.
Как видно из его названия, он получает форму списка регистра сведений.
Также имеет три необязательных параметра, аналогичных описанным для
предыдущего метода. Если не указать первый параметр, метод вернет основную форму регистра. Посмотрите пример из листинга 18.7.
Листинг 18.7. Использование метода ПолучитьФормуСписка
// Получаем форму списка
ФормаСписка = РегистрыСведений.ПаспортныеДанныеФизЛиц.ПолучитьФормуСписка
("ФормаСписка");
// Получаем ссылку на физлицо
ФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Паша");
// Устанавливаем отбор
ФормаСписка.РегистрСведенийСписок.Отбор.ФизЛицо.Значение = ФизЛицоСсылка;
ФормаСписка.РегистрСведенийСписок.Отбор.ФизЛицо.Использование = Истина;
// Отображаем форму на экране
ФормаСписка.Открыть();
Здесь мы получили форму списка. Затем из справочника физических лиц выбрали запись и передали в расширение формы списка записей регистра ссылку на физическое лицо. Поскольку форма списка имеет тип РегистрСведенийСписок, мы смогли напрямую обратиться к объекту Отбор, установив
значение и использование отбора по заданному значению.
На этом, думаю, можно завершить тему получения форм регистра сведений и
перейти к следующей — получение макетов.
Глава 19
Получение макета
Для регистров сведений, как и для регистров накопления, разрешено использование макетов. На их основе можно формировать отчеты и печатные документы с последующим выводом на принтер. Если для документов макеты
играют большую роль при создании печатных форм (бланков строгой отчетности), то здесь их применение может быть обусловлено, скорее, внутренними потребностями компании или отдельных подразделений на получение
определенной информации в печатном виде.
При создании нового макета предлагается выбрать его тип: табличный документ, текстовый документ, двоичные данные, Active-документ или HTMLдокумент. В большинстве случаев выбирают табличный тип, а при необходимости вывода на матричный принтер — текстовый. Далее посредством
табличного редактора создаются области, форматируются ячейки, добавляются диаграммы, рисунки или сводные таблицы. Более подробно с построением печатных форм на базе макетов мы познакомимся в главе 32. Сейчас же
мы поговорим о программировании простого макета, который специально
создадим для регистра сведений.
Объект РегистрСведенийМенеджер содержит специальный метод ПолучитьМакет, позволяющий обратиться к любому макету выбранного регистра сведений. В единственном параметре метода следует указать имя макета, как
оно задано в конфигураторе. Посмотрите пример получения макета, представленный в листинге 19.1.
Листинг 19.1. Получение макета регистра сведений
// Получаем макет регистра сведений
Макет = РегистрыСведений.КурсыВалют.ПолучитьМакет("КурсыВалют");
Глава 19. Получение макета
241
В результате выполнения данного примера мы получим табличный документ
макета. После этого можно использовать методы и свойства объекта ТабличныйДокумент для настройки и заполнения макета данными с последующим
выводом на экран (или принтер).
Чтобы во всем разобраться, давайте сформируем в конфигураторе новый макет для регистра сведений КурсыВалют и назовем его КурсыВалют. Для
этого выберите в дереве конфигурации нужный регистр. Установите курсор
мыши на ветку Макет и правой кнопкой вызовите контекстное меню. В меню нажмите пункт Добавить, после чего появится диалог для установки начальных свойств макета. Введите имя макета, а тип оставьте без изменений
(по умолчанию это табличный документ). Для сохранения вновь созданного
макета нажмите кнопку Готово. В результате будет создан пустой табличный
документ, который мы должны будем отформатировать в соответствии с решаемой задачей. Поскольку нам нужно получить список курсов валют, изменим базовый документ так, как показано на рис. 19.1.
Рис. 19.1. Табличный документ КурсыВалют
Как видно из рисунка, мы создали три отдельные области: шапку, заголовок
таблицы и строку таблицы. Для ячеек Период, Дата, Валюта, Курс и Кратность
установили тип заполнения из параметра. Эти ячейки на рисунке содержат уг-
242
Часть III. Приемы программирования регистров сведений
ловые скобки и позволяют заполнять их динамически, во время формирования
печатного документа. Теперь нам остается сохранить вновь созданный макет
в конфигурации и написать код, который будет заполнять макет данными из
регистра. Посмотрите пример, представленный в листинге 19.2.
Листинг 19.2. Заполнение макета данными из регистра сведений КурсыВалют
// Создаем новый табличный документ
ТаблицаКурсыВалют = Новый ТабличныйДокумент;
// Получаем макет регистра сведений
Макет = РегистрыСведений.КурсыВалют.ПолучитьМакет("КурсыВалют");
// Получаем область шапки
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
// Заполняем параметр Период
ОбластьШапка.Параметры.Период = ТекущаяДата();
// Выводим заполненную область в табличный документ
ТаблицаКурсыВалют.Вывести(ОбластьШапка);
// Получаем область заголовка таблицы
ОбластьЗаголовокТаблицы = Макет.ПолучитьОбласть("ЗаголовокТаблицы");
// Выводим область заголовка в табличный документ
ТаблицаКурсыВалют.Вывести(ОбластьЗаголовокТаблицы);
// Устанавливаем период выборки
ДатаНачальная = НачалоМесяца(ТекущаяДата());
ДатаКонечная = ТекущаяДата();
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.Выбрать(ДатаНачальная, ДатаКонечная);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем область строки таблицы
ОбластьСтрокаТаблицы = Макет.ПолучитьОбласть("СтрокаТаблицы");
// Заполняем параметр Дата
ОбластьСтрокаТаблицы.Параметры.Дата = ВыборкаДанные.Период;
// Заполняем параметр Валюта
ОбластьСтрокаТаблицы.Параметры.Валюта = ВыборкаДанные.Валюта;
// Заполняем параметр Курс
ОбластьСтрокаТаблицы.Параметры.Курс = ВыборкаДанные.Курс;
// Заполняем параметр Кратность
Глава 19. Получение макета
243
ОбластьСтрокаТаблицы.Параметры.Кратность =
ВыборкаДанные.Кратность;
// Выводим заполненную область в табличный документ
ТаблицаКурсыВалют.Вывести(ОбластьСтрокаТаблицы);
КонецЦикла;
// Отображаем созданный печатный документ на экране
ТаблицаКурсыВалют.Показать();
Разберем подробно представленный пример. Вначале мы создали новый табличный документ. Далее с помощью метода ПолучитьМакет получили макет
регистра сведений. После этого последовательно прочитали области макета,
заполнили, если нужно, данными и вывели в табличный документ. Данные из
регистра КурсыВалют выбрали посредством метода Выбрать, указав диапазон
дат. Когда полученный печатный документ сформирован, отображаем его на
экране. Как уже говорилось, более подробно с методами и свойствами объекта ТабличныйДокумент (на базе которого строится макет) мы поговорим
позже. Вот и все, что хотелось рассказать по использованию макетов для регистров сведений.
Глава 20
Получение срезов
В этой главе мы рассмотрим еще один характерный способ получения данных из периодических регистров сведений, точнее, из виртуальных таблиц.
До этого мы говорили о выборке информации из основной таблицы, но существуют еще две виртуальные, которые позволяют выбирать данные на определенный момент времени по различным измерениям. Они, как правило, ничем не отличаются от основной таблицы, а их главной задачей является
доступ к последним (или наиболее ранним) записям регистра.
Для работы с виртуальными таблицами предназначены два метода: СрезПервых
и СрезПоследних. Первый служит для выборки первых, а второй, соответственно, последних записей. Еще раз замечу, что эти методы доступны только
для периодических регистров сведений. Метод СрезПервых содержит два параметра: начальную дату и условие отбора. Например, чтобы получить первые записи курсов валют на начало месяца, следует написать код, представленный в листинге 20.1.
Листинг 20.1. Получение первых записей регистра на указанную дату
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Задаем период выборки
ДатаНачальная = НачалоМесяца(ТекущаяДата());
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.СрезПервых(ДатаНачальная);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные.Период);
Глава 20. Получение срезов
245
// Получаем валюту
Сообщить("Валюта: " + ВыборкаДанные.Валюта);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные.Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные.Курс);
КонецЦикла;
В результате выборки мы получим самые первые на начало текущего месяца
курсы всех имеющихся валют. Если требуется узнать самое раннее значение
курса только по определенной валюте, следует задать условие отбора, как
показано в листинге 20.2.
Листинг 20.2. Получение первого значения курса на указанную дату
по выбранной валюте
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Задаем период выборки
ДатаНачальная = НачалоМесяца(ТекущаяДата());
// Делаем отбор по выбранной валюте
ВалютаОтбор = Новый Структура("Валюта");
// Передаем ссылку на выбранную валюту
ВалютаОтбор.Валюта = Справочники.Валюты.НайтиПоНаименованию("USD");
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.СрезПервых(ДатаНачальная, ВалютаОтбор);
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные[0].Период);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные[0].Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные[0].Курс);
Как видите, в результате выполнения запроса мы получили одну самую раннюю запись регистра для выбранной валюты.
Метод СрезПоследних имеет гораздо большее практическое применение, поскольку выбирает самые последние записи регистра, наиболее актуальные на
указанный момент времени. Например, чтобы получить последнее значение
курса валюты, можно использовать код, представленный в листинге 20.3.
246
Часть III. Приемы программирования регистров сведений
Листинг 20.3. Получение последнего значения курса валюты
// Задаем регистр сведений
РегистрКурсыВалют = РегистрыСведений.КурсыВалют;
// Задаем период выборки
ДатаКонечная = ТекущаяДата();
// Делаем отбор по выбранной валюте
ВалютаОтбор = Новый Структура("Валюта");
// Передаем ссылку на выбранную валюту
ВалютаОтбор.Валюта = Справочники.Валюты.НайтиПоНаименованию("USD");
// Делаем выборку
ВыборкаДанные = РегистрКурсыВалют.СрезПервых(ДатаКонечная, ВалютаОтбор);
// Получаем значение периода
Сообщить("Дата записи: " + ВыборкаДанные[0].Период);
// Получаем кратность
Сообщить("Кратность: " + ВыборкаДанные[0].Кратность);
// Получаем курс валюты
Сообщить("Курс: " + ВыборкаДанные[0].Курс);
В рассмотренном примере мы получили актуальное значение курса валюты.
Если бы мы не указали условие отбора, запрос нам вернул бы последние курсы по всем валютам, зарегистрированным в программе.
А теперь получим последних сотрудников, назначенных ответственными по
выбранным складам компании. Для этого сделаем запрос к регистру ОтветственныеЛицаКомпании, как показано в листинге 20.4.
Листинг 20.4. Получение ответственных по выбранным складам компании
// Задаем регистр сведений
РегистрСотрудники = РегистрыСведений.ОтветственныеЛицаКомпании;
// Получаем ссылку на склад хранения
СкладСсылка =
Справочники.СкладыКомпании.НайтиПоНаименованию("СкладХранения");
ОтборСклад = Новый Структура("СтруктурнаяЕдиница", СкладСсылка);
// Делаем выборку
СтруктураДанные = РегистрСотрудники.СрезПоследних(
ТекущаяДата(), ОтборСклад);
// Выводим в окно сообщений полученные данные
Сообщить("Ответственный по складу хранения компании: " +
Глава 20. Получение срезов
247
СтруктураДанные[0].ФизическоеЛицо);
// Получаем ссылку на склад поставщика
СкладСсылка =
Справочники.СкладыКомпании.НайтиПоНаименованию("СкладПоставщика");
ОтборСклад = Новый Структура("СтруктурнаяЕдиница", СкладСсылка);
// Делаем выборку
СтруктураДанные = РегистрСотрудники.СрезПоследних(
ТекущаяДата(), ОтборСклад);
// Выводим в окно сообщений полученные данные
Сообщить("Ответственный по складу поставщика компании: " +
СтруктураДанные[0].ФизическоеЛицо);
В приведенном примере мы получили ответственных сотрудников по двум
складам компании.
Чтобы узнать актуальный адрес юридического лица на текущую дату, следует
воспользоваться регистром сведений Адреса. Поскольку обычно существует
два вида адреса (юридический и фактический), получим их оба. Юридический
адрес, как правило, указывает головной офис компании, а фактический может
определять место расположения складов. Посмотрите код, представленный
в листинге 20.5.
Листинг 20.5. Получение юридического и фактического адресов компании
// Задаем регистр сведений
РегистрАдреса = РегистрыСведений.Адреса;
// Получаем ссылку на юридическое лицо
ЮрЛицоСсылка =
Справочники.ЮридическиеЛица.НайтиПоНаименованию("Компания");
// Юридический адрес
ВидАдреса = Перечисления.ВидыАдресов.Юридический;
ОтборАдрес =
Новый Структура("ЮрФизЛицо, ВидАдреса", ЮрЛицоСсылка, ВидАдреса);
// Получаем последний юридический адрес
СтруктураДанные = РегистрАдреса.СрезПоследних(
ТекущаяДата(), ОтборАдрес);
// Выводим в окно сообщений полученные данные
Сообщить("Юридический адрес: " +
СтруктураДанные[0].ПредставлениеАдреса);
// Фактический адрес
248
Часть III. Приемы программирования регистров сведений
ВидАдреса = Перечисления.ВидыАдресов.Фактический;
ОтборАдрес =
Новый Структура("ЮрФизЛицо, ВидАдреса", ЮрЛицоСсылка, ВидАдреса);
// Делаем последний юридический адрес
СтруктураДанные = РегистрАдреса.СрезПоследних(
ТекущаяДата(), ОтборАдрес);
// Выводим в окно сообщений полученные данные
Сообщить("Фактический адрес: " +
СтруктураДанные[0].ПредставлениеАдреса);
Вначале мы получили ссылку на юридическое лицо. Затем с помощью перечисления ВидыАдресов установили вид адреса и получили последнее значение
на текущую дату. То же самое проделали и для фактического адреса. Самостоятельно вы можете попробовать реализовать данный алгоритм для выборки адресов физического лица.
Для того чтобы узнать актуальные паспортные данные конкретного физического лица, следует обратиться к регистру ПаспортныеДанныеФизЛиц. Как
это сделать, показано в листинге 20.6.
Листинг 20.6. Получение паспортных данных физического лица
// Задаем регистр сведений
РегистрПаспортныхДанных = РегистрыСведений.ПаспортныеДанныеФизЛиц;
// Получаем ссылку на физическое лицо
ФизЛицоСсылка =
Справочники.ФизическиеЛица.НайтиПоНаименованию("Гоман В.");
// Устанавливаем отбор по физическому лицу
ОтборФизЛицо =
Новый Структура("ФизЛицо", ФизЛицоСсылка);
// Получаем данные
СтруктураДанные = РегистрПаспортныхДанных.СрезПоследних(
ТекущаяДата(), ОтборФизЛицо);
// Получаем вид документа
Сообщить("Вид документа: " + СтруктураДанные[0].ДокументВид);
// Получаем серию
Сообщить("Серия документа: " + СтруктураДанные[0].ДокументСерия);
// Получаем номер
Сообщить("Номер документа: " + СтруктураДанные[0].ДокументНомер);
// Получаем дату выдачи
Глава 20. Получение срезов
249
Сообщить("Дата выдачи: " + СтруктураДанные[0].ДокументДатаВыдачи);
// Получаем наименование органа, выдавшего документ
Сообщить("Выдан: " + СтруктураДанные[0].ДокументКемВыдан);
// Получаем код подразделения
Сообщить("Код подразделения: " +
СтруктураДанные[0].ДокументКодПодразделения);
Итак, мы сделали отбор по измерению регистра, передав ссылку на физическое лицо, и получили все паспортные данные, актуальные на текущую дату.
Воспользовавшись методом СрезПоследних, получим актуальную цену номенклатуры из регистра ЦеныКомпании. Посмотрите пример, показанный
в листинге 20.7.
Листинг 20.7. Получение последней установленной цены на товар
// Задаем регистр сведений
РегистрЦеныКомпании = РегистрыСведений.ЦеныКомпании;
// Получаем ссылку на номенклатуру
ТоварСсылка = Справочники.Номенклатура.НайтиПоКоду("1978");
// Получаем ссылку на тип цены
ТипЦенСсылка = Справочники.ТипыЦен.НайтиПоКоду("02");
// Заполняем условия отбора
ОтборТовар =
Новый Структура("Номенклатура, ТипЦен", ТоварСсылка, ТипЦенСсылка);
// Получаем данные
СтруктураДанные = РегистрЦеныКомпании.СрезПоследних(
ТекущаяДата(), ОтборТовар);
// Получаем единицу измерения
Сообщить("Единица измерения: " + СтруктураДанные[0].ЕдиницаИзмерения);
// Получаем валюту
Сообщить("Валюта: " + СтруктураДанные[0].Валюта);
// Получаем процент скидки или наценки
Сообщить("Процент: " + СтруктураДанные[0].ПроцентСкидкиНаценки);
// Получаем цену товара
Сообщить("Цена: " + СтруктураДанные[0].Цена);
Чтобы получить цену выбранной номенклатуры, нам понадобилось установить отбор по двум измерениям регистра: номенклатуре и типу цен. Если бы
мы не указали отбор по типу цен, результатом запроса могла бы быть не одна
250
Часть III. Приемы программирования регистров сведений
запись, а несколько, по каждому типу цены. Поэтому важно помнить, что получить единственное значение из регистра сведений с помощью метода СрезПоследних можно лишь при наличии отборов по всем измерениям. Только
такой вариант гарантирует ожидаемый результат операции.
И последний пример, который мы рассмотрим, позволяет выбрать наиболее
реальную цену на номенклатуру контрагента. Цены контрагентов хранятся
в регистре ЦеныКонтрагентов. Поскольку данный регистр содержит три измерения (Контрагент, Номенклатура, Тип цен), придется в отборе задать условия по каждому из них. Как это сделать, показано в листинге 20.8.
Листинг 20.8. Получение последней цены номенклатуры по контрагенту
// Задаем регистр сведений
РегистрЦеныКонтрагентов = РегистрыСведений.ЦеныКонтрагентов;
// Заполняем условия отбора
ОтборТовар = Новый Структура("Контрагент, Номенклатура, ТипЦен");
ОтборТовар.Контрагент =
Справочники.Контрагенты.НайтиПоНаименованию("Фабрика");
ОтборТовар.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("2000");
ОтборТовар.ТипЦен = Справочники.ТипыЦен.НайтиПоКоду("04");
// Получаем данные
СтруктураДанные = РегистрЦеныКонтрагентов.СрезПоследних(
ТекущаяДата(), ОтборТовар);
// Получаем единицу измерения
Сообщить("Единица измерения: " + СтруктураДанные[0].ЕдиницаИзмерения);
// Получаем валюту
Сообщить("Валюта: " + СтруктураДанные[0].Валюта);
// Получаем цену товара
Сообщить("Цена: " + СтруктураДанные[0].Цена);
Как видите, принцип работы с разными регистрами сведений практически не
отличается. Главное, правильно организовать условия отбора, чтобы получить требуемую информацию.
На этом мы завершим тему выборки данных и немного познакомимся с обновлением и записью регистров сведений.
Глава 21
Запись
Регистры сведений поддерживают программную запись и обновление
данных. Для этого предназначены три базовых объекта: РегистрСведений
МенеджерЗаписи, РегистрСведенийЗапись и РегистрСведенийНаборЗаписей.
Первый служит для записи, удаления или чтения данных. Второй вызывается
косвенно через другие объекты, предоставляя им доступ к отдельной записи
регистра. Третий позволяет читать и записывать наборы записей.
Например, если требуется внести запись в регистр ПаспортныеДанныеФизЛиц, можно выполнить код, представленный в листинге 21.1.
Листинг 21.1. Добавление записи в регистр ПаспортныеДанныеФизЛиц
// Создаем объект РегистрСведенийМенеджерЗаписи
Запись = ПаспортныеДанныеФизЛиц.СоздатьМенеджерЗаписи();
// Заполняем значения измерений и ресурсов
// Период
Запись.Период = ТекущаяДата();
// Физическое лицо
Запись.ФизЛицо =
Справочники.ФизическиеЛица.НайтиПоНаименованию("Нараб И.");
// Вид документа
Запись.ДокументВид =
Справочники.ДокументыУдостоверяющиеЛичность.НайтиПоНаименованию(
"Права");
// Серия документа
Запись.ДокументСерия = "AA";
// Номер документа
252
Часть III. Приемы программирования регистров сведений
Запись.ДокументНомер = "00245678";
// Дата выдачи документа
Запись.ДокументДатаВыдачи = Дата("20/03/2002");
// Орган, выдавший документ
Запись.ДокументКемВыдан = "Мартовский РУВД";
// Код подразделения
Запись.ДокументКодПодразделения = "735";
// Записываем данные в регистр сведений
Попытка
Запись.Записать(Ложь);
Исключение
Сообщить(ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Вначале мы сформировали объект РегистрСведенийМенеджерЗаписи, применив метод СоздатьМенеджерЗаписи. Он принадлежит базовому объекту РегистрСведенийМенеджер. После этого заполняем значения измерений и ресурсов регистра сведений. Когда все готово, вызываем метод Записать.
Метод Записать имеет один необязательный параметр. Когда он устанавливается в значение ИСТИНА, существующая аналогичная запись регистра замещается новой, иначе просто добавляется новая запись.
Рассмотрим пример записи в регистр КурсыВалют, представленный в листинге 21.2.
Листинг 21.2. Запись в регистр КурсыВалют
// Получаем ссылку на валюту
ВалютаСсылка = Справочники.Валюты.НайтиПоНаименованию("BYR");
// Создаем объект РегистрСведенийМенеджерЗаписи
Запись = КурсыВалют.СоздатьМенеджерЗаписи();
// Записываем значения ключевых полей
Запись.Период = ТекущаяДата();
Запись.Валюта = ВалютаСсылка;
// Читаем запись из регистра
Запись.Прочитать();
// Если запись не считана, заполняем измерения и ресурсы
Если НЕ Запись.Выбран() Тогда
// Заполняем значения измерений и ресурсов
Глава 21. Запись
253
Запись.Период = ТекущаяДата();
Запись.Валюта = ВалютаСсылка;
Запись.Кратность = 1;
Запись.Курс = 2150;
КонецЕсли;
// Сохраняем данные в регистр
Запись.Записать();
В этом примере мы обновили курс выбранной валюты. Для этого сразу прочитали текущую запись, используя значения ключевых полей регистра и метод Прочитать (объект РегистрСведенийМенеджерЗаписи). Далее проверяем,
изменялась ли уже запись, с помощью метода Выбран (объект РегистрСведенийМенеджерЗаписи). Если она изменялась, устанавливаем значения и записываем данные в регистр.
Чтобы удалить запись из регистра, следует вначале установить значения полей, а затем вызвать метод Удалить (объект РегистрСведенийМенеджерЗаписи). Посмотрите, как это делается в листинге 21.3.
Листинг 21.3. Удаление записи из регистра КурсыВалют
// Получаем ссылку на валюту
ВалютаСсылка = Справочники.Валюты.НайтиПоНаименованию("USD");
// Создаем объект РегистрСведенийМенеджерЗаписи
Запись = КурсыВалют.СоздатьМенеджерЗаписи();
// Записываем значения ключевых полей
Запись.Период = ТекущаяДата();
Запись.Валюта = ВалютаСсылка;
// Удаляем запись из регистра
Запись.Удалить();
Как видно из примера, перед удалением записи мы установили значения для
ключевых полей и только после этого вызвали метод Удалить. Если этого не
сделать, из регистра будут удалены все записи.
Для управления набором записей следует использовать методы и свойства
объекта РегистрСведенийНаборЗаписей. Получить к нему доступ можно
через вызов метода СоздатьНаборЗаписей (объект РегистрСведенийМенеджер). Рассмотрим пример обновления курса валюты, представленный
в листинге 21.4.
254
Часть III. Приемы программирования регистров сведений
Листинг 21.4. Добавление записей в регистр КурсыВалют
// Получаем ссылку на валюту
ВалютаСсылка = Справочники.Валюты.НайтиПоНаименованию("USD");
// Создаем набор записей
НаборЗначений = КурсыВалют.СоздатьНаборЗаписей();
// Задаем отбор записей регистра
НаборЗначений.Отбор.Период.Установить(НачалоДня(ТекущаяДата()));
НаборЗначений.Отбор.Валюта.Установить(ВалютаСсылка);
// Читаем набор
НаборЗначений.Прочитать();
// Если данных нет
Если НаборЗначений.Количество() = 0 Тогда
// Добавляем запись
НоваяЗапись = НаборЗначений.Добавить();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.Валюта = ВалютаСсылка;
НоваяЗапись.Курс = 26.65;
НоваяЗапись.Кратность = 1;
КонецЕсли;
// Сохраняем набор в регистр
НаборЗначений.Записать();
После того как мы создали набор записей, мы установили отбор по ключевым
полям регистра и прочитали данные. Если записей нет (метод Количество), добавили новую, заполнив предварительно все измерения и ресурсы регистра.
И в завершение вызвали метод Записать. Метод Установить разрешает использовать отбор по указанному измерению.
Выполним еще один пример, позволяющий изменить цену выбранного товара. Посмотрите код, представленный в листинге 21.5.
Листинг 21.5. Изменение цены номенклатуры в регистре ЦеныКомпании
// Получаем ссылку на товар
ТоварСсылка = Справочники.Номенклатура.НайтиПоКоду("0971");
// Получаем ссылку на тип цен
ТипЦенСсылка = Справочники.ТипыЦен.НайтиПоКоду("01");
// Создаем набор записей
НаборЗначений = ЦеныКомпании.СоздатьНаборЗаписей();
Глава 21. Запись
255
// Задаем отбор записей регистра
НаборЗначений.Отбор.Период.ЗначениеС = НачалоМесяца(ТекущаяДата());
НаборЗначений.Отбор.Период.ЗначениеПо = ТекущаяДата();
НаборЗначений.Отбор.Период.Использование = Истина;
НаборЗначений.Отбор.Номенклатура.Установить(ТоварСсылка);
НаборЗначений.Отбор.ТипЦен.Установить(ТипЦенСсылка);
// Читаем набор
НаборЗначений.Прочитать();
// Если данных нет
Если НаборЗначений.Количество() Тогда
// Добавляем запись
НоваяЗапись = НаборЗначений.Добавить();
НоваяЗапись.Период = ТекущаяДата();
НоваяЗапись.Валюта = ВалютаСсылка;
НоваяЗапись.ЕдиницаИзмерения = ЕдиницаИзмеренияСсылка;
НоваяЗапись.ПроцентСкидкиНаценки = 0;
НоваяЗапись.Цена = НовоеЗначениеЦены;
КонецЕсли;
// Сохраняем набор в регистр
НаборЗначений.Записать(Истина);
Итак, вначале мы установили отбор по полям регистра. После этого прочитали набор и изменили цену выбранного товара. В завершение вызвали метод
Записать с параметром ИСТИНА, что позволило полностью обновить все записи набора.
Как видите, управлять программно записями регистра сведений достаточно
просто. Важно при этом не забывать, что перед записью обязательно нужно
задавать критерии отбора, чтобы не затереть все данные регистра.
На этом, пожалуй, все, и нам осталось только познакомиться с использованием
запросов, что мы и сделаем в следующей главе.
Глава 22
Использование запросов
В этой главе мы поговорим о получении данных из регистров сведений с помощью встроенного языка запросов. Сразу замечу, что данный способ является наиболее оптимальным и правильным. Рекомендуется, где это только
возможно, использовать именно его, а не объекты системы.
Для формирования запросов применяется уже знакомый вам объект Запрос.
Текст запроса передается свойству Текст. Если необходимо установить
или прочитать параметры запроса, вызываются, соответственно, методы
УстановитьПараметр и НайтиПараметры. Чтобы запустить запрос на выполнение, используют метод Выполнить.
Как нам уже известно, периодические регистры сведений поддерживают,
кроме основной, еще две виртуальные таблицы: СрезПервых и СрезПоследних.
Здесь мы познакомимся с приемами их программирования. Для начала построим запрос, выбирающий адрес юридического лица из регистра Адреса.
Как это сделать, показано в листинге 22.1.
Листинг 22.1. Получение адреса юридического лица из регистра Адреса
// Получаем ссылку на юридическое лицо из справочника
ЮрЛицоСсылка = Справочники.ЮридическиеЛица.НайтиПоНаименованию("Завод");
// Задаем вид адреса
ВидАдресаСсылка = Перечисления.ВидыАдресов.Юридический;
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
Глава 22. Использование запросов
257
|
Период
КАК Период,
|
ПредставлениеАдреса КАК ЮридическийАдрес
|ИЗ
|
РегистрСведений.Адреса
|
|ГДЕ
|
ЮрФизЛицо = &ЮрФизЛицо И
|
ВидАдреса = &ВидАдреса
|
|УПОРЯДОЧИТЬ ПО
|
Период УБЫВ";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ЮрФизЛицо", ЮрЛицоСсылка);
Запрос.УстановитьПараметр("ВидАдреса", ВидАдресаСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Если запрос вернул данные, выполняем их обработку
Если НЕ ДанныеВыборки.Пустой() Тогда
// Выбираем данные
Данные = ДанныеВыборки.Выбрать();
Пока Данные.Следующий() Цикл
Сообщить("Период: " + Данные.Период);
Сообщить("Адрес: " + Данные.ЮридическийАдрес);
КонецЦикла;
КонецЕсли;
Мы сделали запрос к регистру Адреса, чтобы получить юридический адрес
юридического лица. Для этого понадобилось передать в условие запроса два
параметра: ссылку на юридическое лицо из справочника ЮридическиеЛица
и ссылку на перечисление ВидыАдресов. Аналогичным образом можно получить адрес физического лица, зарегистрированного в справочнике ФизическиеЛица. Например, чтобы выбрать наиболее поздний фактический адрес
физического лица, воспользуйтесь кодом, представленным в листинге 22.2.
Листинг 22.2. Получение фактического адреса физического лица из регистра
Адреса
// Получаем ссылку на юридическое лицо из справочника
ФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Фома");
// Задаем вид адреса
ВидАдресаСсылка = Перечисления.ВидыАдресов.Фактический;
258
Часть III. Приемы программирования регистров сведений
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ПредставлениеАдреса КАК ФактическийАдрес
|ИЗ
|
РегистрСведений.Адреса.СрезПоследних(Период = &Период)
|
|ГДЕ
|
ЮрФизЛицо = &ФизЛиц И
|
ВидАдреса = &ВидАдреса";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("ФизЛицо", ФизЛицоСсылка);
Запрос.УстановитьПараметр("ВидАдреса", ВидАдресаСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим одну-единственную запись
с описанием фактического адреса. Связано это с тем, что мы использовали
в запросе виртуальную таблицу регистра Адреса. Кроме того, значение адреса будет наиболее актуальным на выбранную дату, поскольку был задан регистр Адреса.СрезПоследних. Период выборки мы указали как параметр регистра. Вообще он содержит два параметра. Второй служит для установки
условий отбора. Другими словами, мы могли бы переписать запрос, убрав
условия из внешнего запроса и поместив их в виде второго параметра регистра.
Теперь попробуем получить текущие курсы валют из регистра КурсыВалют.
Посмотрите пример, представленный в листинге 22.3.
Листинг 22.3. Получение текущих курсов валют из регистра КурсыВалют
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Валюта
КАК Валюта,
|
Курс
КАК Курс,
|
|ИЗ
Кратность КАК Кратность
Глава 22. Использование запросов
259
|
РегистрСведений.КурсыВалют.СрезПоследних(
|
&ДатаКонечная, Валюта В (&СписокВалютСсылка))";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ДатаКонечная", ТекущаяДата());
Запрос.УстановитьПараметр("СписокВалютСсылка", СписокВалютСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом выполнения приведенного примера будет набор записей, равный
количеству видов валют из справочника Валюты, а также значения курса
и кратности по каждой валюте. Чтобы получить курс только по одной валюте, следует передать в запрос ссылку на нее.
А сейчас посмотрим, как получить права доступа пользователя к выбранному
объекту конфигурации. Для этого нам понадобится регистр ПраваДоступаПользователей. Пример приведен в листинге 22.4.
Листинг 22.4. Получение прав доступа пользователя из регистра
ПраваДоступаПользователей
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ОбъектДоступа КАК Значение
|ИЗ
|
РегистрСведений.ПраваДоступаПользователей
|
|ГДЕ
|
Запись = ИСТИНА И
|
Пользователь = &Пользователь И
|
ОбъектДоступа ССЫЛКА Справочник.Номенклатура
|
ОбластьДанных = &Область";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("Пользователь", ПользовательСсылка);
Запрос.УстановитьПараметр("Область", ОбластьМенеджерСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В данном запросе мы проверили, имеет ли выбранный пользователь ограничение на запись данных в справочник Номенклатура.
260
Часть III. Приемы программирования регистров сведений
На производственном или торговом предприятии обычной практикой является
формирование комплектов номенклатуры на базе отдельных элементов. Для
учета комплектов применятся регистр сведений КомплектующиеНоменклатуры. Чтобы получить данные по элементам комплекта, можно написать код, как
показано в листинге 22.5.
Листинг 22.5. Получение комплектующих номенклатуры из регистра
КомплектующиеНоменклатуры
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
Комплектующая
КАК Комплектующая,
|
ЕдиницаИзмерения КАК ЕдиницаИзмерения,
|
Количество
КАК Количество,
|
ДоляСтоимости
КАК ДоляСтоимости
|ИЗ
|
РегистрСведений.КомплектующиеНоменклатуры
|
|ГДЕ
|
Номенклатура В ИЕРАРХИИ (&Номенклатура)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку номенклатуру
Запрос.УстановитьПараметр("Номенклатура", СписокНоменклатуры);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим комплектующие для выбранной номенклатуры с учетом иерархии в справочнике Номенклатура.
Из всего изложенного можно подвести итог, что использование языка запросов
дает многочисленные преимущества по отбору необходимых данных из регистров сведений. Не только простота и удобство, но и выигрыш в скорости,
а также существенное снижение стоимости запроса, по сравнению с применением объектов конфигурации, служит основанием для выбора такого способа
получения данных, как наиболее оптимального и разумного.
На этом позволю себе завершить тему программирования регистров сведений, чтобы перейти к более сложным вопросам анализа данных посредством запросов.
Часть IV
Анализ данных
262
Часть I. Приемы программирования документов
Глава 23
Анализ заказов
В этой части книги мы поговорим о том, как правильно использовать регистры накопления для анализа различных сторон торговой деятельности предприятия. Темы, которые мы рассмотрим, наиболее востребованы в повседневной работе и помогут вам разработать собственные отчеты под нужды и
текущие задачи пользователей.
Данная глава включает в себя несколько отдельных тем: анализ заказов покупателей, анализ заказов поставщикам, размещение заказов поставщикам под
заказ покупателя и анализ оплаты по заказам покупателей. По каждой теме
построим запрос на базе соответствующих регистров оборотов и остатков.
В дальнейшем вы сможете самостоятельно добавить интерфейс пользователя,
превратив запрос в готовый многофункциональный отчет (см. главу 27).
Анализ заказов покупателей
Документ "Заказ покупателя" оформляется менеджером по продажам на основании намерений покупателя (контрагента) приобрести какой-либо товар.
На момент создания заказа нужного товара может не быть на складах предприятия (в свободном остатке), и тогда он резервируется для этого заказа под
будущие поступления. Резервирование может быть сделано как по складу,
так и по заказам поставщикам. Важными критериями заказа служат дата отгрузки и дата оплаты, по которым можно оценить состояние документа и
взаиморасчеты с контрагентом. Дата отгрузки указывает крайний срок, не
позднее которого необходимо отгрузить товар покупателю. Когда заказ размещается в заказах поставщикам, эта дата пересекается с датой поставки по
заказам поставщикам и должна быть больше, так как вначале товар должен
поступить на склад, а затем уже отгружен заказчику. Дата оплаты предусматривает крайний срок поступления денежных средств по оплате заказанного
264
Часть III. Приемы программирования регистров сведений
покупателем товара. Оплата по заказу ведется на основании договора взаиморасчетов и может предусматривать частичную или полную предоплату за
товар. Наличные платежи оформляются документом "Приходный кассовый
ордер", а безналичные — приходным документом "Строка выписки банка".
В процессе работы могут вноситься различные изменения в открытый заказ:
добавление или удаление отдельных товаров, изменение размещения и др.
Все эти корректировки оформляются дополнительными документами "Корректировка заказа покупателя". Отгрузка товаров по заказу покупателя осуществляется посредством документа реализации. Если заказ просрочен, можно снять с него резерв посредством документа "Закрытие заказа".
Для анализа данных по заказам покупателей нам необходимо выбрать соответствующий регистр накопления. В данном случае этот регистр называется
ЗаказыПокупателей. Он поддерживает виртуальные таблицы остатков и оборотов. Регистраторами являются следующие документы: ВнутреннийЗаказ,
ЗаказПокупателя, ЗакрытиеЗаказовПокупателей, КорректировкаЗаказаПокупателя, ПеремещенияТоваров и РеализацияТоваров. Другими словами, по
данному регистру можно проследить все движения заказа покупателя с учетом перечисленных документов. Кроме того, сам документ заказа делает
движения по определенным регистрам накопления (например, ОстаткиТоваровКомпании), что также можно использовать в анализе.
Теперь нужно определиться, что требуется проанализировать и какие результаты получить. Если нас интересуют остатки по заказу, то достаточно взять регистр остатков. Если нужно оценить обороты или проследить историю движений по документам (сколько запланировано, сколько отгружено, сколько
осталось отгрузить), лучше воспользоваться регистром оборотов. Когда требуется и то, и другое — поможет регистр остатков и оборотов, объединяющий
информацию по остаткам и оборотам в разрезе документов движения. Для анализа остатков построим запрос, который показан в листинге 23.1.
Листинг 23.1. Анализ остатков номенклатуры по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДоговорВзаиморасчетов,
|
ЗаказПокупателя,
Глава 23. Анализ заказов
|
265
КоличествоОстаток
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(,
|
ЗаказПокупателя = &ЗаказПокупателя)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ЗаказПокупателя", СсылкаЗаказПокупателя);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Этот тривиальный запрос позволяет получить остатки по выбранному заказу
покупателя. Следует заметить, что регистр ЗаказыПокупателей хранит остатки
не только по заказу покупателя, но и по внутреннему заказу. Чтобы проанализировать, сколько заказано номенклатуры по двум этим документам на определенную дату, изменим запрос на другой, представленный в листинге 23.2.
Листинг 23.2. Анализ остатков номенклатуры по заказу покупателя
и внутреннему заказу
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДоговорВзаиморасчетов,
|
СУММА(ВЫБОР
|
КОГДА ЗаказПокупателя ССЫЛКА Документ.ЗаказПокупателя
|
ТОГДА КоличествоОстаток
|
ИНАЧЕ 0
|
КОНЕЦ) КАК ОстатокПоЗаказуПокупателя,
|
СУММА(ВЫБОР
|
КОГДА ЗаказПокупателя ССЫЛКА Документ.ВнутреннийЗаказ
|
ТОГДА КоличествоОстаток
|
|
ИНАЧЕ 0
КОНЕЦ) КАК ОстатокПоВнутреннемуЗаказу
266
Часть III. Приемы программирования регистров сведений
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки(
|
&Период, Номенклатура = &Номенклатура)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса период
Запрос.УстановитьПараметр("Период", ТекущаяДата());
// Передаем в параметр запроса ссылку на выбранную номенклатуру
Запрос.УстановитьПараметр("Номенклатура", СсылкаНоменклатура);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на номенклатуру, мы получим все остатки по заказам покупателей и внутренним заказам на заданную дату. Попробуйте самостоятельно получить остатки номенклатуры в разрезе контрагентов.
Применив регистр оборотов и остатков, можно узнать, сколько номенклатуры было заказано, сколько отгружено и сколько осталось отгрузить. Посмотрите пример, представленный в листинге 23.3.
Листинг 23.3. Анализ оборотов номенклатуры по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДоговорВзаиморасчетов,
|
ЗаказПокупателя,
|
КоличествоПриход
КАК ЗаказаноКоличество,
|
КоличествоРасход
КАК ОтгруженоКоличество,
|
КоличествоКонечныйОстаток КАК ОсталосьОтгрузитьКоличество
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.ОстаткиИОбороты(
Глава 23. Анализ заказов
267
|
, , , , ЗаказПокупателя = &ЗаказПокупателя)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ЗаказПокупателя", СсылкаЗаказПокупателя);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на заказ, мы получим требуемые данные. Дополнительно можно проследить изменения в разрезе документов регистраторов за
определенный период. Для этого достаточно передать непосредственно в параметры регистра значения начальной (первый параметр) и конечной (второй
параметр) даты, а также периодичность (третий параметр) по регистратору.
Анализ заказов поставщикам
Документ "Заказ поставщику" оформляется менеджером по закупке на основании предварительной договоренности с поставщиком о приобретении
у него определенных товаров. Он может быть связан с заказами покупателей,
под которые делается заказ номенклатуры. Важным компонентом является
дата поставки, определяющая предполагаемую дату поступления товаров от
поставщика. Движение денежных средств (оплата товаров) по заказу поставщику связано с документами "Расходный кассовый ордер" или "Платежное
поручение". Регистр, отслеживающий все эти движения, называется Заказы
Поставщикам. Регистраторами служат следующие документы: Внутренний
Заказ, ЗаказПокупателя, ЗаказПоставщику, документы закрытия заказов
покупателей и поставщикам, документы корректировок и поступления. Документ также поддерживает регистр оборотов и остатков. По регистру остатков можно проверить, какое количество товара осталось получить в разрезе
заказа. Оборотный регистр позволяет проследить всю историю движения заказанного поставщику товара в разрезе документов регистраторов.
В общем случае получить остатки номенклатуры по заказу можно так, как
показано в листинге 23.4.
Листинг 23.4. Анализ остатков номенклатуры по заказу поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
268
Часть III. Приемы программирования регистров сведений
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДоговорВзаиморасчетов,
|
ЗаказПоставщику,
|
КоличествоОстаток
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(
|
, ЗаказПоставщику = &ЗаказПоставщику)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на текущий заказ поставщику
Запрос.УстановитьПараметр("ЗаказПоставщику", СсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос выбирает остатки номенклатуры, которую необходимо получить
у поставщика. Чтобы проанализировать, сколько заказано, сколько поступило и сколько осталось получить номенклатуры в разрезе документов движения, следует применить код, аналогичный представленному в листинге 23.5.
Листинг 23.5. Анализ оборотов номенклатуры по заказу поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
|
ХарактеристикаНоменклатуры,
КАК Номенклатура,
|
Регистратор
|
ДоговорВзаиморасчетов,
|
КоличествоПриход
КАК ЗаказаноКоличество,
|
КоличествоРасход
КАК ПоступилоКоличество,
|
КоличествоКонечныйОстаток КАК ОсталосьПолучить
КАК ДокументДвижения,
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.ОстаткиИОбороты(
Глава 23. Анализ заказов
|
269
, , Регистратор, , ЗаказПоставщику = &ЗаказПоставщику)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на текущий заказ поставщику
Запрос.УстановитьПараметр("ЗаказПоставщику", СсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Для решения задачи мы воспользовались регистром оборотов и остатков. Для
формирования отчета по всем заказам за период в разрезе номенклатуры следует передать в регистр начальную (первый параметр) и конечную (второй
параметр) даты, а вместо условия по заказу — записать ссылку на номенклатуру. Соответствующий пример представлен в листинге 23.6.
Листинг 23.6. Анализ оборотов по заказам поставщикам в разрезе номенклатуры
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
СУММА(КоличествоПриход)
КАК ЗаказаноКоличество,
|
СУММА(КоличествоРасход)
КАК ПоступилоКоличество,
|
СУММА(КоличествоКонечныйОстаток) КАК ОсталосьПолучить
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.ОстаткиИОбороты(
|
&НачальнаяДата, &КонечнаяДата,
|
Регистратор, Движения, Номенклатура = &Номенклатура)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на номенклатуру
270
Часть III. Приемы программирования регистров сведений
Запрос.УстановитьПараметр("Номенклатура", СсылкаНоменклатура);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Как уже говорилось, заказ поставщику тесно связан с заказом покупателя,
под который закупается товар, поэтому можно проанализировать закупки
в разрезе предстоящих отгрузок.
Анализ размещения заказов поставщикам
под заказы покупателей
В первую очередь, можно узнать остатки номенклатуры по заказу покупателя, размещенные в заказах поставщикам. Для этого нам понадобятся регистр
остатков ЗаказыПоставщикам и регистр оборотов ЗаказыПокупателей. Пример кода представлен в листинге 23.7.
Листинг 23.7. Анализ остатков номенклатуры по заказу покупателя,
размещенной в заказах поставщикам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказыПокупателей.Номенклатура КАК Номенклатура,
|
ЗаказыПокупателей.ХарактеристикаНоменклатуры,
|
ЗаказыПокупателей.КоличествоПриход КАК Запланировано,
|
ЗаказыПокупателей.КоличествоРасход КАК Отгружено,
|
ЗаказыПокупателей.КоличествоНачальныйОстаток
|
|
КАК ОсталосьОтгрузить,
ЗаказыПоставщикам.КоличествоОстаток КАК ОсталосьПолучить
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.ОстаткиИОбороты(
|
, &КонечнаяДата, , ,ЗаказПокупателя = &ЗаказПокупателя)
|
КАК ЗаказыПокупателей
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(&КонечнаяДата)
|
КАК ЗаказыПоставщикам
|ПО ЗаказыПокупателей.ЗаказПокупателя = ЗаказыПоставщикам.ЗаказПокупателя
Глава 23. Анализ заказов
271
|
И ЗаказыПокупателей.Номенклатура = ЗаказыПоставщикам.Номенклатура
|
И ЗаказыПокупателей.ХарактеристикаНоменклатуры =
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры
|
|ИТОГИ СУММА(Запланировано),
|
СУММА(Отгружено),
|
СУММА(ОсталосьОтгрузить),
|
СУММА(ОсталосьПолучить)
|ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса конечную дату
Запрос.УстановитьПараметр("КонечнаяДата", ТекущаяДата());
// Передаем в параметр запроса ссылку на заказ
Запрос.УстановитьПараметр("ЗаказПокупателя", СсылкаЗаказПокупателя);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Соединив два регистра накопления по заказу, номенклатуре и характеристике, получили остатки и обороты номенклатуры на выбранную дату.
В документе заказа поставщику предусмотрен реквизит ДатаПоступления, который задает предполагаемую дату поставки заказанной номенклатуры. Пользуясь им, можно проанализировать, какие заказы покупателей просрочены изза срыва поставок. Такая аналитика необходима для оперативного контроля
торговых операций и выявления несогласованности в работе менеджеров закупки и продажи. Пример такого анализа представлен в листинге 23.8.
Листинг 23.8. Анализ даты поступления по заказу поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период,
|
ЗаказПокупателя,
|
ДоговорВзаиморасчетов,
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
272
Часть III. Приемы программирования регистров сведений
|
Количество,
|
ЗаказПоставщику.ДатаПоступления КАК ДатаПоставки,
|
ЗаказПокупателя.ДатаОтгрузки КАК ДатаОтгрузки
|
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам
|
|ГДЕ
|
(ЗаказПоставщику.ДатаПоступления > ЗаказПокупателя.ДатаОтгрузки)
И
|
ЗаказыПоставщикам.Период МЕЖДУ &НачальнаяДата И &КонечнаяДата
|
|УПОРЯДОЧИТЬ ПО
|
ДатаОтгрузки УБЫВ";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса ссылку на конечную дату периода
Запрос.УстановитьПараметр("КонечнаяДата", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы за выбранной период получим список
заказов, по которым срываются сроки отгрузки. Другими словами, мы сравниваем дату поступления из заказа поставщику с датой отгрузки по связанному заказу покупателя. Если первая превышает вторую, выбираем документ
в таблицу.
Анализ заказов покупателей по оплате
Важной составляющей анализа данных является учет денежных средств, поступающих в результате отгрузок по заказам покупателей. Поскольку заказ
может быть оформлен на несколько различных номенклатурных позиций,
необходимо контролировать, какая часть товаров по заказу уже оплачена,
а какая — нет. Для этого можно воспользоваться оборотным регистром СуммыЗаказов, как показано в листинге 23.9.
Листинг 23.9. Анализ оплаты заказа покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Глава 23. Анализ заказов
273
Запрос.Текст = "
|ВЫБРАТЬ
|
Заказ КАК Заказ,
|
Заказ.ДоговорВзаиморасчетов,
|
Заказ.ДатаОплаты,
|
СуммаЗаказаОборот КАК СуммаЗаказаОборот,
|
СуммаОплатыОборот КАК СуммаОплатыОборот
|ИЗ
|
РегистрНакопления.СуммыЗаказов.Обороты(, &КонечнаяДата, ,
|
Заказ В (ВЫБРАТЬ Ссылка
|
ИЗ Документ.ЗаказПокупателя
|
ГДЕ ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов))
|
|ИТОГИ
|
СУММА(СуммаЗаказаОборот),
|
СУММА(СуммаОплатыОборот)
|ПО
|
Заказ";
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
СсылкаДоговорВзаиморасчетов);
// Передаем в параметр запроса конечную дату
Запрос.УстановитьПараметр("КонечнаяДата", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на договор взаиморасчетов с контрагентом и конечную дату, мы получим общие сведения по оплате заказов покупателей в разрезе договора. Чтобы проанализировать задолженность контрагента по оплате
номенклатуры в разрезе договора взаиморасчетов, воспользуемся регистром
остатков КонтрагентыВзаиморасчетыКомпании. Как это сделать, показано
в листинге 23.10.
Листинг 23.10. Анализ задолженности контрагента по оплате в разрезе
договора взаиморасчетов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
274
Часть III. Приемы программирования регистров сведений
|ВЫБРАТЬ
|
СуммаОстаток
|
СуммаВалОстаток КАК СуммаВалОстаток
КАК СуммаОстаток,
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&Период, ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов)";
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
СсылкаДоговорВзаиморасчетов);
// Передаем в параметр запроса конечную дату
Запрос.УстановитьПараметр("Период", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Чтобы проанализировать долг контрагента в разрезе заказа покупателя, придется немного изменить запрос, как показано в листинге 23.11.
Листинг 23.11. Анализ задолженности контрагента в разрезе заказа покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СуммаОстаток
КАК СуммаОстаток,
|
СуммаВалОстаток КАК СуммаВалОстаток
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
, Сделка = &Заказ)";
// Передаем в параметр запроса ссылку на заказ
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказ);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на заказ, мы получим данные о задолженности
в разрезе выбранного заказа покупателя. Можно еще воспользоваться регистром накопления ЗаказыПокупателей, чтобы рассчитать сумму реализованной номенклатуры по заказу. Как это сделать, показано в листинге 23.12.
Глава 23. Анализ заказов
275
Листинг 23.12. Анализ суммы продаж по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
СУММА(Регистратор.СуммаДокумента)
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей
|
|ГДЕ
|
ЗаказПокупателя = &ЗаказПокупателя И
|
(Регистратор ССЫЛКА Документ.РеализацияТоваров)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса ссылку на заказ
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказ);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим сумму реализации по каждой
позиции номенклатуры заказа.
Для оценки ожидаемых платежей по заказам покупателей следует обратиться
к регистру ОжидаемыеВходящиеПлатежиКомпании. Данный регистр формирует движения при проведении различных платежных документов и в том
числе по заказам покупателей. Пример запроса, формирующего ожидаемую
сумму оплаты по заказу, представлен в листинге 23.13.
Листинг 23.13. Анализ ожидаемых платежей в разрезе заказа покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
276
Часть III. Приемы программирования регистров сведений
|ВЫБРАТЬ
|
Период КАК Период,
|
ПодразделениеКомпании,
|
Регистратор,
|
СтруктурнаяЕдиница,
|
ОжидаемыйПлатеж,
|
ОжидаемыйПлатеж.ДатаОплаты,
|
ОжидаемыйПлатеж.ДоговорВзаиморасчетов,
|
ОжидаемыйПлатеж.Контрагент,
|
ОжидаемыйПлатеж.Организация,
|
СуммаВзаиморасчетов
|ИЗ
|
РегистрНакопления.ОжидаемыеВходящиеПлатежиКомпании
|
|ГДЕ
|
Регистратор ССЫЛКА Документ.ЗаказПокупателя И
|
Регистратор = &Заказ И
|
ОжидаемыйПлатеж.ДатаОплаты <= &ДатаАнализа
|
|УПОРЯДОЧИТЬ ПО
|
Период";
// Передаем в параметр запроса ссылку на заказ
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказ);
// Передаем в параметр запроса дату анализа
Запрос.УстановитьПараметр("ДатаАнализа", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим ожидаемые платежи по выбранному заказу покупателя на текущую дату. Чтобы проанализировать ожидаемые платежи по заказам в разрезе контрагентов, добавьте в запрос группировку по контрагентам.
Вот и все, что хотелось рассказать по анализу заказов. Думаю, информация,
представленная здесь, поможет вам самостоятельно подбирать регистры для
оптимального формирования запросов и получения необходимой информации.
В завершение хочу предложить несколько советов по оптимизации запросов:
1. В запросе указывайте только те поля, которые вам действительно нужны.
Глава 23. Анализ заказов
277
2. Если вы планируете использовать ссылочные поля запроса (например,
Номенклатура или ЗаказПокупателя) только для отображения, применяйте
для них встроенную функцию ПРЕДСТАВЛЕНИЕ. Она извлекает только строковое значение поля.
3. Если используются регистры оборотов или остатков, старайтесь условия
запроса переносить в сам регистр, а не использовать команду ГДЕ. Это существенно уменьшит время выполнения запроса и задействованные ресурсы.
4. Для получения типа объекта из составного поля применяйте команду
ССЫЛКА или ВЫРАЗИТЬ.
5. Избегайте, по возможности, вложенных запросов.
На этом мы завершим тему анализа заказов в 1С и перейдем к анализу продаж в разрезе заказов и документов поступления.
Глава 24
Анализ продаж
Важным критерием оценки деятельности предприятия являются объемы
продаж товаров или услуг. По ним можно судить об эффективности работы
отдельно взятых сотрудников, подразделений и в целом организации. На основании результатов продаж делаются прогнозы дальнейшего развития, планирование будущих периодов, рассчитываются зарплаты и премии. На многих предприятиях уровень заработной платы менеджеров напрямую зависит
от объемов реализованной продукции за расчетный период.
В системе 1С продажи формируются документами реализации. Возврат товаров
от покупателя по каким-либо причинам учитывается документами возврата.
Предварительно товар оформляется в виде заказов и закупается у поставщиков.
Затем он приходуется на склады документами поступления и оприходования.
В процессе работы товар может перемещаться с одно склада на другой посредством документов перемещений. Реализуется товар как по заказу покупателя, так и
непосредственно по документу поступления. В этой главе мы разберем, как проанализировать продажи товаров предприятия.
Анализ объемов продаж за период
Анализ продаж можно разделить на две основные части: планируемый и фактический. Планируемые продажи формируются на этапе оформления заказов покупателей и позволяют оценить предполагаемые объемы реализации номенклатуры
за период в разрезе документов движения, контрагентов, подразделений и менеджеров. В конфигурации Управление Торговлей для анализа планируемых
продаж можно воспользоваться регистром оборотов ПланыПродажКомпании,
движения по которому делают документы ПланПродаж. Для получения данных
за определенный период по всем документам планирования можно применить
код, показанный в листинге 24.1.
Глава 24. Анализ продаж
279
Листинг 24.1. Анализ планируемых продаж за определенный период
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПланирования,
|
СУММА(КоличествоОборот)
КАК ПланируемоеКоличество,
|
СУММА(СуммаПродажиОборот) КАК ПланируемаяСумма
|ИЗ
|
РегистрНакопления.ПланыПродажКомпании.Обороты(
|
&НачальнаяДата, &КонечнаяДата, ,)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПланирования
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("КонечнаяДата", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате запрос возвратит нам планируемое количество и сумму продажи
по всем документам планирования за указанный период времени. Чтобы проанализировать планируемые продажи по конкретному менеджеру, следует добавить в запрос соответствующее условие отбора, как показано в листинге 24.2.
Листинг 24.2. Анализ планируемых продаж за определенный период
по выбранному менеджеру
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
280
Часть IV. Анализ данных
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПланирования,
|
СУММА(КоличествоОборот)
|
СУММА(СуммаПродажиОборот) КАК ПланируемаяСумма
КАК ПланируемоеКоличество,
|ИЗ
|
РегистрНакопления.ПланыПродажКомпании.Обороты(
|
&НачальнаяДата, &КонечнаяДата, ,
|
ДокументПланирования.Ответственный =
&ОтвественныйМенеджер)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПланирования
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("КонечнаяДата", ТекущаяДата());
// Передаем в параметр запроса ссылку на сотрудника
Запрос.УстановитьПараметр("ОтвественныйМенеджер", СсылкаПользователь);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Важным моментом является прогноз объемов продаж в разрезе подразделений предприятия для оценки эффективности работы. Сделать это можно с помощью запроса, представленного в листинге 24.3.
Листинг 24.3. Анализ планируемых продаж за определенный период
по подразделениям предприятия
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Глава 24. Анализ продаж
281
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
КоличествоОборот
КАК КоличествоОборот,
|
СуммаПродажиОборот
КАК СуммаПродажиОборот,
|
ПодразделениеКомпании
КАК ПодразделениеКомпании
|ИЗ
|
РегистрНакопления.ПланыПродажКомпании.Обороты(
|
&НачальнаяДата, &КонечнаяДата, , )
|
|УПОРЯДОЧИТЬ ПО
|
ПодразделениеКомпании
|
|ИТОГИ СУММА(КоличествоОборот),
|
СУММА(СуммаПродажиОборот)
|ПО
|
ПодразделениеКомпании,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачальнаяДата", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("КонечнаяДата", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате мы получим планируемые значения итогов по каждому подразделению предприятия за выбранный период времени.
Как уже говорилось ранее, после того, как оформлены документы реализации, можно говорить о фактических продажах номенклатуры покупателям.
Для оценки продажи применяется регистр накопления ПродажиКомпании.
По нему можно строить всевозможные отчеты: с учетом заказов, документов
поступления и документов продажи. В общем случае посмотрите, как получить количество и сумму продажи за определенный период, на примере,
представленном в листинге 24.4.
282
Часть IV. Анализ данных
Листинг 24.4. Анализ продаж номенклатуры за период
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СУММА(Количество)
КАК ПроданоКоличество,
|
СУММА(СуммаПродажи)
КАК СуммаПродажиКоличество
|ИЗ
|
РегистрНакопления.ПродажиКомпании
|
|ГДЕ
|
ПродажиКомпании.Период МЕЖДУ &НачалоПериода И &КонецПериода
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Чтобы получить продажи в разрезе заказа покупателя, необходимо добавить
в условие ссылку на выбранный документ (листинг 24.5).
Листинг 24.5. Анализ продаж за период в разрезе заказа покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Глава 24. Анализ продаж
283
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СУММА(Количество)
КАК ПроданоКоличество,
|
СУММА(СуммаПродажи)
КАК СуммаПродажиКоличество
|ИЗ
|
РегистрНакопления.ПродажиКомпании
|
|ГДЕ
|
ПродажиКомпании.Период МЕЖДУ &НачалоПериода И &КонецПериода И
|
ЗаказПокупателя = &Заказ
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказПокупателя);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Выполнив данный запрос, мы сможем оценить количество проданной номенклатуры и сумму в разрезе заказа покупателя.
Как уже говорилось, в случае возврата проданного товара покупателем,
оформляется документ ВозвратТоваровОтПокупателя. Поскольку он делает
движения по регистру ПродажиКомпании, имеет смысл разделить фактические продажи от возвратов. Для этого можно применить запрос, представленный в листинге 24.6.
284
Часть IV. Анализ данных
Листинг 24.6. Анализ продаж номенклатуры за период с учетом документов
возврата
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПродажи,
|
СУММА(ВЫБОР
|
КОГДА ДокументПродажи ССЫЛКА Документ.РеализацияТоваров
|
ТОГДА КоличествоОборот
|
ИНАЧЕ 0
|
КОНЕЦ) КАК РеализованоКоличество,
|
СУММА(ВЫБОР
|
КОГДА ДокументПродажи ССЫЛКА
|
|
|
Документ.ВозвратТоваровОтПокупателя
ТОГДА КоличествоОборот
ИНАЧЕ 0
|
КОНЕЦ) КАК ВозвращеноКоличество,
|
СУММА(ВЫБОР
|
КОГДА ДокументПродажи ССЫЛКА Документ.РеализацияТоваров
|
ТОГДА СуммаПродажиОборот
|
ИНАЧЕ 0
|
КОНЕЦ) КАК РеализованоСумма,
|
СУММА(ВЫБОР
|
КОГДА ДокументПродажи ССЫЛКА
|
Документ.ВозвратТоваровОтПокупателя
|
ТОГДА СуммаПродажиОборот
|
ИНАЧЕ 0
|
КОНЕЦ) КАК ВозвращеноСумма
|ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты(
|
&НачалоПериода, &КонецПериода, , )
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
Глава 24. Анализ продаж
|
ХарактеристикаНоменклатуры,
|
ДокументПродажи
285
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим статистику продаж и возвратов за выбранный период времени.
Любой руководитель хотел бы оценить работу менеджеров. Сделать это
можно также посредством регистра ПродажиКомпании. Для этого достаточно
проанализировать продажи за определенный период в разрезе ответственного по заказу покупателя. Готовый пример такого запроса представлен в листинге 24.7.
Листинг 24.7. Анализ продаж за период в разрезе менеджера по продажам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПродажи,
|
ДоговорВзаиморасчетовПокупателя,
|
ЗаказПокупателя,
|
КоличествоОборот,
|
СуммаПродажиОборот
|ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты(
|
&НачалоПериода, &КонецПериода, ,
|
ЗаказПокупателя.Ответственный = &Ответственный)
286
Часть IV. Анализ данных
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("Ответственный", СсылкаПользователь);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
После выполнения запроса мы получим количество и сумму продаж номенклатуры за определенный период в разрезе менеджера по продажам. Чтобы
вывести данные о продажах сразу по всем менеджерам, воспользуйтесь запросом, показанным в листинге 24.8.
Листинг 24.8. Анализ продаж за период в разрезе всех менеджеров по продажам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СУММА(КоличествоОборот) КАК КоличествоОборот,
|
СУММА(СуммаПродажиОборот) КАК СуммаПродажиОборот,
|
ЗаказПокупателя.Ответственный
|ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты(
|
&НачалоПериода, &КонецПериода, , )
|
|СГРУППИРОВАТЬ ПО
|
ЗаказПокупателя.Ответственный,
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
Глава 24. Анализ продаж
287
|УПОРЯДОЧИТЬ ПО
|
Номенклатура
|
|ИТОГИ
|
СУММА(КоличествоОборот),
|
СУММА(СуммаПродажиОборот)
|ПО
|
ОБЩИЕ,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате работы запроса мы получим количество и суммы продаж по каждому менеджеру, а также общие итоги по номенклатуре и характеристике.
Однако по продажам можно оценить работу не только менеджеров продаж,
но и менеджеров закупки. Как это сделать, рассказывается дальше.
Анализ продаж в разрезе поступлений
Согласитесь, часто необходимо узнать не только объемы продаж товаров, но
и сравнить поступления и реализацию. Связано это с тем, что требуется проанализировать поставки в разрезе контрагентов и менеджеров, сравнить их
с реализацией, чтобы оценить наиболее выгодных партнеров по бизнесу,
а также работу собственных сотрудников, занимающихся закупками. Сделать
это можно с помощью того же регистра ПродажиКомпании.
Например, попробуем написать запрос, отображающий продажи в разрезе
выбранного заказа поставщику. Посмотрите пример, представленный в листинге 24.9.
Листинг 24.9. Анализ продаж за период в разрезе заказа поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
288
Часть IV. Анализ данных
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
КоличествоОборот КАК РеализованоКоличество,
|
СуммаПродажиОборот КАК РеализованоСумма
|ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты(
|
, , , ДокументПоставки = &Заказ)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса ссылку на заказ поставщику
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на заказ, мы получим количество и сумму продаж
по данному заказу поставщику. Аналогичным образом можно получить продажи в разрезе менеджеров закупки. Как это сделать, показано в листинге 24.10.
Листинг 24.10. Анализ продаж за период в разрезе менеджеров закупки
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СУММА(КоличествоОборот) КАК КоличествоОборот,
|
СУММА(СуммаПродажиОборот) КАК СуммаПродажиОборот,
|
ПродажиКомпанииОбороты.ДокументПоставки.Ответственный
|ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты(
|
&НачалоПериода, &КонецПериода, Регистратор,
|
ДокументПоставки ССЫЛКА Документ.ЗаказПоставщику)
|
Глава 24. Анализ продаж
289
|СГРУППИРОВАТЬ ПО
|
ДокументПоставки.Ответственный,
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура
|
|ИТОГИ
|
СУММА(КоличествоОборот),
|
СУММА(СуммаПродажиОборот)
| ПО
|
ОБЩИЕ,
|
ДокументПоставкиОтветственный,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим количество и сумму продаж
номенклатуры по каждому менеджеру за выбранный период времени.
Следующий момент, который мы разберем, поможет нам разложить данные
регистра по основным документам поставки: ВозвратТоваровПоставщику,
ЗаказПоставщику, ОприходованиеТоваров, ПоступлениеТоваров, ПриходныйОрдерНаТовары и РеализацияТоваров. Посмотрите запрос, представленный в листинге 24.11.
Листинг 24.11. Анализ продаж номенклатуры в разрезе документов движения
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
290
Часть IV. Анализ данных
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
ДокументПоставки КАК ДокументПоставки,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА
|
|
Документ.ВозвратТоваровПоставщику
ТОГДА Количество ИНАЧЕ 0
|
КОНЕЦ) КАК ВозвращеноПоставщикуКоличество,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА Документ.ЗаказПоставщику
|
ТОГДА Количество ИНАЧЕ 0
|
КОНЕЦ) КАК ПоЗаказуПоставщикуКоличество,
|
СУММА(ВЫБОР
|
|
КОГДА ДокументПоставки ССЫЛКА Документ.ОприходованиеТоваров
ТОГДА Количество ИНАЧЕ 0
|
КОНЕЦ) КАК ОприходованоКоличесство,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА Документ.ПоступлениеТоваров
|
ТОГДА Количество ИНАЧЕ 0
|
КОНЕЦ) КАК ПоступилоКоличество,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА
|
|
Документ.ПриходныйОрдерНаТовары
ТОГДА Количество ИНАЧЕ 0
|
КОНЕЦ) КАК ПоОрдеруПриходКоличество,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА Документ.РеализацияТоваров
|
ТОГДА Количество ИНАЧЕ 0
|
КОНЕЦ) КАК РеализованоКоличество,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА
|
|
Документ.ВозвратТоваровПоставщику
ТОГДА СуммаПродажи ИНАЧЕ 0
|
КОНЕЦ) КАК ВозвращеноПоставщикуСумма,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА Документ.ЗаказПоставщику
|
ТОГДА СуммаПродажи ИНАЧЕ 0
|
КОНЕЦ) КАК ПоЗаказуПоставщикуСумма,
|
СУММА(ВЫБОР
Глава 24. Анализ продаж
291
|
КОГДА ДокументПоставки ССЫЛКА Документ.ОприходованиеТоваров
|
ТОГДА СуммаПродажи ИНАЧЕ 0
|
КОНЕЦ) КАК ОприходованоСумма,
|
СУММА(ВЫБОР
|
|
КОГДА ДокументПоставки ССЫЛКА Документ.ПоступлениеТоваров
ТОГДА СуммаПродажи ИНАЧЕ 0
|
КОНЕЦ) КАК ПоступилоСумма,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА
|
|
Документ.ПриходныйОрдерНаТовары
ТОГДА СуммаПродажи ИНАЧЕ 0
|
КОНЕЦ) КАК ПоОрдеруПриходСумма,
|
СУММА(ВЫБОР
|
КОГДА ДокументПоставки ССЫЛКА Документ.РеализацияТоваров
|
ТОГДА СуммаПродажи ИНАЧЕ 0
|
КОНЕЦ) КАК РеализованоСумма
|ИЗ
|
РегистрНакопления.ПродажиКомпании
|
|ГДЕ
|
Номенклатура = &Номенклатура И
|
ПродажиКомпании.Период МЕЖДУ &НачалоПериода И &КонецПериода
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПоставки
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура
|
|ИТОГИ
|
СУММА(ВозвращеноПоставщикуКоличество),
|
СУММА(ПоЗаказуПоставщикуКоличество),
|
СУММА(ОприходованоКоличесство),
|
СУММА(ПоступилоКоличество),
|
СУММА(ПоОрдеруПриходКоличество),
|
СУММА(РеализованоКоличество),
292
Часть IV. Анализ данных
|
СУММА(ВозвращеноПоставщикуСумма),
|
СУММА(ПоЗаказуПоставщикуСумма),
|
СУММА(ОприходованоСумма),
|
СУММА(ПоступилоСумма),
|
СУММА(ПоОрдеруПриходСумма),
|
СУММА(РеализованоСумма)
|ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДокументПоставки";
// Передаем в параметр запроса начальную дату периода
Запрос.УстановитьПараметр("НачалоПериода",
НачалоКвартала(ТекущаяДата()));
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("НачалоПериода", ТекущаяДата());
// Передаем в параметр запроса конечную дату периода
Запрос.УстановитьПараметр("Номенклатура", СсылкаНоменклатура);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на номенклатуру и период выборки, мы получим
количество и суммы продаж с учетом документов движения.
Как видите, единственный регистр продажи позволяет строить всевозможные
варианты анализа данных. Подключив к нему другие регистры (например, по
партиям товаров на складах и остаткам товаров на складах), можно сформировать всевозможные отчеты, чтобы учесть требования любого заказчика.
Глава 25
Анализ закупок
На предприятии, занимающемся оптовой торговлей, где проходят тысячи
и десятки тысяч наименований номенклатуры от различных поставщиков,
имеет большое значение анализ закупочной деятельности. Как правило, товар
закупается менеджерами по закупкам у различных поставщиков. Решение
установки объемов закупок принимается на основании анализа продаж и существующей конъюнктуры рынка, поэтому важно с момента оформления
заказа до реализации товара проводить анализ его движения по системе.
Решение о закупке номенклатуры оформляется в виде заказа поставщику.
Документ может быть создан на основании существующего заказа покупателя, а также под внутренний заказ для будущих продаж. Когда появляется товар, оформляется документ поступления (в некоторых случаях документ оприходования) и номенклатура распределяется по складам предприятия.
Перемещения между складами организуются документом перемещения. Если
товар был закуплен на основании заказа покупателя, он может автоматически
резервироваться на складе под этот заказ. После того как товар поступил на
склад, начинают работать менеджеры по продажам и формируют документы
реализации для выполнения заявок покупателей.
Мы поговорим о том, как выполнить анализ закупок и оценить работу менеджеров.
Анализ объемов закупок за период
Вначале попробуем получить данные по выполнению заказов поставщикам
и остатки номенклатуры, которую требуется закупить. Для этого можно использовать регистр накопления ЗаказПоставщику. Пример запроса представлен в листинге 25.1.
294
Часть IV. Анализ данных
Листинг 25.1. Анализ остатков номенклатуры по заказам поставщикам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ДоговорВзаиморасчетов,
|
ЗаказПоставщику.ДатаПоступления,
|
ЗаказПоставщику.ДатаОплаты,
|
ЗаказПоставщику.Контрагент,
|
ЗаказПоставщику.Организация,
|
ЗаказПоставщику.СкладКомпании,
|
КоличествоОстаток
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(
|
&ДатаОтчета, ЗаказПоставщику = &ЗаказПоставщику)
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Передаем в параметр запроса ссылку на текущий заказ поставщику
Запрос.УстановитьПараметр("ЗаказПоставщику", СсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос ссылку на заказ и дату расчеты, мы получим список номенклатуры и дополнительные реквизиты, а также остаток номенклатуры,
недополученный по заказу.
Через этот же регистр можно проверить выполнение обязательств поставщиком, пользуясь датой предполагаемой поставки. Значение этой даты особенно важно, когда под заказ поставщику сделаны заказы покупателей с установленной датой отгрузки покупателю. Естественно, когда дата поступления
по заказу поставщику превышает дату отгрузки по заказу покупателя, имеет
смысл говорить о срыве поставок потребителю. Посмотрите пример, представленный в листинге 25.2.
Глава 25. Анализ закупок
295
Листинг 25.2. Анализ заказов поставщикам, по которым срываются отгрузки
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПоставщику.ДатаПоступления,
|
ЗаказПокупателя.ДатаОтгрузки,
|
ЗаказПоставщику.Контрагент,
|
СУММА(КоличествоОстаток)
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(
|
&ДатаОтчета,
|
ЗаказПокупателя.Товары.Размещение = ЗаказПоставщику И
|
ЗаказПоставщику.ДатаПоступления > ЗаказПокупателя.ДатаОтгрузки)
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПоставщику.ДатаПоступления,
|
ЗаказПокупателя.ДатаОтгрузки,
|
ЗаказПоставщику.Контрагент
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения отчета мы получим список заказов поставщикам,
по которым срываются отгрузки.
Для анализа закупок номенклатуры в разрезе документов движения можно
воспользоваться регистром оборотов. Дополнительно можно проверить поступления номенклатуры по заказу покупателя и свободный остаток на складах. Как это сделать, показано в листинге 25.3.
296
Часть IV. Анализ данных
Листинг 25.3. Анализ закупок номенклатуры по документам движения
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказыПоставщикам.Номенклатура КАК Номенклатура,
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры,
|
СУММА(ЗаказыПоставщикам.КоличествоПриход) КАК ЗаказаноКоличество,
|
СУММА(ЗаказыПоставщикам.КоличествоРасход)
|
|
КАК ПоступилоПоЗаказуКоличество,
СУММА(ЗаказыПоставщикам.КоличествоКонечныйОстаток)
|
КАК ОсталосьПолучитьПоЗаказуКоличество,
|
СУММА(ПартииТоваров.КоличествоОстаток) КАК ПоступилоКоличество,
|
СУММА(ОстаткиТоваров.КоличествоОстаток)
|
КАК ВСвободномОстаткеКоличество
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.ОстаткиИОбороты(
|
, &ДатаОтчета, , , Номенклатура = &Номенклатура)
|
КАК ЗаказыПоставщикам
|
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ПартииТоваровКомпании.Остатки(
|
&ДатаОтчета, Номенклатура = &Номенклатура) КАК ПартииТоваров
|ПО ЗаказыПоставщикам.ЗаказПокупателя = ПартииТоваров.Заказ И
|
ЗаказыПоставщикам.Номенклатура = ПартииТоваров.Номенклатура И
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры =
|
ПартииТоваров.ХарактеристикаНоменклатуры
|
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваровКомпании.Остатки
|
(&ДатаОтчета, Номенклатура = &Номенклатура И Заказ = NULL)
|
КАК ОстаткиТоваров
|ПО ЗаказыПоставщикам.Номенклатура = ОстаткиТоваров.Номенклатура И
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры =
|
ОстаткиТоваров.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ЗаказыПоставщикам.Номенклатура,
Глава 25. Анализ закупок
|
297
ЗаказыПоставщикам.ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Номенклатура
|
|ИТОГИ
|
СУММА(ЗаказаноКоличество),
|
СУММА(ПоступилоПоЗаказуКоличество),
|
СУММА(ОсталосьПолучитьПоЗаказуКоличество),
|
СУММА(ПоступилоКоличество),
|
СУММА(ВСвободномОстаткеКоличество)
|ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Передаем в параметр запроса ссылку на номенклатуру
Запрос.УстановитьПараметр("Номенклатура", СсылкаНоменклатура);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в параметры запроса дату отчета и ссылку на товар, мы получим количество по заказу, по поступлению и свободный остаток. Поступления мы
берем из регистра остатков ПартииТоваровКомпании, а свободный остаток
из регистра остатков ОстаткиТоваровКомпании.
Если предприятие располагает несколькими складами, имеет смысл проверить, сколько закупленной номенклатуры находится на каждом складе в разрезе заказа поставщику. Сделать это можно с помощью запроса, представленного в листинге 25.4.
Листинг 25.4. Анализ закупленной номенклатуры по складам в разрезе заказа
поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
"ВЫБРАТЬ
|
ЗаказыПоставщикам.ЗаказПоставщику КАК ЗаказПоставщику,
298
Часть IV. Анализ данных
|
СУММА(ЗаказыПоставщикам.КоличествоПриход) КАК ЗаказаноКоличество,
|
СУММА(ЗаказыПоставщикам.КоличествоКонечныйОстаток)
|
КАК ОсталосьПолучитьКоличество,
|
СУММА(ПартииТоваров.КоличествоПриход) КАК ПоступилоКоличество,
|
СУММА(ПартииТоваров.КоличествоРасход) КАК ПроданоКоличество,
|
ПартииТоваров.СкладКомпании КАК СкладКомпании
|ИЗ
|
|
РегистрНакопления.ЗаказыПоставщикам.ОстаткиИОбороты(
|
&НачалоПериода, &КонецПериода, , , ЗаказПоставщику = &Заказ)
|
КАК ЗаказыПоставщикам
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ПартииТоваровКомпании.Обороты(
|
&НачалоПериода, &КонецПериода) КАК ПартииТоваров
|
ПО ЗаказыПоставщикам.ЗаказПокупателя = ПартииТоваров.Заказ И
|
ЗаказыПоставщикам.Номенклатура = ПартииТоваров.Номенклатура И
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры =
|
ПартииТоваров.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ПартииТоваров.СкладКомпании,
|
ЗаказыПоставщикам.ЗаказПоставщику
|
|УПОРЯДОЧИТЬ ПО
|
СкладКомпании
|
|ИТОГИ
|
СУММА(ЗаказаноКоличество),
|
СУММА(ОсталосьПолучитьКоличество),
|
СУММА(ПоступилоКоличество)
|ПО
|
ОБЩИЕ,
|
СкладКомпании,
|
ЗаказПоставщику";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса дату окончания периода
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Передаем в параметр запроса ссылку на заказ
Глава 25. Анализ закупок
299
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим количество номенклатуры по
заказу поставщику в разрезе складов предприятия.
Для анализа закупок в разрезе контрагентов можно сформировать запрос на
базе регистра остатков ЗаказыПоставщикам. Пример такого запроса представлен в листинге 25.5.
Листинг 25.5. Анализ закупок по заказам поставщиков в разрезе контрагентов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказПоставщику.Контрагент КАК Контрагент,
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
СУММА(КоличествоОстаток) КАК ОсталосьПолучить
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(&ДатаОтчета)
|
|СГРУППИРОВАТЬ ПО
|
ЗаказПоставщику.Контрагент,
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
ОсталосьПолучить УБЫВ
|
|ИТОГИ СУММА(ОсталосьПолучить) ПО
|
Контрагент,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
300
Часть IV. Анализ данных
Мы сгруппировали результат запроса и упорядочили по количеству номенклатуры в остатке. Если вам требуется выбрать данные только по определенному контрагенту, добавьте дополнительный параметр в условие запроса.
Чтобы получить значения сумм по заказам поставщиков, можно применить
регистр СуммыЗаказов. По нему видны суммы заказов и суммы оплаты по
заказам. Посмотрите пример, представленный в листинге 25.6.
Листинг 25.6. Анализ заказанных и оплаченных сумм по заказам поставщикам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период КАК Период,
|
Заказ КАК Заказ,
|
Заказ.ДатаПоступления,
|
Заказ.ДатаОплаты,
|
Заказ.ДоговорВзаиморасчетов КАК ДоговорВзаиморасчетов,
|
Заказ.Контрагент КАК Контрагент,
|
Заказ.Ответственный КАК Ответственный,
|
Заказ.ПодразделениеКомпании КАК ПодразделениеКомпании,
|
Заказ.СкладКомпании,
|
СуммаЗаказа КАК СуммаЗаказа,
|
СуммаОплаты КАК СуммаОплаты
|ИЗ
|
РегистрНакопления.СуммыЗаказов
|
|ГДЕ
|
СуммыЗаказов.Период МЕЖДУ &НачалоПериода И &КонецПериода
|
И Заказ ССЫЛКА Документ.ЗаказПоставщику
|
|УПОРЯДОЧИТЬ ПО
|
Период
|
|ИТОГИ
|
СУММА(СуммаЗаказа),
|
СУММА(СуммаОплаты)
|ПО
Глава 25. Анализ закупок
|
Заказ,
|
ДоговорВзаиморасчетов,
|
Ответственный,
|
ПодразделениеКомпании,
|
Контрагент";
301
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса дату окончания периода
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом выполнения запроса будут суммы оплат и суммы заказов в разрезе заказов поставщикам. Как видите, анализировать закупки приходится
или в разрезе заказов, или документов поступлений. Однако важной составляющей закупок является также оценка работы менеджеров, о которой мы
и поговорим.
Анализ оценки работы менеджеров
по закупке
Проанализировать работу менеджеров по закупке можно по следующим критериям:
по оформленным заказам поставщикам;
по партиям товаров на складах, оприходованных документами поступле-
ний на основании заказов поставщикам;
по объемам продаж товаров, заказанных менеджерами закупки.
Как видите, вариантов оценки эффективности работы менеджеров по закупкам не так много. Кроме того, первые два являются лишь косвенным подтверждением результатов работы. Третий вариант наиболее точно отражает
желаемый результат, но он напрямую связан уже с работой менеджеров по
продажам. Если пренебречь этим фактом, получится относительно разумный
критерий анализа деятельности сотрудников отдела закупки. Несмотря на
это, попробуем применить каждый из перечисленных вариантов и построить
соответствующие запросы.
Чтобы проанализировать оформленные заказы поставщикам, достаточно
взять регистр накопления ЗаказыПоставщикам и сгруппировать результат по
ответственным. Как это сделать, показано в листинге 25.7.
302
Часть IV. Анализ данных
Листинг 25.7. Анализ заказанных и оплаченных сумм по заказам поставщикам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период,
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
ЗаказПоставщику КАК ЗаказПоставщику,
|
СУММА(Количество) КАК Количество,
|
СУММА(СуммаВзаиморасчетов) КАК СуммаВзаиморасчетов,
|
СУММА(СуммаУпрУчета) КАК СуммаУпрУчета,
|
ЗаказПоставщику.Контрагент КАК Контрагент,
|
ЗаказПоставщику.Ответственный КАК Ответственный
|ИЗ
|
РегистрНакопления.ЗаказыПоставщикам
|
|ГДЕ
|
ЗаказыПоставщикам.Период МЕЖДУ &НачалоПериода И &КонецПериода
|
|СГРУППИРОВАТЬ ПО
|
Период,
|
ЗаказПоставщику,
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
СуммаУпрУчета УБЫВ
|
|ИТОГИ
|
СУММА(Количество),
|
СУММА(СуммаВзаиморасчетов),
|
СУММА(СуммаУпрУчета)
|ПО
|
ОБЩИЕ,
|
Ответственный,
|
ЗаказПоставщику,
Глава 25. Анализ закупок
303
|
Контрагент,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса дату окончания периода
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос период выборки, мы получим количество и сумму по заказам поставщикам в разрезе менеджеров и контрагентов. Кроме того, запрос
возвратит дополнительные итоговые записи основным полям таблицы.
Для анализа по второму варианту нам понадобится два регистра: ПартииТоваровКомпании и ЗаказПоставщикам. Пример запроса показан в листинге 25.8.
Листинг 25.8. Анализ работы менеджеров по остаткам товаров на складах
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ПартииТоваров.Номенклатура КАК Номенклатура,
|
ПартииТоваров.ХарактеристикаНоменклатуры
|
КАК ХарактеристикаНоменклатуры,
|
СУММА(ПартииТоваров.КоличествоОстаток) КАК ПоступилоКоличество,
|
СУММА(ПартииТоваров.СтоимостьОстаток) КАК ПоступилоСтоимость,
|
СУММА(ЗаказыПоставщикам.Количество) КАК ЗаказаноКоличество,
|
ПартииТоваров.ДокументПоставки.Ответственный
|
КАК Ответственный
|ИЗ
|
|
|
|
|
|
|
РегистрНакопления.ПартииТоваровКомпании.Остатки(&ПериодОтчета)
КАК ПартииТоваров
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыПоставщикам
КАК ЗаказыПоставщикам
ПО ПартииТоваров.ДокументПоставки.Сделка =
ЗаказыПоставщикам.ЗаказПоставщику И
ПартииТоваров.Номенклатура = ЗаказыПоставщикам.Номенклатура И
304
Часть IV. Анализ данных
|
ПартииТоваров.ХарактеристикаНоменклатуры =
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры
|
|СГРУППИРОВАТЬ ПО
|
ПартииТоваров.ДокументПоставки.Ответственный,
|
ПартииТоваров.Номенклатура,
|
ПартииТоваров.ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
Ответственный
|
|ИТОГИ
|
СУММА(ПоступилоКоличество),
|
СУММА(ПоступилоСтоимость),
|
СУММА(ЗаказаноКоличество)
|ПО
|
Ответственный,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("ПериодОтчета", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Связав таблицы регистров через левое соединение, мы получили партии товаров по всем менеджерам закупки и итоговые данные по каждому сотруднику
отдельно. Чтобы получить сведения только по определенному менеджеру, добавьте в параметр регистра ПартииТоваровКомпании соответствующее условие отбора.
И последний вариант анализа работы менеджеров по закупке, связанный
с фактическими объемами продаж, мы реализуем с помощью регистра ПродажиКомпании. Как это сделать, показано в листинге 25.9.
Листинг 25.9. Анализ работы менеджеров по объемам продаж
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
Глава 25. Анализ закупок
305
|ВЫБРАТЬ
|
Период,
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
ДокументПоставки.Ответственный КАК Ответственный,
|
СУММА(Количество) КАК Количество,
|
СУММА(СуммаПродажи) КАК СуммаПродажи
|ИЗ
|
РегистрНакопления.ПродажиКомпании
|
|ГДЕ
|
Период МЕЖДУ &НачалоПериода И &КонецПериода И
|
Ответственный = &Ответственный И
|
(ДокументПоставки ССЫЛКА Документ.ЗаказПоставщику ИЛИ
|
ДокументПоставки ССЫЛКА Документ.ВозвратТоваровПоставщику ИЛИ
|
ДокументПоставки ССЫЛКА Документ.ЗакрытиеЗаказовПоставщикам ИЛИ
|
ИЛИ
ДокументПоставки ССЫЛКА Документ.КорректировкаЗаказаПоставщику
|
ДокументПоставки ССЫЛКА Документ.ПоступлениеТоваров)
|
|СГРУППИРОВАТЬ ПО
|
ДокументПоставки.Ответственный,
|
Период,
|
Номенклатура,
|
ХарактеристикаНоменклатуры
|
|УПОРЯДОЧИТЬ ПО
|
СуммаПродажи УБЫВ
|
|ИТОГИ
|
СУММА(Количество),
|
СУММА(СуммаПродажи) ПО
|
ОБЩИЕ,
|
Ответственный,
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса дату окончания периода
306
Часть IV. Анализ данных
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Передаем в параметр запроса ссылку на сотрудника
Запрос.УстановитьПараметр("Ответственный", СсылкаПользователь);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в отчет начальную и конечную даты выборки, а также ссылку на сотрудника, мы получим объем продаж за соответствующий период по выбранному менеджеру. Чтобы получить данные по всем менеджерам, удалите
из запроса условие отбора по сотруднику.
Вот и все, что хотелось рассказать про анализ продаж и оценку работы менеджеров. В следующей главе мы рассмотрим вопросы взаиморасчетов по
сделкам с поставщиками и покупателями.
Глава 26
Анализ взаиморасчетов
В этой главе мы поговорим о взаиморасчетах с поставщиками товаров и покупателями. Взаиморасчеты являются важной составляющей успешного бизнеса. Правильно спланированные затраты на закупку товаров, а равно и умелая политика в отношении покупателей позволяют не только укрепить
позиции на рынке, но и противостоять многочисленным конкурентам.
В системе 1С все взаимоотношения строятся на базе договоров взаиморасчетов, в которых определяются основные условия совместной деятельности.
Договором определяются следующие основные элементы: наименование
контрагента, валюта, тип взаиморасчетов, контроль суммы и числа дней дебиторской задолженности.
При оформлении многих документов конфигурации используется договор
взаиморасчетов. Количество договоров по одному контрагенту не ограничено. Предусмотрено несколько способов ведения взаиморасчетов с контрагентами: по заказам (договор ведется в рамках документов "Заказ покупателя"
и "Заказ поставщику", а сумма поступлений, отгрузок и оплат контролируется по каждому конкретному заказу), по договору в целом (взаиморасчеты
контролируются по всем документам, связанным с договором), по расчетным
документам (взаиморасчеты по суммам оплаты и отгрузки ведутся по соответствующим документам). При необходимости в договоре можно задать
предельную задолженность контрагента, а также требуемый процент предоплаты.
Рассмотрим несколько общих примеров работы с договором взаиморасчетов.
Например, выберем из регистра ЗаказыПокупателей заказы, по договорам
которых установлено ограничение на сумму задолженности с контрагентом
(листинг 26.1).
308
Часть IV. Анализ данных
Листинг 26.1. Анализ заказов покупателей по предельной сумме задолженности
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказПокупателя,
|
ЗаказПокупателя.Контрагент КАК Контрагент,
|
ДоговорВзаиморасчетов.ДопустимаяСуммаЗадолженности
|
КАК ДопустимаяСуммаЗадолженности
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей
|
|ГДЕ
|
ДоговорВзаиморасчетов.КонтролироватьСуммуЗадолженности = ИСТИНА И
|
ЗаказПокупателя ССЫЛКА Документ.ЗаказПокупателя И
|
Период МЕЖДУ &НачалоПериода И &КонецПериода
|
|УПОРЯДОЧИТЬ ПО
|
Контрагент УБЫВ
|
|ИТОГИ
|
СУММА(ДопустимаяСуммаЗадолженности)
|ПО
|
Контрагент";
// Передаем в параметр запроса начало периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса конец периода
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в запрос период выборки, мы получим список контрагентов и заказов, по которым установлен признак контроля суммы задолженности. Похожим образом можно проанализировать признак ограничения количества дней
задолженности контрагентов по заказам покупателей. Как это сделать, показано в листинге 26.2.
Глава 26. Анализ взаиморасчетов
309
Листинг 26.2. Анализ заказов покупателей по ограничению числа дней
задолженности
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказПокупателя,
|
ЗаказПокупателя.Контрагент КАК Контрагент,
|
ДоговорВзаиморасчетов.ДопустимоеЧислоДнейЗадолженности
|
КАК ДопустимоеЧислоДнейЗадолженности
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей
|
|ГДЕ
|
ДоговорВзаиморасчетов.КонтролироватьЧислоДнейЗадолженности =
|
ИСТИНА И
|
ЗаказПокупателя ССЫЛКА Документ.ЗаказПокупателя И
|
Период МЕЖДУ &НачалоПериода И &КонецПериода
|
|УПОРЯДОЧИТЬ ПО
|
ДопустимоеЧислоДнейЗадолженности УБЫВ
|
|ИТОГИ
|
СУММА(ДопустимоеЧислоДнейЗадолженности)
|ПО
|
Контрагент";
// Передаем в параметр запроса начало периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДата()));
// Передаем в параметр запроса конец периода
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом запроса будет список контрагентов, имеющих установленный
признак количества дней задолженности. Кроме того, все записи упорядочены по числу дней, начиная с наибольшего значения.
310
Часть IV. Анализ данных
Чтобы получить все договоры взаиморасчетов в разрезе контрагентов, можно
воспользоваться справочником ДоговорыВзаиморасчетов. Пример такого
запроса представлен в листинге 26.3.
Листинг 26.3. Получение списка договоров в разрезе контрагентов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Владелец КАК Контрагент,
|
Владелец.Наименование КАК КонтрагентНаименование,
|
ВалютаВзаиморасчетов КАК Валюта,
|
ВедениеВзаиморасчетов КАК ТипВеденияВзаиморасчетов
|ИЗ
|
Справочник.ДоговорыВзаиморасчетов
|
|УПОРЯДОЧИТЬ ПО
|
КонтрагентНаименование";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос возвратит список всех договоров контрагентов. Получить основные
договоры контрагентов можно из справочника Контрагенты.
Как вы уже знаете, многие документы оформляются с обязательным заполнением договора взаиморасчетов. Пользуясь этим, можно анализировать
данные по ним в разрезе договоров. Например, получим список реализаций
по выбранному контрагенту (каждый контрагент может иметь неограниченное количество договоров), где взаиморасчеты по договорам ведутся по заказам. Пример такого запроса представлен в листинге 26.4.
Листинг 26.4. Анализ реализаций по договорам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура,
Глава 26. Анализ взаиморасчетов
311
|
ХарактеристикаНоменклатуры,
|
ЗаказПокупателя КАК ЗаказПокупателя,
|
ДокументПоставки,
|
ДокументПродажи,
|
СУММА(Количество),
|
СУММА(СуммаПродажи)
|ИЗ
|
РегистрНакопления.ПродажиКомпании
|
|ГДЕ
|
Регистратор ССЫЛКА Документ.РеализацияТоваров И
|
ДоговорВзаиморасчетовПокупателя.ВедениеВзаиморасчетов =
|
&ВедениеВзаиморасчетов
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПокупателя,
|
ДокументПоставки,
|
ДокументПродажи
|
|УПОРЯДОЧИТЬ ПО
|
ЗаказПокупателя";
// Передаем в параметр запроса тип ведения взаиморасчетов по договору
Запрос.УстановитьПараметр("ВедениеВзаиморасчетов",
Перечисления.ВедениеВзаиморасчетовПоДоговорам.ПоЗаказам);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос возвратит сведения по документам реализаций, для которых взаиморасчеты ведутся по заказам.
Чтобы получить сумму задолженности по договору, следует воспользоваться
регистром остатков КонтрагентыВзаиморасчетыКомпании. Посмотрите пример, представленный в листинге 26.5.
Листинг 26.5. Получение суммы задолженности по договору
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
312
Часть IV. Анализ данных
Запрос.Текст = "
|ВЫБРАТЬ
|
СуммаВалОстаток,
|
СуммаОстаток
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки
|
(, ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов)";
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
СсылкаДоговорВзаиморасчетов);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Результатом запроса будут значения сумм задолженности по договору в валюте взаиморасчетов и управленческой валюте.
По договорам взаиморасчетов можно проанализировать движение денежных
средств предприятия. Например, если требуется оценить поступление наличных денежных средств в разрезе договора, следует воспользоваться регистром ДенежныеСредстваКомпании (листинг 26.6).
Листинг 26.6. Анализ поступления наличных денежных средств в разрезе
договора взаиморасчетов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
|
ПодразделениеКомпании КАК ПодразделениеКомпании,
КАК Период,
|
СтруктурнаяЕдиница
КАК СтруктурнаяЕдиница,
|
Сумма
КАК Сумма,
|
СуммаУпр
КАК СуммаУпр
|ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании
|
|ГДЕ
|
Регистратор ССЫЛКА Документ.ПриходныйКассовыйОрдер И
|
Регистратор.ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов
Глава 26. Анализ взаиморасчетов
313
|
|УПОРЯДОЧИТЬ ПО
|
Период
|
|ИТОГИ
|
СУММА(Сумма),
|
СУММА(СуммаУпр)
|ПО
|
ПодразделениеКомпании,
|
СтруктурнаяЕдиница";
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
СсылкаДоговорВзаиморасчетов);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
После выполнения запроса мы получим суммы поступления наличных денежных средств по выбранному договору. Напомню, что поступление наличных денег оформляется документом ПриходныйКассовыйОрдер. Для анализа
безналичных платежей по договору необходимо немного изменить запрос,
как показано в листинге 26.7.
Листинг 26.7. Анализ поступления безналичных денежных средств в разрезе
договора взаиморасчетов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
|
ПодразделениеКомпании КАК ПодразделениеКомпании,
КАК Период,
|
СтруктурнаяЕдиница
КАК СтруктурнаяЕдиница,
|
Сумма
КАК Сумма,
|
СуммаУпр
КАК СуммаУпр
|ИЗ
|
|
|ГДЕ
РегистрНакопления.ДенежныеСредстваКомпании
314
Часть IV. Анализ данных
|
Регистратор ССЫЛКА Документ.СтрокаВыпискиПриход И
|
Регистратор.ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов
|
|УПОРЯДОЧИТЬ ПО
|
Период
|
|ИТОГИ
|
СУММА(Сумма),
|
СУММА(СуммаУпр)
|ПО
|
ПодразделениеКомпании,
|
СтруктурнаяЕдиница";
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
СсылкаДоговорВзаиморасчетов);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Поступление безналичных денежных средств оформляется документом
СтрокаВыпискиПриход. Аналогичным образом можно проанализировать
расход наличных или безналичных денежных средств в разрезе договора.
Посмотрите пример, представленный в листинге 26.8.
Листинг 26.8. Анализ расходования наличных денежных средств в разрезе
договора взаиморасчетов
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
ПодразделениеКомпании КАК ПодразделениеКомпании,
|
СтруктурнаяЕдиница
КАК СтруктурнаяЕдиница,
|
Сумма
КАК Сумма,
|
СуммаУпр
КАК СуммаВУправленческойВалюте
|ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании
|
|ГДЕ
Глава 26. Анализ взаиморасчетов
|
Регистратор ССЫЛКА Документ.РасходныйКассовыйОрдер И
|
Регистратор.ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов
315
|
|УПОРЯДОЧИТЬ ПО
|
Период
|
|ИТОГИ
|
СУММА(Сумма),
|
СУММА(СуммаУпр)
|ПО
|
ПодразделениеКомпании,
|
СтруктурнаяЕдиница";
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов",
СсылкаДоговорВзаиморасчетов);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим суммы расхода наличных денежных средств по договору.
Взаиморасчеты с покупателями
Как уже говорилось ранее, все взаиморасчеты ведутся в разрезе договоров.
Например, при оформлении документа ЗаказПокупателя в обязательном порядке заполняется поле договора взаиморасчетов. Кроме того, валюта документа и договора должны совпадать. При проведении документов в регистры накопления заносятся суммы и ссылки на договоры, по которым в последующем формируется анализ взаиморасчетов с контрагентами.
В большинстве случаев для анализа взаиморасчетов применяется регистр
КонтрагентыВзаиморасчетыКомпании. Например, чтобы получить остаток
суммы взаиморасчетов в разрезе заказа покупателя, можно написать запрос,
как показано в листинге 26.9.
Листинг 26.9. Анализ взаиморасчетов по заказу покупателя
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
316
Часть IV. Анализ данных
Запрос.Текст = "
|ВЫБРАТЬ
|
ДоговорВзаиморасчетов КАК Договор,
|
Сделка
КАК ЗаказПокупателя,
|
СуммаВалОстаток
КАК СуммаВалОстаток,
|
СуммаОстаток
КАК СуммаОстаток
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&ДатаОтчета,
|
Сделка ССЫЛКА Документ.ЗаказПокупателя И
|
Сделка = &Заказ)";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказПокупателя);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате запрос возвратит остаток задолженности по выбранному заказу
покупателя. Чтобы проанализировать суммарную задолженность по всем заказам покупателей за период, можно написать запрос, представленный в листинге 26.10.
Листинг 26.10. Анализ задолженности по заказам покупателей за период
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Сделка
КАК ЗаказПокупателя,
|
СУММА(Сумма)
КАК Сумма,
|
СУММА(СуммаВал) КАК СуммаВал
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании
|
|ГДЕ
|
Сделка ССЫЛКА Документ.ЗаказПокупателя И
Глава 26. Анализ взаиморасчетов
|
317
Период МЕЖДУ &НачалоПериода И &КонецПериода
|
|СГРУППИРОВАТЬ ПО
|
Период,
|
Сделка
|
|УПОРЯДОЧИТЬ ПО
|
Сумма УБЫВ
|
|ИТОГИ
|
СУММА(Сумма),
|
СУММА(СуммаВал)
|ПО
|
ЗаказПокупателя";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДата()));
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим список заказов покупателей,
по которым существует задолженность.
Для анализа взаиморасчетов с конкретным контрагентом за выбранный период времени можно применить код из листинга 26.11.
Листинг 26.11. Анализ взаиморасчетов по контрагенту за период
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
Сделка
КАК Сделка,
|
СУММА(Сумма)
КАК Сумма,
|
СУММА(СуммаВал)
КАК СуммаВал,
|
ДоговорВзаиморасчетов.Владелец КАК Контрагент
|ИЗ
318
|
Часть IV. Анализ данных
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании
|
|ГДЕ
|
Период МЕЖДУ &НачалоПериода И &КонецПериода И
|
ДоговорВзаиморасчетов.Владелец = &Контрагент
|
|СГРУППИРОВАТЬ ПО
|
Период,
|
Сделка,
|
ДоговорВзаиморасчетов.Владелец
|
|УПОРЯДОЧИТЬ ПО
|
Сумма УБЫВ
|
|ИТОГИ
|
СУММА(Сумма),
|
СУММА(СуммаВал)
|ПО
|
Сделка";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДата()));
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Передаем в параметр запроса ссылку на контрагента
Запрос.УстановитьПараметр("Контрагент", СсылкаКонтрагент);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Выполнив запрос, мы получим сумму взаиморасчетов по выбранному контрагенту. Чтобы проследить взаиморасчеты по контрагенту в разрезе документов движения, воспользуемся оборотным регистром. Посмотрите пример запроса, показанный в листинге 26.12.
Листинг 26.12. Взаиморасчеты по контрагенту в разрезе документов движения
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
Глава 26. Анализ взаиморасчетов
319
|ВЫБРАТЬ
|
Сделка КАК Сделка,
|
СУММА(СуммаВалОборот) КАК СуммаВалОборот,
|
СУММА(СуммаВалПриход) КАК ЗаказаноСуммаВал,
|
СУММА(СуммаВалРасход) КАК ОплаченоСуммаВал,
|
СУММА(СуммаОборот) КАК СуммаОборот,
|
СУММА(СуммаПриход) КАК ЗаказаноСумма,
|
СУММА(СуммаРасход) КАК ОплаченоСумма
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Обороты(
|
&НачалоПериода, &КонецПериода, Регистратор,
|
ДоговорВзаиморасчетов.Владелец = &Контрагент)
|
|СГРУППИРОВАТЬ ПО
|
Сделка
|
|УПОРЯДОЧИТЬ ПО
|
Сделка
|
|ИТОГИ
|
СУММА(СуммаВалОборот),
|
СУММА(ЗаказаноСуммаВал),
|
СУММА(ОплаченоСуммаВал),
|
СУММА(СуммаОборот),
|
СУММА(ЗаказаноСумма),
|
СУММА(ОплаченоСумма)
|ПО
|
Сделка";
// Передаем в параметр запроса дату начала периода
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДата()));
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("КонецПериода", ТекущаяДата());
// Передаем в параметр запроса ссылку на контрагента
Запрос.УстановитьПараметр("Контрагент", СсылкаКонтрагент);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос возвратит нам историю взаиморасчетов по выбранному контрагенту
в пределах заданного периода. По результатам можно проанализировать, на
какую сумму было заказано товаров и в каком объеме они были оплачены.
320
Часть IV. Анализ данных
Чтобы проследить задолженность по оплате при ведении взаиморасчетов по
расчетным документам, поможет запрос, приведенный в листинге 26.13.
Листинг 26.13. Анализ задолженности по оплате при ведении взаиморасчетов
по расчетным документам
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка КАК Сделка,
|
ДоговорВзаиморасчетов,
ДоговорВзаиморасчетов.Владелец КАК Контрагент
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&ДатаОтчета,
|
ДоговорВзаиморасчетов.ВедениеВзаиморасчетов =
|
&ВедениеВзаиморасчетов И
|
ДоговорВзаиморасчетов = &ДоговорВзаиморасчетов)
|
|ГДЕ
|
СуммаОстаток > 0 И
|
ДоговорВзаиморасчетов.КонтролироватьЧислоДнейЗадолженности =
|
ИСТИНА
|
|УПОРЯДОЧИТЬ ПО
|
Сделка";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Передаем в параметр запроса ссылку на договор взаиморасчетов
Запрос.УстановитьПараметр("ДоговорВзаиморасчетов", СсылкаДоговор);
// Передаем в параметр запроса ссылку на контрагента
Запрос.УстановитьПараметр("ВедениеВзаиморасчетов",
Перечисления.ВедениеВзаиморасчетовПоДоговорам.ПоРасчетнымДокументам);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запрос возвратит нам договоры, по которым контролируется количество дней задолженности.
Глава 26. Анализ взаиморасчетов
321
Для получения курса валюты взаиморасчетов по контрагенту на выбранную
дату нам понадобится включить в запрос регистр сведений КурсыВалют. Как
это сделать, показано в листинге 26.14.
Листинг 26.14. Получение курса взаиморасчетов в разрезе контрагента
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
ВзаиморасчетыКомпании.ДоговорВзаиморасчетов,
|
ВзаиморасчетыКомпании.Сделка,
|
ВзаиморасчетыКомпании.СуммаВалОстаток,
|
КурсыВалют.Кратность,
|
КурсыВалют.Курс,
|
ВзаиморасчетыКомпании.ДоговорВзаиморасчетов.ВалютаВзаиморасчетов
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&ДатаОтчета, ДоговорВзаиморасчетов.Владелец = &Контрагент)
|
КАК ВзаиморасчетыКомпании
|
ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрСведений.КурсыВалют.СрезПоследних(&ДатаОтчета)
|
КАК КурсыВалют
|
ПО
|
=
ВзаиморасчетыКомпании.ДоговорВзаиморасчетов.ВалютаВзаиморасчетов
|
КурсыВалют.Валюта";
// Передаем в параметр запроса дату отчета
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Передаем в параметр запроса ссылку на контрагента
Запрос.УстановитьПараметр("Контрагент", СсылкаКонтрагент);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос вернет нам остаток суммы взаиморасчетов и курс валюты договора на
дату отчета.
322
Часть IV. Анализ данных
Взаиморасчеты с поставщиками
Ведение взаиморасчетов с поставщиками позволяет проследить собственные
платежи за поставленную продукцию. После того как оформлен заказ поставщику, заключается договор взаиморасчетов, в котором оговариваются
условия оплаты за поставленный товар. Факт закупки товаров регистрируется
документами поступлений. Если договор предусматривает предоплату, формируются соответствующие платежные документы расхода.
Например, чтобы проанализировать взаиморасчеты по заказу поставщику,
можно написать запрос, показанный в листинге 26.15.
Листинг 26.15. Анализ взаиморасчетов по заказу поставщику
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СуммаВалОстаток,
|
СуммаОстаток,
|
Сделка,
|
ДоговорВзаиморасчетов
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки
|
|ГДЕ
|
Сделка ССЫЛКА Документ.ЗаказПоставщику И
|
Сделка = &Заказ";
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("Заказ", СсылкаЗаказПоставщику);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения запроса мы получим суммы взаиморасчетов по заказу в валюте документа и управленческой валюте. Чтобы получить данные
взаиморасчетов по всем документам поступлений на выбранную дату, можно
написать запрос, как показано в листинге 26.16.
Глава 26. Анализ взаиморасчетов
323
Листинг 26.16. Анализ взаиморасчетов по документам поступлений
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
СуммаВалОстаток
КАК СуммаВалОстаток,
|
СуммаОстаток
КАК СуммаОстаток,
|
Сделка
КАК ДокументПоступление,
|
ДоговорВзаиморасчетов КАК ДоговорВзаиморасчетов
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&ДатаОтчета,
|
Сделка ССЫЛКА Документ.ПоступлениеТоваров)
|
|СГРУППИРОВАТЬ ПО
|
Сделка,
|
ДоговорВзаиморасчетов
|
|УПОРЯДОЧИТЬ ПО
|
ДокументПоступление
|
|ИТОГИ
|
СУММА(СуммаВалОстаток),
|
СУММА(СуммаОстаток)
|ПО
|
ДокументПоступление,
|
ДоговорВзаиморасчетов";
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос возвратит суммы взаиморасчетов по документам поступлений, а также итоговые значения на выбранную дату формирования отчета.
Для анализа взаиморасчетов по контрагентам в разрезе документов заказов
и поступлений можно использовать запрос, представленный в листинге 26.17.
324
Часть IV. Анализ данных
Листинг 26.17. Анализ взаиморасчетов по контрагентам в разрезе заказов
и поступлений
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка КАК Сделка,
|
СУММА(СуммаВалОстаток) КАК СуммаВалОстаток,
|
СУММА(СуммаОстаток) КАК СуммаОстаток,
|
ДоговорВзаиморасчетов.Владелец КАК Контрагент
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&ДатаОтчета)
|
|ГДЕ
|
Сделка ССЫЛКА Документ.ПоступлениеТоваров ИЛИ
|
Сделка ССЫЛКА Документ.ЗаказПоставщику
|
|СГРУППИРОВАТЬ ПО
|
ДоговорВзаиморасчетов.Владелец,
|
Сделка
|
|УПОРЯДОЧИТЬ ПО
|
Контрагент
|
|ИТОГИ
|
СУММА(СуммаВалОстаток),
|
СУММА(СуммаОстаток)
|ПО
|
Контрагент,
|
Сделка";
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Глава 26. Анализ взаиморасчетов
325
Запрос возвратит все документы (заказы поставщикам и поступления товаров) на выбранную дату, сгруппировав их по контрагентам.
Чтобы получить документы, по которым существует кредиторская задолженность перед контрагентами, выполните запрос, показанный в листинге 26.18.
Листинг 26.18. Анализ кредиторской задолженности на выбранную дату
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Сделка КАК Сделка,
|
СуммаВалОстаток,
|
СуммаОстаток,
|
ДоговорВзаиморасчетов
|ИЗ
|
РегистрНакопления.КонтрагентыВзаиморасчетыКомпании.Остатки(
|
&ДатаОтчета)
|
|ГДЕ
|
СуммаОстаток < 0
|
|УПОРЯДОЧИТЬ ПО
|
Сделка";
// Передаем в параметр запроса ссылку на заказ покупателя
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
В результате выполнения отчета мы получим список документов, по которым
существует кредиторская задолженность на выбранную дату.
На этом хотелось бы завершить тему анализа данных в 1С. Как видите, зная
структуру регистров, можно эффективно и целенаправленно формировать
запросы, отвечающие требованиям заказчиков.
326
Часть IV. Анализ данных
Часть V
Отчеты
328
Часть I. Приемы программирования документов
Глава 27
Создание и оформление отчетов
Одной из наиболее важных составляющих системы "1С:Предприятие 8.0"
являются отчеты. Они служат своеобразным мостом между накопленной
в процессе работы информацией (посредством документов, справочников
и регистров) и средствами получения последней (выборками и запросами).
И правда, мало пользы от данных, полученных из запроса, если они не имеют
удобного интерфейса для отображения и настройки. На основе отчетов строятся объекты любой сложности, позволяющие анализировать и рассчитывать
собранную информацию, а также выводить на печать в виде таблиц, графиков и диаграмм. Предлагаются широкие возможности по настройке условий
отборов, расположению элементов в таблице, внешнего вида отчетов. Одним
словом, фирма 1С приложила немало трудов, чтобы облегчить труд разработчиков, но в то же время предложить пользователям удобные средства для
сбора и анализа имеющихся в базе данных.
В этой главе мы познакомимся с тем, как создавать отчеты на базе существующих и с помощью специального объекта — построителя отчетов. Конечно,
трудно охватить все аспекты, связанные с отчетами. Для этого понадобится
отдельная книга. Поэтому изучим наиболее важные и базовые элементы,
чтобы в дальнейшем вы могли самостоятельно осваивать новые и не вошедшие в эту книгу возможности.
Объект отчета в 1С является такой же самостоятельной частью, как документы или справочники. Он создается в конфигураторе и поддерживает реквизиты, табличные части, формы и макеты. Для работы с ним предназначены
следующие базовые объекты:
ОтчетыМенеджер — позволяет получать доступ и управлять коллекцией
объектов типа ОтчетМенеджер;
330
Часть V. Отчеты
ОтчетМенеджер — предназначен для доступа к определенному объекту
отчета;
ОтчетОбъект — позволяет работать с реквизитами, табличными частями,
формами и макетами отчета.
О том, как применять свойства и методы этих объектов, мы еще поговорим.
Создание нового отчета на базе
существующих
Если вы работаете в уже готовой конфигурации, у вас есть возможность использовать готовые отчеты, входящие в поставку. Они, как правило, предоставляют базовый функционал для сбора и анализа данных. Забегая вперед,
скажу, что в ходе работы выясняется необходимость в создании собственных
отчетных форм, удовлетворяющих текущим требованиям работы вашего
предприятия. Но это происходит не сразу, и на начальном этапе вполне хватает встроенных возможностей. Например, существует весьма полезный отчет АнализЗаказаПокупателя, который выводит аналитику по текущему документу ЗаказПокупателя (или ВнутреннийЗаказ). Для его формирования
можно применить код, представленный в листинге 27.1.
Листинг 27.1. Создание отчета на базе отчета АнализЗаказаПокупателя
// Создаем новый объект отчета АнализЗаказаПокупателя
АнализЗаказаОтчет = Отчеты.АнализЗаказаПокупателя.Создать();
// Получаем ссылку на заказ
ЗаказСсылка = Документы.ЗаказПокупателя.НайтиПоНомеру("0020");
// Передаем ссылку на заказ покупателя
АнализЗаказаОтчет.ЗаказПокупателя = ЗаказСсылка;
// Получаем основную форму отчета
АнализЗаказаФорма = АнализЗаказаОтчет.ПолучитьФорму();
// Заполняем отчет данными
АнализЗаказаОтчет.СформироватьОтчет(
АнализЗаказаФорма.ЭлементыФормы.ДокументРезультат);
// Выводим созданный отчет на экран
АнализЗаказаФорма.Открыть();
Из примера видно, что создать новый отчет на базе существующего достаточно просто. Сперва мы использовали метод Создать (ОтчетМенеджер) для
Глава 27. Создание и оформление отчетов
331
создания нового объекта ОтчетОбъект. Затем получили ссылку на существующий документ ЗаказПокупателя и передали ее в соответствующий реквизит базового отчета АнализЗаказаПокупателя. Далее получили основную
форму отчета посредством метода ПолучитьФорму (объект ОтчетОбъект).
Самое важное, формирование данных для отчета выполнено в процедуре
СформироватьОтчет, которая принимает в качестве параметра ссылку на табличный документ базового отчета. В завершение мы вызвали метод Открыть
(объект Форма), чтобы отобразить результат на экране. Следует заметить, что
процедура СформироватьОтчет не является встроенной, и ее необходимо написать самостоятельно. Пример процедуры, формирующей отчет, представлен в листинге 27.2.
Листинг 27.2. Пример процедуры для формирования отчета
Процедура СформироватьОтчет(ДокументРезультатСсылка)
// Создаем новый запрос
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
|
ЗаказыПокупателей.Номенклатура КАК Номенклатура,
|
ЗаказыПокупателей.ХарактеристикаНоменклатуры
|
КАК ХарактеристикаНоменклатуры,
|
ЗаказыПокупателей.КоличествоПриход КАК Запланировано,
|
ЗаказыПокупателей.КоличествоРасход КАК ОтгруженоОтменено,
|
ЗаказыПокупателей.КоличествоКонечныйОстаток
|
КАК ОсталосьОтгрузить,
|
ОстаткиТоваров.КоличествоОстаток КАК Резерв,
|
ЗаказыПоставщикам.КоличествоОстаток КАК Заказано,
|
ОстаткиТоваровСвободные.КоличествоОстаток
|
КАК СвободныйОстаток
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.ОстаткиИОбороты(
|
,&Период, , ,ЗаказПокупателя = &Заказ)
|
КАК ЗаказыПокупателей
|ЛЕВОЕ СОЕДИНЕНИЕ
|
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки(
&Период) КАК ОстаткиТоваров
|ПО ЗаказыПокупателей.Номенклатура = ОстаткиТоваров.Номенклатура
|
И ЗаказыПокупателей.ЗаказПокупателя = ОстаткиТоваров.Заказ
332
Часть V. Отчеты
|
И ЗаказыПокупателей.ХарактеристикаНоменклатуры =
|
ОстаткиТоваров.ХарактеристикаНоменклатуры
|
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ЗаказыПоставщикам.Остатки(
|
&Период) КАК ЗаказыПоставщикам
|ПО ЗаказыПокупателей.ЗаказПокупателя =
|
ЗаказыПоставщикам.ЗаказПокупателя
|И ЗаказыПокупателей.Номенклатура = ЗаказыПоставщикам.Номенклатура
|И ЗаказыПокупателей.ХарактеристикаНоменклатуры =
|
ЗаказыПоставщикам.ХарактеристикаНоменклатуры
|
|ЛЕВОЕ СОЕДИНЕНИЕ
|
РегистрНакопления.ОстаткиТоваровКомпании.Остатки(
|
&Период, Заказ = NULL) КАК ОстаткиТоваровСвободные
|ПО ЗаказыПокупателей.Номенклатура =
|
ОстаткиТоваровСвободные.Номенклатура
|
И ЗаказыПокупателей.ХарактеристикаНоменклатуры =
|
ОстаткиТоваровСвободные.ХарактеристикаНоменклатуры
|
|ИТОГИ
|
СУММА(Запланировано),
|
СУММА(ОтгруженоОтменено),
|
СУММА(ОсталосьОтгрузить),
|
СУММА(Резерв),
|
СУММА(Заказано),
|
СУММА(СвободныйОстаток)
|ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры";
// Задаем значения параметров
Запрос.УстановитьПараметр("Период", ДатаАнализа);
Запрос.УстановитьПараметр("Заказ", ЗаказСсылка);
// Выполняем запрос
Выборка = Запрос.Выполнить().Выбрать();
// Получаем стандартный макет из объекта отчета
Макет = ЭтотОбъект.ПолучитьМакет("Макет");
// Получаем область шапки
Шапка = Макет.ПолучитьОбласть("Шапка");
Глава 27. Создание и оформление отчетов
333
// Выводим в табличный документ
ДокументРезультатСсылка.Вывести(Шапка);
// Получаем область строки
Строка = Макет.ПолучитьОбласть("Строка");
// Заполняем параметры шапки
Пока Выборка.Следующий() Цикл
// Заполняем значения параметров
Строка.Параметры.ЗначениеГруппировки =
Выборка.Номенклатура + Выборка.ХарактеристикаНоменклатуры;
Строка.Параметры.Запланировано = Выборка.Запланировано;
Строка.Параметры.ОтгруженоОтменено =
Выборка.ОтгруженоОтменено;
Строка.Параметры.ОсталосьОтгрузить =
Выборка.ОсталосьОтгрузить;
Строка.Параметры.Резерв = Выборка.Резерв;
Строка.Параметры.Заказано = Выборка.Заказано;
Строка.Параметры.СвободныйОстаток =
Выборка.СвободныйОстаток;
Строка.Параметры.Заказать = Выборка.ОсталосьОтгрузить –
Выборка.Резерв - Выборка.Заказано - Выборка.СвободныйОстаток;
ДокументРезультатСсылка.Вывести(Строка);
КонецЦикла;
КонецПроцедуры
Основными показателями состояния заказа покупателя являются количественные значения: сколько заказано, сколько отгружено, сколько в резерве
и в свободном остатке. Чтобы получить эти данные, мы выполнили запрос
к нескольким регистрам накопления, соединив их по полям номенклатуры
и характеристике номенклатуры. Учет по характеристике товара обязателен,
поскольку один и тот же товар может иметь несколько характеристик. Далее
мы получаем стандартный макет отчета и заполняем его данными запроса.
В реальном отчете АнализЗаказаПокупателя дополнительно анализируются
суммы взаиморасчетов, но мы этого делать не будем, поскольку основной
целью являются методы использования готовых отчетов.
Следующий метод работы с готовыми отчетами позволяет сформировать
и вывести готовый отчет с заданными параметрами группировок, показателей, полей выборки и упорядочивания. Он более эффективен, когда нужно на
основании определенного значения таблицы построить анализ и представить
334
Часть V. Отчеты
пользователю в виде стандартной отчетной формы. Рассмотрим пример на
базе стандартного отчета ПланыПродаж, представленный в листинге 27.3.
Листинг 27.3. Программная настройка стандартного отчета
// Создаем новый объект отчета ПланыПродаж
ПланыПродажОтчет = Отчеты.ПланыПродаж.Создать();
// Заполняем реквизиты отчета
ПланыПродажОтчет.ДатаНач = НачалоМесяца(ТекущаяДата());
ПланыПродажОтчет.ДатаКон = ТекущаяДата();
// Добавляем группировку по номенклатуре
ПараметрГруппировки = ПланыПродажОтчет.ГруппировкиОтчета.Добавить();
ПараметрГруппировки.ИмяГруппировки = "Номенклатура";
ПараметрГруппировки.ПредставлениеГруппировки = "Номенклатура";
ПараметрГруппировки.ОписаниеГруппировки = "Номенклатура";
ПараметрГруппировки.РассчитыватьИтоги = Истина;
ПараметрГруппировки.РассчитыватьИтогиПоИерархии = Истина;
// Добавляем группировку по документу планирования
ПараметрГруппировки = ПланыПродажОтчет.ГруппировкиОтчета.Добавить();
ПараметрГруппировки.ИмяГруппировки = "ДокументПланирования";
ПараметрГруппировки.ПредставлениеГруппировки = "Документ планирования";
ПараметрГруппировки.ОписаниеГруппировки = "ДокументПланирования";
ПараметрГруппировки.РассчитыватьИтоги = Истина;
ПараметрГруппировки.РассчитыватьИтогиПоИерархии = Ложь;
// Добавляем показатели
Показатель = ПланыПродажОтчет.ПоказателиОтчета.Добавить();
Показатель.ИмяПоказателя = "Количество";
Показатель.ПредставлениеПоказателя = "Количество (в единицах хранения)";
Показатель.ОписаниеПоказателя = "КоличествоОборот";
Показатель = ПланыПродажОтчет.ПоказателиОтчета.Добавить();
Показатель.ИмяПоказателя = "СуммаПродажи";
Показатель.ПредставлениеПоказателя = "Сумма продаж за период";
Показатель.ОписаниеПоказателя = "СуммаПродажиОборот";
Показатель = ПланыПродажОтчет.ПоказателиОтчета.Добавить();
Показатель.ИмяПоказателя = "Себестоимость";
Показатель.ПредставлениеПоказателя = "Себестоимость";
Показатель.ОписаниеПоказателя = "СебестоимостьОборот";
// Получаем основную форму отчета
ПланыПродажФорма = ПланыПродажОтчет.ПолучитьФорму();
Глава 27. Создание и оформление отчетов
335
// Заполняем отчет данными
ПланыПродажОтчет.СформироватьОтчет(
ПланыПродажФорма.ЭлементыФормы.ДокументРезультат);
// Выводим созданный отчет на экран
ПланыПродажФорма.Открыть();
Отчет ПланыПродаж содержит табличные части ПоказателиОтчета и ГруппировкиОтчета, позволяющие настраивать соответствующие параметры отчета. Воспользовавшись этим, мы программно заполнили их определенными
значениями и вывели сформированный отчет на экран. Поскольку здесь все
просто, перейдем к следующей теме. Хочу только заметить, что все необходимые сведения о табличных частях, параметрах группировок и показателях
можно получить в конфигураторе.
Использование построителя отчетов
Как я уже говорил, в процессе работы с 1С приходишь к тому, что стандартных отчетов при всем их разнообразии недостаточно. Связано это, прежде
всего, со спецификой организации работы каждого отдельно взятого предприятия. И тогда возникает естественный вопрос: создавать новый отчет от
начала до конца самостоятельно или найти возможность упростить процесс и
сократить время, затраченное на разработку. Фирма 1С предложила программистам универсальное средство, позволяющее в короткие строки построить полноценный отчет, оснастив его всеми необходимыми пользователю настройками. Этим средством является специальный объект, именуемый
ПостроительОтчета. Мы изучим основные методы и свойства этого объекта
и научимся создавать на его базе собственный отчет.
В первую очередь, мы должны создать в конфигураторе новый объект Отчет
или внешнюю обработку. Затем следует добавить новый реквизит (например,
НашОтчет) и указать тип значения как ссылку на объект ПостроительОтчета.
Далее в модуле объекта необходимо написать текст запроса так, как показано
в листинге 27.4.
Листинг 27.4. Текст запроса, передаваемый в объект ПостроительОтчета
// Формируем текст запроса
НашОтчет.Текст = "
|ВЫБРАТЬ
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
336
Часть V. Отчеты
|
ДокументПоставки КАК ДокументПоставки,
|
ДокументПродажи КАК ДокументПродажи,
|
ЗаказПокупателя КАК ЗаказПокупателя,
|
КоличествоОборот КАК КоличествоОборот,
|
СуммаПродажиОборот КАК СуммаПродажиОборот
|ИЗ
|
РегистрНакопления.ПродажиКомпании.Обороты
|
|ИТОГИ СУММА(КоличествоОборот), СУММА(СуммаПродажиОборот) ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПокупателя,
|
ДокументПоставки,
|
ДокументПродажи
|АВТОУПОРЯДОЧИВАНИЕ";
// Заполняем настройки отчета из текста запроса
НашОтчет.ЗаполнитьНастройки();
Итак, мы написали запрос, использовав свойство Текст, и вызвали метод
ЗаполнитьНастройки (объект ПостроительОтчета), который выполнил автоматическое заполнение настроек построителя из текста запроса. На этом этапе можно сказать, что основная часть нашего отчета готова. Теперь создайте
новую форму и для кнопки Выполнить задайте обработчик нажатия. Добавьте в него код, как показано в листинге 27.5.
Листинг 27.5. Обработчик нажатия кнопки Выполнить
Процедура КнопкаСформироватьНажатие(Элемент)
НашОтчет.Выполнить();
НашОтчет.Вывести();
КонецПроцедуры
Первый метод Выполнить предназначен для выполнения запроса, заданного
в свойстве Текст. Второй метод позволяет вывести результат запроса в табличный документ. Если указать в параметре метода Вывести ссылку на табличный документ, то данные будут выводиться в него (иначе будет создан
новый табличный документ). Для этого добавьте в форму поле табличного
документа и передайте его имя в метод Вывести. Чтобы добавить настройки
отбора, достаточно добавить в форму новое табличное поле и присвоить
Глава 27. Создание и оформление отчетов
337
свойству Данные значение НашОтчет.Отбор. Кроме этого, можно добавить
табличные поля для следующих настроек отчета: НашОтчет.Порядок (упорядочивание результирующих полей запроса), НашОтчет.ДоступныеПоля (список
полей, доступных для выбора), НашОтчет.ВыбранныеПоля (список полей, доступных для вывода в отчет), НашОтчет.ИзмеренияСтроки (доступные измерения по строкам) и НашОтчет.ИзмеренияКолонки (доступные измерения по колонкам). Как видите, достаточно просто добавить в новый отчет любые
имеющиеся в 1С стандартные настройки. Свойства объекта Построитель
Отчета позволяют легко управлять всеми перечисленными настройками. Например, чтобы добавить условия отбора из доступных полей запроса, можно
применить код, представленный в листинге 27.6.
Листинг 27.6. Добавление условий отбора
Отбор = НашОтчет.Отбор;
// Добавляем отбор по номенклатуре
Если Отбор.Найти("Номенклатура") = Неопределено Тогда
Отбор.Добавить("Номенклатура");
Отбор["Номенклатура"].Использование = Истина;
Отбор["Номенклатура"].Значение = НоменклатураСсылка;
Отбор["Номенклатура"].ВидСравнения = ВидСравнения.Равно;
КонецЕсли;
// Добавляем отбор по характеристике номенклатуры
Если Отбор.Найти("ХарактеристикаНоменклатуры") = Неопределено Тогда
Отбор.Добавить("ХарактеристикаНоменклатуры");
Отбор["ХарактеристикаНоменклатуры"].Использование = Истина;
Отбор["ХарактеристикаНоменклатуры"].Значение =
ХарактеристикаНоменклатурыСсылка;
Отбор["ХарактеристикаНоменклатуры"].ВидСравнения =
ВидСравнения.Равно;
КонецЕсли;
// Добавляем отбор по заказу покупателя
Если Отбор.Найти("ЗаказПокупателя") = Неопределено Тогда
Отбор.Добавить("ЗаказПокупателя");
Отбор["ЗаказПокупателя"].Использование = Истина;
Отбор["ЗаказПокупателя"].Значение = ЗаказПокупателяСсылка;
Отбор["ЗаказПокупателя"].ВидСравнения = ВидСравнения.Равно;
КонецЕсли;
338
Часть V. Отчеты
Чтобы настроить условия отбора, мы вначале получили доступные отборы
построителя отчета. Затем, пользуясь методами объекта Отбор, убедились,
что отборы по интересующим нас полям отсутствуют, и заполнили их вручную. Заметьте, что свойство построителя отчета Отбор доступно только для
чтения, поэтому мы читаем свойство отбора и записываем в объект Отбор,
а уже затем добавляем поля в условия отбора.
Аналогичным способом добавляются поля упорядочивания. Посмотрите
пример, показанный в листинге 27.7.
Листинг 27.7. Добавление полей упорядочивания
Порядок = НашОтчет.Порядок;
// Добавляем упорядочивание по номенклатуре
Если Порядок.Найти("Номенклатура") = Неопределено Тогда
Порядок.Добавить("Номенклатура");
КонецЕсли;
// Добавляем упорядочивание по характеристике номенклатуры
Если Порядок.Найти("ХарактеристикаНоменклатуры") = Неопределено Тогда
Порядок.Добавить("ХарактеристикаНоменклатуры");
КонецЕсли;
Пользуясь методами объекта Порядок, мы добавили в построитель отчета
поля, по которым будет выполняться упорядочивание данных.
Для управления выбором полей для отчета предназначено свойство ВыбранныеПоля.
Оно также доступно только для чтения и возвращает ссылку на выбранные
поля построителя отчета. Чтобы добавить нужные поля, воспользуйтесь примером из листинга 27.8.
Листинг 27.8. Добавление выбранных полей в построитель отчета
Поля = НашОтчет.ВыбранныеПоля;
// Добавляем поле наименования номенклатуры
Если Поля.Найти("Наименование номенклатуры") = Неопределено Тогда
Поля.Добавить("Номенклатура.Наименование",
"Наименование номенклатуры");
КонецЕсли;
// Добавляем поле организации из заказа покупателя
Если Поля.Найти("Организация") = Неопределено Тогда
Глава 27. Создание и оформление отчетов
339
Поля.Добавить("ЗаказПокупателя.Организация", "Организация");
КонецЕсли;
// Добавляем валюту документа продажи
Если Поля.Найти("Валюта") = Неопределено Тогда
Поля.Добавить("ДокументПродажи.Валюта", "Валюта");
КонецЕсли;
Вначале мы прочитали существующие поля и сохранили в объекте ПоляПостроителяОтчета (переменная Поля). Затем с помощью метода Добавить записали новые значения для выбранных полей отчета. Этот метод имеет два аргумента. Первый задает путь для поля выборки, а второй содержит имя поля,
которое будет использовано построителем.
Естественно, выбранные поля построителя отчета можно не только добавлять, но и удалять. Делается это посредством метода Удалить (удаляет одно
поле) или метода Очистить (удаляет все поля). В листинге 27.9 показаны
примеры использования данных методов.
Листинг 27.9. Удаление выбранных полей из построителя отчета
Поля = НашОтчет.ВыбранныеПоля;
// Удаляем поле номенклатуры
ПолеНоменклатуры = Поля.Найти("Номенклатура");
Если ПолеНоменклатуры <> Неопределено Тогда
Поля.Удалить(ПолеНоменклатуры);
КонецЕсли;
// Удаляем поле документа поставки
ПолеДокПоставки = Поля.Найти("ДокументПоставки");
Если ПолеДокПоставки <> Неопределено Тогда
Поля.Удалить(ПолеДокПоставки);
КонецЕсли;
// Очищаем всю коллекцию выбранных полей отчет
Поля.Очистить();
Метод Удалить принимает ссылку на удаляемое поле, которую возвращает
метод Найти. Метод Очистить просто удаляет все доступные поля.
Немного иначе удаляются отборы из отчета. Посмотрите пример, представленный в листинге 27.10.
340
Часть V. Отчеты
Листинг 27.10. Удаление отборов из построителя отчета
Отборы = НашОтчет.Отбор;
// Удаляем отборы отчета
Для Индекс = 1 По Отбор.Количество() - 1 Цикл
// Удаляем отборы по индексу
Отбор.Удалить(Индекс);
КонецЦикла;
Метод Удалить объекта Отбор принимает в качестве параметра индекс удаляемого элемента. Чтобы вычислить доступное количество отборов, мы использовали метод Количество. Существует также возможность не удалять
поля отборов, а лишь сбросить признак использования. Делается это посредством метода Сбросить. Соответствующий пример показан в листинге 27.11.
Листинг 27.11. Сброс признака использования полей отбора
// Получаем доступные отборы построителя отчета
Отборы = НашОтчет.Отбор;
// Сбрасываем признак использования
Отборы.Сбросить();
Думаю, здесь все понятно. Замечу только, что метод Сбросить отключает использование не отдельного поля отбора, а всех вместе.
Для удаления полей упорядочивания предназначен метод Удалить объекта
Порядок. Чтобы очистить все поля, можно применить метод Очистить. Пример работы с данными методами представлен в листинге 27.12.
Листинг 27.12. Удаление полей упорядочивания из построителя отчета
// Получаем доступные элементы порядка отчета
Порядки = НашОтчет.Порядок;
// Удаляем элементы порядка
Для Индекс = 1 По Порядки.Количество() - 1 Цикл
// Удаляем отборы по индексу
Порядки.Удалить(Индекс);
КонецЦикла;
Глава 27. Создание и оформление отчетов
341
Следующие свойства объекта ПостроительОтчета, о которых мы поговорим,
позволяют управлять выводом различных областей макета в табличную часть
отчета. Для отображения заголовка отчета предназначено свойство ВыводитьЗаголовокОтчета. Чтобы показать шапку таблицы, служит свойство ВыводитьШапкуТаблицы. Отобразить подвал таблицы помогает свойство ВыводитьПодвалТаблицы.
И последние два свойства, ВыводитьОбщиеИтоги
и ВыводитьПодвалОтчета, управляют, соответственно, подвалом отчета и
областью итогов по таблице. Пример использования представленных свойств
показан в листинге 27.13.
Листинг 27.13. Использование свойств вывода областей макета построителя
отчета
// Выводим заголовок отчета
НашОтчет.ВыводитьЗаголовокОтчета = Истина;
// Выводим шапку таблицы отчета
НашОтчет.ВыводитьШапкуТаблицы = Истина;
// Запрещаем вывод подвала таблицы
Если НашОтчет.ВыводитьПодвалТаблицы Тогда
НашОтчет.ВыводитьПодвалТаблицы = Ложь;
КонецЕсли;
// Выводим общие итоги отчета
НашОтчет.ВыводитьОбщиеИтоги = Истина;
// Запрещаем вывод подвала отчета
Если НашОтчет.ВыводитьПодвалОтчета Тогда
НашОтчет.ВыводитьПодвалОтчета = Ложь;
КонецЕсли;
Думаю, рассмотренный пример не требует дополнительных комментариев.
Свойство ЗаполнениеРасшифровки построителя отчета позволяет установить
или получить текущий вид расшифровки. Доступны следующие виды расшифровки: ЗначенияГруппировок (расшифровка заполняется данными группировок), Расшифровка (расшифровка заполняется структурой данных по
всем группировкам) и НеЗаполнять (расшифровка не используется). Пример
работы с этим свойством представлен в листинге 27.14.
Листинг 27.14. Использование свойства ЗаполнениеРасшифровки
// Формируем текст запроса
НашОтчет.Текст = "
|ВЫБРАТЬ
342
Часть V. Отчеты
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
ЗаказПокупателя КАК ЗаказПокупателя,
|
ДоговорВзаиморасчетов КАК ДоговорВзаиморасчетов,
|
КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ЗаказыПокупателей.Остатки
|
|ИТОГИ СУММА(КоличествоОстаток) ПО
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
ЗаказПокупателя,
|
ДоговорВзаиморасчетов
|АВТОУПОРЯДОЧИВАНИЕ";
// Заполняем настройки отчета из текста запроса
НашОтчет.ЗаполнитьНастройки();
// Устанавливаем вид расшифровки
НашОтчет.ЗаполнениеРасшифровки =
ВидЗаполненияРасшифровкиПостроителяОтчета.ЗначенияГруппировок;
// Выводим заголовок отчета
НашОтчет.ВыводитьЗаголовокОтчета = Истина;
// Задаем заголовок отчета
НашОтчет.ТекстЗаголовка = "Наш отчет по заказам покупателей";
// Формируем отчет
НашОтчет.Выполнить();
Как видите, вид расшифровки мы задали на этапе построения отчета, но до
его выполнения. Когда отчет будет сформирован, можно получить расшифровку по любому полю группировки. Дополнительно в рассмотренном примере мы привели свойство ТекстЗаголовка для установки собственного заголовка отчета.
Следующее свойство ИсточникДанных помогает легко задать объект, по которому будет строиться отчет. В качестве источника данных может выступать один из объектов: РезультатЗапроса, ТаблицаЗначений, ТабличнаяЧасть,
РегистрНакопленияНаборЗаписей или РегистрСведенийНаборЗаписей. Например, напишем код, в котором источником данных будет таблица значений из
полей выборки запроса (листинг 27.15).
Глава 27. Создание и оформление отчетов
Листинг 27.15. Использование свойства ИсточникДанных
// Формируем текст запроса
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
|
Период КАК Период,
|
Номенклатура КАК Номенклатура,
|
ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
|
Заказ КАК Заказ,
|
Регистратор КАК Регистратор,
|
Количество КАК Количество
|ИЗ
|
РегистрНакопления.ОстаткиТоваровКомпании
|
|ИТОГИ СУММА(Количество) ПО
|
Период,
|
Номенклатура,
|
ХарактеристикаНоменклатуры,
|
Заказ,
|
Регистратор
|АВТОУПОРЯДОЧИВАНИЕ";
// Выполняем запрос
Выборка = Запрос.Выполнить();
// Выгружаем результат запроса в таблицу значений
ТабЗначений = Выборка.Выгрузить();
// Формируем новый источник данных для построителя
ИсточникДанных = Новый ОписаниеИсточникаДанных (ТабЗначенй);
// Передаем ссылку на источник данных в отчет
НашОтчет.ИсточникДанных = ИсточникДанных;
// Заполняем настройки отчета из текста запроса
НашОтчет.ЗаполнитьНастройки();
// Устанавливаем вид расшифровки
НашОтчет.ЗаполнениеРасшифровки =
ВидЗаполненияРасшифровкиПостроителяОтчета.ЗначенияГруппировок;
// Выводим заголовок отчета
НашОтчет.ВыводитьЗаголовокОтчета = Истина;
// Задаем заголовок отчета
343
344
Часть V. Отчеты
НашОтчет.ТекстЗаголовка = "Отчет по остаткам товаров компании";
// Формируем отчет
НашОтчет.Выполнить();
Итак, вначале мы создали объект Запрос и результат выгрузили в таблицу
значений. Затем присвоили свойству ИсточникДанных вновь созданное описание источника данных. Далее заполнили настройки и расшифровку отчета,
чтобы сформировать готовый отчет по остаткам товаров компании на складах. Как видите, все достаточно просто и понятно.
Построитель отчета включает в себя готовые макеты, которые можно настраивать по собственному усмотрению. Для использования макета предназначено
свойство Макет. Оно доступно для чтения и записи. В качестве доступного значения используется объект ТабличныйДокумент. По умолчанию построитель
формирует готовый макет в соответствии с полями запроса. Если разработчик
хочет применить свой макет или подкорректировать существующий, следует
прочитать свойство Макет, внести изменения и записать повторно. Пример кода, демонстрирующий сказанное, представлен в листинге 27.16.
Листинг 27.16. Использование свойства Макет
// Сбрасываем свойство Макет
НашОтчет.Макет = Неопределено;
// Получаем сгенерированный построителем отчета макет
Макет = НашОтчет.Макет;
// Корректируем цвет текста макета
Макет.Область().ЦветТекста = Новый Цвет(40, 180, 60); // зеленый
// Корректируем цвет фона макета
Макет.Область().ЦветФона = Новый Цвет(192, 192, 192); // серый
// Меняем шрифт
Макет.Область().Шрифт = Новый Шрифт("Courier New", 12);
// Делаем отступ от края ячеек макета
Макет.Область().Отступ = 2; // два символа
// Записываем измененный макет
НашОтчет.Макет = Макет;
Прежде всего, мы сбросили существующий макет, присвоив значение
Неопределено. Затем получили сгенерированный построителем вариант макета. С помощью метода Область (объект ТабличныйДокумент) получили
ссылку на область макета. Далее настроили требуемые свойства и записали
Глава 27. Создание и оформление отчетов
345
скорректированный макет в объект построителя отчета. Основным правилом
здесь является то, что перед внесением каких-либо изменений в автоматически созданный макет требуется вначале сбросить и прочитать сгенерированный объект, а после корректировки записать обратно.
Естественно, кроме общих настроек макета, может понадобиться изменить
формат или внешний вид отдельных ячеек. Например, если нужно поменять
формат представления суммы в ячейке, воспользуйтесь исходным текстом,
показанным в листинге 27.17.
Листинг 27.17. Корректировка отдельной ячейки макета
// Сбрасываем свойство Макет
НашОтчет.Макет = Неопределено;
// Получаем сгенерированный построителем отчета макет
Макет = НашОтчет.Макет;
// Задаем область поиска
ОбластьЯчейки = Неопределено;
// Выполняем поиск нужной ячейки
Пока 1 = 1 Цикл
ОбластьЯчейки = Макет.НайтиТекст("СуммаПродажиОборот",
ОбластьЯчейки, Макет.Область(), Истина, Истина, Истина, Ложь);
// Проверяем результат поиска
Если ОбластьЯчейки <> Неопределено Тогда
// Читаем текущий параметр ячейки
Если ОбластьЯчейки.Параметр = "СуммаПродажиОборот" Тогда
// Задаем формат представления суммы
ОбластьЯчейки.Формат = "ЧЦ=15;ЧДЦ=2";
КонецЕсли;
Иначе
// Выходим из цикла
Прервать;
КонецЕсли;
КонецЦикла;
// Записываем измененный макет
НашОтчет.Макет = Макет;
В этом примере мы прочитали сгенерированный построителем отчета макет.
Затем посредством метода НайтиТекст (объект ТабличныйДокумент) выполнили поиск нужного значения ячейки. Если оно было найдено, установили
346
Часть V. Отчеты
новый формат представления суммы оборотов из таблицы запроса. Таким же
образом можно скорректировать любую ячейку отчета, имеющую заданный
параметр. Например, чтобы выделить жирным шрифтом ячейку, хранящую
наименование номенклатуры, воспользуйтесь кодом, представленным в листинге 27.18.
Листинг 27.18. Корректировка шрифта выбранной ячейки макета
// Сбрасываем свойство Макет
НашОтчет.Макет = Неопределено;
// Получаем сгенерированный построителем отчета макет
Макет = НашОтчет.Макет;
// Задаем область поиска
ОбластьЯчейки = Неопределено;
// Выполняем поиск нужной ячейки
Пока 1 = 1 Цикл
ОбластьЯчейки = Макет.НайтиТекст("Номенклатура",
ОбластьЯчейки, Макет.Область(), Истина, Истина, Истина, Ложь);
// Проверяем результат поиска
Если ОбластьЯчейки <> Неопределено Тогда
// Читаем текущий параметр ячейки
Если ОбластьЯчейки.Параметр = "СуммаПродажиОборот" Тогда
// Задаем жирный шрифт
ОбластьЯчейки.Шрифт =
Новый Шрифт("Courier New", 12, Истина);
КонецЕсли;
Иначе
// Выходим из цикла
Прервать;
КонецЕсли;
КонецЦикла;
// Записываем измененный макет
НашОтчет.Макет = Макет;
Думаю, дополнительных комментариев здесь не требуется.
Если вам требуется добавить в сгенерированный построителем отчета макет
логотип или любое другое изображение, можно применить исходный код,
показанный в листинге 27.19.
Глава 27. Создание и оформление отчетов
347
Листинг 27.19. Добавление файла изображения в макет построителя отчета
// Сбрасываем свойство Макет
НашОтчет.Макет = Неопределено;
// Получаем сгенерированный построителем отчета макет
Макет = НашОтчет.Макет;
// Загружаем стандартный диалог выбора файлов
ДиалогВыбораФайлов =
Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогВыбораФайлов.Заголовок = "Выбрать файл изображения";
ДиалогВыбораФайлов.ПредварительныйПросмотр = Истина;
ДиалогВыбораФайлов.Фильтр =
"Файл BMP (*.bmp)|*.bmp|Файл JPEG (*.jpg)|*.jpg|Файл ICO (*.ico)|*.ico";
// Выбираем файл
Если ДиалогВыбораФайлов.Выбрать() Тогда
// Формируем новый объект изображения
ВыбранноеИзображение =
Новый Картинка(ДиалогВыбораФайлов.ПолноеИмяФайла);
// Сохраняем изображение в существующей константе ТоварныйЗнак
Константы.ТоварныйЗнак.Установить(
Новый ХранилищеЗначения(ВыбранноеИзображение);
КонецЕсли;
// Получаем область заголовка макета
ЗаголовокОбласть = Макет.ПолучитьОбласть("Заголовок");
// Подготавливаем область для вывода изображения
Изображение = ЗаголовокОбласть.Рисунки.Добавить(
ТипРисункаТабличногоДокумента.Картинка);
// Задаем размеры изображения
Изображение.Высота = 20;
Изображение.Ширина = 30;
// Задаем изменение размеров изображения
Изображение.РазмерКартинки = РазмерКартинки.Пропорционально;
// Загружаем изображение в подготовленную область
Изображение.Картинка = Константы.ТоварныйЗнак.Получить().Получить();
// Записываем измененный макет
НашОтчет.Макет = Макет;
Перед тем как записать изображение в область заголовка макета, мы выбрали необходимый файл на диске компьютера (объект ДиалогВыбораФайла)
348
Часть V. Отчеты
и сохранили его в хранилище значений (объект ХранилищеЗначения). Затем
получили область макета и подготовили ее для записи изображения. Для
этого мы воспользовались методом Добавить (объект КоллекцияРисунков
ТабличногоДокумента) и свойствами Высота, Ширина и РазмерКартинки объекта РисунокТабличногоДокумента. После этого осталось только загрузить
изображение в область макета посредством свойства Картинка (объект
РисунокТабличногоДокумента) и сохранить измененный макет в объекте построителя отчета.
Кроме основного макета, построитель отчета предоставляет разработчику дополнительные макеты определенных областей использования. Рассмотрим их
по порядку. Свойство МакетДетальныхЗаписей предоставляет доступ непосредственно к области макета детальных записей. Для использования области заголовка предназначено свойство МакетЗаголовкаОтчета. Посмотрите пример работы с макетом заголовка отчета, представленный в листинге 27.20.
Листинг 27.20. Использование свойства МакетЗаголовкаОтчета
// Получаем сгенерированный построителем отчета макет заголовка
МакетЗаголовка = НашОтчет.МакетЗаголовкаОтчета;
// Получаем область макета заголовка
ОбластьЗаголовка = МакетЗаголовка.Область(2, 4);
// Формируем текст заголовка
ОбластьЗаголовка.Текст = "Новый заголовок";
// Задаем новый размер шрифта заголовка
ОбластьЗаголовка.Шрифт = Новый Шрифт(ОбластьЗаголовка.Шрифт, 20, Истина);
// Выводим дополнительное описание отчета
МакетЗаголовка.Область(3, 1).Текст = "Отчет по продажам компании";
// Записываем измененный макет заголовка
НашОтчет.МакетЗаголовкаОтчета = МакетЗаголовка;
Как видите, принцип использования дополнительного макета заголовка ничем не отличается от работы с основным.
Следующее свойство МакетОбщихИтогов позволяет настроить область общих
итогов отчета. Аналогичные возможности предоставляют свойства построителя
отчета: МакетШапкиТаблицы, МакетПодвалаТаблицы и МакетПодвалаОтчета.
Отдельно хочется отметить свойство МакетОформления. С его помощью можно легко изменить внешний вид всего отчета. Система 1С предоставляет набор готовых вариантов оформления отчета, которые разработчик выбирает
сам или предлагает сделать такой выбор пользователю. Пример использования макета оформления представлен в листинге 27.21.
Глава 27. Создание и оформление отчетов
Листинг 27.21. Использование свойства МакетОформления
// Процедура выбирает тип оформления макета отчета
Процедура ВыбратьОформлениеОтчета(НашОтчетСсылка, ВидОформления)
Оформление = Неопределено;
// Сбрасываем текущее оформление
НашОтчетСсылка.МакетОформления = Неопределено;
Если ВидОформления = 1 Тогда
Оформление = СтандартноеОформление.БезОформления;
ИначеЕсли ВидОформления = 2 Тогда
Оформление = СтандартноеОформление.Апельсин;
ИначеЕсли ВидОформления = 3 Тогда
Оформление = СтандартноеОформление.Асфальт;
ИначеЕсли ВидОформления = 4 Тогда
Оформление = СтандартноеОформление.Бирюза;
ИначеЕсли ВидОформления = 5 Тогда
Оформление = СтандартноеОформление.Бронза;
ИначеЕсли ВидОформления = 6 Тогда
Оформление = СтандартноеОформление.Весна;
ИначеЕсли ВидОформления = 7 Тогда
Оформление = СтандартноеОформление.Дерево;
ИначеЕсли ВидОформления = 8 Тогда
Оформление = СтандартноеОформление.Зима;
ИначеЕсли ВидОформления = 9 Тогда
Оформление = СтандартноеОформление.Интерфейс;
ИначеЕсли ВидОформления = 10 Тогда
Оформление = СтандартноеОформление.Камень;
ИначеЕсли ВидОформления = 11 Тогда
Оформление = СтандартноеОформление.Классика;
ИначеЕсли ВидОформления = 12 Тогда
Оформление = СтандартноеОформление.Лед;
ИначеЕсли ВидОформления = 13 Тогда
Оформление = СтандартноеОформление.Лето;
ИначеЕсли ВидОформления = 14 Тогда
Оформление = СтандартноеОформление.Медь;
ИначеЕсли ВидОформления = 15 Тогда
Оформление = СтандартноеОформление.Осень;
ИначеЕсли ВидОформления = 16 Тогда
349
350
Часть V. Отчеты
Оформление = СтандартноеОформление.Песок;
ИначеЕсли ВидОформления = 17 Тогда
Оформление = СтандартноеОформление.Платина;
ИначеЕсли ВидОформления = 18 Тогда
Оформление = СтандартноеОформление.Серебро;
ИначеЕсли ВидОформления = 19 Тогда
Оформление = СтандартноеОформление.Текстиль;
ИначеЕсли ВидОформления = 20 Тогда
Оформление = СтандартноеОформление.Трава;
КонецЕсли;
// Записываем выбранный вид оформления
НашОтчетСсылка.МакетОформления =
ПолучитьМакетОформления(Оформление);
КонецПроцедуры
В примере мы написали процедуру, которая выбирает один из стандартных
видов оформления макета отчета. Далее посредством системной функции
ПолучитьМакетОформления мы записываем выбранное оформление в объект
построителя отчета. Чтобы изменения работали в процессе выполнения программы, следует после вызова метода Выполнить (объект ПостроительОтчета) вызвать метод обновления ОформитьМакет (объект ПостроительОтчета).
Посмотрите пример, представленный в листинге 27.22.
Листинг 27.22. Использование метода ОформитьМакет
// Создаем и настраиваем построитель отчета
// . . .
// Получаем выбранный вид оформления
ВидОформления = ПолучитьМакетОформления(СтандартноеОформление.Лето);
// Записываем вид оформления в объект построителя отчета
НашОтчет.МакетОформления = ВидОформления;
// Выполняем отчет
НашОтчет.Выполнить();
// Применяем оформление макета
НашОтчет.ОформитьМакет();
// Выводим созданный отчет в табличную часть
НашОтчет.Вывести(НашаТабличнаяЧасть);
Глава 27. Создание и оформление отчетов
351
В этом примере мы опять воспользовались системной функцией ПолучитьМачтобы выбрать желаемый вид оформления макета. Далее мы
сформировали отчет и применили выбранное оформление. Как видите, все
просто.
кетОформления,
Еще одно полезное свойство, позволяющее пользователю в любой момент прервать выполнение отчета с помощью комбинации клавиш <Ctrl>+<Break>,
называется ОбрабатыватьПрерываниеПользователя. Оно принимает значение
Истина (опция включена) или Ложь (опция отключена). По умолчанию данная возможность активна.
Чтобы отображать на панели состояния ход выполнения отчета, установите
свойство ОтображатьСостояние в значение Истина. По умолчанию это свойство включено.
Иногда возникает необходимость не сразу выводить данные отчета в табличный документ, или обработать результат работы построителя отчета как
обычный запрос. Сделать это поможет свойство Результат. Оно представляет
собой ссылку на объект РезультатЗапроса, с которым мы уже знакомы. Его
доступные методы Выбрать и Выполнить дают возможность, соответственно,
сформировать выборку записей из запроса или выгрузить данные в таблицу
значений (объект ТаблицаЗначений).
На этом мы завершим изучение свойств построителя отчета и немного поговорим о методах этого объекта. Вы уже познакомились с методами Вывести
(выводит результат отчета в табличный документ) и Выполнить (выполняет
генерацию всех необходимых настроек и запускает выполнение запроса).
Кроме того, вы уже знаете метод ЗаполнитьНастройки, который автоматически заполняет настройки отчета данными запроса. С помощью метода
ПолучитьЗапрос можно прочитать данные запроса посредством объекта
Запрос. Использовать этот метод можно тогда, когда не задано свойство ИсточникДанных. Метод ПолучитьНастройки читает все доступные настройки
построителя, а метод УстановитьНастройки загружает собственные настройки для отчета. Чтобы сохранять текущие настройки отчета между вызовами,
можно использовать оба этих метода. Пример работы с ними представлен
в листинге 27.23.
Листинг 27.23. Использование методов ПолучитьНастройки и УстановитьНастройки
// Сохраняем настройки отчета перед закрытием формы отчета
Процедура ПередЗакрытием(Отказ, СтандартнаяОбработка)
// Отключаем обработку по умолчанию
СтандартнаяОбработка = Ложь;
352
Часть V. Отчеты
// Сохраняем настройки отчета
СохранитьЗначение("НашОтчет_Настройки",
НашОтчет.ПолучитьНастройки);
КонецПроцедуры
// Перед открытием формы отчета восстанавливаем настройки
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
// Отключаем обработку по умолчанию
СтандартнаяОбработка = Ложь;
// Читаем сохраненные настройки отчета
НастройкиОтчета = ВосстановитьЗначение("НашОтчет_Настройки");
// Проверяем, были ли сохранены настройки
Если НастройкиОтчета <> Неопределено Тогда
// Читаем настройки
НашОтчет.УстановитьНастройки(НастройкиОтчета);
КонецЕсли;
КонецПроцедуры
Как видно из примера, методы сохранения и восстановления настроек отчета
мы поместили в обработчики формы ПередЗакрытием и ПередОткрытием. Таким образом при закрытии формы отчета настройки будут автоматически
сохранены. А при открытии формы сохраненные ранее настройки будут восстановлены. Чтобы записать текущие настройки, мы применили встроенную
функцию СохранитьЗначение. Они принимает два параметра: произвольное
имя переменной и ее значение. В данном случае значением переменной
выступил объект НастройкиПостроителяОтчета, который вернул метод
ПолучитьНастройки. Для чтения текущих настроек вызвали другую встроенную функцию — ВосстановитьЗначение, передав ей в качестве единственного параметра имя сохраненной настройки. Далее посредством метода
УстановитьНастройки загрузили настройки в объект построителя отчета.
В завершение изучения работы с построителем отчета рассмотрим, как можно сформировать сводную диаграмму по результатам выполнения отчета.
Посмотрите пример, представленный в листинге 27.24.
Листинг 27.24. Формирование сводной диаграммы по результатам отчета
// Формируем текст запроса
НашОтчет.Текст = "
|ВЫБРАТЬ
|
СтруктурнаяЕдиница КАК СтруктурнаяЕдиница,
Глава 27. Создание и оформление отчетов
|
СуммаОстаток КАК СуммаОстаток,
|
СуммаУпрОстаток КАК СуммаУпрОстаток
353
|ИЗ
|
РегистрНакопления.ДенежныеСредстваКомпании.Остатки
|
|ИТОГИ СУММА(СуммаОстаток),
|
СУММА(СуммаУпрОстаток) ПО
|
СтруктурнаяЕдиница
|АВТОУПОРЯДОЧИВАНИЕ";
// Заполняем настройки отчета из текста запроса
НашОтчет.ЗаполнитьНастройки();
// Устанавливаем вид расшифровки
НашОтчет.ЗаполнениеРасшифровки =
ВидЗаполненияРасшифровкиПостроителяОтчета.ЗначенияГруппировок;
// Выводим заголовок отчета
НашОтчет.ВыводитьЗаголовокОтчета = Истина;
// Задаем заголовок отчета
НашОтчет.ТекстЗаголовка = "Отчет по движению денежных средств";
// Формируем отчет
НашОтчет.Выполнить();
// Задаем число значений диаграммы по горизонтальной оси
ЭлементыФормы.СводДиаграмма.ГоризонтальнаяПоддержкаМасштаба =
ПоддержкаМасштабаСводнойДиаграммы.КоличествоЗначений;
// Задаем число одновременно отображаемых значений по горизонтали
ЭлементыФормы.СводДиаграмма.КоличествоЗначенийГоризонтальнойШкалы = 4;
// Используем визуальный эффект освещенности
ЭлементыФормы.СводДиаграмма.Свет = Истина;
// Заполняем сводную диаграмму данными из построителя отчета
ЭлементыФормы.СводДиаграмма.ИсточникДанных = НашОтчет;
Перед использованием этого примера необходимо добавить элемент управления диаграммы в форму и назначить ей имя СводДиаграмма. Далее мы
формируем запрос, заполняем настройки и расшифровку построителя отчетов и выполняем отчет. Затем настраиваем сводную диаграмму и записываем
в ее свойство ИсточникДанных ссылку на наш объект построителя отчетов.
Вот, в принципе, и все. Рассматривать свойства сводной диаграммы мы здесь
не будем, поскольку эта тема выходит за рамки книги.
Думаю, многочисленные примеры работы с построителем отчета убедили вас
в его широких возможностях и простоте использования.
354
Часть V. Отчеты
Программирование и настройка отчетов
Мы уже познакомились с основными принципами построения отчетов в 1С,
а также подробно разобрали универсальное средство создания отчетов — построитель отчета. Поэтому сейчас мы немного поговорим об общих методах
программирования отчетов и настройке их свойств. Эти сведения вам пригодятся в любых задачах, так или иначе связанных с отчетами.
Для формирования отчета на базе существующего можно написать стандартную процедуру, пример которой показан в листинге 27.25.
Листинг 27.25. Универсальная процедура формирования отчета на базе
существующего
Процедура СформироватьОтчетПланыПродаж(ИмяОтчета, ДокументРезультат,
ИмяНастроекОтчета = "")
// Создаем новый отчет
Отчет = Отчеты[ИмяОтчета].Создать();
// Получаем форму отчета
ФормаОтчета = Отчет.ПолучитьФорму();
// Читаем настройки отчета
НастройкиОтчета = ВосстановитьЗначение(ИмяНастроекОтчета);
// Проверяем существование настроек отчета
Если НастройкиОтчета <> Неопределено Тогда
// Передаем начальные настройки в форму отчета
ФормаОтчета.НачальноеЗначениеВыбора = НастройкиОтчета;
КонецЕсли;
// Формируем отчет
Отчет.СформироватьОтчет(ДокументРезультат);
// Открываем форму отчета
ФормаОтчета.Открыть();
КонецПроцедуры
В этом примере мы продемонстрировали общую схему формирования отчета
на основе существующего. Вначале создали объект стандартного отчета
и получили основную форму. Затем прочитали настройки отчета и загрузили
их в форму. И, наконец, выполнили отчет и отобразили его на экране. Процедура может работать с любым стандартным отчетом. В качестве первого
параметра требуется указать строку с именем отчета. Во второй параметр
нужно записать ссылку на табличный документ. Третий параметр принимает
строку с именем настроек отчета.
Глава 27. Создание и оформление отчетов
355
Как вам уже известно, в 1С существует понятие расшифровки, когда пользователь, щелкнув мышью на определенной ячейке отчета или поле диаграммы,
может вывести расширенную информацию. По умолчанию система выполняет стандартную обработку. Чтобы указать собственную процедуру обработки, следует воспользоваться событием ОбработкаРасшифровки (например, для
элемента ТабличноеПоле). Пример использования расшифровки представлен
в листинге 27.26.
Листинг 27.26. Использование события ОбработкаРасшифровки
Процедура ИмяЭлементаОбработкаРасшифровки(Элемент, Расшифровка,
СтандартнаяОбработка)
// Отключаем стандартную обработку
СтандартнаяОбработка = Ложь;
гда
// Проверяем тип значения выбранной ячейки
Если ТипЗнч(Расшифровка) = Тип("ДокументСсылка.ИмяДокумента") То// Получаем нужный отчет
Отчет = Отчеты.ИмяОтчета.Создать();
// Передаем значение для реквизита документа
Отчет.ИмяРеквизита = Расшифровка;
// Формируем отчет
Отчет.СформироватьОтчет(ЭлементыФормы.ИмяТабличногоПоля);
КонецЕсли;
// Проверяем тип значения выбранной ячейки
Если ТипЗнч(Расшифровка) =
Тип("СправочникСсылка.ИмяСправочника") Тогда
// Получаем нужный отчет
Отчет = Отчеты.ИмяОтчета.Создать();
// Передаем значение для реквизита документа
Отчет.ИмяРеквизита = Расшифровка;
// Получаем форму отчета
ОтчетФорма = Отчет.ПолучитьФорму();
// Формируем отчет
Отчет.СформироватьОтчет(
ОтчетФорма.ЭлементыФормы.ДокументРезультат);
// Выводим форму отчета на экран
ОтчетФорма.Открыть();
КонецЕсли;
// Проверяем тип значения выбранной ячейки
Если ТипЗнч(Расшифровка) = Тип("ДокументСсылка.ИмяДокумента") Тогда
КонецЕсли;
// Восстанавливаем режим стандартной обработки
СтандартнаяОбработка = Истина;
КонецПроцедуры
356
Часть V. Отчеты
В этом примере приведена условная процедура обработки расшифровки. Это
значит, что в реальном коде следует явно указать имена объектов и убедиться
в наличии у них необходимых реквизитов, элементов управления и процедур
формирования отчета. Сам принцип обработки достаточно простой. Вначале
отключаем стандартную расшифровку. Затем проверяем тип значения расшифровки и, в зависимости от результата проверки, вызываем соответствующий блок кода.
Иногда возникает необходимость в программном формировании нескольких
копий одного отчета. Добиться этого можно с помощью глобального объекта
Метаданные. Пример процедуры, формирующей копию существующего отчета, представлен в листинге 27.27.
Листинг 27.27. Создание копии существующего отчета
Процедура СоздатьКопиюОтчета(ИмяОтчета, ОтчетОбъектСсылка)
// Формируем новый отчет
Отчет = Отчеты[ИмяОтчета].Создать();
// Копируем реквизиты существующего отчета в новый
Для Каждого Реквизит Из Метаданные.Отчеты[ИмяОтчета].Реквизиты
Цикл
Отчет[Реквизит.Имя] = ОтчетОбъектСсылка[Реквизит.Имя];
КонецЦикла;
// Получаем форму нового отчета
ОтчетФорма = Отчет.ПолучитьФорму();
// Заполняем таблицу нового отчета данными
ОтчетФорма.ЭлементыФормы.ДокументРезультат.Вывести(
ЭлементыФормы.ДокументРезультат.ПолучитьОбласть());
// Выводим форму отчета на экран
ОтчетФорма.Открыть();
КонецПроцедуры
Таким образом, мы скопировали данные существующего отчета и сформировали новый отчет. В качестве параметров наша процедура принимает имя
отчета (строковый тип данных) и ссылку на объект существующего отчета.
Далее мы создали новый отчет и, воспользовавшись глобальным объектом
Метаданные, скопировали все реквизиты из одного отчета в другой. После
этого осталось получить форму и заполнить табличный документ (объект
ТабличныйДокумент) в форме данными. Чтобы пользователь увидел результат, вызвали метод Открыть. Если вам дополнительно требуется скопировать
вместе с реквизитами и табличные части отчета (например, формирование
Глава 27. Создание и оформление отчетов
357
структуры настроек отчета для расшифровки), которые содержат настройки
отборов, группировок и показателей, используйте код, приведенный в листинге 27.28.
Листинг 27.28. Копирование реквизитов и табличных частей существующего
отчета
Процедура СформироватьРасшифровку(ИмяОтчета, ОтчетОбъектСсылка,
ДокументРезультат)
// Создаем структуру настроек отчета
НастройкиОтчета = Новый Структура;
// Добавляем имя отчета
НастройкиОтчета.Вставить("ИмяОтчета", ИмяОтчета);
// Записываем в структуру настроек реквизиты отчета
Для Каждого Реквизит Из Метаданные.Отчеты[ИмяОтчета].Реквизиты
Цикл
// Добавляем настройку
НастройкиОтчета.Вставить(Реквизит.Имя,
ОтчетОбъектСсылка[Реквизит.Имя];);
КонецЦикла;
// Записываем в структуру настроек табличные части
Для Каждого ТабличнаяЧасть Из
Метаданные.Отчеты[ИмяОтчета].ТабличныеЧасти Цикл
НастройкиОтчета.Вставить(ТабличнаяЧасть.Имя,
ОтчетОбъектСсылка[ТабличнаяЧасть.Имя].Выгрузить());
КонецЦикла;
// Заполняем расшифровку табличного документа
ДокументРезультат.Область(1, 1).Расшифровка = НастройкиОтчета;
КонецПроцедуры
Процедура принимает три аргумента: строковое наименование отчета, ссылку на объект отчета и ссылку на табличный документ формы отчета. Далее
мы создаем структуру настроек и копируем в нее все реквизиты и табличные
части отчета. В завершение передаем в область табличного документа в качестве расшифровки ссылку на структуру.
Когда вы создаете собственный отчет без помощи построителя, вам необходимо самостоятельно заполнить соответствующие реквизиты отчета данными
отборов или группировок. Для этого формируется специальная процедура,
представленная в листинге 27.29.
358
Часть V. Отчеты
Листинг 27.29. Заполнение значений отборов для отчета
Процедура ЗаполнитьОтборы()
// Добавим фильтры
ТаблицаОтборов = ЭлементыФормы.ИмяТабличногоПоляОтборов.Значение;
// Очищаем табличную часть отборов
ТабличнаяЧастьОтборы.Очистить();
// Копируем данные табличного поля в табличную часть отчета
Для Каждого Строка Из ТаблицаОтборов Цикл
// Добавляем новую строку отбора
СтрокаОтбора = ТабличнаяЧастьОтборы.Добавить();
// Заполняем реквизиты табличной части отборов
СтрокаОтбора.ИмяФильтра = Строка.ИмяФильтра;
СтрокаОтбора.ПредставлениеФильтра =
Строка.ПредставлениеФильтра;
СтрокаОтбора.ЗначениеФильтра =
Строка.ЗначениеФильтра;
СтрокаОтбора.ОписаниеФильтра =
Строка.ОписаниеФильтра;
СтрокаОтбора.ТипФильтра = Строка.ТипФильтра;
КонецЦикла;
КонецПроцедуры
Итак, мы получили ссылку на табличное поле отборов, расположенное в форме,
и скопировали данные отборов в табличную часть отчета, предварительно
очистив ее от предыдущих настроек. Аналогичным образом выполняется заполнение группировок и показателей отчета.
И последняя тема, которую мы разберем, касается сохранения настроек отчета. Один из вариантов был уже рассмотрен ранее, а здесь мы познакомимся
с более удобным и простым способом. К слову, этот вариант наиболее часто
применяется в популярных конфигурациях программы 1С. Заключается он
в следующем. Нужно открыть диалоговое окно свойств основной формы отчета и активизировать свойство Сохранять значения. Затем указать в списке
свойства Сохраняемые значения нужные параметры. После этого остается
добавить в командную панель кнопки сохранения и восстановления настроек
(через свойство Действие). Выполнив эти нехитрые операции, вы получите
стандартный интерфейс для управления настройками отчета. Дополнительно
можно обработать два события: ПередСохранениемЗначений и ПослеВосстановленияЗначений. Посмотрите пример, представленный в листинге 27.30.
Глава 27. Создание и оформление отчетов
359
Листинг 27.30. Обработка событий сохранения и восстановления настроек
// Обработчик события сохранения настроек
Процедура ПередСохранениемЗначений(Отказ)
// Просим подтверждение пользователя
Ответ = Вопрос("Сохранить текущие настройки?",
РежимДиалогаВопрос.ДаНет, , КодВозвратаДиалога.Да,);
// Если отказ, отменяем операцию сохранения
Если Ответ = КодВозвратаДиалога.Нет Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
// Обработчик события восстановления настроек
Процедура ПослеВосстановленияЗначений()
// Выводим информацию для пользователя в окно сообщений
Сообщить("Настройки восстановлены.");
КонецПроцедуры
Представленный пример имеет скорее познавательное, чем практическое
значение. Главное — понимать, что в событии ПередСохранениемЗначений
можно отменить операцию сохранения, передав значение Истина параметру
Отказ. Как это использовать, решайте сами.
Чтобы сохранить параметры настроек построителя отчета, можно применить
несколько иной подход. В отчет следует добавить новый реквизит и установить для него произвольный тип данных. Далее добавить в обработчики событий код, показанный в листинге 27.31.
Листинг 27.31. Сохранение и восстановление настроек построителя отчета
// Обработчик события сохранения настроек
Процедура ПередСохранениемЗначений(Отказ)
// Создаем новую структуру
СтруктураНастроек = Новый Структура;
// Заполняем структуру настройками из построителя отчета
СтруктураНастроек.Вставить("НастройкиПостроителя",
НашОтчет.ПолучитьНастройки());
// Записываем настройки в реквизит
НастройкиПостроителяОтчета = СтруктураНастроек;
КонецПроцедуры
360
Часть V. Отчеты
// Обработчик события восстановления настроек
Процедура ПослеВосстановленияЗначений()
// Проверяем тип реквизита настроек
Если ТипЗнач(НастройкиПостроителяОтчета) = Тип("Структура")
// Заполняем настройки построителя отчета
НашОтчет.УстановитьНастройки(НастройкиПостроителяОтчета);
КонецЕсли;
КонецПроцедуры
Таким образом можно добиться сохранения всех доступных настроек построителя отчета.
На этом мы завершим изучение вопросов программирования отчетов и перейдем к следующей теме, посвященной настройке существующих отчетов
и оптимальному их использованию в повседневной деятельности предприятия.
Глава 28
Работа со стандартными
отчетами
Любая конфигурация, поставляемая самой фирмой 1С или ее партнерами,
обязательно включает в себя набор стандартных отчетов. Они могут отличаться друг от друга интерфейсом, функционалом или настройками, но
принципы использования любого из них будут неизменны. Другими словами,
изучив способы работы с одним, можно без особых трудностей разобраться
со всеми остальными. Связано это, прежде всего, с общими базовыми и системными объектами, применяемыми при конструировании отчетов и внешних обработок. Мы уже познакомились в предыдущей главе с этими объектами, разобрались, как их следует программно формировать и настраивать.
Здесь же разговор, в первую очередь, пойдет о непосредственной работе
с готовыми отчетами, их настройке и взаимодействию между собой. Материал, представленный тут, будет полезен не только разработчикам, но и
пользователям системы 1С.
Какие вообще отчеты могут потребоваться для анализа работы предприятия?
В общем случае необходимо вести учет номенклатуры: оприходованной, закупленной, проданной и списанной (или возвращенной). Этот учет можно
вести в разрезе контрагентов и организаций, складов, подразделений и сотрудников (например, менеджеров). Часто требуется количественный учет
в разрезе партий, характеристик номенклатуры и дополнительных свойств.
Кроме учета движения товаров, необходимо вести учет движения денежных
средств, взаиморасчетов с контрагентами, внутренних платежей. Для анализа
эффективности работы сотрудников может понадобиться отчетность в разрезе проданной и закупленной номенклатуры, взаимодействия с представителями покупателей и продавцов и многое другое. Все эти вопросы решаются
с помощью уже готовых отчетов и обработок.
Каждый отчет в 1С, как правило, содержит средства настройки. Информация,
изменяющаяся во времени, требует задания периода. Например, учет товаров
362
Часть V. Отчеты
по партиям в разрезе документов движения всегда имеет поля для установки
начального и конечного периода. Для анализа остатков товаров на складах
достаточно задать только конечную дату, на которую формируется отчет.
Кроме настроек периода, важной составляющей отчета служат показатели.
Они представляют собой числовые значения (количественные и суммовые),
по которым строятся итоги. Перед выполнением отчета должен быть выбран
хотя бы один показатель. Для итоговых полей дополнительно могут назначаться расшифровки, позволяющие получить дополнительные сведения в разрезе периодов, свойств, документов движения и т. д.
После настройки показателей обычно определяют поля группировок отчета.
Группировки дают возможность исключить повторяющиеся значения, свернув результат запроса по колонкам или строкам. Например, формируя отчет
на основании заказов покупателей, можно сгруппировать поля номенклатуры
и таким образом получить правильные итоговые значения по количеству каждого товара и сумме.
Следующие настройки, которые помогают уменьшить время выполнения отчета и отсеять ненужные данные, делаются посредством отборов (иначе говоря, фильтров). Согласитесь, когда вам требуется проанализировать сумму
продаж по конкретному товару, делать отчет по всей номенклатуре, а потом
искать нужную не очень удобно. Правильнее будет установить поле отбора
и указать для него ссылку на выбранный товар. При этом отчет будет выполнен гораздо быстрее и покажет только сумму проданного товара, который вас
интересует.
Чтобы представить данные отчета в более удобном для восприятия виде,
служат настройки упорядочивания (другими словами, сортировки). Выбираются поля различного типа, по которым будет выполнено упорядочивание.
Например, при формировании отчета по продажам за определенный период
в разрезе контрагентов, можно упорядочить результат по итоговой сумме
продаж и количеству в порядке убывания. Это позволит проанализировать,
какие контрагенты наиболее активно закупали на вашем предприятии товар,
в каком количестве и на какую сумму. Кроме того, поля упорядочивания часто используются для сортировки значений по алфавиту (например, по наименованию номенклатуры).
И последний вид настроек в стандартных отчетах относится к выбору дополнительных полей, которые будут выведены в табличную часть отчета. По
умолчанию используются только заданные поля группировок, показателей,
сортировок. Если необходимо дополнить отчет новыми полями из доступных, требуется указать их в окне настройки выбранных полей. Например,
у вас в отчете заданы группировки по номенклатуре, характеристике и заказу,
Глава 28. Работа со стандартными отчетами
363
сортировка по периоду и показатели количества и суммы. Чтобы дополнительно получить валюту заказа и склад номенклатуры, нужно добавить дополнительно два соответствующих поля.
С рассмотренными видами настроек вы столкнетесь в любом случае при работе со стандартными отчетами. В отчетах, созданных на базе построителя
отчета, может быть использована еще одна настройка — оформление. С ее
помощью можно задавать внешний вид всего отчета и отдельных областей,
а также настраивать формат отдельных ячеек.
Чтобы лучше понять, как работают настройки отчетов, мы рассмотрим некоторые стандартные отчеты (в основном из конфигурации "Управление торговлей") и научимся их настраивать с учетом требований пользователей.
Первый отчет называется "Анализ заказа покупателя" и позволяет проанализировать состояние номенклатуры, оформленной заказом покупателя. Внешний вид отчета представлен на рис. 28.1.
Рис. 28.1. Отчет "Анализ заказа покупателя"
Из настроек можно установить только ссылку на заказ покупателя и дату
формирования отчета. В результате выполнения отчета мы получим данные о состоянии номенклатуры по заказу, а также суммы взаиморасчетов
364
Часть V. Отчеты
с контрагентом. Посмотрите, как выглядит сформированный отчет на
рис. 28.2.
Рис. 28.2. Результат выполнения отчета "Анализ заказа покупателя"
В шапке выводится наименование и дата создания отчета. Кроме того, добавлены сведения о контрагенте и договоре взаиморасчетов. В первой таблице
представлена вся заказанная номенклатура, ее движения с учетом документов
реализации и складов хранения. Во второй приведены данные по оплате текущего заказа и общие сведения по взаиморасчетам с контрагентом. Для построения отчета используется несколько регистров накопления. Чтобы проанализировать движение номенклатуры по заказу, применяется регистр
остатков и оборотов ЗаказыПокупателей. Из него мы получаем количество
запланированной номенклатуры (ресурс КоличествоПриход), количество
отгруженной номенклатуры (ресурс КоличествоРасход) и остаток (ресурс
КоличествоКонечныйОстаток). Значения ресурсов регистра формируются на
основе документов-регистраторов. В частности, ресурс КоличествоПриход
образуется посредством документа ЗаказПокупателя, а ресурс КоличествоРасход — документом РеализацияТоваров. Кроме перечисленных документов, движения по регистру делают и некоторые другие документы (например,
ПеремещениеТоваров). В любом случае, все эти движения возникают в момент проведения документов.
Глава 28. Работа со стандартными отчетами
365
Поле отчета Заказано строится по данным регистра остатков ЗаказыПоставщикам. Ресурс КоличествоОстаток содержит актуальную информацию об остатках номенклатуры, заказанной под анализируемый заказ покупателя.
В общем случае при оформлении поступлений, остаток уменьшается.
Поля Резерв и СвободныйОстаток формируются на базе регистра остатков
ОстаткиТоваровКомпании. Единственное отличие состоит в том, что первое
поле учитывает остатки товаров только по заказу покупателя (товары, размещенные на складах не под заказ), а второе учитывает остатки не по заказу,
другими словами, находящиеся в свободном остатке.
Вторая таблица отчета строится на основании регистра оборотов СуммыЗаказов. Движения по этому регистру формируются при проведении различных платежных документов (например, ПриходныйКассовыйОрдер), отражающих поступление денежных средств по заказу покупателя в разрезе
контрагентов. Поле Запланировано отражается в ресурсе СуммаЗаказОборот
и характеризует обороты по заказу, а поле Оплачено (ресурс СуммаОплаты
Оборот) — обороты по оплате. Поле Предоплата рассчитывается как значение процента предоплаты по договору взаиморасчетов, деленное на 100
и умноженное на значение ресурса СуммаЗаказОборот. Иными словами, это
поле отражает процент предоплаты, внесенный контрагентом за приобретаемый товар от суммы заказа. Остальные поля также являются рассчитываемыми и содержат разность между суммой заказа и суммой оплаты по
этому заказу.
Как вы догадались, пользуясь конфигуратором, можно получить все необходимые сведения, касающиеся документов движения по регистрам, а также по
используемым в отчете ресурсам и измерениям.
Следующий стандартный отчет, который мы разберем, называется "Планы
продаж". Он позволяет проследить запланированные продажи номенклатуры
за определенный период. В отличие от предыдущего отчета, здесь имеются
гораздо более широкие возможности настройки, такие как группировки
и упорядочивание, а также задание периодичности выборки данных. Общий
вид отчета представлен на рис. 28.3.
На первой закладке отчета можно указать период и периодичность выборки.
Кроме того, можно выбрать готовый вариант настроек. Вторая закладка
(рис. 28.4) позволяет самостоятельно настроить показатели, группировки
и порядок полей в отчете.
Последняя закладка содержит непосредственно табличное поле для вывода
результатов выполнения отчета. Чтобы научиться работать с этим отчетом,
разберем несколько вариантов настройки.
366
Часть V. Отчеты
Рис. 28.3. Отчет "Планы продаж"
Рис. 28.4. Закладка настроек отчета "Планы продаж"
Глава 28. Работа со стандартными отчетами
367
Например, нам требуется получить запланированную сумму продаж и количество по выбранной номенклатуре за определенный период. Кроме этого,
нужно отобразить документы планирования в разрезе каждого товара. Естественно, вначале мы должны настроить отчет в соответствии с требованиями
задания:
1. Установим период выборки отчета равным одному году (в данном случае
2008).
2. Установим периодичность данных равной одному месяцу.
3. Добавим два поля группировки: по номенклатуре и документу планирования.
4. В таблице показателей добавим следующие поля: сумма продаж за период, количество в единицах хранения и количество в базовых единицах.
5. В качестве отбора выберем из справочника Номенклатура необходимый
товар.
После выполнения этих действий настройки отчета должны соответствовать
тем, что изображены на рис. 28.5.
Рис. 28.5. Настройки отчета для анализа планируемых продаж по номенклатуре
368
Часть V. Отчеты
Теперь выполним отчет, нажав кнопку Сформировать. Полученные результаты отчета будут отображены в табличном поле на третьей закладке отчета
(рис. 28.6).
Рис. 28.6. Результат выполнения отчета по анализу планируемых продаж
выбранной номенклатуры
В шапке отчета отображены наименование отчета и значения настроек. Далее
идет таблица. В левом столбце представлена выбранная номенклатура в разрезе документов поступления. Остальные столбцы содержат значения показателей. Дополнительная строка таблицы подводит итоговые значения по
каждому показателю. Кроме того, по всем полям, исключая строку итогов,
можно получить расшифровку, выполнив двойной щелчок кнопкой мыши.
Например, щелкнув по документу планирования, мы откроем соответствующую форму документа ПланПродаж. Чтобы вывести отчет по всей номенклатуре, достаточно удалить поле отбора, а для получения суммы ожидаемой
прибыли — добавить в список показателей нужное значение из набора доступных показателей.
Настройки отчета можно сохранить, выбрав из выпадающего меню Действия
пункт Сохранить. При этом откроется диалоговое окно, показанное на рис. 28.7.
Глава 28. Работа со стандартными отчетами
369
Рис. 28.7. Диалоговое окно Сохранение настройки
Здесь следует указать имя пользователя и наименование настройки и нажать
кнопку OK. Чтобы настройка была доступна всем пользователям, дополнительно требуется установить флажок Общая. Теперь, открыв отчет, можно
воспользоваться сохраненной настройкой, выбрав из выпадающего меню
пункт Восстановить.
А теперь разберемся, как программно формируется отчет "Планы продаж".
Все необходимые данные находятся в регистре оборотов ПланыПродаж
Компании. В качестве регистратора, осуществляющего движение по этому
регистру, выступает документ ПланПродаж. Он позволяет запланировать
продажи номенклатуры по всему предприятию с различной периодичностью,
а также в разрезе отдельных подразделений. По результатам отчета, построенного на основании документов планируемых продаж, можно выполнять
анализ запланированных и фактически реализованных товаров. Поле отчета
Сумма продаж за период формируется по ресурсу СуммаПродажиОборот, а поле Количество — по КоличествоОборот. Более подробно познакомиться со
структурами, формирующими отчет, вы можете в конфигураторе.
Следующий стандартный отчет "Оперативный товарный календарь" служит
для анализа остатков номенклатуры на складах с учетом поступлений по заказам поставщикам и отгрузок по заказам покупателей. Общий вид отчета
представлен на рис. 28.8.
Чтобы проанализировать текущие остатки номенклатуры по всем складам
предприятия, выполним следующие настройки:
1. Добавим группировку по номенклатуре.
2. Добавим дополнительное поле группировки по складу.
370
Часть V. Отчеты
Рис. 28.8. Отчет "Оперативный товарный календарь"
Рис. 28.9. Настройки отчета для анализа текущих остатков номенклатуры
Глава 28. Работа со стандартными отчетами
371
3. Добавим отбор по выбранной номенклатуре.
4. В качестве показателя выберем Текущий остаток.
5. Установим дату, на которую будут формироваться данные отчета.
После заполнения всех этих настроек отчет примет вид, показанный на рис. 28.9.
Теперь выполните отчет, нажав кнопку Сформировать. В результирующей
таблице будет выведен отчет по выбранной номенклатуре. Из него видно,
какое количество номенклатуры осталось на складе. Чтобы проследить, какое количество товара было получено по заказам поставщикам, добавьте показатель "Поступление по заказам". Количество отгруженного товара по заказам покупателей можно узнать, добавив показатель "Отгрузка по заказам".
Как видите, пользоваться этим отчетом достаточно просто. Кроме того, отчет
поддерживает сохранение и восстановление собственных настроек.
Для формирования отчета используются несколько регистров остатков: ЗаказыПокупателей, ЗаказыПоставщикам и ОстаткиТоваровКомпании. Количество отгруженного товара мы читаем из ресурса КоличествоОстаток регистра
ЗаказыПокупателей. Количество поступившего товара по заказам поставщикам мы берем из ресурса КоличествоОстаток регистра ЗаказыПоставщикам.
Остатки товаров получаем из ресурса КоличествоОстаток регистра ОстаткиТоваровКомпании. Чтобы учесть все движения товаров, делаем три отдельных запроса по каждому из регистров, а затем объединяем их по номенклатуре и характеристике номенклатуры. Поскольку данные для отчета берутся на
основании документов-заказов, результат выполнения запроса носит прогнозируемый характер, поэтому он помогает спланировать поступления и отгрузку номенклатуры, а также проверить, срываются ли запланированные
отгрузки по заказам покупателей из-за задержек поступления товаров в разрезе заказов поставщикам.
Следующий отчет называется "План-фактный анализ" и дает возможность
сравнить планируемые реализации и фактические отгрузки номенклатуры за
определенный период времени. Можно получить сведения как по количеству, так и по сумме. Для реализации отчета используются оборотные регистры
ПланыПродажКомпании и ПродажиКомпании. Движения первого регистра
формируются документом ПланПродаж, а второго — документами реализации (РеализацияТоваров, ВозвратТоваровОтПокупателя и др.). Внешний вид
отчета представлен на рис. 28.10.
Как видите, отчет имеет много настроек, в которых мы попытаемся разобраться. Прежде всего нам необходимо заполнить таблицу данных отчета.
Здесь мы должны выбрать планируемые и фактические отгрузки и задать период сравнения. После этого можно настроить поля группировок. В данном
случае нам достаточно указать только номенклатуру.
372
Часть V. Отчеты
Рис. 28.10. Отчет "План-фактный анализ"
Рис. 28.11. Настройки отчета "План-фактный анализ"
Глава 28. Работа со стандартными отчетами
373
Рис. 28.12. Результат работы отчета "План-фактный анализ"
Если вы хотите проанализировать конкретный товар, добавьте его в таблицу
отборов. И в конце выберите в таблице показателей интересующие вас значения (мы выбрали количество в единицах хранения и сумму продаж за период).
Чтобы оценить отклонения (в процентах или абсолютных величинах), установите соответствующий флажок. В результате этих действий отчет примет вид,
как на рис. 28.11.
Теперь нажмите кнопку Сформировать, чтобы вывести результат отчета
в табличное поле. То, что у нас получилось, представлено на рис. 28.12.
В левом столбце разместились номенклатурные позиции, а в двух правых —
планируемые и фактические показатели отгрузок за выбранный период. Если
установить признак отклонений, добавится еще несколько столбцов (по числу показателей), в которых будет рассчитана разница фактических и планируемых отгрузок с учетом знака. Дополнительно для отчета можно выбрать
показатели прибыли и себестоимости.
На уровне кода для формирования запроса по отчету используются оборотные регистры накопления: ПланыПродажКомпании и ПродажиКомпании.
Из первого регистра считываются данные по запланированным отгрузкам
(на основании документов ПланПродаж), а из второго — по фактически реализованным товарам (документы РеализацияТоваров). Если вы посмотрите
в конструкторе запросов регистр ПродажиКомпании, то сможете узнать,
374
Часть V. Отчеты
какие документы продажи (измерение ДокументПродажи) и поставки (измерение ДокументПоставки) делают по нему движения.
И последний отчет, о котором мы поговорим, называется "Универсальный
отчет кросс-таблица". Это мощное средство, включающее в себя целый набор
всевозможных отчетов, а также возможность построения диаграмм. Внешний
вид отчета показан на рис. 28.13.
Рис. 28.13. Отчет "Универсальный отчет кросс-таблица"
Для настройки отчета прежде всего нужно выбрать раздел учета. Мы не будем приводить здесь весь список доступных вариантов, поскольку их достаточно много. Затем нужно задать дату или период выборки данных. Это напрямую зависит от типа отчета. Для каждого типа будут автоматически
подгружены необходимые параметры настроек: показатели, поля группировок, отборы и т. д.
Например, если требуется получить остатки денежных средств по счетам
(кассам) предприятия, нужно выбрать раздел учета "Денежные средства компании: остатки". После этого задать дату отчета, группировки по счетам (кассам)
Глава 28. Работа со стандартными отчетами
375
и показатели суммы в валюте счета и управленческой валюте. Пример такого
отчета представлен на рис. 28.14.
Рис. 28.14. Настройки отчета "Денежные средства компании: остатки"
Чтобы получить остатки по определенному счету (кассе), добавьте в таблицу
отборов соответствующее значение.
Следующий отчет позволит проанализировать остатки товаров на складах по
заказам покупателей. Например, определим, по каким контрагентам (покупателям) и в каком количестве зарезервирована на складах номенклатура. Для
этого выберем раздел учета "Заказы покупателей: остатки" и заполним настройки, как показано на рис. 28.15.
Поскольку группировку по контрагентам мы вынесли на верхний уровень,
в отчете по каждому покупателю будет выведен список заказанной номенклатуры, а по каждой номенклатуре будет выведен один или несколько связанных заказов покупателя. Как все это выглядит, показано на
рис. 28.16.
376
Часть V. Отчеты
Рис. 28.15. Настройки отчета "Заказы покупателей: остатки"
Рис. 28.16. Результат выполнения отчета "Заказы покупателей: остатки"
Глава 28. Работа со стандартными отчетами
Рис. 28.17. Настройки отчета "Остатки товаров компании: остатки"
Рис. 28.18. Диалоговое окно настроек параметров учета в 1С
377
378
Часть V. Отчеты
Щелкнув правой кнопкой мыши слева от таблицы отчета, можно свернуть
(пункт меню Уровни группировок) данные по полям группировок для лучшего анализа полученных результатов. Аналогичный отчет, только в разрезе
заказов поставщикам, можно построить, выбрав раздел учета "Заказы поставщикам: остатки".
Чтобы получить остатки номенклатуры по каждому складу, можно воспользоваться отчетом "Остатки товаров компании: остатки". Настройте отчет так,
как это сделано на рис. 28.17.
В результате выполнения отчета мы получим остатки номенклатуры с учетом всех складов на указанную дату. Чтобы проанализировать определенную
номенклатуру, добавьте в таблицу отборов ссылку из справочника Номенклатура.
Универсальный отчет дает также возможность проанализировать остатки товаров на складах в разрезе партий при условии, что на предприятии ведется
учет по партиям. Проверить это можно, открыв настройки учета программы
(выберите в главном меню Сервис пункт Настройки параметров учета).
В зависимости от конфигурации, диалоговое окно настроек учета может меняться. Для конфигурации "Управление торговлей" оно выглядит так, как
показано на рис. 28.18.
После того как вы убедитесь в том, что учет по партиям установлен, выполните настройки отчета. Вначале выберите раздел учета "Партии товаров компании: остатки". Затем добавьте поля группировок, показатели и установите
дату формирования отчета. Пример настроек представлен на рис. 28.19.
Поскольку группировку по складу мы поместили на самый верхний уровень,
можно будет по результатам отчета проанализировать остатки партий номенклатуры по каждому отдельно взятому складу.
Рассмотрим пример работы с диаграммой и рассчитаем продажи номенклатуры за определенный период. Выберите раздел учета "Продажи компании:
обороты". Добавьте период отчета, поле группировки по номенклатуре и
один показатель (например, количество), как показано на рис. 28.20.
Следует отметить, что при построении диаграммы будет использоваться
только самый первый показатель из списка. Перейдите на закладку Диаграмма и установите вид Гистограмма. После выполнения отчета данные
будут иметь вид примерно такой, как показано на рис. 28.21.
Как вы убедились, универсальный отчет позволяет анализировать данные по
всем основным видам деятельности предприятия. Каждый вид аналитики
включает собственные настройки, и его можно сохранять независимо от других для последующего использования.
Глава 28. Работа со стандартными отчетами
Рис. 28.19. Настройки отчета "Партии товаров компании: остатки"
Рис. 28.20. Настройки отчета "Продажи компании: обороты"
379
380
Часть V. Отчеты
Рис. 28.21. Результат выполнения отчета "Продажи компании: обороты"
На рассмотренных примерах мы постарались освоить базовые методы работы с отчетами. Этих сведений вполне достаточно, чтобы самостоятельно разобраться с любыми другими стандартными отчетами. Внешний вид их различается в зависимости от конфигурации. Наиболее полные и удобные
настройки представлены в УПП (Управление производственным предприятием). На этом, думаю, можно завершить тему отчетов и перейти к изучению других, не менее важных объектов.
Часть VI
Другие объекты
конфигурации
382
Часть I. Приемы программирования документов
Глава 29
Справочники
Справочники в 1С представляют собой базовое звено, на основании которого
формируются и заполняются данными другие объекты конфигурации. Они
предназначены для хранения постоянной и условно-постоянной информации,
как в виде обычного списка однородных элементов, так и в виде иерархии.
Создание справочника начинается в конфигураторе, где задаются все необходимые свойства и настройки: реквизиты, табличные части, иерархия, формы
и макеты. Поскольку наша цель состоит не в изучении структуры справочника, а в способах его программирования, перейдем непосредственно к базовым
объектам, позволяющим управлять любым доступным справочником. К ним
относятся:
СправочникиМенеджер — коллекция объектов СправочникМенеджер,
предоставляющая доступ к любому справочнику системы;
СправочникМенеджер — служит для непосредственного управления
справочником, создания новых элементов, организации поиска, получения
форм и макетов;
СправочникВыборка — позволяет осуществлять выборку данных из спра-
вочника;
СправочникОбъект — предназначен для модификации значений элемен-
тов справочника;
СправочникСписок — управляет отображением списка элементов спра-
вочника;
СправочникСсылка — предоставляет ссылку другим объектам конфигу-
рации на элементы справочника.
Каждый справочник (как и документ) по умолчанию имеет два реквизита: Код и Наименование, которые назначаются системой автоматически.
384
Часть VI. Другие объекты конфигурации
Они не отображаются в конфигураторе, но о них не стоит забывать при обращении к отдельным элементам. Рекизит Код может иметь строковый или
числовой тип, который может контролироваться системой автоматически.
Все остальные реквизиты, формы, табличные части и макеты создаются разработчиком непосредственно.
Для создания нового элемента справочника предназначен метод СоздатьЭлемент,
принадлежащий объекту СправочникМенеджер. Посмотрите пример, представленный в листинге 29.1.
Листинг 29.1. Создание нового элемента справочника ФизическиеЛица
// Формируем новый элемент справочника
НовоеФизЛицо = Справочники.ФизическиеЛица.СоздатьЭлемент();
// Заполняем реквизиты
НовоеФизЛицо.Наименование = "Иванов И.И.";
НовоеФизЛицо.Фамилия
= "Иванов";
НовоеФизЛицо.Имя
= "Иван";
НовоеФизЛицо.Отчество
= "Иванович";
// Записываем новый элемент в справочник
НовоеФизЛицо.Записать();
Как видите, вначале мы вызвали метод СоздатьЭлемент для ссылки на объект
СправочникОбъект. Далее заполнили необходимые реквизиты и сохранили
запись посредством метода Записать. Поскольку для данного справочника
задана автонумерация, значение кода указывать не обязательно. В справочнике Валюты, наоборот, следует указать код записи. Посмотрите пример из
листинга 29.2.
Листинг 29.2. Создание нового элемента справочника Валюты
// Формируем новый элемент справочника
НоваяВалюта = Справочники.Валюты.СоздатьЭлемент();
// Заполняем реквизиты
НоваяВалюта.Код = "974";
НоваяВалюта.Наименование = "BYR";
НоваяВалюта.ПолноеНаименование = "Белорусские рубли";
// Записываем новый элемент в справочник
НоваяВалюта.Записать();
Глава 29. Справочники
385
В данном случае мы явно указали код элемента. При записи значения элемента проверяется уникальность кода, и если такой уже существует в базе,
будет выдано сообщение, а процесс записи остановлен. Для контроля уникальности в свойствах справочника задается специальный признак. Надеюсь,
добавление новых записей в справочник не вызовет у вас затруднений.
Для удобного представления информации в справочниках существует возможность создавать отдельные группы с разной степенью вложенности, что,
по сути, представляет собой иерархическое дерево. Например, телевизор
и видеокамеру можно выделить в группу видеотехники, а лопату и кирку —
в группу садового инвентаря. Чтобы создать новую группу, служит метод
СоздатьГруппу (объект СправочникОбъект). Посмотрите пример, представленный в листинге 29.3.
Листинг 29.3. Создание новой группы справочника
// Формируем новую группу
НоваяГруппа = Справочники["Номенклатура"].СоздатьГруппу();
// Назначаем имя группе
НоваяГруппа.Наименование = "Бытовая техника";
// Записываем новую группу в справочник
НоваяГруппа.Записать();
Как видите, создать новую группу для справочника очень просто.
Следующая тема, о которой мы поговорим, касается удаления элемента
из справочника. Здесь возможны два варианта: непосредственное удаление
записи из базы и пометка на удаление (отложенное удаление). В первом случае элемент удаляется без возможности восстановления. Кроме того, при таком способе не учитывается ссылочная целостность, что может вызвать проблемы. Во втором случае запись не удаляется, а лишь помечается на
удаление и может быть удалена или восстановлена позднее. Для удаления
записи предназначен метод Удалить объекта СправочникОбъект. Рассмотрим
пример, представленный в листинге 29.4.
Листинг 29.4. Непосредственное удаление элемента справочника
// Получаем ссылку на справочник
КонтрагентыСсылка = Справочники.Контрагенты;
// Получаем ссылку на удаляемую запись
ЗаписьСсылка = КонтрагентыСсылка.НайтиПоНаименованию(
"Завод оборудования");
386
Часть VI. Другие объекты конфигурации
// Проверяем существование элемента в справочнике
Если НЕ ЗаписьСсылка.ПустаяСсылка() Тогда
// Получаем объект
ОбъектКонтрагент = ЗаписьСсылка.ПолучитьОбъект();
// Удаляем элемент из справочника
ОбъектКонтрагент.Удалить();
КонецЕсли;
Итак, вначале мы получили ссылку на удаляемую запись справочника. Для
этого вызвали метод НайтиПоНаименованию. После проверки существования элемента (метод ПустаяСсылка) получили доступ к самому объекту,
воспользовавшись методом ПолучитьОбъект (объект СправочникСсылка).
В результате выполнения выбранный элемент справочника безвозвратно
удаляется без проверки ссылочной целостности. Это значит, что действующие ссылки на данный элемент, имеющиеся в базе (в документах, регистрах и других объектах), будут указывать на отсутствующие данные, что
вызовет ошибки выполнения. Решением этой проблемы служит второй
способ удаления, при котором элемент только помечается как подлежащий
удалению. При этом непосредственное удаление может быть выполнено
позже, после проверки всех ссылок, или наоборот, удаляемый элемент
опять восстанавливается. Для пометки элемента на удаление предназначен
метод УстановитьПометкуУдаления (объект СправочникОбъект), а также
свойство ПометкаУдаления (объект СправочникОбъект). Данный метод
имеет два параметра. Первый определяет признак установки или снятия
пометки на удаление и является обязательным. Второй позволяет пометить
элемент не только в основном, но и во всех подчиненных справочниках.
Посмотрите код, показанный в листинге 29.5.
Листинг 29.5. Пометка элемента справочника на удаление
// Получаем ссылку на справочник
КонтрагентыСсылка = Справочники.Контрагенты;
// Получаем ссылку на удаляемую запись
ЗаписьСсылка = КонтрагентыСсылка.НайтиПоНаименованию("Завод керамики");
// Проверяем существование элемента в справочнике
Если НЕ ЗаписьСсылка.ПустаяСсылка() Тогда
// Получаем объект
ОбъектКонтрагент = ЗаписьСсылка.ПолучитьОбъект();
// Помечаем элемент на удаление
ОбъектКонтрагент.УстановитьПоменткуУдаления(Истина);
Глава 29. Справочники
387
// или через свойство
ОбъектКонтрагент.ПометкаУдаления = Истина;
КонецЕсли;
После того как была получена ссылка на элемент справочника, мы вызвали метод УстановитьПометкуУдаления со значением Истина для первого
параметра. Аналогичный результат был получен при установке свойства
ПометкаУдаления. Для восстановления элемента достаточно вызвать данный метод со значением Ложь или присвоить это значение свойству ПометкаУдаления.
После того как мы научились создавать новые элементы и группы справочника и удалять существующие, перейдем к не менее важной теме получения
выборки и поиска записей.
Для организации выборки элементов справочника предназначены два метода
объекта СправочникМенеджер: Выбрать и ВыбратьИерархически. Результатом
их работы является объект СправочникВыборка. Первый метод имеет четыре
необязательных параметра: ссылка на родительский справочник, ссылка на
владельца для подчиненного справочника, условия отбора и сортировка элементов. Приведем пример выборки в листинге 29.6.
Листинг 29.6. Выборка всех элементов справочника
// Получаем ссылку на справочник
НоменклатураСсылка = Справочники.Номенклатура;
// Выбираем элементы справочника
ВыборкаДанные = НоменклатураСсылка.Выбрать();
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Код номенклатуры
Сообщить("Код: " + ВыборкаДанные.Код);
// Наименование
Сообщить("Наименование: " + ВыборкаДанные.Наименование);
КонецЦикла;
Вначале мы получили ссылку на справочник. Затем вызвали метод Выбрать
без параметров, чтобы получить все имеющиеся элементы. После этого организовали цикл, в котором последовательно перебрали все записи и вывели
в окно сообщений код и наименование для каждого товара.
388
Часть VI. Другие объекты конфигурации
А теперь попробуем выбрать из справочника Номенклатура только те записи,
которые относятся к услугам. Для этого мы воспользуемся отбором, как показано в листинге 29.7.
Листинг 29.7. Выборка записей из справочника Номенклатура, являющихся
услугой
// Получаем ссылку на справочник
НоменклатураСсылка = Справочники.Номенклатура;
// Задаем условия отбора
ОтборУслуга = Новый Структура("Услуга");
ОтборУслуга.Услуга = Истина;
// Строка сортировки
СтрокаПорядок = "Номенклатура";
// Выбираем элементы справочника
ВыборкаДанные = НоменклатураСсылка.Выбрать(, , ОтборУслуга,
СтрокаПорядок);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Код услуги
Сообщить("Код: " + ВыборкаДанные.Код);
// Наименование услуги
Сообщить("Наименование: " + ВыборкаДанные.Наименование);
КонецЦикла;
В результате выполнения мы получим все записи справочника, относящиеся
к услугам. Для этого мы поставили условие отбора по значению реквизита
Услуга. Кроме того, сделали сортировку записей по номенклатуре в порядке
возрастания.
Выборкой можно воспользоваться для модификации или удаления существующих записей справочника. Рассмотрим пример, позволяющий удалить
все записи, являющиеся услугой (листинг 29.8).
Листинг 29.8. Удаление записей из справочника Номенклатура, являющихся
услугой
// Получаем ссылку на справочник
НоменклатураСсылка = Справочники.Номенклатура;
// Задаем условия отбора
ОтборУслуга = Новый Структура("Услуга");
Глава 29. Справочники
389
ОтборУслуга.Услуга = Истина;
// Строка сортировки
СтрокаПорядок = "Номенклатура";
// Выбираем элементы справочника
ВыборкаДанные = НоменклатураСсылка.Выбрать(, , ОтборУслуга,
СтрокаПорядок);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем ссылку на объект
ОбъектУслуга = ВыборкаДанные.ПолучитьОбъект();
// Удаляем элемент
ОбъектУслуга.Удалить();
КонецЦикла;
Здесь мы сделали отбор по всей номенклатуре, относящейся к услугам. После
этого, получив ссылку на каждый выбранный элемент, удалили его из базы.
Следующий метод ВыбратьИерархически помогает выбрать элементы в иерархическом порядке. В этом режиме для каждой записи вначале выводятся все
подчиненные элементы, а затем выбирается следующая запись. Данный метод имеет четыре необязательных параметра, аналогичных методу Выбрать,
и возвращает ссылку на объект СправочникВыборка. Посмотрите пример
использования данного метода, представленный в листинге 29.9.
Листинг 29.9. Выборка элементов справочника по иерархии
// Получаем ссылку на справочник
НоменклатураСсылка = Справочники.Номенклатура;
// Строка сортировки
СтрокаПорядок = "Номенклатура";
// Выбираем элементы справочника
ВыборкаДанные = НоменклатураСсылка.ВыбратьИерархически(, , ,
СтрокаПорядок);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Код номенклатуры
Сообщить("Код: " + ВыборкаДанные.Код);
// Наименование номенклатуры
Сообщить("Наименование: " + ВыборкаДанные.Наименование);
// Получаем уровень элемента в выборке
Сообщить("Уровень: " + ВыборкаДанные.УровеньВВыборке());
КонецЦикла;
390
Часть VI. Другие объекты конфигурации
В приведенном примере мы вызвали метод ВыбратьИерархически только
с заданной сортировкой. Результат запроса вернул нам список номенклатуры,
сгруппированной по иерархии. Для каждой записи вначале читаются все
подчиненные элементы, а затем выводится следующая запись выборки.
С помощью метода УровеньВВыборке (объект СправочникВыборка) можно
узнать уровень, на котором находится элемент в выборке. Уровень верхнего
элемента равен всегда 0. И еще следует помнить, что когда для справочника
в конфигураторе не установлен признак иерархии (в справочнике не используется иерархия по группам и элементам), выполнение метода вызовет ошибку. Если на этапе выполнения вы не знаете этого, можно применить обработчик ошибок, как показано в листинге 29.10.
Листинг 29.10. Выборка элементов справочника с проверкой поддержки
иерархии
// Получаем ссылку на справочник
НоменклатураСсылка = Справочники.Номенклатура;
// Строка сортировки
СтрокаПорядок = "Номенклатура";
// Выбираем элементы справочника
Попытка
ВыборкаДанные = НоменклатураСсылка.ВыбратьИерархически(, , ,
СтрокаПорядок);
Исключение
ВыборкаДанные = НоменклатураСсылка.Выбрать(, , , СтрокаПорядок);
КонецПопытки;
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Код номенклатуры
Сообщить("Код: " + ВыборкаДанные.Код);
// Наименование номенклатуры
Сообщить("Наименование: " + ВыборкаДанные.Наименование);
КонецЦикла;
В этом случае, если при попытке использовать выборку по иерархии произойдет ошибка, обработчик вызовет метод Выбрать.
Следующий вопрос, о котором мы поговорим, относится к блокировке объекта, позволяющей исключить изменения, вносимые другими пользователями, в момент модификации элемента. Для этого предназначены два метода:
Заблокировать и Разблокировать. Например, для корректной пометки элемен-
Глава 29. Справочники
391
тов справочника на удаление предварительно заблокируем их от изменений,
как показано в листинге 29.11.
Листинг 29.11. Выполнение блокировки элементов справочника
// Получаем ссылку на справочник
КонтрагентыСсылка = Справочники.Контрагенты;
// Выбираем элементы справочника
ВыборкаДанные = КонтрагентыСсылка.Выбрать(, , ОтборУслуга);
// Обрабатываем полученные данные
Пока ВыборкаДанные.Следующий() Цикл
// Получаем ссылку на объект
ОбъектКонтрагент = ВыборкаДанные.ПолучитьОбъект();
Попытка
// Блокируем выбранный элемент
ОбъектКонтрагент.Заблокировать();
// Делаем пометку об удалении
ОбъектКонтрагент.УстановитьПоменткуУдаления(Истина);
// Снимаем блокировку
ОбъектКонтрагент.Разблокировать();
Исключение
Сообщить("Не удалось заблокировать текущий элемент!");
КонецПопытки;
КонецЦикла;
После того как мы получили выборку, получаем ссылку на текущий элемент.
Далее вызываем метод Заблокировать и пытаемся установить блокировку.
Если это не удается сделать (например, объект уже заблокирован другим
пользователем), происходит ошибка выполнения. В случае, когда операция
блокировки проходит успешно, помечаем элемент на удаление и снимаем
блокировку методом Разблокировать.
А теперь познакомимся с методами, осуществляющими поиск нужных записей в справочнике. Для этого в объекте СправочникМенеджер предусмотрены три метода: НайтиПоКоду, НайтиПоНаименованию и НайтиПоРеквизиту.
Первый из них предусматривает поиск по коду (числовому или строковому),
второй ищет по наименованию элемента, а третий — по значению реквизита.
Метод НайтиПоКоду имеет четыре параметра, первый из которых является
обязательным. Остальные служат для установки признака поиска по полному
коду, по родителю и по подчиненным элементам. Пример работы с данным
методом представлен в листинге 29.12.
392
Часть VI. Другие объекты конфигурации
Листинг 29.12. Поиск элемента справочника с помощью метода НайтиПоКоду
// Получаем ссылку на справочник
КонтрагентыСсылка = Справочники.Контрагенты;
// Получаем ссылку на запись
ЗаписьСсылка = КонтрагентыСсылка.НайтиПоКоду("123");
// Проверяем существование элемента в справочнике
Если НЕ ЗаписьСсылка.ПустаяСсылка() Тогда
// Код
Сообщить("Код: " + ЗаписьСсылка.Код);
// Наименование
Сообщить("Наименование: " + ЗаписьСсылка.Наименование);
КонецЕсли;
Указав строковое значение кода контрагента, мы получили ссылку на существующую запись справочника.
Метод НайтиПоНаименованию имеет тоже четыре параметра, обязательным из
которых является только первый. Второй параметр задает поиск по точному
соответствию, третий и четвертый — аналогичны параметрам из предыдущего метода. Пример работы с этим методом приводить не буду, поскольку мы
уже им пользовались. Отмечу только, что поиск по точному соответствию
подразумевает точное совпадение искомого наименования с существующим
в справочнике, за исключением завершающих пробелов.
Последний из методов поиска НайтиПоРеквизиту позволяет найти элемент
справочника по значению реквизита. Если существуют одинаковые значения
для разных реквизитов, будет получен только один из них. Метод имеет четыре параметра: имя реквизита, значение, родитель и владелец. Первые два
являются обязательными. Посмотрите пример использования данного метода, представленный в листинге 29.13.
Листинг 29.13. Поиск элемента справочника с помощью метода НайтиПоРеквизиту
// Получаем ссылку на справочник
ПользователиСсылка = Справочники.Пользователи;
// Получаем ссылку на контрагента
ПоставщикСсылка = Справочники.Контрагенты.НайтиПоНаименованию("Фабрика");
// Получаем ссылку на запись
ЗаписьСсылка = ПользователиСсылка.НайтиПоРеквизиту("ОсновнойПоставщик",
ПоставщикСсылка);
Глава 29. Справочники
393
// Проверяем существование элемента в справочнике
Если НЕ ЗаписьСсылка.ПустаяСсылка() Тогда
// Наименование пользователя
Сообщить("Пользователь: " + ЗаписьСсылка.Наименование);
КонецЕсли;
Итак, мы выполнили поиск пользователя по значению реквизита. Запрос вернул элемент справочника, для которого в качестве значения реквизита установлен выбранный контрагент. Как видите, все достаточно просто.
Следующей темой для разговора будет получение форм справочника. Поддерживаются следующие основные типы форм: списка, элемента, группы,
выбора и выбора группы. Объект СправочникМенеджер содержит несколько
методов получения форм. К ним относятся:
ПолучитьФорму — получает указанную форму справочника;
ПолучитьФормуНовогоЭлемента — получает пустую форму для заполнения
нового элемента справочника;
ПолучитьФормуНовойГруппы — получает пустую форму для заполнения
новой группы;
ПолучитьФормуВыбора — получает форму списка для выбора элемента
справочника;
ПолучитьФормуСписка — получает форму списка элементов справочника.
Первый метод ПолучитьФорму имеет три параметра и позволяет получить любую заданную в конфигураторе форму справочника. Посмотрите пример,
приведенный в листинге 29.14.
Листинг 29.14. Получение форм списка с помощью метода ПолучитьФорму
// Получаем форму списка
ФормаСписка = Справочники.Номенклатура.ПолучитьФорму("ФормаСписка");
// Проверяем, если форма уже открыта, обновляем ее
Если ФормаСписка.Открыта() Тогда
ФормаСписка.Обновить();
Иначе
ФормаСписка.Открыть();
КонецЕсли;
В рассмотренном примере мы получили форму списка. Если она уже открыта, мы просто обновили ее, если нет — открыли и вывели на экран. Следует
394
Часть VI. Другие объекты конфигурации
помнить, если указана форма редактирования элемента (группы), то будет
открыта новая форма для ввода элемента. Как вы заметили, в качестве первого параметра мы передали имя требуемой формы, как это задано в конфигураторе. Второй параметр задает имя владельца формы, а третий позволяет
установить уникальный ключ, по которому можно однозначно идентифицировать форму среди других. Как применять эти параметры, подробно описано в главе 4.
Методы ПолучитьФормуНовогоЭлемента и ПолучитьФормуНовойГруппы получают соответствующие формы для заполнения новых элементов справочника. Они также поддерживают параметры владельца и уникального ключа.
Метод ПолучитьФормуСписка получает заданную в конфигураторе форму для
отображения списка элементов справочника, а метод ПолучитьФормуВыбора
читает специальную форму, которая организует выбор групп или отдельных
элементов для передачи ссылки в другие объекты конфигурации. Например,
если вы заполняете номенклатурную часть документа, вызывается форма выбора. Оба этих метода имеют по три необязательных параметра: имя формы,
владелец и уникальный ключ. Примеры работы с этими методами рассматривать мы не будем, поскольку они ничем особым не отличаются от метода ПолучитьФорму.
Базовые объекты для работы со справочниками содержат определенные
свойства. Некоторые из них мы уже рассмотрели, с другими познакомимся
прямо сейчас.
Объекты СправочникОбъект и СправочникСсылка предоставляют для работы с подчиненными справочниками свойство Владелец, которое содержит
ссылку на владельца элемента справочника. Например, при добавлении новой записи можно явно указать владельца. Посмотрите пример, показанный
в листинге 29.15.
Листинг 29.15. Использование свойства Владелец
// Получаем ссылку на владельца
ВладелецСсылка =
Справочники.Номенклатура.НайтиПоНаименованию("Садовый инвентарь");
// Формируем новый элемент справочника
НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
// Заполняем реквизиты
НоваяНоменклатура.Владелец = ВладелецСсылка;
НоваяНоменклатура.Код = "0022";
НоваяНоменклатура.Наименование = "Лопата";
Глава 29. Справочники
395
НоваяНоменклатура.ПолноеНаименование = "Лопата совковая";
// Записываем новый элемент в справочник
НоваяНоменклатура.Записать();
Как видите, сначала мы получили ссылку на владельца элементов справочника. Затем создали новую запись и передали свойству Владелец полученную
ссылку. В результате новый созданный элемент будет добавлен в группу владельца справочника номенклатуры. Данное свойство, естественно, доступно
и для чтения.
Следующее свойство называется Предопределенный и позволяет определить
признак предопределенности для выбранного элемента справочника (доступно только для чтения). К предопределенным элементам в 1С относятся те,
для которых система автоматически формирует объекты с заданными значениями на этапе создания конфигурации, а не во время выполнения программы. Как использовать это свойство, продемонстрировано в листинге 29.16.
Листинг 29.16. Использование свойства Предопределенный
// Получаем ссылку на справочник
КонтрагентыСсылка = Справочники.Контрагенты;
// Получаем ссылку на запись
ЗаписьСсылка = КонтрагентыСсылка.НайтиПоКоду("002");
// Проверяем существование предопределенного элемента в справочнике
Если НЕ ЗаписьСсылка.ПустаяСсылка() И ЗаписьСсылка.Предопределенный Тогда
Сообщить("Код: " + ЗаписьСсылка.Код);
Сообщить("Наименование: " + ЗаписьСсылка.Наименование);
КонецЕсли;
В этом коде мы проверяем, является ли полученный элемент справочника
предопределенным. Думаю, дальнейшие пояснения не требуются, поскольку
все достаточно просто.
И последнее свойство, которое мы разберем, называется ЭтоГруппа. Оно доступно только для чтения и позволяет определить, является ли текущий элемент справочника группой. Пример использования данного свойства представлен в листинге 29.17.
Листинг 29.17. Использование свойства ЭтоГруппа
// Получаем ссылку на элемент справочника
НоменклатураСсылка =
Справочники.Номенклатура.НайтиПоНаименованию("Садовый инвентарь");
396
Часть VI. Другие объекты конфигурации
// Проверяем, является ли полученный элемент группой
Если НоменклатураСсылка.ЭтоГруппа Тогда
Сообщить("Выбранный элемент является группой!");
КонецЕсли;
Думаю, представленный пример не нуждается в комментариях.
Базовый объект СправочникОбъект, кроме свойств и методов, предоставляет
несколько предопределенных событий:
ПередЗаписью — происходит перед записью элемента в справочник;
ПриЗаписи — событие вызывается после сохранения элемента в базе, но до
завершения транзакции;
ПередУдалением — происходит перед удалением элемента из базы данных;
ПриКопировании — возникает в момент создания нового элемента спра-
вочника посредством операции копирования или вызова метода Скопировать;
ОбработкаЗаполнения — происходит при формировании новой записи на
основании или с помощью метода Заполнить (объект СправочникОбъект);
ПриУстановкеНовогоКода — вызывается при формировании нового кода
для элемента справочника.
События позволяют выполнить проверку данных перед записью в базу данных, внести какие-либо изменения или отменить операцию. Все они могут
быть вызваны и обработаны из модуля объекта или формы. Однако следует
помнить, что события модуля объекта вызываются в любом случае, а модуля
формы — только при интерактивной записи. Например, попробуем в событии ПередЗаписью проверить, является ли записываемый элемент группой.
Как это сделать, показано в листинге 29.18.
Листинг 29.18. Использование события ПередЗаписью
Процедура ПередЗаписью(Отказ)
Если ЭтоГруппа Тогда
Сообщить("Записываемый элемент является группой!");
КонецЕсли;
КонецПроцедуры
В обработчике мы проверяем свойство объекта ЭтоГруппа и выводим сообщение. Как вы заметили, предопределенная процедура содержит один параметр, управляющий процессом записи. Если установить его в значение
Глава 29. Справочники
397
ИСТИНА,
операция записи будет отменена. Например, чтобы ограничить запись
нового элемента, являющегося услугой, в справочник Номенклатура, выполните код, представленный в листинге 29.19.
Листинг 29.19. Использование события ПриЗаписи
Процедура ПриЗаписи(Отказ)
Если (НЕ ЭтоГруппа)
И (НЕ Услуга.Пустая()) Тогда
Сообщить("Записываем элемент в базу!");
Иначе
// Отменяем операцию записи
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
В результате выполнения кода пользователь не сможет записать новый элемент справочника, относящегося к услуге.
Посредством события ПриКопировании можно определить заполнение отдельных реквизитов по умолчанию, например, как показано в листинге 29.20.
Листинг 29.20. Использование события ПриКопировании для справочника
Контрагенты
Процедура ПриКопировании(ОбъектКопирования)
Если НЕ ЭтотОбъект.ЭтоГруппа Тогда
// Назначаем физическое лицо по умолчанию
ФизЛицоСсылка =
Справочники.ФизическиеЛица.НайтиПоНаименованию("ИвановИ.");
ЭтотОбъект.ОсновноеЮрФизЛицо = ФизЛицоСсылка;
// Устанавливаем значение по умолчанию основного договора
ЭтотОбъект.ОсновнойДоговорВзаиморасчетов = Неопределено;
КонецЕсли;
КонецПроцедуры
На данном примере мы продемонстрировали использование данного события
для заполнения значений реквизитов по умолчанию. Вы должны сами продумать процедуру ПриКопировании в соответствии с поставленными задачами.
Событие ПередУдалением дает возможность отменить удаление элемента
справочника из базы при определенных условиях. Единственный параметр
398
Часть VI. Другие объекты конфигурации
процедуры содержит признак отмены операции. Чтобы отменить удаление,
следует передать в него значение ИСТИНА. Обработчик данного события вызывается при интерактивном копировании и при вызове метода Скопировать
(объект СправочникОбъект).
Событие ОбработкаЗаполнения более характерно для документов, где организован ввод на основании. В справочниках оно применяется редко. Содержит единственный параметр — ссылку на документ-основание. Если вы планируете вводить новые элементы справочника на основании других объектов,
следует переопределить обработчик этого события для правильного заполнения данными.
Последнее событие ПриУстановкеНовогоКода позволяет установить собственные правила формирования кода для каждого нового элемента справочника.
Имеет два параметра: признак стандартной обработки и значение префикса.
При установке первого параметра в значение ИСТИНА будет выполняться
стандартная генерация кода элемента, заданная системой. Второй параметр
используется для передачи префикса, на основании которого будет создан
новый код (только для строкового типа). Примеры работы с этими событиями мы опустим, поскольку здесь нет ничего сложного, и вы сможете самостоятельно во всем разобраться.
Последняя тема, о которой пойдет речь, относится к использованию языка
запросов для получения данных из справочников. Как и прежде, здесь следует создать новый объект Запрос и установить свойство Текст. После этого
достаточно вызвать метод Выполнить и обработать результат запроса. Например, чтобы получить пользователя из справочника Пользователи, можно воспользоваться кодом, представленным в листинге 29.21.
Листинг 29.21. Организация выборки из справочника Пользователи
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|
Наименование
КАК Наименование,
|
ОсновнаяОрганизация
КАК ОсновнаяОрганизация,
|
ОсновнойПокупатель
КАК ОсновнойПокупатель,
|
ОсновнойПоставщик
КАК ОсновнойПоставщик,
|
ОсновнойСкладКомпании
КАК ОсновнойСклад
|ИЗ
|
Справочник.Пользователи
Глава 29. Справочники
399
|
|ГДЕ
|
(НЕ ЭтоГруппа) И
|
Пользователи.Код = &КодПользователя";
// Передаем код
Запрос.УстановитьПараметр("КодПользователя", КодПользователя);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Передав в качестве параметра запроса код пользователя, мы получили из
справочника Пользователи все интересующие нас сведения. А теперь получим из справочника Номенклатура все записи, являющиеся услугами (листинг 29.22).
Листинг 29.22. Получение услуг из справочника Номенклатура
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|
Код
КАК КодНоменклатуры,
|
Наименование КАК Номенклатура,
|
Ссылка
КАК Ссылка
|ИЗ
|
Справочник.Номенклатура
|
|ГДЕ
|
(НЕ(ЭтоГруппа) И Услуга)
|
|УПОРЯДОЧИТЬ ПО
|
Наименование";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Запрос вернет нам все элементы, связанные с услугами. В следующем запросе мы получим сведения по контрагенту, передав в качестве параметра запроса ссылку на основное физическое лицо, связанное с ним. Как это сделать, показано в листинге 29.23.
400
Часть VI. Другие объекты конфигурации
Листинг 29.23. Получение данных из справочника Контрагенты
// Получаем ссылку на физическое лицо
ФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Петров");
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|
Код
КАК КодКонтрагента,
|
Наименование
КАК НаименованиеКонтрагента,
|
ОсновноеЮрФизЛицо
КАК Сотрудник,
|
ОсновнойДоговорВзаиморасчетов КАК ОсновнойДоговор
|ИЗ
|
Справочник.Контрагенты
|
|ГДЕ
|
(ВЫРАЗИТЬ(ОсновноеЮрФизЛицо КАК Справочник.ФизическиеЛица) =
|
&ФизЛицо) И
|
(НЕ ЭтоГруппа)";
// Передаем код
Запрос.УстановитьПараметр("ФизЛицо", ФизЛицоСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
Чтобы получить контрагента, связанного с выбранным физическим лицом,
мы передали в запрос ссылку на это лицо. В самом запросе преобразовали
поле ОсновноеЮрФизЛицо, имеющее составной тип, к ссылке на справочник
физических лиц.
Как вы убедились, использовать язык запросов для получения данных из
справочников довольно просто. Старайтесь, где это возможно, пользоваться
именно запросами, а не объектами конфигурации, чтобы извлечь нужную
информацию. А мы на этом завершим изучение справочников и перейдем
к другим объектам системы — перечислениям.
Глава 30
Перечисления
В этой главе мы остановимся на еще одном прикладном объекте конфигурации — перечислении. Перечисление представляет собой набор постоянных значений, задаваемых для описания возможных значений определенной характеристики какого-либо объекта или обобщенного понятия. Например, перечисление
ВидыАдресов содержит возможные виды адресов (юридический адрес и фактический адрес) и может использоваться с различными объектами конфигурации.
Перечисление создается в конфигураторе на этапе проектирования конфигурации. Кроме того, существуют системные перечисления, применяемые в качестве
значений свойств объектов и параметров методов. Примером системного перечисления служит РежимПроведенияДокумента, имеющее два значения: оперативное и неоперативное проведение. В отличие от перечислений, заданных разработчиком в конфигураторе, системные недоступны для изменения. Для работы
с перечислениями в 1С предназначены следующие базовые объекты:
ПеречисленияМенеджер — позволяет получить доступ к любому объекту
перечисления через ПеречислениеМенеджер;
ПеречислениеМенеджер — предназначен для получения ссылок на значе-
ния перечисления;
ПеречислениеСсылка — позволяет получить ссылку на значение перечис-
ления.
Рассмотрим пример получения значений перечисления, представленный
в листинге 30.1.
Листинг 30.1. Получение значений перечисления
// Получаем ссылку на выбранное перечисление
ВидыВзаиморасчетовСсылка = Перечисления.ВедениеВзаиморасчетовПоДоговорам;
// Получаем все доступные значения
402
Часть VI. Другие объекты конфигурации
Для Каждого Значение Из ВидыВзаиморасчетовСсылка Цикл
Сообщить(Значение);
КонецЦикла;
В результате выполнения кода в окно сообщений будут выведены все значения перечисления ВедениеВзаиморасчетовПоДоговорам. Для этого мы вначале получили ссылку на объект ПеречислениеМенеджер, а затем в цикле
прочитали все элементы данного объекта.
Перечисления часто используются для сравнения передаваемых данных. Например, в следующем коде выполняется проверка статуса оприходованной партии (перечисление СтатусыПартий) по коду операции (перечисление
КодыОпераций), связанной с поступлением товара (листинг 30.2).
Листинг 30.2. Определение статуса партии номенклатуры
// Задаем значение для сравнения
КодОперацииСсылка = Перечисления.КодыОпераций.ПоступлениеТоваровКомиссия;
// Проверяем ссылку
Если КодОперацииСсылка = Перечисления.КодыОпераций.ОприходованиеТоваров
ИЛИ КодОперацииСсылка = Перечисления.КодыОпераций.ПоступлениеТоваров
Тогда
Сообщить("Статус партии: купленный");
ИначеЕсли
КодОперацииСсылка =
Перечисления.КодыОпераций.ПоступлениеТоваровКомиссия Тогда
Сообщить("Статус партии: принят на комиссию");
КонецЕсли;
Чтобы определить статус партии, мы сравнили имеющееся значение кода
операции со значениями перечисления КодыОпераций. Сравнение можно
выполнять и с полями выборки запроса, как показано в листинге 30.3.
Листинг 30.3. Использование перечисления для обработки данных запроса
// Получаем ссылку на организацию
ОрганизацияСсылка = Справочники.Организации.НайтиПоКоду("0005");
// Создаем объект запроса
Запрос = Новый Запрос;
// Формируем текст запроса
Глава 30. Перечисления
403
Запрос.Текст = "
|ВЫБРАТЬ
|
ОтветственноеЛицо КАК ОтветственноеЛицо,
|
ФизическоеЛицо
КАК ФизическоеЛицо
|ИЗ
|
РегистрСведений.ОтветственныеЛицаОрганизации.СрезПоследних(
|
&ДатаСреза, СтруктурнаяЕдиница = &ОрганизацияСсылка)";
// Устанавливаем параметры запроса
Запрос.УстановитьПараметр("ДатаСреза", ТекущаяДата());
Запрос.УстановитьПараметр("ОрганизацияСсылка", ОрганизацияСсылка);
// Выполняем запрос
ВыборкаДанные = Запрос.Выполнить().Выбрать();
// Обрабатываем результат запроса
Пока ВыборкаДанные.Следующий() Цикл
Если ВыборкаДанные.ОтветственноеЛицо =
Перечисления.ОтветственныеЛицаОрганизации.ГлавныйБухгалтер Тогда
// Выводим имя главного бухгалтера организации
Сообщить(ВыборкаДанные.ФизическоеЛицо);
ИначеЕсли ВыборкаДанные.ОтветственноеЛицо =
Перечисления.ОтветственныеЛицаОрганизации.Руководитель Тогда
// Выводим имя руководителя организации
Сообщить(ВыборкаДанные.ФизическоеЛицо);
КонецЕсли;
КонецЦикла;
В данном примере мы вначале сделали запрос к регистру сведений ОтветственныеЛицаОрганизации, чтобы получить имена руководителя и главного бухгалтера для выбранной организации. Затем сравнили значение поля выборки
ОтветственноеЛицо со значением перечисления ОтветственныеЛицаОрганизации, чтобы определить тип полученного значения. Следует заметить, что измерение ОтветственноеЛицо регистра сведений ОтветственныеЛицаОрганизации
имеет тип ПеречислениеСсылка.ОтветственныеЛицаОрганизации, поэтому мы
смогли выполнить сравнение значение поля из запроса со значением перечисления.
Как уже было сказано, кроме пользовательских, существует целый набор
системных перечислений. С некоторыми из них мы знакомились в предыдущих главах, а некоторые наиболее востребованные разберем сейчас. В первую очередь, это перечисление ВидСравнения. Оно содержит набор значений
404
Часть VI. Другие объекты конфигурации
для выбора вариантов сравнения в условиях отбора и чаще всего применяется
в отчетах. Рассмотрим пример использования данного перечисления, представленный в листинге 30.4.
Листинг 30.4. Использование системного перечисления ВидСравнения
// Получаем форму выбора документа РеализацияТоваров
ФормаВыбора = Документы.РеализацияТоваров.ПолучитьФормуВыбора();
// Устанавливаем режим выбора
ФормаВыбора.РежимВыбора = Истина;
// Формируем список по видам операций документа
ВидыОперацийСписок = Новый СписокЗначений;
ВидыОперацийСписок.Добавить(
Перечисления.ВидыОперацийРеализацияТоваров.РеализацияТоваров);
ВидыОперацийСписок.Добавить(
Перечисления.ВидыОперацийРеализацияТоваров.РеализацияТоваровКомиссия);
ВидыОперацийСписок.Добавить(
Перечисления.ВидыОперацийРеализацияТоваров.РеализацияТоваровРозница);
// Заполняем отбор формы выбора
ФормаВыбора.Отбор.ВидОперации.ВидСравнения = ВидСравнения.ВСписке;
ФормаВыбора.Отбор.ВидОперации.Значение = ВидыОперацийСписок;
ФормаВыбора.Отбор.ВидОперации.Использование = Истина;
// Открываем форму выбора
ДокументРеализация = ФормаВыбора.ОткрытьМодально();
В данном примере мы применили ВидСравнения для установки отбора для
формы выбора документа. Для этого вначале мы сформировали список значений по видам операций, доступных для документа, а затем передали его
в объект отбора формы. При этом мы задали вид сравнения в списке, при котором будут выбраны документы, имеющие значение реквизита ВидОперации равным хотя бы одному значению из списка.
Следующее перечисление называется ОбходРезультатаЗапроса и используется в качестве первого необязательного параметра метода Выбрать объекта
Запрос. Оно позволяет задать вид обхода полученных в результате запроса
данных и может принимать одно из трех возможных значений: Прямой, ПоГруппировкам и ПоГруппировкамСИерархией. Посмотрите пример, представленный в листинге 30.5.
Глава 30. Перечисления
405
Листинг 30.5. Использование системного перечисления ОбходРезультатаЗапроса
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Контрагент
КАК Поставшик,
|
СуммаДокумента КАК Сумма
|ИЗ
|
Документ.ВозвратТоваровПоставщику
|ГДЕ
|
Дата МЕЖДУ &ДатаНачальная И &ДатаКонечная И
|
Сделка ССЫЛКА Документ.ЗаказПоставщику
|УПОРЯДОЧИТЬ ПО
|
Дата
|ИТОГИ ПО
|
Контрагент";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("ДатаНачальная", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаКонечная", КонецДня(ТекущаяДата()));
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
// Выбираем данные с группировкой по полю Поставщик
Данные = ДанныеВыборки.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Как видите, мы сделали запрос по документу, выполнив группировку по полю поставщика. После выполнения запроса вызвали метод Выбрать, установив вид обхода по группировкам.
Еще одно полезное перечисление НаправлениеСортировки позволяет указать
порядок размещения данных в списке значений. В листинге 30.6 показан
пример работы с этим перечислением.
Листинг 30.6. Использование системного перечисления НаправлениеСортировки
// Формируем новый список значений
Список = Новый СписокЗначений;
// Добавляем элементы в список
Список.Добавить("Холодильник");
406
Часть VI. Другие объекты конфигурации
Список.Добавить("Электроплита");
Список.Добавить("Кухонный комбайн");
// Упорядочиваем элементы списка в порядке возрастания
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
Здесь мы применили перечисление для упорядочивания элементов в списке
значений. Для этого вызвали метод СортироватьПоЗначению (объект СписокЗначений) и передали в качестве параметра вид сортировки.
И последние два системных перечисления, о которых пойдет речь, называются РежимДиалогаВопрос и КодВозвратаДиалога. Они применяются совместно со встроенной функцией Вопрос. Эта функция служит для интерактивного взаимодействия с пользователем. Пример их использования приведен
в листинге 30.7.
Листинг 30.7. Использование системных перечислений РежимДиалогаВопрос
и КодВозвратаДиалога
// Формируем окно вопроса для пользователя
ВозвращаемоеЗначение = Вопрос("Продолжить операцию?",
РежимДиалогаВопрос.ДаНет, , КодВозвратаДиалога.Нет, );
// Проверяем ответ пользователя
Если ВозвращаемоеЗначение = КодВозвратаДиалога.Да Тогда
// Продолжаем выполнение операции
Иначе
// Прерываем выполнение операции
КонецЕсли;
Думаю, пояснения к этому примеру не требуются, поскольку все и так достаточно просто.
На этом мы завершим тему перечислений. Все представленные в этой главе
примеры наглядно демонстрируют способы работы и возможности пользовательских и системных перечислений. Самостоятельное изучение и использование других перечислений не вызовет у вас никаких затруднений.
Глава 31
Планы видов характеристик
В этой главе мы познакомимся еще с одним прикладным объектом конфигурации 1С — планом видов характеристик. Данный объект предназначен для
хранения характеристик различных объектов конфигурации, которые можно
создавать непосредственно в процессе работы с программой. Другими словами, пользователи могут использовать заданные разработчиком планы видов характеристик для своих нужд: ввода дополнительных свойств и характеристик номенклатуры, контрагентов и других целей.
На этапе проектирования устанавливается свойство "Тип значения", которое
может состоять из различных типов данных (примитивных и ссылочных),
имеющихся в системе. Кроме этого, назначается второе свойство "Дополнительные значения характеристик", указывающее на подчиненный справочник,
где будут храниться все значения характеристик и свойств различных объектов. На основании информации, хранящейся в планах видов характеристик,
можно проводить анализ и строить отчеты в разрезе отдельных характеристик.
Также с помощью данного вида объекта организуется аналитический учет по
субконто, используемый в бухгалтерском учете. Как и другие уже изученные
объекты конфигурации, он поддерживает создание форм и макетов.
Для работы с ним предназначены следующие базовые объекты:
ПланыВидовХарактеристикМенеджер — позволяет получить доступ к от-
дельным значениям типа ПланВидовХарактеристикМенеджер;
ПланВидовХарактеристикМенеджер — предназначен для общей работы с от-
дельным объектом плана видов характеристик (создание, поиск, выборка);
ПланВидовХарактеристикОбъект — позволяет получить непосредствен-
ный доступ к объекту для модификации и удаления;
ПланВидовХарактеристикСписок — предназначен для управления спи-
ском элементов;
408
Часть VI. Другие объекты конфигурации
ПланВидовХарактеристикСсылка — служит для передачи ссылки другим
объектам конфигурации;
ПланВидовХарактеристикВыборка — служит в качестве возвращаемого
значения для методов выборки.
Все они предоставляют необходимые свойства, методы и события, позволяющие программно управлять объектом. Мы рассмотрим наиболее важные
моменты работы с планами видов характеристик, а также научимся делать
запросы посредством объекта Запрос.
Вначале поговорим о том, как создать новый или изменить существующий
элемент плана видов характеристик. Посмотрите код, представленный в листинге 31.1.
Листинг 31.1. Создание нового элемента
// Получаем ссылку на созданный элемент
НовыйЭлемент = ПланыВидовХарактеристик.СвойстваОбъектов.СоздатьЭлемент();
НовыйЭлемент.Код = "122";
НовыйЭлемент.Наименование = "Количество дней для отгрузки";
НовыйЭлемент.НазначениеСвойства =
ПланыВидовХарактеристик.НазначенияСвойствКатегорийОбъектов.Справочник_
Контрагенты;
// Задаем тип значения для нового элемента
НовыйЭлемент.ТипЗначения = Новый ОписаниеТипов
("СправочникСсылка.Контрагенты");
// Сохраняем новый элемент в базе данных
НовыйЭлементОбъект.Записать();
Для создания нового элемента мы вначале вызвали метод СоздатьЭлемент
(объект ПланВидовХарактеристикМенеджер). В результате получили новый
элемент и заполнили необходимые реквизиты. После этого установили тип
значения как ссылку на справочник ЗначенияСвойствОбъектов посредством
специального объекта ОписаниеТипов (управляет различными типами значений для передачи их другим объектам). Чтобы записать элемент в базу, вызвали метод Записать (объект ПланВидовХарактеристикОбъект). Итогом наших
действий стал новый элемент, который можно использовать для свойства справочника Контрагенты. Удалить план видов характеристик из базы данных
можно с помощью метода Удалить (объект ПланВидовХарактеристикОбъект).
Можно также прочитать свойство из плана вида характеристик, применив
код из листинга 31.2.
Глава 31. Планы видов характеристик
409
Листинг 31.2. Получение свойства
// Имя свойства
СвойствоИмя = "Количество дней для отгрузки";
// Получаем ссылку на свойство
СвойствоСсылка =
ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию
("СвойствоИмя");
// Проверяем, является ли найденный элемент группой
Если НЕ СвойствоСсылка.Пустая() Тогда
Сообщить("Свойство найдено!");
КонецЕсли;
Поиск элемента мы организовали с помощью метода НайтиПоНаименованию
(объект ПланВидовХарактеристикМенеджер). В качестве первого обязательного параметра он принимает наименования свойства. При наличии нескольких
свойств с одинаковым именем будет получено только одно. После получения
искомого значения вызываем метод Пустая (объект ПланВидовХарактеристикСсылка), чтобы убедиться в наличии ссылки на элемент.
Объект плана видов характеристик, как и другие объекты в 1С, поддерживает
получение выборки набора записей по заданным условиям. Делается это посредством метода Выбрать (объект ПланВидовХарактеристикМенеджер). Метод содержит три необязательных параметра: ссылку на родителя, условие
отбора и порядок сортировки. Посмотрите пример, представленный в листинге 31.3.
Листинг 31.3. Выборка элементов
// Устанавливаем порядок сортировки по возрастанию
СтрокаПорядок = "Наименование";
// Делаем выборку всех элементов
ВыборкаЭлементы =
ПланыВидовХарактеристик.СвойстваОбъектов.Выбрать(,,СтрокаПорядок);
// Обрабатываем полученные записи
Пока ВыборкаЭлементы.Следующий() Цикл
// Получаем ссылку на объект
ЭлементОбъект = ВыборкаЭлементы.ПолучитьОбъект();
// Проверяем предопределенные элементы
Если ЭлементОбъект.Предопределенный Тогда
410
Часть VI. Другие объекты конфигурации
Сообщить("Предопределенный:" + ЭлементОбъект.Наименование);
Иначе
Сообщить("Имя элемента:" + ЭлементОбъект.Наименование);
КонецЕсли;
КонецЦикла;
Воспользовавшись методом Выбрать, мы получили набор элементов (объект
ПланВидовХарактеристикВыборка), которые обрабатываем в цикле. Кроме
того, установили порядок сортировки по наименованию. Чтобы получить
доступ к объекту выбранного элемента, применяем метод ПолучитьОбъект
(объект ПланВидовХарактеристикВыборка). Далее через полученный объект
читаем наименование каждого элемента. Свойство Предопределенный (объект
ПланВидовХарактеристикВыборка) используется для проверки того, является ли выбранный элемент предопределенным (заданным на этапе построения
конфигурации). Кроме метода Выбрать, существует еще один — ВыбратьИерархически. Он позволяет выбрать элементы с учетом иерархии: вначале выбираются все записи, подчиненные родителю, а затем считывается следующий элемент. Рассматривать мы его не будем, поскольку он ничем больше не
отличается от первого метода.
Для поиска определенного элемента предусмотрены три метода объекта
ПланВидовХарактеристикМенеджер: НайтиПоКоду, НайтиПоНаименованию и
НайтиПоРеквизиту. Метод НайтиПоКоду содержит два параметра, первый из
которых содержит строковое значение кода, а второй, необязательный, ссылается на родительский элемент. Следующий метод выполняет поиск по наименованию элемента. Первый параметр передает имя элемента, второй задает признак поиска по точному совпадению, а третий ссылается на
родителя, в пределах которого будет выполнен поиск. Последний метод дает
возможность получить необходимый элемент по реквизиту и его значению.
Он также позволяет указать и родителя. Пример, представленный листингом 31.4, демонстрирует работу с этими методами.
Листинг 31.4. Поиск элементов
// Получаем ссылку на план видов характеристик
СвойстваОбъектовСсылка = ПланыВидовХарактеристик.СвойстваОбъектов;
// Получаем ссылку на элемент
ЭлементСсылка = СвойстваОбъектовСсылка.НайтиПоКоду("00110");
// Проверяем существование элемента
Если НЕ ЭлементСсылка.Пустая() Тогда
Глава 31. Планы видов характеристик
411
// Наименование
Сообщить("Наименование: " + ЭлементСсылка.Наименование);
КонецЕсли;
// Выполняем поиск по наименованию элемента
ЭлементСсылка = СвойстваОбъектовСсылка.НайтиПоНаименованию("Поставщик");
// Проверяем существование элемента
Если НЕ ЭлементСсылка.Пустая() Тогда
// Код
Сообщить("Код: " + ЭлементСсылка.Код);
КонецЕсли;
// Выполняем поиск по значению реквизита элемента
ЭлементСсылка = СвойстваОбъектовСсылка.НайтиПоРекизиту(
НазначениеСвойства", ЗначениеРеквизита);
// Проверяем существование элемента
Если НЕ ЭлементСсылка.Пустая() Тогда
// Наименование
Сообщить("Код: " + ЭлементСсылка.Код);
// Наименование
Сообщить("Наименование: " + ЭлементСсылка.Наименование);
КонецЕсли;
Как видно из примера, процесс поиска элемента плана видов характеристик
не содержит никаких трудностей.
Теперь поговорим о том, как получить формы и макеты, если они заданы
в конфигурации. Объект ПланВидовХарактеристикМенеджер предоставляет
несколько методов для решения этой задачи:
ПолучитьФорму — получает форму по заданному имени;
ПолучитьФормуНовогоЭлемента — получает форму для создания нового
элемента плана видов характеристик;
ПолучитьФормуНовойГруппы — получает форму для формирования новой
группы элементов;
ПолучитьФормуСписка — получает форму списка элементов;
ПолучитьФормуВыбора — получает специальную форму списка, из которо-
го можно выбрать один элемент;
ПолучитьФормуВыбораГруппы — получает специальную форму списка для
выбора группы.
Все эти методы содержат по три одинаковых параметра: имени формы, владельца и уникального ключа. Единственное отличие состоит в том, что пара-
412
Часть VI. Другие объекты конфигурации
метр метода Получить является обязательным. Приведем примеры работы
с этими методами (листинг 31.5).
Листинг 31.5. Получение форм
// Получаем форму списка
ФормаСписка =
ПланыВидовХарактеристик.СвойстваОбъектов.ПолучитьФорму("ФормаСписка");
// Проверяем, если форма уже открыта, обновляем ее
Если ФормаСписка.Открыта() Тогда
ФормаСписка.Обновить();
Иначе
ФормаСписка.Открыть();
КонецЕсли;
// Получаем форму нового элемента
ФормаЭлемента =
ПланыВидовХарактеристик.СвойстваОбъектов.ПолучитьФормуНовогоЭлемента();
ФормаЭлемента.Открыть();
Результатом работы методов является объект Форма. Как использовать параметры владельца и уникального ключа, подробно описано в главе 12.
И последнее, с чем мы познакомимся — это применение языка запросов для
выборки данных из планов видов характеристик. Для этого следует создать
новый объект Запрос, передать свойству Текст строку запроса и вызвать метод Выполнить. Посмотрите пример, представленный в листинге 31.6.
Листинг 31.6. Получение выборки через запрос
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Код
КАК Код,
|
Наименование
КАК Наименование,
|
НазначениеСвойства КАК НазначениеСсылка
|ИЗ
|
|
ПланВидовХарактеристик.СвойстваОбъектов
Глава 31. Планы видов характеристик
413
|УПОРЯДОЧИТЬ ПО
|
Наименование";
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
В результате запроса мы получим все элементы плана видов характеристик
СвойстваОбъектов, упорядоченные по наименованию. Чтобы выбрать значения свойств определенного объекта, можно воспользоваться кодом, представленным в листинге 31.7.
Листинг 31.7. Получение значений свойств выбранного объекта
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Свойства.Свойство
КАК Свойство,
|
ЗначенияСвойствОбъектов.Значение КАК Значение
|ИЗ
|
(ВЫБРАТЬ
|
|
Ссылка КАК Свойство
ИЗ
|
|
ПланВидовХарактеристик.СвойстваОбъектов
ГДЕ
|
НазначениеСвойства = &НазначениеСвойства) КАК Свойства
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов
|
КАК Значения
|
ПО Значения.Свойство = Свойства.Свойство И
|
Значения.Объект = &ОбъектСсылка
|
|УПОРЯДОЧИТЬ ПО
|
Свойства.Свойство.Наименование";
// Передаем параметры начальной и конечной даты
Запрос.УстановитьПараметр("НазначениеСвойства", НазначениеСсылка);
Запрос.УстановитьПараметр("ОбъектСсылка", ОбъектСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить();
414
Часть VI. Другие объекты конфигурации
С помощью данного запроса мы получим свойства и их значения для выбранного объекта конфигурации. Сами свойства мы считываем из плана видов характеристик СвойстваОбъектов, а значения выбираем из регистра сведений ЗначенияСвойствОбъектов. Это достигается через левое соединение
двух объектов. Кроме того, с помощью параметров запроса задаем ссылки на
выбранный объект (например, справочник Контрагенты) и назначение
свойств.
Не думаю, что для вас составит трудность самостоятельно продолжить изучение планов видов характеристик, а мы тем временем перейдем к следующей теме — построению печатных форм на основе макетов.
Часть VII
Печатные документы
416
Часть I. Приемы программирования документов
Глава 32
Создание и оформление
печатных документов
Немаловажную роль в системе "1С:Предприятие 8.0" играют макеты, с помощью которых можно создавать всевозможные печатные документы и отчеты.
Связано это, в первую очередь, с тем, что наряду с электронными активно используются бумажные документы, для полноценного ведения торговых и бухгалтерских операций. Например, при реализации товаров оформляются
и распечатываются на бумаге товарная накладная и счет-фактура. Для работы
с денежными средствами применяются специальные печатные документы,
например, платежное поручение (входящее или исходящее) или кассовый
ордер (приходный или расходный). Все они относятся к типовым (утвержденным соответствующими государственными органами) и помогают поддерживать требуемое однообразие документов строгой отчетности. Кроме
них, в каждой организации могут существовать собственные печатные документы, которые применяются исключительно внутри предприятия и решают
конкретные внутренние задачи. Не обходятся без макетов и многочисленные
отчеты. Все это свидетельствует о важности темы, о которой пойдет речь
в этой части книги. В официальной справочной информации настолько мало
информации о построении печатных форм, что было бы несправедливо не
уделить внимание данному вопросу. Мы попытаемся восполнить пробел
и познакомимся с тем, как создавать печатные документы на основе макетов,
а также научимся программно заполнять данными готовые печатные формы.
По своей сути макет представляет собой табличный документ и построен на
базе системного объекта ТабличныйДокумент. Он очень похож на таблицу
известной программы Microsoft Excel, поскольку обладает широкими возможностями по форматированию и настройке как отдельных ячеек, так и
любых связанных блоков. Можно сказать, что фирма 1С очень постаралась,
чтобы предложить разработчикам гибкое и универсальное средство, позволяющее легко и достаточно быстро создавать шаблоны печатных документов
418
Часть VII. Печатные документы
любой сложности. К слову, надо добавить несколько слов о наличии некоторых полезных особенностей, не имеющихся в популярном продукте Microsoft Excel.
Создание нового макета и корректировка существующих выполняется в конфигураторе. Для этого предусмотрен специальный конструктор, который помогает задать имя и тип макета. Поддерживается пять типов макетов:
Табличный документ — макет формируется на основании базового сис-
темного объекта ТабличныйДокумент;
Текстовый документ — структура макета описывается обычным тексто-
вым файлом;
Двоичные данные — макет формируется из двоичного файла, заданного
разработчиком;
Active document — для создания макета используется технология OLE
(например, на базе файла Microsoft Excel);
HTML-документ — макет формируется как файл с расширением HTM.
Мы рассмотрим создание макета только на базе табличного документа, так
как этот вариант является наиболее приемлемым для формирования печатных документов строгой отчетности. И еще хочу заметить, если у вас имеются файлы печатных форм в формате Microsoft Excel, не стоит их подключать
к 1С посредством Active document. Практика показывает, что данный способ — не лучший выход, и в будущем возникают непредвиденные сложности
с поддержкой и настройкой документов, а также выводом их на печать.
В данном случае правильнее будет заново построить новый печатный документ на основе табличного документа, тем более, когда система 1С внедряется на вашем предприятии в качестве основной программы управления
товарно-денежными операциями и бухгалтерского учета.
Прежде чем приступить к построению новой печатной формы, мы изучим
методы и свойства объекта ТабличныйДокумент. С их помощью можно настраивать как отдельные ячейки, так и целые области документа. Можно создавать именованные области и форматировать параметры страницы перед
выводом на экран или принтер. Именованная область представляет собой
набор связанных ячеек таблицы, расположенных по горизонтали или вертикали. Это позволяет обращаться к ним по имени, выводить в табличный документ столько раз, сколько это требуется, присоединять одну именованную
область к другой и многое другое. Типичным примером использования повторяющейся именованной области может служить заполнение печатной
формы записями из товарной части документа, когда структура данных постоянна, а меняются только значения. Кроме того, объект табличного документа поддерживает многостраничные печатные документы.
Глава 32. Создание и оформление печатных документов
419
Сразу познакомимся со свойствами, предназначенными для форматирования
всего печатного документа и подготовки к выводу на печать. Часто бывает,
что готовый печатный документ не помещается по ширине на одной странице. Решить данную проблему можно, установив свойство АвтоМасштаб в значение Истина. При этом система самостоятельно настроит масштаб таким
образом, чтобы данные поместились на одной странице. Если необходимо
вручную настроить масштаб, можно применить другое свойство объекта
ТабличныйДокумент — МасштабПечати. Ему можно присвоить положительное (увеличение) или отрицательное (уменьшение) значение, выраженное
в процентах. Данное свойство имеет смысл использовать только тогда, когда
отключено автомасштабирование (свойство АвтоМасштаб). Следующее важное свойство позволяет установить ориентацию страницы. Оно называется
ОриентацияСтраницы и принимает одно из следующих значений: Портрет или
Ландшафт. Для управления отступами от краев служат следующие свойства:
ПолеСверху, ПолеСнизу, ПолеСлева и ПолеСправа. Для установки отступов
следует присвоить этим свойствам числовые значения, выраженные в миллиметрах. По умолчанию отступы равны 10. Свойство ЭкземпляровНаСтранице
позволяет задать количество страниц на одном листе при выводе на печать.
Может принимать определенные значения: 0 — автоматическое размещение,
1 — одна страница, 2 — две страницы. Следующее свойство КоличествоЭкземпляров предназначено для настройки количества копий печатного документа. Отдельное место занимает свойство ИмяПараметровПечати. В качестве значения ему следует передать строку, хранящую имя настроек печати,
которое будет сохранено в базе. Это позволит автоматически восстанавливать все заданные прежде параметры при повторной печати документа. Чтобы передать имя установленного в системе принтера, имеет смысл воспользоваться свойством ИмяПринтера, принимающим строковое значение.
Сохранить параметры отображения окна печатного документа можно, присвоив строковое значение параметров настройки свойству ИмяСохраненияПоложенияОкна. Для управления предварительным просмотром документа перед
выводом на печать следует применить свойство ТолькоПросмотр, передав ему
значение Истина или Ложь. Дополнительно имеется возможность установить
черно-белый режим просмотра посредством свойства ЧерноБелыйПросмотр.
В листинге 32.1 показаны варианты использования свойств объекта ТабличныйДокумент, необходимых для настройки параметров отображения и печати.
Листинг 32.1. Использование свойств для настройки параметров вывода
на печать
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Отключаем автоматическое масштабирование
420
Часть VII. Печатные документы
ТабДокумент.АвтоМасштаб = Ложь;
// Устанавливаем собственный масштаб печати 50%
ТабДокумент.МасштабПечати = 50;
// Задаем ориентацию страницы
ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
// Настраиваем отступы
ТабДокумент.ПолеСверху = 25;
ТабДокумент.ПолеСнизу = 25;
ТабДокумент.ПолеСлева = 30;
ТабДокумент.ПолеСправа = 10;
// Количество страниц на одном листе
ТабДокумент.ЭкземпляровНаСтранице = 1;
// Количество копий документа
ТабДокумент.КоличествоЭкземпляров = 1;
// Сохраняем параметры печати в переменную
ТабДокумент.ИмяПараметровПечати = "ПараметрыПечати_Пример";
// Сохраняем параметры окна
ТабДокумент.ИмяСохраненияПоложенияОкна = "ПараметрыОкна_Пример";
// Задаем предварительный просмотр перед выводом на печать
ТабДокумент.ТолькоПросмотр = Истина;
// Выводим табличный документ на экран
ТабДокумент.Показать();
Думаю, приведенный пример не требует дополнительных комментариев. Замечу только, что для вывода созданного табличного документа на экран мы
воспользовались методом Показать (объект ТабличныйДокумент).
Следующая группа свойств, о которых пойдет речь, предназначена для работы с отдельными ячейками и областями печатного документа. Под областью
понимается как одна-единственная ячейка таблицы, так и целый набор связанных ячеек.
Для поддержки выделенных областей табличного документа применяются
два свойства: ТекущаяОбласть и ВыделенныеОбласти. Первое из них ссылается на активную область (рисунок) таблицы и поддерживает методы и свойства объекта ОбластьЯчеекТабличногоДокумента. Через этот объект можно
управлять форматированием, группировкой и оформлением текущей области
печатного документа. Второе свойство позволяет управлять одновременно
всеми выделенными областями и ссылается на объект ВыделенныеОбластиТабличногоДокумента. С его помощью можно добавлять новые и удалять
существующие, сдвигать и вставлять, а также очищать все выделенные об-
Глава 32. Создание и оформление печатных документов
421
ласти. Как правило, данные свойства используются совместно с методом Об(объект ТабличныйДокумент). Он имеет один обязательный параметр — имя области, которое может быть задано как в виде строки, так
и числовыми значениями, определяющими адрес области. В качестве возвращаемого значения данного метода служит объект ОбластьЯчеекТабличногоДокумента. Кроме того, существует свойство Области, предназначенное
для работы с именованными областями табличного документа. Оно доступно
только для чтения и управляется посредством методов и свойств объекта
КоллекцияОбластейТабличногоДокумента. Посмотрите на примере, представленном в листинге 32.2, как использовать свойство ТекущаяОбласть.
ласть
Листинг 32.2. Работа с текущей областью табличного документа
// Получаем ссылку на текущую область
ТекущаяОбласть = ТабДокумент.ТекущаяОбласть;
// Получаем координаты текущей области
НомерПервойСтрокиОбласти = ТекущаяОбласть.Верх;
НомерПоследнейСтрокиОбласти = ТекущаяОбласть.Низ;
НомерПервойКолонкиОбласти = ТекущаяОбласть.Лево;
НомерПоследнейКолонкиОбласти = ТекущаяОбласть.Право;
// Устанавливаем выделение отрицательных чисел специальным цветом
ТекущаяОбласть.ВыделятьОтрицательные = Истина;
// Задаем выравнивание данных по горизонтали
ТекущаяОбласть.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Право;
// Задаем выравнивание данных по вертикали
ТекущаяОбласть.ВертикальноеПоложение = ВертикальноеПоложение.Центр;
// Устанавливаем жирный шрифт
ТекущаяОбласть.Шрифт = Новый Шрифт("Arial", 12, Истина);
// Формируем рамку вокруг текущей области
ТекущаяОбласть.ГраницаСверху =
Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная, 2);
ТекущаяОбласть.ГраницаСнизу =
Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная, 2);
ТекущаяОбласть.ГраницаСлева =
Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная, 2);
ТекущаяОбласть.ГраницаСправа =
Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная, 2);
Как видно из примера, мы получили ссылку на текущую область документа
и настроили некоторые свойства объекта ОбластьЯчеекТабличногоДокумента,
422
Часть VII. Печатные документы
а также получили числовые координаты области. А теперь попробуем воспользоваться свойством Области для обращения к именованным областям
табличного документа. Напомню, что именованные области формируются
в конфигураторе или в процессе работы программы и позволяют назначать
имена связанным ячейкам таблицы. Демонстрационный пример представлен
в листинге 32.3.
Листинг 32.3. Использование свойства Области
// Получаем ссылку на область с именем "ШапкаДокумента"
ШапкаДокументаОбласть = ТабДокумент.Области.ШапкаДокумента;
// Устанавливаем шрифт для именованной области
ШапкаДокументаОбласть.Шрифт = Новый Шрифт("Arial", 10);
// Получаем ссылку на область с именем "ШапкаТаблицы"
ШапкаТаблицыОбласть = ТабДокумент.Области.ШапкаТаблица;
// Устанавливаем шрифт для именованной области
ШапкаТаблицыОбласть.Шрифт = Новый Шрифт("Arial", 8);
// Получаем ссылку на область с именем "Подвал"
ПодвалОбласть = ТабДокумент.Области.Подвал;
// Устанавливаем шрифт для именованной области
ПодвалОбласть.Шрифт = Новый Шрифт("Arial", 12, Истина);
Предполагая, что табличный документ содержит именованные области, мы
воспользовались свойством Области и получили ссылки на них. Поскольку
полученные ссылки указывают на объект ОбластьЯчеекТабличногоДокумента,
нам доступны все его методы и свойства.
Имеется возможность получить все области ячеек именованной области
в цикле, как это сделано в листинге 32.4.
Листинг 32.4. Получение областей табличного документа в цикле
// Получаем ссылку на область с именем "Параметры"
ПараметрыОбласть = ТабДокумент.Области.Параметры;
// Получаем области ячеек
Для Каждого Область Из ПараметрыОбласть Цикл
// Получаем область первого параметра
Параметр1Область = Найти(Область.ИмяОбласти, "Параметр_1");
// Получаем область второго параметра
Параметр2Область = Найти(Область.ИмяОбласти, "Параметр_2");
КонецЦикла;
Глава 32. Создание и оформление печатных документов
423
Таким образом можно читать значения областей ячеек, размещенных в общей именованной области. Для этого вначале мы получили ссылку на именованную область "Параметры", а затем в цикле выполнили поиск каждой
ячейки. Чтобы найти нужную ячейку по имени, использовали встроенную
функцию Найти.
Когда вы создаете новый печатный документ, предполагается, что он будет
использоваться неоднократно. Решается это с помощью специальных свойств
ячейки макета: Заполнение, Параметр, Параметр расшифровки и Использование расшифровки. Все они задаются в конфигураторе на этапе проектирования. Свойство Заполнение поддерживает следующие значения:
Текст — ячейка хранит постоянное строковое значение;
Параметр — ячейка может принимать различные значения, передаваемые
в процессе выполнения программы;
Шаблон — ячейка содержит постоянные и изменяемые значения.
В первом случае все просто и понятно. На этапе создания макета записываются определенные значения, которые не изменяются в процессе работы.
К ним, как правило, относятся заголовок печатного документа, наименования
реквизитов, а также шапка таблицы и подвал. Во втором случае вы должны
записать имя параметра в свойство Параметр. При этом в режиме редактирования макета оно будет заключено в угловые скобки. Для передачи ему какоголибо значения, необходимо в коде указать имя параметра, используя свойство Параметры объекта ТабличныйДокумент. Это свойство представляет собой
коллекцию всех параметров макета. Рассмотрим пример заполнения параметров макета, представленный в листинге 32.5.
Листинг 32.5. Использование свойства Параметры для заполнения макета
данными
// Получаем ссылку на юридическое лицо из справочника
ЮрЛицоСсылка = Справочники.ЮридическиеЛица.НайтиПоНаименованию("Завод");
// Задаем вид адреса
ВидАдресаСсылка = Перечисления.ВидыАдресов.Юридический;
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Период
КАК Период,
|
ПредставлениеАдреса КАК ЮридическийАдрес
424
Часть VII. Печатные документы
|ИЗ
|
РегистрСведений.Адреса
|
|ГДЕ
|
ЮрФизЛицо = &ЮрФизЛицо И
|
ВидАдреса = &ВидАдреса
|
|УПОРЯДОЧИТЬ ПО
|
Период УБЫВ";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ЮрФизЛицо", ЮрЛицоСсылка);
Запрос.УстановитьПараметр("ВидАдреса", ВидАдресаСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
ДанныеВыборки.Следующий();
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Получаем существующий макет
Макет = Документы.ИмяНашегоДокумента.ПолучитьМакет("Тест");
// Получаем область макета
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
// Заполняем значения параметров макета
ОбластьЗаголовок.Параметры.НашПараметрПериод = ДанныеВыборки.Период;
ОбластьЗаголовок.Параметры.НашПараметрЮридическийАдрес =
ДанныеВыборки.НашПараметрЮридическийАдрес;
// Выводим заполненную область в табличный документ
ТабДокумент.Вывести(ОбластьЗаголовок);
// Задаем ориентацию страницы
ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
// Настраиваем отступы
ТабДокумент.ПолеСверху = 25;
ТабДокумент.ПолеСнизу = 25;
ТабДокумент.ПолеСлева = 30;
ТабДокумент.ПолеСправа = 10;
// Сохраняем параметры печати в переменную
ТабДокумент.ИмяПараметровПечати = "ПараметрыПечати_Пример";
// Выводим заполненный печатный документ на экран
ТабДокумент.Показать();
Глава 32. Создание и оформление печатных документов
425
Предположим, что у нас имеется созданный в конфигураторе макет с именем
"Тест", в котором заданы два параметра: период и юридический адрес. Далее
мы делаем запрос к регистру сведений и получаем результат выборки. Затем
создаем новый объект ТабличныйДокумент и получаем макет с помощью
метода ПолучитьМакет. Данный метод, как вы уже знаете, поддерживается
многими объектами конфигурации. Он возвращает ссылку на табличный документ. После этого, посредством метода ПолучитьОбласть (объект ТабличныйДокумент), читаем именованную область печатного документа (в нашем
случае она называется "Заголовок"). Теперь, пользуясь свойством Параметры,
передаем значения запроса в табличную форму. Чтобы зафиксировать область, применяем метод Вывести, который добавляет данные в результирующую таблицу. В завершение настраиваем параметры отображения и выводим
полученный печатный документ на экран. Как видите, все достаточно просто.
Давайте теперь подробнее остановимся на методах объекта ТабличныйДокумент. С методом Вывести вы уже познакомились. Он позволяет выводить
табличный документ (например, именованную область) в результирующий
табличный документ. Поскольку заданных областей может быть несколько,
данный метод следует вызывать после каждого заполнения данными очередной области. Посмотрите пример использования этого метода для вывода
в печатный документ товарной части документа (листинг 32.6).
Листинг 32.6. Использование метода Вывести
// Получаем ссылку на юридическое лицо из справочника
ЗаказСсылка = Документы.ЗаказПоставщику.НайтиПоНомеру("0002");
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ЗаказПокупателя
КАК ЗаказПокупателя,
|
Цена
КАК Цена,
|
СУММА(Количество)
КАК Количество,
|
СУММА(Сумма)
КАК Сумма,
|
МИНИМУМ(НомерСтроки) КАК НомерСтроки
|ИЗ
|
|ГДЕ
Документ.ЗаказПоставщику.Товары
426
|
Часть VII. Печатные документы
Ссылка = &ЗаказСсылка
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ЗаказПокупателя,
|
Цена
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Передаем в параметр запроса ссылку на текущий заказ поставщику
Запрос.УстановитьПараметр("ЗаказСсылка", ЗаказСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
ДанныеВыборки.Следующий();
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Получаем существующий макет
Макет = Документы.ЗаказПоставщику.ПолучитьМакет("Тест");
// Получаем область макета
ОбластьСтрока = Макет.ПолучитьОбласть("СтрокаТаблицыТоваров");
// Определяем переменные для учета итоговых значений
ИтогоКоличество = 0;
ИтогоСумма = 0;
// Выводим в табличный документ все товары по заказу поставщику
Пока ДанныеВыборки.Следующий() Цикл
ОбластьСтрока.Параметры.НомерСтроки = ДанныеВыборки.НомерСтроки;
ОбластьСтрока.Параметры.НаименованиеНоменклатуры =
ДанныеВыборки.Номенклатура;
ОбластьСтрока.Параметры.ЗаказПокупателя =
ДанныеВыборки.ЗаказПокупателя;
ОбластьСтрока.Параметры.Цена = ДанныеВыборки.Цена;
ОбластьСтрока.Параметры.Количество = ДанныеВыборки.Количество;
ОбластьСтрока.Параметры.Сумма = ДанныеВыборки.Сумма;
// Выводим данные в табличный документ
ТабДокумент.Вывести(ОбластьСтрока);
// Считаем итоговые значения
ИтогоКоличество = ИтогоКоличество + ДанныеВыборки.Количество;
ИтогоСумма = ИтогоСумма + ДанныеВыборки.Сумма;
КонецЦикла;
// Получаем область итогов
Глава 32. Создание и оформление печатных документов
427
ОбластьИтоги = Макет.ПолучитьОбласть("Итоги");
// Заполняем итоговые параметры макета
ОбластьИтоги.Параметры.Количество = ИтогоКоличество;
ОбластьИтоги.Параметры.Сумма = ИтогоСумма;
// Выводим итоги в результирующий табличный документ
ТабДокумент.Вывести(ОбластьИтоги);
// Сохраняем параметры печати в переменную
ТабДокумент.ИмяПараметровПечати = "ПараметрыПечати_Пример";
// Выводим заполненный печатный документ на экран
ТабДокумент.Показать();
Как видите, мы сделали запрос к товарной части заказа поставщику и вывели
результат в подготовленную печатную форму. Вначале получили область
строки таблицы макета и в цикле прочитали каждую номенклатурную позицию заказа, добавив ее в табличный документ. Затем получили область итогов, заполнили ее и тоже вывели в результирующую таблицу. Как вы заметили, нам пришлось передавать значение каждого параметра из товарной части,
что бывает не всегда удобно. Если исходная таблица содержит много параметров, можно использовать метод Заполнить. В качестве единственного параметра он принимает объект, данными из которого требуется заполнить
таблицу. Пример работы с этим методом представлен в листинге 32.7.
Листинг 32.7. Использование метода Заполнить
// Получаем ссылку на юридическое лицо из справочника
ЗаказСсылка = Документы.ЗаказПоставщику.НайтиПоНомеру("0002");
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ЗаказПокупателя
КАК ЗаказПокупателя,
|
Цена
КАК Цена,
|
СУММА(Количество)
КАК Количество,
|
СУММА(Сумма)
КАК Сумма,
|
МИНИМУМ(НомерСтроки) КАК НомерСтроки
|ИЗ
|
Документ.ЗаказПоставщику.Товары
428
Часть VII. Печатные документы
|ГДЕ
|
Ссылка = &ЗаказСсылка
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ЗаказПокупателя,
|
Цена
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Передаем в параметр запроса ссылку на текущий заказ поставщику
Запрос.УстановитьПараметр("ЗаказСсылка", ЗаказСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
ДанныеВыборки.Следующий();
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Получаем существующий макет
Макет = Документы.ЗаказПоставщику.ПолучитьМакет("Тест");
// Получаем область макета
ОбластьСтрока = Макет.ПолучитьОбласть("СтрокаТаблицыТоваров");
// Выводим в табличный документ все товары по заказу поставщику
Пока ДанныеВыборки.Следующий() Цикл
// Заполняем значения параметров макета
ОбластьСтрока.Параметры.Заполнить(ДанныеВыборки);
// Выводим данные в табличный документ
ТабДокумент.Вывести(ОбластьСтрока);
КонецЦикла;
// Выводим итоги в результирующий табличный документ
ТабДокумент.Вывести(ОбластьИтоги);
Теперь нам не придется заполнять значение каждого параметра отдельно.
Метод Заполнить автоматически присваивает значения полей запроса параметрам табличного документа.
Следующие методы объекта ТабличныйДокумент, на которых мы остановимся, применяются для разбивки табличного документа на отдельные
страницы по вертикали или горизонтали и называются соответственно: ВывестиВертикальныйРазделительСтраниц (вставляет вертикальный разделитель) и ВывестиГоризонтальныйРазделительСтраниц (вставляет горизонтальный разделитель). Эти методы имеет смысл использовать, когда требуется
Глава 32. Создание и оформление печатных документов
429
разбить печатный документ на два (или более) листа. Например, товарная
накладная может содержать несколько десятков номенклатурных позиций.
Естественно, такой документ не поместится на одном листе. Поэтому следует
предусмотреть алгоритм, автоматически разбивающий документ на несколько страниц, сохраняя при этом требуемый формат (размещение шапки таблицы на каждом листе, смещение подвала). Одним словом, используйте эти методы, если необходимо явно задать завершение одной страницы и переход на
другую (аналогично разрыву страницы в программе Microsoft Word).
Объект ТабличныйДокумент поддерживает сохранение печатного документа
в файл и чтение из файла. Для этого применяются методы Записать и Прочитать.
Первый имеет два параметра: имя и тип файла (HTML, MXL, TXT или XLS).
Если не указать тип сохраняемого файла, будет выбран MXL. Второй получает один параметр — имя исходного файла, который нужно прочитать,
и возвращает объект ТабличныйДокумент. Посмотрите пример работы с
данными методами, показанный в листинге 32.8.
Листинг 32.8. Сохранение печатного документа в файл
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Получаем существующий макет
Макет = Документы.ИмяНашегоДокумента.ПолучитьМакет("Тест");
// Получаем область макета
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
// Заполняем значения параметров макета
ОбластьЗаголовок.Параметры.НашПараметр1 = ДанныеВыборки.Параметр1;
ОбластьЗаголовок.Параметры.НашПараметр2 = ДанныеВыборки.Параметр2;
// Выводим заполненную область в табличный документ
ТабДокумент.Вывести(ОбластьЗаголовок);
// Сохраняем табличный документ в формате MXL
ТабДокумент.Записать("C:\Печатный документ.mxl");
// Сохраняем табличный документ в текстовый формат
ТабДокумент.Записать("C:\Печатный документ.txt",
ТипФайлаТабличногоДокумента.TXT);
// Сохраняем табличный документ в формат HTML
ТабДокумент.Записать("C:\Печатный документ.htm",
ТипФайлаТабличногоДокумента.HTML);
// Сохраняем табличный документ в формате Microsoft Excel
ТабДокумент.Записать("C:\Печатный документ.xls",
430
Часть VII. Печатные документы
ТипФайлаТабличногоДокумента.XLS);
// Очищаем табличный документ
ТабДокумент.Очистить();
// Читаем данные из файла
ТабДокумент.Прочитать("C:\Печатный документ.mxl");
Как видите, можно сохранить файл в удобном формате и использовать в других программах. Сразу отмечу, что при сохранении в любимом многими
формате электронных таблиц Microsoft Excel могут теряться некоторые настройки форматирования. В первую очередь, это относится к форматированию
ячеек, а также к возможностям редактора макетов 1С, не поддерживаемым
программой Excel (например, сдвиг ячеек по горизонтали друг относительно
друга). Хочется надеяться, что фирма 1С решит эти проблемы в следующих
версиях. Чтение данных из файла реализуется очень просто и не должно вызывать трудностей.
Для того чтобы распечатать подготовленный документ, предназначен метод
Напечатать. Единственный параметр метода позволяет указать, будет ли перед началом процесса показан диалог печати. Установка параметра в Ложь
выводит диалог на экран.
Интересной и полезной особенностью 1С являются расшифровки. При двойном щелчке мышью по ячейке табличного документа можно вывести дополнительную информацию об интересующем значении. Например, сформировать и выполнить стандартный отчет или отобразить документ (справочник),
на который ссылается значение ячейки табличного документа. Чтобы использовать расшифровку в печатном документе, следует свойству Параметр
расшифровки ячейки макета присвоить значение расшифровки. Это может
быть ссылка на документ или справочник или наименование процедуры, которая будет обрабатывать расшифровку значения. Рассмотрим пример работы с расшифровкой, представленный в листинге 32.9.
Листинг 32.9. Использование расшифровки
// Получаем ссылку на юридическое лицо из справочника
ЗаказСсылка = Документы.ЗаказПоставщику.НайтиПоНомеру("0005");
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
Глава 32. Создание и оформление печатных документов
431
|
Номенклатура
КАК Номенклатура,
|
ЗаказПокупателя
КАК ЗаказПокупателя,
|
Цена
КАК Цена,
|
СУММА(Количество)
КАК Количество,
|
СУММА(Сумма)
КАК Сумма,
|
МИНИМУМ(НомерСтроки) КАК НомерСтроки
|ИЗ
|
Документ.ЗаказПоставщику.Товары
|ГДЕ
|
Ссылка = &ЗаказСсылка
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ЗаказПокупателя,
|
Цена
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
// Передаем в параметр запроса ссылку на текущий заказ поставщику
Запрос.УстановитьПараметр("ЗаказСсылка", ЗаказСсылка);
// Выполняем запрос
ДанныеВыборки = Запрос.Выполнить().Выбрать();
ДанныеВыборки.Следующий();
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Получаем существующий макет
Макет = Документы.ЗаказПоставщику.ПолучитьМакет("Тест");
// Получаем область макета
ОбластьСтрока = Макет.ПолучитьОбласть("СтрокаТаблицыТоваров");
// Выводим в табличный документ все товары по заказу поставщику
Пока ДанныеВыборки.Следующий() Цикл
ОбластьСтрока.Параметры.НомерСтроки = ДанныеВыборки.НомерСтроки;
ОбластьСтрока.Параметры.НаименованиеНоменклатуры =
ДанныеВыборки.Номенклатура;
// Передаем значение параметра расшифровки по номенклатуре
ОбластьСтрока.Параметры.Расшифровка = ДанныеВыборки.Номенклатура;
ОбластьСтрока.Параметры.ЗаказПокупателя =
ДанныеВыборки.ЗаказПокупателя;
// Передаем значение параметра расшифровки по заказу
ОбластьСтрока.Параметры.Расшифровка =
ДанныеВыборки.ЗаказПокупателя;
432
Часть VII. Печатные документы
ОбластьСтрока.Параметры.Цена = ДанныеВыборки.Цена;
ОбластьСтрока.Параметры.Количество = ДанныеВыборки.Количество;
ОбластьСтрока.Параметры.Сумма = ДанныеВыборки.Сумма;
// Выводим данные в табличный документ
ТабДокумент.Вывести(ОбластьСтрока);
КонецЦикла;
Итак, мы установили в свойствах макета расшифровки по номенклатуре
и заказу покупателя. При обработке результата запроса мы присваиваем параметру расшифровки ссылки на номенклатуру и заказ. После формирования
печатного документа и вывода на экран двойной щелчок мышью по ячейке
номенклатуры автоматически отобразит форму элемента справочника Номенклатура. Двойной щелчок по ячейке заказа отобразит форму документа
ЗаказПокупателя. Не правда ли, очень удобная возможность перейти к подробному описанию выбранного значения.
После того как мы познакомились с основными свойствами и методами объекта ТабличныйДокумент, попробуем создать новый печатный документ
произвольной формы на базе документа ЗаказПокупателя.
Откройте в конфигураторе документ заказа покупателя и добавьте новый
макет с именем "ЗаказПокупателя". Заполните макет данными, как показано
на рис. 32.1.
Как видно из рисунка, мы определили внешний вид будущей печатной формы. Чтобы использовать ее с любым документом ЗаказПокупателя, представили изменяющиеся данные в виде параметров (надписи на рисунке, заключенные в угловые скобки). Для этого нам понадобилось настроить свойства
соответствующих ячеек определенным образом. Пример настройки параметра НаименованиеПродавца представлен на рис. 32.2.
В свойстве Заполнение установили использование параметра для отображения значений. Свойству Параметр присвоили имя, по которому к нему можно
будет обращаться из кода. Для работы расшифровки записали в свойство
Параметр расшифровки имя для передачи значения. И последнее свойство
(Использование расшифровки) оставили без изменений. Аналогичным образом настроили другие ячейки таблицы, чтобы можно было передавать значения параметров. Кроме того, мы определили области макета и присвоили им
собственные наименования. В результате у нас получились четыре области:
ШапкаДокумента, ШапкаТаблицы, СтрокаТаблицы и Подвал. Для ячеек даты
установили формат ДЛФ=DD, а для числовых полей — ЧЦ=15; ЧДЦ=2. Теперь
нам осталось написать соответствующую процедуру, которая будет формировать печатный документ на основании текущего заказа покупателя. Пример такой процедуры представлен в листинге 32.10.
Глава 32. Создание и оформление печатных документов
Рис. 32.1. Макет "ЗаказПокупателя"
Рис. 32.2. Свойства параметра НаименованиеПродавца
433
434
Часть VII. Печатные документы
Листинг 32.10. Процедура для формирования печатного документа по заказу
покупателя
Процедура СформироватьПечатнуюФорму(ЗаказПокупателяСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса и передаем в объект
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер
КАК НомерДокумента,
|
Дата
КАК ДатаДокумента,
|
Организация
КАК Организация,
|
ПРЕДСТАВЛЕНИЕ(Организация)
КАК ПредставлениеОрганизации,
|
Контрагент
КАК Контрагент,
|
ПРЕДСТАВЛЕНИЕ(Контрагент)
КАК ПредставлениеКонтрагента,
|
ДатаОтгрузки
КАК ДатаОтгрузки,
|
ДатаОплаты
КАК ДатаОплаты,
|
ДоговорВзаиморасчетов
КАК Договор,
|
ПРЕДСТАВЛЕНИЕ(ДоговорВзаиморасчетов)
|
КАК ПредставлениеДоговора,
|
СкладКомпании
|
ПРЕДСТАВЛЕНИЕ(СкладКомпании) КАК ПредставлениеСклада,
|
ВалютаДокумента
|
Товары.(
КАК Склад,
КАК ВалютаДокумента,
|
НомерСтроки
КАК НомерСтроки,
|
Номенклатура
КАК Номенклатура,
|
ПРЕДСТАВЛЕНИЕ(Номенклатура)
КАК ИмяТовара,
|
Количество
КАК Количество,
|
ЕдиницаИзмерения
КАК ЕдИзм,
|
Цена
КАК Цена,
|
Сумма
КАК Сумма,
|
СтавкаНДС
КАК СтавкаНДС,
|
СуммаНДС
КАК СуммаНДС,
|
|
Товары.Сумма + Товары.СуммаНДС КАК Всего
)
|ИЗ
|
|
Документ.ЗаказПокупателя
Глава 32. Создание и оформление печатных документов
435
|ГДЕ
|
Ссылка = &ТекущийЗаказСсылка";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ТекущийЗаказСсылка",
ЗаказПокупателяСсылка);
// Выполняем запрос
Шапка = Запрос.Выполнить().Выбрать();
Шапка.Следующий();
// Формируем еще один запрос для получения адреса организации
ЗапросПоАдресам = Новый Запрос();
// Формируем текст запроса и передаем в объект
ЗапросПоАдресам.Текст = "
|ВЫБРАТЬ
|
ПредставлениеАдреса КАК Адрес
|ИЗ
|
РегистрСведений.Адреса.СрезПоследних(&ДатаПериода,
|
ВидАдреса = &ВидАдреса)
|
|ГДЕ
|
АдресаСрезПоследних.ЮрФизЛицо.Ссылка = &ОрганизацияСсылка";
// Записываем параметры запроса
ЗапросПоАдресам.УстановитьПараметр("ОрганизацияСсылка",
Шапка.Организация);
ЗапросПоАдресам.УстановитьПараметр("ДатаПериода", ТекущаяДата());
ЗапросПоАдресам.УстановитьПараметр("ВидАдреса",
Перечисления.ВидыАдресов.Юридический);
// Выполняем запрос
ВыборкаАдрес = ЗапросПоАдресам.Выполнить().Выбрать();
ВыборкаАдрес.Следующий();
// Сохраняем адрес организации
ОрганизацияАдрес = ВыборкаАдрес.Адрес;
// Формируем еще один запрос для получения адреса контрагента
ЗапросПоАдресам.Текст = "
|ВЫБРАТЬ
|
ПредставлениеАдреса КАК Адрес
|ИЗ
|
РегистрСведений.Адреса.СрезПоследних(&ДатаПериода,
|
ВидАдреса = &ВидАдреса)
|
436
Часть VII. Печатные документы
|ГДЕ
|
АдресаСрезПоследних.ЮрФизЛицо.Ссылка = &КонтрагентСсылка";
// Записываем параметры запроса
ЗапросПоАдресам.УстановитьПараметр("КонтрагентСсылка",
Шапка.Контрагент);
ЗапросПоАдресам.УстановитьПараметр("ДатаПериода", ТекущаяДата());
ЗапросПоАдресам.УстановитьПараметр("ВидАдреса",
Перечисления.ВидыАдресов.Юридический);
// Выполняем запрос
ВыборкаАдрес = ЗапросПоАдресам.Выполнить().Выбрать();
ВыборкаАдрес.Следующий();
// Сохраняем адрес контрагента
КонтрагентАдрес = ВыборкаАдрес.Адрес;
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Получаем макет
Макет = ПолучитьМакет("ЗаказПокупателя");
// Получаем область макета
ШапкаДокумента = Макет.ПолучитьОбласть("ШапкаДокумента");
// Заполняем параметры шапки
ШапкаДокумента.Параметры.НомерДокумента = Шапка.НомерДокумента;
ШапкаДокумента.Параметры.ДатаДокумента = Шапка.ДатаДокумента;
ШапкаДокумента.Параметры.НаименованиеПродавца =
Шапка.ПредставлениеОрганизации;
// Заполняем расшифровку по организации
ШапкаДокумента.Параметры.Организация = Шапка.Организация;
ШапкаДокумента.Параметры.АдресПродавца = ОрганизацияАдрес;
ШапкаДокумента.Параметры.НаименованиеПокупателя =
Шапка.ПредставлениеКонтрагента;
// Заполняем расшифровку по контрагенту
ШапкаДокумента.Параметры.Контрагент = Шапка.Контрагент;
ШапкаДокумента.Параметры.АдресПокупателя = КонтрагентАдрес;
ШапкаДокумента.Параметры.ДатаОтгрузки = Шапка.ДатаОтгрузки;
ШапкаДокумента.Параметры.ДатаОплаты = Шапка.ДатаОплаты;
ШапкаДокумента.Параметры.НаименованиеДоговора =
Шапка.ПредставлениеДоговора;
Глава 32. Создание и оформление печатных документов
437
// Заполняем расшифровку по договору
ШапкаДокумента.Параметры.Договор = Шапка.Договор;
ШапкаДокумента.Параметры.НаименованиеСклада =
Шапка.ПредставлениеСклада;
// Заполняем расшифровку по складу
ШапкаДокумента.Параметры.Склад = Шапка.Склад;
// Выводим шапку документа
ТабДокумент.Вывести(ШапкаДокумента);
// Получаем область шапки таблицы
ШапкаТаблицы = Макет.ПолучитьОбласть("ШапкаТаблицы");
// Выводим шапку таблицы
ТабДокумент.Вывести(ШапкаТаблицы);
// Получаем товарную часть документа
ВыборкаТовары = Шапка.Товары.Выбрать();
ИтогиСумма = 0;
ИтогиСуммаНДС = 0;
// Получаем область строки таблицы
ОбластьСтроки = Макет.ПолучитьОбласть("СтрокаТаблицы");
Пока ВыборкаТовары.Следующий() Цикл
ОбластьСтроки.Параметры.НомерСтроки =
ВыборкаТовары.НомерСтроки;
ОбластьСтроки.Параметры.НаименованиеНоменклатуры =
ВыборкаТовары.ИмяТовара;
// Заполняем расшифровку по номенклатуре
ОбластьСтроки.Параметры.Номенклатура =
ВыборкаТовары.Номенклатура;
ОбластьСтроки.Параметры.Количество =
ВыборкаТовары.Количество;
ОбластьСтроки.Параметры.ЕдИзм = ВыборкаТовары.ЕдИзм;
ОбластьСтроки.Параметры.Цена = ВыборкаТовары.Цена;
ОбластьСтроки.Параметры.Сумма = ВыборкаТовары.Сумма;
ОбластьСтроки.Параметры.СтавкаНДС =
ВыборкаТовары.СтавкаНДС;
ОбластьСтроки.Параметры.СуммаНДС = ВыборкаТовары.СуммаНДС;
ОбластьСтроки.Параметры.Всего = ВыборкаТовары.Всего;
// Выводим строку в табличный документ
ТабДокумент.Вывести(ОбластьСтроки);
// Считаем итоги
ИтогиСумма = ИтогиСумма + ВыборкаТовары.Сумма;
438
Часть VII. Печатные документы
ИтогиСуммаНДС = ИтогиСуммаНДС + ВыборкаТовары.СуммаНДС;
КонецЦикла;
// Получаем область подвала
Подвал = Макет.ПолучитьОбласть("Подвал");
// Заполняем суммы по документу
Подвал.Параметры.СуммаНДСПрописью =
СформироватьСуммаПрописью(ИтогиСуммаНДС, ШапкаДокумента.ВалютаДокумента);
Подвал.Параметры.СуммаПрописью =
СформироватьСуммаПрописью (ИтогиСумма, ШапкаДокумента.ВалютаДокумента);
// Выводим подвал
ТабДокумент.Вывести(Подвал);
// Настраиваем отображение печатной формы
ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Ландшафт;
// Настраиваем отступы
ТабДокумент.ПолеСверху = 25;
ТабДокумент.ПолеСнизу = 25;
ТабДокумент.ПолеСлева = 30;
ТабДокумент.ПолеСправа = 10;
// Сохраняем параметры печати в переменную
ТабДокумент.ИмяПараметровПечати = "ПараметрыПечати_Заказ";
// Выводим табличный документ на экран
ТабДокумент.Показать();
КонецПроцедуры
Несмотря на внушительный вид, процедура достаточно простая и понятная.
Вначале мы сформировали запросы по заказу покупателя и регистру сведений Адреса. Затем последовательно получили именованные области макета
и заполнили параметры соответствующими значениями. В завершение выполнили настройку отображения печатной формы и вывели ее на экран, чтобы пользователь мог проверить правильность заполнения и самостоятельно
вывести на печать.
Как вы заметили на примере, наиболее сложным моментом при создании
новой печатной формы является не написание кода, а рисование и форматирование макета. Особенно явно эти сложности проявляются при создании
печатных документов строгой отчетности, поскольку они требуют четкого
и правильного размещения элементов макета.
На этом мы завершим тему создания печатных документов и немного поговорим об использовании внешних обработок для печатных форм и их подключении к системе 1С.
Глава 33
Подключение печатных документов
к базе
В предыдущей главе мы рассмотрели создание печатных документов в 1С на
базе макетов. При этом предполагалось, что они будут вызываться из самого
объекта (документа, справочника или отчета), для которого предназначены.
Однако это не всегда удобно. В первую очередь, потому, что при внесении
каких-либо поправок приходится отключать от базы всех пользователей (обновление данных в конфигураторе возможно только в монопольном режиме,
т. е. пользователи в этот момент не должны работать с базой) и загружать
конфигуратор. Кроме того, печатных документов постепенно накапливается
так много за счет новых и модификации существующих, что возникают проблемы из-за путаницы с версиями и неудобства редактирования многочисленных макетов в конфигураторе.
Фирма 1С предложила разработчикам интересный выход из данной ситуации. Как вы знаете, программа поддерживает специальный вид объектов —
обработки. Они позволяют создавать всевозможные многофункциональные
модули для решения различных общих и сервисных задач. Обработки могут
содержать реквизиты, табличные части, формы и макеты. Отдельным видом
обработок являются так называемые внешние обработки. Основным их отличием служит то, что они хранятся отдельно от конфигурации во внешних
файлах. Для использования их в программе достаточно выбрать в меню
пункт открытия файлов и загрузить обработку в рабочую область программы.
Думаю, вы догадались, что внешняя обработка как нельзя лучше подходит
для хранения различных печатных документов. А главное, вам не придется
загружать каждый раз конфигуратор и отключать пользователей при внесении изменений в печатные формы. Максимум — пользователям придется
закрыть документ, который они хотят вывести на печать, и открыть его повторно. Для упрощения этой задачи и облегчения труда программистов, фирма 1С предоставила специальную обработку, называемую Дополнительные
440
Часть VII. Печатные документы
печатные формы. С ее помощью очень просто организуется подключение
внешних обработок к базе 1С. Все, что требуется от программиста — это
создать обработку по заданным правилам и подключить ее к базе данных.
Как раз об этом мы и поговорим в этой главе. Вначале научимся создавать
внешнюю обработку, а затем правильно зарегистрировать ее в системе.
Для создания внешней обработки достаточно выбрать в меню Файл конфигуратора пункт Новый. В появившемся диалоговом окне следует указать, что
создается внешняя обработка, и система сформирует новый объект, который
можно сохранить на диск в формате EPF. В созданном окне обработки будут
доступны настройка свойств и реквизитов, а также модуль объекта, конструктор выходных форм и добавление справочной информации. Внешний вид
обработки представлен на рис. 33.1.
Рис. 33.1. Новая внешняя обработка
После того как будет задано имя для внешней обработки, нажмите кнопку
Действия и выберите пункт Открыть модуль объекта. Откроется модуль
для ввода исходного текста. По установленным правилам для правильной
работы печатного документа должна присутствовать как минимум одна
Глава 33. Подключение печатных документов к базе
441
функция с именем Печать. Она принимает один обязательный аргумент —
ссылку на объект (документ, справочник), для которого будет вызываться
печатная форма. Кроме того, эта функция должна возвращать объект ТабличныйДокумент. Теперь необходимо добавить один-единственный реквизит
с именем СсылкаНаОбъект и выбрать тип значения — ссылку на документ.
Как видите, требования достаточно понятные и простые. Дополнительно в
модуле вы можете расположить собственные процедуры и функции, необходимые по ходу работы.
Естественно, кроме модуля объекта, нам понадобится создать и настроить макет самой печатной формы. На этот раз мы построим макет документа строгой
отчетности — товарную накладную. Понятно, что в книге не расскажешь подробно, как рисовать печатный документ, поэтому создайте новый макет для
внешней обработки и скопируйте туда готовую печатную форму ТоварнаяНакладная из файла на прилагаемом к книге компакт-диске. После этого откройте
модуль объекта и добавьте код, представленный в листинге 33.1.
Листинг 33.1. Функция печатного документа
Функция Печать (ДокументСсылка)
// Создаем новую копию объекта Запрос
Запрос = Новый Запрос();
// Формируем текст запроса по шапке документа
Запрос.Текст = "
|ВЫБРАТЬ
|
Номер КАК НомерДокумента,
|
Дата КАК ДатаДокумента,
|
Организация КАК Организация,
|
ПРЕДСТАВЛЕНИЕ(Организация),
|
БанковскийСчетОрганизации,
|
Контрагент КАК Контрагент,
|
ПРЕДСТАВЛЕНИЕ(Контрагент),
|
Грузополучатель КАК Грузополучатель,
|
ПРЕДСТАВЛЕНИЕ(Грузополучатель),
|
Грузоотправитель КАК Грузоотправитель,
|
ПРЕДСТАВЛЕНИЕ(Грузоотправитель),
|
ДоговорКонтрагента КАК Договор,
|
ПРЕДСТАВЛЕНИЕ(ДоговорКонтрагента),
|
Подразделение КАК Подразделение,
|
ВалютаДокумента КАК Валюта,
|
КурсВзаиморасчетов КАК Курс,
442
Часть VII. Печатные документы
|
КратностьВзаиморасчетов КАК Кратность,
|
УчитыватьНДС КАК ПризнакУчитыватьНДС,
|
СуммаВключаетНДС КАК ПризнакСуммаВключаетНДС
|ИЗ
|
Документ.РеализацияТоваровУслуг
|
|ГДЕ
|
Документ.РеализацияТоваровУслуг.Ссылка = &ДокументСсылка";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
Запрос.УстановитьПараметр("ДокументСсылка", ДокументСсылка);
// Выполняем запрос
Шапка = Запрос.Выполнить().Выбрать();
Шапка.Следующий();
// Создаем новый табличный документ
ТабДокумент = Новый ТабличныйДокумент;
// Сохраняем параметры печати
ТабДокумент.ИмяПараметровПечати = "Параметры_ТоварнаяНакладная";
// Получаем макет
Макет = ПолучитьМакет("ТоварнаяНакладная");
// Получаем область макета
ШапкаДокумента = Макет.ПолучитьОбласть("ШапкаДокумента");
// Заполняем параметры шапки
ШапкаДокумента.Параметры.НомерДокумента = Шапка.НомерДокумента;
ШапкаДокумента.Параметры.ДатаДокумента = Шапка.ДатаДокумента;
// Получаем сведения о Поставщике
Поставщик = СведенияОЮрФизЛице(Шапка.Организация,
Шапка.ДатаДокумента);
ШапкаДокумента.Параметры.ПредставлениеОрганизации =
ОписаниеОрганизации(Поставщик);
// Заполняем расшифровку по организации
ШапкаДокумента.Параметры.Организация = Шапка.Организация;
// Выводим сведения о подразделении
ШапкаДокумента.Параметры.ПредставлениеПодразделения =
Шапка.Подразделение;
// Выводим сведения о поставщике
ШапкаДокумента.Параметры.ПредставлениеПоставщика =
ОписаниеОрганизации(Поставщик);
// Заполняем расшифровку по поставщику
ШапкаДокумента.Параметры.Организация = Шапка.Организация;
// Получаем сведения о покупателе
Глава 33. Подключение печатных документов к базе
443
Покупатель = СведенияОЮрФизЛице(Шапка.Контрагент,
Шапка.ДатаДокумента);
// Выводим сведения о грузополучателе
ШапкаДокумента.Параметры.ПредставлениеГрузополучатель =
ОписаниеОрганизации(Покупатель);
// Заполняем расшифровку по грузополучателю
ШапкаДокумента.Параметры.ПредставлениеГрузополучатель =
Шапка.Контрагент;
// Выводим сведения о плательщике
ШапкаДокумента.Параметры.ПредставлениеПлательщика =
ОписаниеОрганизации(Покупатель);
// Заполняем расшифровку по плательщику
ШапкаДокумента.Параметры.ПредставлениеПлательщика =
Шапка.Контрагент;
// Выводим основание
ШапкаДокумента.Параметры.Основание = Шапка.ДоговорКонтрагента;
// Заполняем расшифровку по договору
ШапкаДокумента.Параметры.Договор = Шапка.Договор;
// Заполняем коды
ШапкаДокумента.Параметры.ОрганизацияПоОКПО = Поставщик.КодПоОКПО;
ШапкаДокумента.Параметры.ВидДеятельностиПоОКДП = "";
ШапкаДокумента.Параметры.ГрузополучательПоОКПО =
Покупатель.КодПоОКПО;
ШапкаДокумента.Параметры.ПоставщикПоОКПО = Поставщик.КодПоОКПО;
ШапкаДокумента.Параметры.ПлательщикПоОКПО = Покупатель.КодПоОКПО;
// Выводим заполненную область в табличный документ
ТабДокумент.Вывести(ШапкаДокумента);
// Выводим шапку таблицы в табличный документ
ШапкаТаблицы = Макет.ПолучитьОбласть("ШапкаТаблица");
ЗаголовокТаблицы.Параметры.НомерСтраницы = "";
ТабДокумент.Вывести(ШапкаТаблицы);
// Делаем запрос по табличной части документа
ЗапросПоТоварам = Новый Запрос();
// Формируем текст запроса по шапке документа
ЗапросПоТоварам.Текст = "
|ВЫБРАТЬ
|
Номенклатура
КАК Номенклатура,
|
ПРЕДСТАВЛЕНИЕ(Номенклатура),
|
ЕдиницаИзмерения
КАК ЕдиницаИзмерения,
|
ПРЕДСТАВЛЕНИЕ(ЕдиницаИзмерения),
444
Часть VII. Печатные документы
|
СтавкаНДС
КАК СтавкаНДС,
|
Цена
КАК Цена,
|
СУММА(Количество)
КАК Количество,
|
СУММА(КоличествоМест) КАК КоличествоМест,
|
СУММА(Сумма)
КАК Сумма,
|
СУММА(СуммаНДС)
КАК СуммаНДС,
|
МИНИМУМ(НомерСтроки) КАК НомерСтроки,
|
0
КАК Метка
|ИЗ
|
Документ.РеализацияТоваровУслуг.Товары
|ГДЕ
|
Ссылка = &ДокументСсылка
|
|СГРУППИРОВАТЬ ПО
|
Номенклатура,
|
ЕдиницаИзмерения,
|
СтавкаНДС,
|
Цена
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|
Номенклатура,
|
ПРЕДСТАВЛЕНИЕ(Номенклатура),
|
Номенклатура.ЕдиницаХраненияОстатков,
|
ПРЕДСТАВЛЕНИЕ(Номенклатура.ЕдиницаХраненияОстатков),
|
СтавкаНДС
КАК СтавкаНДС,
|
Цена
КАК Цена,
|
Количество
КАК Количество,
|
NULL
КАК КоличествоМест,
|
Сумма
КАК Сумма,
|
СуммаНДС
КАК СуммаНДС,
|
НомерСтроки
КАК НомерСтроки,
|
1
КАК Метка
|ИЗ
|
Документ.РеализацияТоваровУслуг.Услуги
|
|ГДЕ
|
|
Ссылка = &ДокументСсылка
Глава 33. Подключение печатных документов к базе
445
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|
Номенклатура,
|
ПРЕДСТАВЛЕНИЕ(Номенклатура),
|
Номенклатура.ЕдиницаХраненияОстатков,
|
ПРЕДСТАВЛЕНИЕ(Номенклатура.ЕдиницаХраненияОстатков),
|
""Без НДС""
КАК СтавкаНДС,
|
Цена
КАК Цена,
|
Количество
КАК Количество,
|
NULL
КАК КоличествоМест,
|
Сумма
КАК Сумма,
|
0
КАК СуммаНДС,
|
НомерСтроки
КАК НомерСтроки,
|
2
КАК Метка
|ИЗ
|
Документ.РеализацияТоваровУслуг.ВозвратнаяТара
|
|ГДЕ
|
Ссылка = &ДокументСсылка
|
|УПОРЯДОЧИТЬ ПО Метка ВОЗР, НомерСтроки ВОЗР";
// Передаем в параметр запроса ссылку на текущий заказ покупателя
ЗапросПоТоварам.УстановитьПараметр("ДокументСсылка",
ДокументСсылка);
// Выполняем запрос
Товары = ЗапросПоТоварам.Выполнить().Выбрать();
// Объявляем переменные для подсчета сумм
ИтогоКоличество = 0;
ИтогоСуммаСНДС = 0;
ИтогоСумма = 0;
ИтогоНДС
= 0;
// Получаем область строки таблицы
СтрокаТаблицы = Макет.ПолучитьОбласть("Строка");
// Выбираем товары по документу
Пока Товары.Следующий() Цикл
СтрокаТаблицы.Параметры.Номер = Товары.НомерСтроки;
СтрокаТаблицы.Параметры.НаименованиеТовара =
Товары.Номенклатура;
// Заполняем расшифровку по номенклатуре
446
Часть VII. Печатные документы
СтрокаТаблицы.Параметры.Номенклатура = Товары.Номенклатура;
СтрокаТаблицы.Параметры.ЕдиницаИзмерения =
Товары.ЕдиницаИзмерения;
СтрокаТаблицы.Параметры.Количество = Товары.Количество;
СтрокаТаблицы.Параметры.Цена = Товары.Цена;
СтрокаТаблицы.Параметры.Сумма = Товары.Сумма;
СтрокаТаблицы.Параметры.СтавкаНДС = Товары.СтавкаНДС;
СтрокаТаблицы.Параметры.СуммаНДС = Товары.СуммаНДС;
СтрокаТаблицы.Параметры.СуммаСНДС =
Товары.Сумма + Товары.СуммаНДС;
// Выводим строку в табличный документ
ТабДокумент.Вывести(СтрокаТаблицы);
// Рассчитываем итоговые значения
ИтогоКоличество = ИтогоКоличество + Товары.Количество;
ИтогоСумма = ИтогоСумма + Товары.Сумма;
ИтогоНДС = ИтогоНДС + Товары.СуммаНДС;
ИтогоСуммаСНДС =
ИтогоСуммаСНДС + Товары.Сумма + Товары.СуммаНДС;
КонецЦикла;
// Получаем область итогов
Итоги = Макет.ПолучитьОбласть("Всего");
Итоги.Параметры.ИтогКоличество = ИтогоКоличество;
Итоги.Параметры.ИтогСумма = ИтогоСумма;
Итоги.Параметры.ИтогНДС = ИтогоНДС;
Итоги.Параметры.ИтогСуммаСНДС = ИтогоСуммаСНДС;
// Выводим итоговую строку в табличный документ
ТабДокумент.Вывести(Итоги);
// Получаем область подвала
Подвал = Макет.ПолучитьОбласть("Подвал");
// Заполняем сумму по документу
Подвал.Параметры.СуммаПрописью =
СформироватьСуммуПрописью(ИтогиСумма, ШапкаДокумента.Валюта);
// Выводим подвал
ТабДокумент.Вывести(Подвал);
// Настраиваем параметры печати
ТабДокумент.ПолеСверху = 0;
ТабДокумент.ПолеСнизу
= 0;
ТабДокумент.ПолеСлева
= 0;
ТабДокумент.ПолеСправа = 0;
Глава 33. Подключение печатных документов к базе
447
ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Ландшафт;
// Возвращаем подготовленный табличный документ
Возврат ТабДокумент;
КонецФункции
В результате у нас получилась функция, которая выполняет все необходимую
работу по формированию и настройке печатного документа. Как видно из
примера, мы последовательно получили все именованные области макета и
заполнили их данными запросов. Отдельно выполнили запрос по шапке документа и его табличным частям. Для получения сведений о юридических
организациях применили готовые функции (СведенияОЮрФизЛице и ОписаниеОрганизации) из общего модуля конфигурации. Замечу, что имена этих
функций могут отличаться от представленных, поэтому вам потребуется самостоятельно найти их описание в одном из общих модулей. Это относится и
к функции СформироватьСуммуПрописью. После заполнения всех параметров
макета мы выполнили настройку печати и возвратили подготовленный печатный документ.
Аналогичным образом можно создавать любые другие печатные формы.
Главное — не забывайте выполнять основные требования:
1. В модуле внешней обработки должна присутствовать функция с именем
Печать.
2. Аргументом этой функции должна быть ссылка на документ (или другой
объект).
3. Функция должна возвращать объект ТабличныйДокумент.
4. Должен присутствовать реквизит с именем СсылкаНаОбъект и типом значения — ссылкой на документ (документы), для которого будет вызываться печатная форма.
Выполняя эти несложные требования, вы без труда научитесь использовать
внешние обработки для всевозможных печатных документов.
Поскольку внешние обработки хранятся в виде отдельных файлов на диске,
их необходимо подключить к программе 1С. Сделать это можно с помощью
встроенной обработки Дополнительные печатные формы. Запустить ее
можно из меню Сервис. Далее следует выбрать печатную форму (файл
внешней обработки) и указать, для каких документов конфигурации она будет вызываться. При этом система сама подключит обработку к выбранным
документам и добавит в меню формы Печать новый пункт с именем печатного документа. Вот, в принципе, и все.
448
Часть VII. Печатные документы
Часть VIII
Взаимодействие 1С
и баз данных
450
Часть I. Приемы программирования документов
Глава 34
Использование в 1С
внешних баз данных
Думаю, многих разработчиков интересует вопрос, имеет ли смысл и можно
ли в принципе использовать в системе "1С:Предприятие 8.0" внешние базы
данных, написанные на других языках (например, T-SQL или Visual Basic).
Как правило, при переходе на новую систему остаются старые наработки
и собственные базы данных. Возникает естественное желание сохранить их
и продолжать использовать в дальнейшем. Это, конечно, не относится к бухгалтерским программам и документообороту. Здесь однозначно придется
вручную переносить все данные (документы) на новую платформу. В остальных же случаях можно найти более разумное решение. Заключается оно
в том, чтобы просто подключить имеющиеся базы к системе 1С, создав для
них отдельные модули (обработки) с удобным, современным интерфейсом.
Другими словами, не придется копировать данные из одной базы и заносить
их в новую, а всего лишь с помощью специальных объектов (например,
ADO — ActiveX Data Objects) создавать соединение с локальной или удаленной базой, читать (записывать) данные, пользуясь средствами 1С. Сразу замечу, что эффективность такого метода приемлема далеко не для всех существующих типов баз данных. Наилучшие результаты получаются при
использовании SQL-баз и им подобных. Связано это, в первую очередь,
с тем, что и сама система 1С построена на MSSQL-базах. Во-вторых, можно
легко найти необходимые объекты и средства разработки. И, в-третьих,
поддержку этого типа баз осуществляет, несомненно, мощная компания
Microsoft.
В этой главе мы кратко рассмотрим интерфейс ADO, язык программирования MSSQL и принципы подключения внешних баз данных к системе 1С.
Надеюсь, те, кого заинтересует эта часть книги, знакомы отчасти с ADO
и T-SQL.
452
Часть VIII. Взаимодействие 1С и баз данных
Интерфейс ADO
Программный интерфейс ADO представляет собой набор объектов, позволяющих управлять локальными и удаленными базами данных любого уровня
сложности. Он в полной мере поддерживает технологии клиент-сервер и
Web-приложения. Удобство и простота использования этого интерфейса не
только не усложняют, но и, наоборот, во многом упрощают разработку программ управления базами данных.
Интерфейс ADO состоит из следующих объектов:
Connection — позволяет настроить и установить соединение с базой данных;
Command — позволяет формировать запросы к базе данных, а также пере-
давать и получать наборы записей (Recordset);
Parameter — служит для поддержки параметров запросов и хранимых
процедур объекта Command;
Recordset — предназначен для управления наборами записей;
Field — предназначен для получения доступа к отдельным полям набора
записей (Recordset);
Property — позволяет получить доступ к свойствам ADO (статическим
и динамическим).
Здесь мы приводим только те объекты, которые нам понадобятся для работы.
Кроме базовых объектов, интерфейс включает в себя несколько типов коллекций:
Parameters — включает в себя все параметры объектов Parameter, ассо-
циируемые с объектом Command;
Fields — включает в себя все поля объектов Field, ассоциированные с объ-
ектом Recordset;
Properties — включает в себя все свойства объектов Property, ассоцииро-
ванные с объектами Connection, Command, Recordset и Field.
Кроме описанных объектов, поддерживаются различные события, позволяющие организовать эффективное взаимодействие с базой данных и интерфейсом пользователя. Для работы нам вряд ли понадобится владеть всеми
представленными объектами и коллекциями интерфейса ADO. Достаточно
будет научиться создавать подключение к базе данных, читать и передавать
данные, а также использовать хранимые процедуры и их параметры. Вначале
рассмотрим методы и свойства нужных нам объектов.
Как уже говорилось, для подключения к базе данных применяется объект
Connection. Прежде всего, нужно задать строку подключения, используя
Глава 34. Использование в 1С внешних баз данных
453
свойство ConnectionString. Она должна включать в себя параметры соединения, разделенные точкой с запятой. К ним относятся имя провайдера, источник данных, имя пользователя (логин), пароль и некоторые другие сведения. Типичный пример строки подключения представлен в листинге 34.1.
Листинг 34.1. Формирование строки подключения
// Создаем новый объект подключения
НовоеСоединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
НовоеСоединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
Вначале мы создали новый объект Connection, а затем сформировали строку
подключения к базе и передали ее свойству ConnectionString. В данном
случае в качестве провайдера мы выбрали SQLOLEDB. Далее указали имя
сервера, имя базы данных и регистрационные данные (имя пользователя
и пароль). Если на сервере используется авторизация Windows, вместо имени
пользователя и пароля добавьте строку "Trusted_Connection=yes".
Свойство ActiveConnection позволяет получить или передать ссылку на текущее открытое подключение или строку подключения, если соединение
с сервером закрыто. Как правило, значение этого свойства применяется в объектах Command и Recordset для передачи ссылки на активное соединение.
Для установки времени ожидания в процессе соединения с сервером предназначено свойство ConnectionTimeout. Оно измеряется в секундах и по умолчанию имеет значение 15. Это значит, что если через 15 секунд не удастся
выполнить подключение, все попытки будут прекращены, и пользователь
получит соответствующее сообщение. Существует аналогичное свойство, но
для задания времени ожидания при выполнении метода Execute. Называется
оно CommandTimeout и тоже измеряется в секундах. По умолчанию задано
значение 30.
И последнее свойство объекта Connection, которое мы рассмотрим, называется Status. Оно позволяет получить текущее состояние объекта и может принимать следующие значения:
adStateClosed — сообщает о том, что объект закрыт и имеет значение 0;
adStateOpen — сообщает, что объект открыт и имеет значение 1;
adStateConnecting — сообщает о наличии подключения к серверу и име-
ет значение 2;
454
Часть VIII. Взаимодействие 1С и баз данных
adStateExecuting — сообщает, что в данный момент выполняется коман-
да и имеет значение 4;
adStateFetching — сообщает о получении очередной записи из курсора.
Кроме свойств, объект Connection поддерживает несколько важных методов.
Метод Open открывает созданное подключение для дальнейшего доступа. Он
содержит несколько необязательных параметров: строку подключения, имя и
пароль пользователя, а также режим работы (асинхронный или синхронный).
Если строка подключения задана в свойстве ConnectionString, здесь ее
можно опустить. Режим работы метода при асинхронном подключении сразу
же вернет управление вызвавшей его программе. Для этого присвойте параметру значение adAsyncConnect (число 16). По умолчанию установлен синхронный режим работы, при котором управление будет передано только по
завершении выполнения метода Open. Посмотрите в листинге 34.2 варианты
использования этого метода.
Листинг 34.2. Варианты использования метода Open
// Создаем новый объект подключения
НовоеСоединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
НовоеСоединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
НовоеСоединение.Open();
// Открываем канал в асинхронном режиме
НовоеСоединение.Open(, , , 16);
// Передаем строку подключения прямо в метод
ИсточникДанных =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase ";
ИмяПользователя = "sa";
Пароль = "";
// Открываем соединение
НовоеСоединение.Open(ИсточникДанных, ИмяПользователя, Пароль);
После того как работа с базой данных закончена, следует закрыть соединение, вызвав метод Close. Он не только закрывает активный канал связи, но
и освобождает занятые ресурсы в памяти, поэтому не забывайте его вызывать
после получения (передачи) данных.
Глава 34. Использование в 1С внешних баз данных
455
Следующий метод Execute позволяет выполнить сформированный в виде
текстовой строки запрос или хранимую процедуру. В результате работы метода возвращается ссылка на объект Recordset. Он содержит три параметра.
В первый следует записать текст запроса, имя таблицы или хранимой процедуры. Второй, необязательный, параметр получает количество записей,
обработанных при подключении к провайдеру. Последний параметр дает
возможность задать способ интерпретации провайдером текста команды
и может принимать одно или несколько следующих значений:
adCmdUnspecified — интерпретация не используется (−1);
adCmdText — используется текстовое описание запроса или хранимой
процедуры (1);
adCmdTable — используется описание таблицы базы данных (2);
adCmdStoredProc — используется для описания хранимой процедуры (4);
adCmdUnknown — используется по умолчанию, когда тип команды неизвес-
тен (8);
adAsyncExecute — определяет асинхронное выполнение команды (16);
adExecuteNoRecords — определяет, что выполнение команды не возвра-
щает записи (128).
Пример работы метода Execute представлен в листинге 34.3.
Листинг 34.3. Использование метода Execute
// Создаем новый объект подключения
НовоеСоединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
НовоеСоединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
НовоеСоединение.Open();
// Формируем текст команды
ТекстКоманды = "DELETE FROM MyTable WHERE ID >= 100";
// Выполняем команду
НовоеСоединение.Execute(ТекстКоманды, , 128);
// Закрываем соединение
НовоеСоединение.Close();
Как видите, мы создали и открыли новый канал, после чего сформировали текст
запроса и передали его методу Execute. Поскольку наш запрос не возвращает
456
Часть VIII. Взаимодействие 1С и баз данных
записей, в третий параметр записали значение 16 (adExecuteNoRecords).
В завершение закрыли канал с помощью метода Close.
Для поддержки транзакций объект Connection имеет три метода: BeginTrans,
RollBackTrans и CommitTrans. Принцип использования этих методов ничем
не отличается от аналогичных в других языках доступа к базам данных. Вначале вызывается метод BeginTrans. Если пакет команд выполнен успешно,
транзакция фиксируется посредством CommitTrans, иначе делается откат всей
транзакции методом RollBackTrans. Пример использования этих методов
представлен в листинге 34.4.
Листинг 34.4. Использование методов BeginTrans, RollBackTrans
и CommitTrans
// Создаем новый объект подключения
НовоеСоединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
НовоеСоединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
НовоеСоединение.Open();
// Начинаем транзакцию
НовоеСоединение.BeginTrans();
Попытка
// Формируем текст первой команды
ТекстКоманды = "DELETE FROM MyTable WHERE ID >= 100";
// Выполняем команду
НовоеСоединение.Execute(ТекстКоманды, , 128);
// Формируем текст второй команды
ТекстКоманды = "DELETE FROM MyTable2 WHERE ID >= 100";
// Выполняем команду
НовоеСоединение.Execute(ТекстКоманды, , 128);
Исключение
// Делаем откат транзакции
НовоеСоединение.RollbackTrans();
КонецПопытки;
// Фиксируем транзакцию
НовоеСоединение.CommitTrans();
// Закрываем соединение
НовоеСоединение.Close();
Глава 34. Использование в 1С внешних баз данных
457
Вначале мы открываем транзакцию. Затем в обработчике ошибок выполняем
команды. Если происходит ошибка, делаем откат транзакции. Как видите, все
достаточно просто. Используйте транзакции всегда при выполнении пакета
команд, добавляющие или обновляющие данные в базе данных.
Следующий объект, с которым мы познакомимся, называется Command. Он
тесно связан с объектом Parameter и поддерживает собственный набор
свойств и методов. Главное его отличие от объекта Connection состоит в том,
что он позволяет обрабатывать параметры запроса. Параметры запроса можно передавать как в виде строки, так и через объект Parameter. Рассмотрим
подробнее наиболее важные свойства и методы этого объекта.
Для получения ссылки на установленное соединение применяется свойство
ActiveConnection. Пример его работы представлен в листинге 34.5.
Листинг 34.5. Использование свойства ActiveConnection
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Команда.ActiveConnection = Соединение;
После того как в объект Command передана ссылка на активный канал связи,
можно использовать любые другие его свойства и методы.
Для передачи текста команды применяется свойство CommandText. Через него
можно передавать или читать инструкцию команды. Однако следует помнить, что такой способ передачи должен поддерживаться провайдером. Посмотрите пример, показанный в листинге 34.6.
Листинг 34.6. Использование свойства CommandText
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
458
Часть VIII. Взаимодействие 1С и баз данных
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Команда.ActiveConnection = Соединение;
// Формируем текст запроса
ТекстЗапроса = "SELECT * FROM MyTable WHERE DataID BETWEEN 12 AND 45";
// Передаем текст запроса в объект
Команда.CommandText = ТекстЗапроса;
// Устанавливаем тип команды
Команда.CommandType = 1; // adCmdText
// Выполняем команду
Команда.Execute();
Итак, мы сформировали текст команды и присвоили его свойству
CommandText. Кроме того, чтобы явно указать тип команды (в данном случае
текст), нам пришлось применить еще одно свойство, CommandType. Оно позволяет определить тип команды и может иметь одно из следующих значений:
adCmdText — используется текстовое описание команды или вызов хра-
нимой процедуры (1);
adCmdTable — используется имя таблицы и наименования отдельных
столбцов (2);
adCmdStoredProc — используется имя хранимой процедуры (4);
adCmdUnknown — используется значение по умолчанию, когда тип коман-
ды неизвестен (8);
adCmdFile — используется имя файла (256);
adCmdTableDirect — используются все столбцы в заданной таблице (512).
Например, чтобы выполнить хранимую процедуру, можно применить код,
представленный в листинге 34.7.
Листинг 34.7. Выполнение хранимой процедуры
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Глава 34. Использование в 1С внешних баз данных
459
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Команда.ActiveConnection = Соединение;
// Заполняем имя хранимой процедуры
ИмяХранимойПроцедуры = "sp_MyStoreProc";
// Передаем имя процедуры
Команда.CommandText = ИмяХранимойПроцедуры;
// Устанавливаем тип команды
Команда.CommandType = 4; // adCmdStoredProc
// Выполняем команду
Команда.Execute();
В этом примере мы указали в свойстве CommandType, что будем использовать
хранимую процедуру.
Свойство Parameters позволяет задавать параметры запроса. Для увеличения
производительности желательно вместе с ним применять свойство Prepared.
Оно указывает, что скомпилированный код команды следует сохранить для
повторных вызовов. Посмотрите пример, представленный в листинге 34.8.
Листинг 34.8. Использование параметров в запросе
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Команда.ActiveConnection = Соединение;
// Устанавливаем тип команды
Команда.CommandType = 1; // adCmdText
460
Часть VIII. Взаимодействие 1С и баз данных
// Устанавливаем свойство Prepared
Команда.Prepared = Истина;
// Формируем текст запроса
ТекстЗапроса = "SELECT * FROM MyTable WHERE ID = ? and MyDate > ?";
// Создаем объект Parameter
Параметр = СоздатьОбъект("ADODB.Parameter");
// Создаем первый параметр
Параметр = Команда.CreateParameter("MyID", 3, 1, 8);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("MyID").Value = 10034;
// Создаем второй параметр
Параметр = Команда.CreateParameter("MyDate", 129, 1, 10);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("MyDate").Value = "20080101";
// Передаем текст запроса в объект
Команда.CommandText = ТекстЗапроса;
// Выполняем команду
Команда.Execute();
Разберем пример подробнее. Как и раньше, мы создали канал связи и сформировали текст запроса. Однако теперь в условии запроса мы указали, что
будут использоваться внешние параметры. После этого создали новый объект
Parameter и посредством метода CreateParameter (объект Command) заполнили реквизиты параметра. Этот метод поддерживает пять необязательных
параметров. Первый представляет собой строковую переменную с именем
передаваемого параметра. Второй определяет тип параметра. Наиболее интересные для нас типы таковы:
adBinary — двоичный тип (128);
adBoolean — булевый тип (11);
adChar — строковый тип (129);
adCurrency — денежный тип (6);
adDate — тип, хранящий дату (7);
adDecimal — тип, выражающий десятичное числовое значение (14);
Глава 34. Использование в 1С внешних баз данных
461
adDouble — числовой тип двойной точности (5);
adInteger — тип, выражающий целое число со знаком (3).
Существуют и другие типы, о которых вы можете узнать из справки к ADO
или посетив сайт фирмы Microsoft. Третий параметр метода CreateParameter
задает вид параметра:
adParamInput — входной параметр (1);
adParamOutput — выходной параметр (2);
adParamInputOutput — входной и выходной параметр одновременно (3);
adParamReturnValue — параметр, используемый для указания возвращае-
мого значения хранимой процедуры (4).
Четвертый параметр устанавливает максимальное значение передаваемого
параметра в байтах или символах. И последний, пятый, служит для передачи
непосредственно значения параметра. В данном случае мы пошли другим
путем и для добавления параметра применили свойство Parameters (ссылается на коллекцию Parameters) и метод коллекции Append. В качестве аргумента метода передали ссылку на созданный параметр (объект Parameter).
Описанные действия мы повторили для второго параметра и, передав текст
запроса в свойство CommandText, выполнили запрос. Аналогичным способом
можно передавать любые типы переменных 1С в объект Command. Замечу,
что свойство Parameters при передаче параметров можно опустить, поскольку оно является свойством по умолчанию.
Процесс передачи параметров в хранимую процедуру немногим отличается
от рассмотренного примера. Для наглядности приведем еще один пример
в листинге 34.9.
Листинг 34.9. Передача параметров в хранимую процедуру
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Команда.ActiveConnection = Соединение;
462
Часть VIII. Взаимодействие 1С и баз данных
// Устанавливаем тип команды
Команда.CommandType = 4; // adCmdStoredProc
// Устанавливаем свойство Prepared
Команда.Prepared = Истина;
// Заполняем имя хранимой процедуры
ИмяХранимойПроцедуры = "sp_MyStoreProc";
// Создаем объект Parameter
Параметр = СоздатьОбъект("ADODB.Parameter");
// Задаем параметр для возвращаемого значения процедуры
Параметр = Команда.CreateParameter("@Return", 3, 4, 8);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Создаем первый параметр
Параметр = Команда.CreateParameter("@BeginDate", 129, 1, 10);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("@BeginDate").Value = "20080101";
// Создаем второй параметр
Параметр = Команда.CreateParameter("@EndDate", 129, 1, 10);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("@EndDate").Value = "20080131";
// Создаем третий параметр
Параметр = Команда.CreateParameter("@Name", 129, 1, 20);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("@Employee").Value = "Иванов";
// Создаем четвертый параметр
Параметр = Команда.CreateParameter("@Organization", 129, 1, 20);
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("@Organization").Value = "Фабрика";
// Передаем текст запроса в объект
Команда.CommandText = ИмяХранимойПроцедуры;
Глава 34. Использование в 1С внешних баз данных
463
// Выполняем команду
Команда.Execute();
// Проверяем возвращаемое значение процедуры
Результат = Команда.Parameters("@Return").Value
Теперь, когда вы знаете, как формировать запросы и передавать параметры
в процедуры, перейдем к объекту Recordset, который дает возможность получать и обрабатывать наборы записей. Данный объект можно применять как
вместе с Command, так и без него. Связано это с тем, что его метод Open может самостоятельно сформировать и выполнить запрос на базе активного соединения. Он принимает пять необязательных параметров: текст запроса,
ссылку на активный канал связи, тип используемого курсора, тип блокировки
и тип обрабатываемого запроса (текст, хранимая процедура и т. д.). Пример
работы с методом Open представлен в листинге 34.10.
Листинг 34.10. Запрос к базе данных посредством метода Open объекта
Recordset
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Recordset
НаборЗаписей = СоздатьОбъект("ADODB.Recordset");
// Формируем текст запроса
ТекстЗапроса = "SELECT * FROM MyTable WHERE DepartmentID = 20";
// Выполняем запрос и получаем результат
ВыборкаДанные =
НаборЗаписей.Open(ТекстЗапроса, Соединение.ActiveConnection);
Как видно из примера, мы создали новое соединение. Затем в созданный объект Recordset передали текст запроса и ссылку на активное соединение. В результате выполнения метода Open будет возвращен набор записей (объект
Recordset), в соответствии с условием запроса.
Для обработки записей запроса нам понадобится свойство Fields (ссылка на
объект Fields) и объект Field. Например, чтобы прочитать данные из таблицы
464
Часть VIII. Взаимодействие 1С и баз данных
базы данных, можно воспользоваться кодом, представленным в листинге 34.11.
Листинг 34.11. Чтение данных из таблицы базы данных
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Recordset
НаборЗаписей = СоздатьОбъект("ADODB.Recordset");
// Задаем имя таблицы
ИмяТаблицыБазыДанных = "tblMyTable";
// Выполняем запрос и получаем результат
ВыборкаДанные =
НаборЗаписей.Open(ИмяТаблицыБазыДанных, Соединение.ActiveConnection);
// Создаем объект Field
ПолеТаблицы = СоздатьОбъект("ADODB.Field");
// Читаем данные из таблицы
Для Каждого ПолеТаблицы Из ВыборкаДанные Цикл
Сообщить(ПолеТаблицы.Name + " " + ПолеТаблицы.Value);
КонецЦикла;
// Закрываем объект Recordset
НаборЗаписей.Close();
Для чтения значений таблицы мы создали объект Field. Далее в цикле выбрали все записи и вывели в окно служебных сообщений наименование и значение каждой строки таблицы. В этом нам помогли свойства объекта Field,
Name и Value. Последнее свойство является заданным по умолчанию, и его
можно явно не указывать. После обработки всех записей мы вызвали метод
Close, чтобы закрыть объект Recordset.
А теперь рассмотрим наиболее важные свойства и методы объекта Recordset.
Чтобы определить число полученных записей, применяется свойство
RecordCount. Для продвижения по набору записей предназначены несколько
методов: MoveFirst (переход к первой записи), MoveLast (переход к последней записи), MoveNext (переход к следующей записи), MovePrevious (переход
Глава 34. Использование в 1С внешних баз данных
465
к предыдущей записи). Проверить границы набора записей можно с помощью свойств BOF (указывает на начало набора записей) и EOF (указывает на
завершения набора записей). В следующем примере показано, как используются эти свойства и методы (листинг 34.12).
Листинг 34.12. Продвижение по набору записей
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Recordset
НаборЗаписей = СоздатьОбъект("ADODB.Recordset");
// Задаем имя таблицы
ИмяТаблицыБазыДанных = "tblMyTable";
// Выполняем запрос и получаем результат
ВыборкаДанные =
НаборЗаписей.Open(ИмяТаблицыБазыДанных, Соединение.ActiveConnection);
// Проверяем число записей
Если ВыборкаДанные.RecordCount > 0 Тогда
// Позиционируем курсор на первую запись
ВыборкаДанные.MoveFirst();
// Читаем данные
Пока НЕ ВыборкаДанные.EOF Цикл
Сообщить(ВыборкаДанные.Fields.Name + " " +
ВыборкаДанные.Fields.Value);
// Перемещаем курсор на следующую запись
ВыборкаДанные.MoveNext();
КонецЦикла;
КонецЕсли;
// Закрываем объект Recordset
НаборЗаписей.Close();
Вначале мы проверяем число записей и, если запрос возвратил хотя бы одну,
формируем цикл. Он работает до тех пор, пока не будет прочитана последняя
запись, и свойство EOF не примет значение Истина. Для продвижения по на-
466
Часть VIII. Взаимодействие 1С и баз данных
бору записей применяется метод MoveNext, продвигающий курсор на одну
позицию вперед. Чтобы явно указать имена полей таблицы, нужно передать
в объект Fields строковое значение. Отрывок кода, демонстрирующий это,
представлен в листинге 34.13.
Листинг 34.13. Получение полей таблицы по имени
// Проверяем число записей
Если ВыборкаДанные.RecordCount > 0 Тогда
// Позиционируем курсор на первую запись
ВыборкаДанные.MoveFirst();
// Читаем данные
Пока НЕ ВыборкаДанные.EOF Цикл
Сообщить(ВыборкаДанные.Fields("DepartmentID"));
Сообщить(ВыборкаДанные.Fields("DepartmentName"));
// Перемещаем курсор на следующую запись
ВыборкаДанные.MoveNext();
КонецЦикла;
КонецЕсли;
Как видите, все довольно просто. На этом мы закончим описание объекта
Recordset (более подробно о нем можно узнать и справки на сайте Microsoft)
и, в целом, интерфейса ADO. Все ключевые моменты для успешного подключения и управления базой данных SQL мы рассмотрели. Теперь немного
коснемся языка программирования баз данных T-SQL.
Создание процедур и функций SQL
Язык T-SQL относится к так называемым скриптовым языкам, поскольку все
модули и объекты описываются в текстовом формате и компилируются перед выполнением. Мы не будем подробно рассматривать все его возможности, а коснемся лишь того, что необходимо для решения наших задач. Сразу
замечу для тех разработчиков 1С, которые никогда не сталкивались с этим
языком раньше, что основные команды практически полностью совпадают
с аналогичными инструкциями в 1С. Естественно, что записываются они на
английском языке. Для работы нам понадобится установленный пакет Microsoft SQL Server 7.0. Тестирование и выполнение примеров будем делать
в приложении SQL Server Query Analyzer, входящем в пакет.
Глава 34. Использование в 1С внешних баз данных
467
Данные в базе хранятся в специальном формате, представленном пользователям в виде таблиц. Каждая таблица содержит заданное количество полей,
имеющих предусмотренные спецификацией языка типы данных. Кроме того,
для таблиц может быть назначен первичный ключ — уникальный индекс по
одному из полей, а также дополнительные индексы (простые и составные).
Дополнительно, для сохранения целостности, одно из полей таблицы может
быть выбрано в качестве уникального идентификатора с последующим автоматическим или ручным наращиванием значения.
Для создания новой таблицы предназначена команда CREATE TABLE, для обновления существующей — ALTER TABLE. Пример создания таблицы представлен в листинге 34.14.
Листинг 34.14. Создание новой таблицы
CREATE TABLE tblOrganization
(
/* Задаем поле уникального идентификатора */
OrgID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
/* Задаем краткое наименование организации */
OrgName varchar(40) NOT NULL,
/* Задаем полное наименование организации */
OrgFullName varchar(100) NULL,
/* Задаем адрес организации */
Address varchar(100) NULL
)
Создание таблицы всегда начинается с команды CREATE TABLE и следующего
за ней имени таблицы. Затем задаются будущие поля таблицы, типы значений каждого поля, ключи, индексы и значения по умолчанию. В данном случае наша таблица содержит три поля. Первое имеет целый тип, является уникальным идентификатором и одновременно первичным ключом таблицы.
Второе поле имеет строковый тип и не может содержать значение NULL. Последнее поле также имеет строковый тип, но допускает значение NULL. После
того как вы запустите скрипт на выполнение, в текущей базе будет создана
новая таблица с именем tblOrganization.
Для обновления таблицы (добавления новых или удаления существующих
столбцов) служит команда ALTER TABLE. Пример ее использования показан
в листинге 34.15.
468
Часть VIII. Взаимодействие 1С и баз данных
Листинг 34.15. Обновление структуры таблицы
/* Добавим новый столбец */
ALTER TABLE tblOrganization ADD DirectorName varchar(30) NULL
/* Удалим существующий столбец */
ALTER TABLE tblOrganization DROP COLUMN Address
Чтобы добавить новый столбец, мы применили инструкцию ADD, а существующий удалили посредством DROP COLUMN.
Для добавления в таблицу значений применяется команда INSERT. Она позволяет только добавлять новые записи в конец таблицы. Источником записей
могут быть как единичные значения, так и результат запроса. Посмотрите
пример, представленный в листинге 34.16.
Листинг 34.16. Добавление записей в таблицу
/* Добавим в таблицу новую запись */
INSERT tblOrganization (OrgName, OrgFullName, DirectorName)
VALUES ('Компания', 'Компания', 'Иванов И.И.')
/* Добавим в таблицу новую запись */
INSERT tblOrganization
VALUES ('Предприятие', 'Предприятие', 'Петров П.П.')
/* Добавим данные в таблицу из запроса */
INSERT INTO tblOrganization
SELECT OrgName, OrgFullName, DirectorName FROM tblOldOrganization
Из примера видно, что после команды INSERT и имени таблицы в круглых
скобках через запятую перечисляются поля, а затем в инструкции VALUES передаются их значения. Как видите, существует несколько способов добавления записей: главное, чтобы количество полей в таблице и число добавляемых полей совпадало. Столбец OrgID мы не трогаем, поскольку он
формируется автоматически путем наращивания каждого последующего значения. Третий вариант команды INSERT использует запрос в качестве источника значений. Все строковые константы должны быть заключены в одиночные кавычки.
Для обновления записей таблицы или отдельных полей служит команда
В отличие от команды INSERT, она позволяет изменить значение в выбранной ячейке таблицы вместо добавления новой записи. Кроме того, подUPDATE.
Глава 34. Использование в 1С внешних баз данных
469
держивает конструкцию WHERE, где можно указать условие обновления текущей таблицы. Для передачи нового значения применяется ключевое слово
SET. Посмотрите примеры, представленные в листинге 34.17.
Листинг 34.17. Обновление записей и отдельных полей таблицы
/* Обновим ячейку таблицы */
UPDATE tblOrganization
/* передаем новое значение */
SET OrgFullName = 'Крупная компания'
/* устанавливаем условие */
WHERE OrgName = 'Компания'
/* Обновим несколько полей таблицы */
UPDATE tblOrganization
/* передаем новое значение */
SET OrgFullName = 'Крупная компания 2', DirectorName = 'Сидоров С.С.'
/* устанавливаем условие */
WHERE OrgName = 'Компания'
/* Обновление целевой таблицы данными из таблицы-источника */
UPDATE tblOrganization
/* передаем новое значение */
SET DirectorName = e.Employee
/* таблица источник */
FROM tblEmployee e
/* устанавливаем условие */
WHERE OrgID = e.OrgID
При изменении нескольких ячеек таблицы ключевое слово SET достаточно
указать один раз. Если не задать условие обновления, будут модифицированы все записи таблицы. Также можно выполнять обновление таблицы на основании данных из другой таблицы. Для этого необходимо указать конструкцию FROM и перечислить таблицы-источники.
Чтобы удалить одну или все записи из таблицы, применяется команда DELETE.
Если не будет задано условие, таблица очистится. Посмотрите пример, показанный в листинге 34.18.
Листинг 34.18. Удаление записей таблицы
/* Удалим запись */
DELETE FROM tblOrganization
/* Задаем условие удаления */
470
Часть VIII. Взаимодействие 1С и баз данных
WHERE OrgName = 'Старая компания'
/* Удаляем все записи таблицы */
DELETE FROM tblOrganization
/* Используем в условии подзапрос */
DELETE FROM tblOrganization
/* Задаем условие удаления */
WHERE DirectorName IN (SELECT Employee FROM tblEmployee
WHERE Employee LIKE 'Иван%')
В первом варианте примера удаляем одну запись, передав в запрос условие
с помощью инструкции WHERE. Во втором очищаем всю таблицу. Третий вариант удаляет только те записи, у которых в поле DirectorName записано
значение, совпадающее со значением из подзапроса. Инструкция LIKE позволяет отфильтровать из запроса записи по признаку совпадения.
Следующие целые три команды, о которых мы поговорим, предназначены
для организации транзакции. Транзакции, как правило, применяются в хранимых процедурах при обновлении или любой другой модификации данных.
Для открытия транзакции служит команда BEGIN TRANSACTION (сокращенный
вариант BEGIN TRAN). Для успешного подтверждения транзакции используется
команда COMMIT TRANSACTION (сокращенный вариант COMMIT TRAN). Если
в процессе транзакции возникли ошибки, вызывается команда ROLLBACK
TRANSACTION (сокращенный вариант ROLLBACK TRAN). Рассмотрим пример работы с этими командами, представленный в листинге 34.19.
Листинг 34.19. Использование транзакции
/* Начинаем новую транзакцию */
BEGIN TRANSACTION t
/* Обновим ячейку таблицы */
UPDATE tblOrganization
/* передаем новое значение */
SET OrgFullName = 'Крупная компания'
/* устанавливаем условие */
WHERE OrgName = 'Компания'
/* Проверяем ошибки */
IF @@error != 0
BEGIN
/* Делаем откат транзакции */
ROLLBACK TRANSACTION t
Глава 34. Использование в 1С внешних баз данных
471
RETURN -1
END
/* Обновим несколько полей таблицы */
UPDATE tblOrganization
/* передаем новое значение */
SET OrgFullName = 'Крупная компания 2', DirectorName = 'Сидоров С.С.'
/* устанавливаем условие */
WHERE OrgName = 'Компания'
IF @@error != 0
BEGIN
/* Делаем откат транзакции */
ROLLBACK TRANSACTION t
RETURN -1
END
/* Подтверждаем успешно выполненную транзакцию */
COMMIT TRANSACTION t
Вначале мы открыли новую транзакцию. Затем после каждого обновления
данных проверили глобальную переменную @@error, которая возвращает состояние выполнения предыдущей команды. Если обнаружена ошибка, вызываем команду отката транзакции и выходим из кода. При успешном обновлении всех таблиц фиксируем транзакцию. При этом обновленные данные из
кэша записываются в базу данных.
Для выборки данных применяется команда SELECT. Мы не будем ее описывать,
потому что она в целом совпадает с командой ВЫБРАТЬ языка запросов 1С.
И последнее, с чем мы познакомимся, — это создание собственных хранимых процедур. По своему назначению они не отличаются от обычных процедур в системе 1С, включая в себя объявление переменных, тексты запросов, входные и выходные параметры. Для создания новой процедуры служит
команда CREATE PROCEDURE (сокращенный вариант CREATE PROC). Пример ее
использования приведен в листинге 34.20.
Листинг 34.20. Создание хранимой процедуры
CREATE PROCEDURE sp_MyProc
/* Задаем параметры процедуры */
@OrgID int,
@OrgName varchar(40) output – выходной параметр
AS
472
Часть VIII. Взаимодействие 1С и баз данных
BEGIN
/* Формируем запрос */
SELECT @OrgName = OrgName
FROM tblOrganization
WHERE OrgID = @OrgID
END
Эта вполне простая процедура принимает идентификатор организации и возвращает ее наименование. Для этого мы делаем выборку из соответствующей
таблицы. В выходной параметр записываем результат выполнения запроса,
а в условие передаем значение входного параметра. При описании процедуры
следует указывать тип каждого параметра, а также признак — входной или
выходной.
Не рассмотренные в данной главе команды и операторы языка в большинстве
своем имеют аналоги в 1С. Пользуясь справочной информацией, вы можете
самостоятельно посмотреть и сравнить английские и русскоязычные эквиваленты.
Глава 35
Создание базы
и подключение к 1С
В этой главе мы рассмотрим практический пример подключения внешней
базы на сервере SQL к программе 1С. Вначале мы создадим серверную часть:
базу, таблицы и хранимые процедуры. Затем построим клиентскую часть,
воспользовавшись возможностями конфигуратора. Результатом нашей работы станет действующее клиент-серверное приложение на платформе 1С, посредством которого можно будет считывать и записывать данные в базу. Для
построения серверной части понадобится проинсталлированный на компьютере пакет Microsoft SQL Server версии 7.0 или выше.
Создание новой базы данных
Первым делом определимся, какую базу данных мы будем создавать. Поскольку она у нас будет тестовой, возьмем за основу структуру персонала
предприятия: организации, подразделения, сотрудники, данные о сотрудниках. Для тех читателей, у которых уже есть готовые базы, можно пропустить
эту часть и перейти к разделу создания внешней обработки в 1С.
В предыдущей главе мы не коснулись вопроса создания новой базы на сервере, поэтому начнем именно с этого. Для программного формирования новой
пустой базы предназначена команда CREATE DATABASE. Откройте приложение
Query Analyzer, входящее в пакет разработчика, и наберите код, представленный в листинге 35.1.
Листинг 35.1. Создание новой пустой базы данных
/* Выбираем системную базу */
USE master
/* Создаем базу данных */
CREATE DATABASE myTest1C
474
Часть VIII. Взаимодействие 1С и баз данных
Как видите, создать новую базу достаточно просто. Команда имеет много
дополнительных настроек, в частности, позволяющих настроить размеры
файлов базы (LOG-файла и файла данных), но здесь мы их рассматривать не
будем. После того как вы запустите данный пример на выполнение, обновите
дерево существующих баз в программе Query Analyzer. При отсутствии ошибок в ходе операции создания в списке появится новое наименование созданной нами базы данных, как показано на рис. 35.1.
Рис. 35.1. Внешний вид главного окна программы Query Analyzer
Разверните дерево с именем нашей базы, и вы увидите список папок для хранения объектов базы: таблиц, хранимых процедур и функций, триггеров
и т. д. Первым делом, придется определиться, какие таблицы нам потребуются. В первую очередь, понадобится таблица для хранения организаций, входящих в состав предприятия. Определим для нее следующие поля: идентификатор организации, краткое наименование, полное наименование и адрес.
Напишем код создания таблицы, как показано в листинге 35.2.
Глава 35. Создание базы и подключение к 1С
475
Листинг 35.2. Создание таблицы tblOrganization
/* Выбираем нашу базу */
USE myTest1C
/* Создаем таблицу */
CREATE TABLE tblOrganization
(
/* Задаем поле уникального идентификатора */
OrgID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
/* Задаем краткое наименование организации */
OrgName varchar(40) NOT NULL,
/* Задаем полное наименование организации */
OrgFullName varchar(100) NULL,
/* Задаем адрес организации */
OrgAddress varchar(100) NULL
)
Выполним этот пример в окне программы Query Analyzer. У нас будет сформирована первая таблица. Если вы заметили, для поля идентификатора мы задали автоматическое приращение значения, другими словами, идентификатор
будет назначаться сервером в момент добавления новой записи и будет уникальным в пределах таблицы. Кроме того, для этого поля мы установили признак первичного ключа. Строка NULL указывает, что поле может быть пустым.
И обратно, строка NOT NULL делает это поле обязательным для заполнения.
Теперь создадим таблицу для хранения информации о департаментах. Определим для нее следующие поля: идентификатор записи и наименование департамента. Соответствующий код представлен в листинге 35.3.
Листинг 35.3. Создание таблицы tblDepartment
/* Выбираем нашу базу */
USE myTest1C
/* Создаем таблицу */
CREATE TABLE tblDepartment
(
/* Задаем поле уникального идентификатора */
DepartmentID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
/* Задаем полное наименование организации */
DepartmentName varchar(40) NOT NULL
)
476
Часть VIII. Взаимодействие 1С и баз данных
Как и раньше, выполните данный код и добавьте в нашу базу еще одну новую таблицу. Для хранения сведений о сотрудниках также потребуется создать отдельную таблицу. Кроме полей идентификатора и имени, определим
дополнительные сведения, такие как адрес и телефон. Пример кода, формирующий таблицу по сотрудникам, представлен в листинге 35.4.
Листинг 35.4. Создание таблицы tblEmployee
/* Выбираем нашу базу */
USE myTest1C
/* Создаем таблицу */
CREATE TABLE tblEmployee
(
/* Задаем поле уникального идентификатора */
EmployeeID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
/* Задаем имя */
EmployeeName varchar(50) NOT NULL,
/* Задаем адрес */
EmployeeAddress varchar(100) NULL,
/* Задаем домашний телефон */
EmployeeHomePhone varchar(20) NULL,
/* Задаем мобильный телефон */
EmployeeGSMPhone varchar(20) NULL,
/* Задаем признак активности */
EmployeeActive tinyint NOT NULL DEFAULT 1
)
Чтобы разграничить служебные обязанности сотрудников предприятия, создадим отдельную таблицу должностей. Пример приведен в листинге 35.5.
Листинг 35.5. Создание таблицы tblPost
/* Выбираем нашу базу */
USE myTest1C
/* Создаем таблицу */
CREATE TABLE tblPost
(
/* Задаем поле уникального идентификатора */
PostID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Глава 35. Создание базы и подключение к 1С
477
/* Задаем наименование должности */
PostName varchar(30) NOT NULL
)
И последняя таблица, которая нам понадобится, распределит каждого сотрудника в разрезе организаций, департаментов и занимаемых должностей.
Ее пример представлен в листинге 35.6.
Листинг 35.6. Создание таблицы tblEmployeeRelations
/* Выбираем нашу базу */
USE myTest1C
/* Создаем таблицу */
CREATE TABLE tblEmployeeRelations
(
/* Добавляем ссылку на таблицу сотрудников */
EmployeeID int NOT NULL DEFAULT 0,
/* Добавляем ссылку на таблицу организаций */
OrgID int NOT NULL DEFAULT 0,
/* Добавляем ссылку на таблицу департаментов */
DepartmentID int NOT NULL DEFAULT 0,
/* Добавляем ссылку на таблицу должностей */
PostID int NOT NULL DEFAULT 0
)
Данная таблица будет связующей между всеми остальными таблицами. Как
видите, состоит она только из идентификаторов других таблиц, что исключает дублирование данных и бессмысленное увеличение размера базы данных.
Теперь, когда все необходимые таблицы созданы, необходимо их заполнить
данными. Для этого напишем один общий скрипт, подобный приведенному
в листинге 35.7.
Листинг 35.7. Заполняем таблицы данными
/* Выбираем нашу базу */
USE myTest1C
/* Заполняем таблицу организаций */
INSERT tblOrganization
(OrgName, OrgFullName, OrgAddress)
478
Часть VIII. Взаимодействие 1С и баз данных
VALUES ('Первая организация', 'Организация по продуктам питания',
'ул.Железнодорожная д.56')
INSERT tblOrganization
(OrgName, OrgFullName, OrgAddress)
VALUES ('Вторая организация', 'Организация по бытовой химии',
'ул.Космонавтов д.23 к.2')
INSERT tblOrganization
(OrgName, OrgFullName, OrgAddress)
VALUES ('Третья организация', 'Организация по стройматериалам',
'ул.Независимости д.90')
INSERT tblOrganization
(OrgName, OrgFullName, OrgAddress)
VALUES ('Четвертая организация', 'Организация по металлопрокату',
'ул.Горького д.12')
/* Заполняем таблицу департаментов */
INSERT tblDepartment (DepartmentName)
VALUES ('Департамент управления')
INSERT tblDepartment (DepartmentName)
VALUES ('Департамент закупок')
INSERT tblDepartment (DepartmentName)
VALUES ('Департамент продаж')
INSERT tblDepartment (DepartmentName)
VALUES ('Департамент финансового учета')
INSERT tblDepartment (DepartmentName)
VALUES ('Департамент хозяйственного управления')
/* Заполняем таблицу сотрудников */
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Иванов И.И,', 'ул.Зеленая д.2 кв.11', '233-32-23', '955-45-65',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Петров П.П,', 'ул.Красная д.43 кв.5', '265-09-12', '654-12-12',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
Глава 35. Создание базы и подключение к 1С
479
VALUES ('Кузнецов С.С.', 'ул.Белая д.4 кв.30', '777-56-23', '970-10-21',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Мишин А.Д.', 'ул.Синяя д.56 кв.30', '653-12-06', '882-52-90',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Луговой С.В.', 'ул.Хоружей д.7 кв.1', '498-07-52', '233-12-60',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Гоман В.И.', 'ул.Желтая д.8 кв.87', '566-35-61', '552-23-45',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Романчук Р.Н.', 'ул.Стали д.36 кв.67', '776-34-78', '885-43-22',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Нараб И.И.', 'ул.Песочная д.72 кв.3', '166-72-22', '684-02-03',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Павлов Ю.С.', 'ул.Радужная д.77 кв.8', '523-93-77', '548-23-93',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
EmployeeActive)
VALUES ('Болотова А.А.', 'ул.Дружбы д.9 кв.80', '433-62-25', '589-63-92',
1)
INSERT tblEmployee
(EmployeeName, EmployeeAddress, EmployeeHomePhone, EmployeeGSMPhone,
480
Часть VIII. Взаимодействие 1С и баз данных
EmployeeActive)
VALUES ('Шевцова О.Г.', 'ул.Победы д.72 кв.28', '586-98-88', '339-43-25',
1)
/* Заполняем таблицу должностей */
INSERT tblPost (PostName) VALUES ('Директор')
INSERT tblPost (PostName) VALUES ('Заместитель директора')
INSERT tblPost (PostName) VALUES ('Финансовый директор')
INSERT tblPost (PostName) VALUES ('Главный бухгалтер')
INSERT tblPost (PostName) VALUES ('Бухгалтер')
INSERT tblPost (PostName) VALUES ('Менеджер по закупкам')
INSERT tblPost (PostName) VALUES ('Менеджер по продажам')
INSERT tblPost (PostName) VALUES ('Начальник хозяйственной службы')
/* Заполняем таблицу связей */
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (1, 1, 1, 1)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (2, 1, 2, 2)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (3, 1, 4, 4)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (4, 2, 5, 7)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (5, 2, 1, 1)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (6, 3, 1, 4)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (7, 3, 5, 6)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (8, 4, 1, 1)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
Глава 35. Создание базы и подключение к 1С
481
VALUES (9, 4, 3, 7)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (10, 4, 5, 3)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (11, 4, 3, 7)
INSERT tblEmployeeRelations
(EmployeeID, OrgID, DepartmentID, PostID)
VALUES (12, 4, 2, 6)
После выполнения данного скрипта все таблицы будут заполнены данными.
Следующим шагом будет создание хранимых процедур для получения данных из справочных таблиц. Прежде всего, нам понадобятся четыре процедуры: по организациям, департаментам, сотрудникам и должностям. Пример
кода, формирующего необходимые процедуры, представлен в листинге 35.8.
Листинг 35.8. Создание хранимых процедур по каждому справочнику
/* Выбираем нашу базу */
USE myTest1C
/* Создаем процедуру по организациям */
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO
CREATE
PROCEDURE sp_GetOrganization
/* Задаем параметры процедуры */
@OrgID int = 0
AS
BEGIN
/* Если нет ссылки на конкретную организацию, возвращаем все */
IF @OrgID = 0
SELECT OrgID, OrgName, OrgFullName, OrgAddress
FROM tblOrganization
/* Иначе получаем сведения только по выбранной организации */
ELSE
SELECT OrgID, OrgName, OrgFullName, OrgAddress
FROM tblOrganization
482
Часть VIII. Взаимодействие 1С и баз данных
WHERE OrgID = @OrgID
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
/* Создаем процедуру по департаментам */
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO
CREATE PROCEDURE sp_GetDepartment
/* Задаем параметры процедуры */
@DepartmentID int = 0
AS
BEGIN
/* Если нет ссылки на департамент, возвращаем все */
IF @DepartmentID = 0
SELECT DepartmentID, DepartmentName
FROM tblDepartment
/* Иначе получаем сведения только по выбранному департаменту */
ELSE
SELECT DepartmentID, DepartmentName
FROM tblDepartment
WHERE DepartmentID = @DepartmentID
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
/* Создаем процедуру по сотрудникам */
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO
CREATE PROCEDURE sp_GetEmployee
/* Задаем параметры процедуры */
Глава 35. Создание базы и подключение к 1С
@EmployeeID int = 0
AS
BEGIN
/* Если нет ссылки на сотрудника, возвращаем все */
IF @EmployeeID = 0
SELECT EmployeeID, EmployeeName
FROM tblEmployee
WHERE EmployeeActive = 1 -- только активные сотрудники
/* Иначе получаем сведения только по выбранному сотруднику */
ELSE
SELECT EmployeeID, EmployeeName, EmployeeAddress,
EmployeeHomePhone, EmployeeGSMPhone
FROM tblEmployee
WHERE EmployeeID = @EmployeeID AND EmployeeActive = 1
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
GO
/* Создаем процедуру по должностям */
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
CREATE PROCEDURE sp_GetPost
/* Задаем параметры процедуры */
@PostID int = 0
AS
BEGIN
/* Если нет ссылки на должность, возвращаем все */
IF @PostID = 0
SELECT PostID, PostName
FROM tblPost
/* Иначе получаем сведения только по выбранной должности */
ELSE
SELECT PostID, PostName
FROM tblPost
WHERE PostID = @PostID
483
484
Часть VIII. Взаимодействие 1С и баз данных
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
Теперь у нас есть основные процедуры для получения данных по каждой
справочной таблице. Нам осталось сделать еще одну общую процедуру, которая будет брать данные из всех таблиц и формировать на их основе полные
сведения по каждому сотруднику в разрезе организаций и департаментов.
Пример такой процедуры представлен в листинге 35.9.
Листинг 35.9. Создание общей процедуры для отчета по сотрудникам
/* Выбираем нашу базу */
USE myTest1C
/* Создаем процедуру */
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO
CREATE PROCEDURE sp_GetEmployeeReport
/* Задаем параметры процедуры */
@EmployeeID int = 0
AS
BEGIN
/* Если не указан сотрудник, выводим данные по всем */
IF @EmployeeID = 0
BEGIN
SELECT e.EmployeeName, p.PostName, o.OrgName,
d.DepartmentName, e.EmployeeAddress,
e.EmployeeHomePhone, e.EmployeeGSMPhone
FROM tblEmployee e
LEFT JOIN tblEmployeeRelations er ON
e.EmployeeID = er.EmployeeID
LEFT JOIN tblOrganization o ON er.OrgID = o.OrgID
LEFT JOIN tblDepartment d ON
er.DepartmentID = d.DepartmentID
Глава 35. Создание базы и подключение к 1С
485
LEFT JOIN tblPost p ON er.PostID = p.PostID
WHERE e.EmployeeID = @EmployeeID AND e.EmployeeActive = 1
END
/* Иначе выбираем данные по указанному сотруднику */
ELSE
BEGIN
SELECT e.EmployeeName, p.PostName, o.OrgName,
d.DepartmentName, e.EmployeeAddress,
e.EmployeeHomePhone, e.EmployeeGSMPhone
FROM tblEmployee e
LEFT JOIN tblEmployeeRelations er ON
e.EmployeeID = er.EmployeeID
LEFT JOIN tblOrganization o ON er.OrgID = o.OrgID
LEFT JOIN tblDepartment d ON
er.DepartmentID = d.DepartmentID
LEFT JOIN tblPost p ON er.PostID = p.PostID
END
END
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
В процедуре мы через левое соединение подключили все необходимые таблицы, связав их по соответствующим полям идентификаторов. При передаче
в параметр процедуры ссылки на выбранного сотрудника она возвратит всю
необходимую информацию, собрав ее из различных таблиц.
Теперь, когда серверная часть в целом готова, перейдем к созданию клиентского интерфейса, который будет взаимодействовать с пользователями программы.
Создание внешней обработки в 1С
Откройте конфигуратор и выберите меню Файл | Новый. В открывшемся
диалоговом окне укажите на внешнюю обработку и нажмите кнопку OK.
Откроется стандартное окно конструктора, в котором следует задать имя обработки, например, Персонал. Далее создадим новую табличную часть для
486
Часть VIII. Взаимодействие 1С и баз данных
основного отчета и добавим соответствующие реквизиты. Типы значений
реквизитов должны совпадать с полями запроса из процедуры
sp_GetEmployeeReport. Очередность не имеет принципиального значения и
зависит от собственных требований к отображению данных. В результате
наших действий должно получиться что-то похожее на рис. 35.2.
Рис. 35.2. Внешняя обработка Персонал
Следующим шагом будет создание основной формы для вывода отчета. Для
этого добавьте новую форму и назовите ее Форма отчета. По умолчанию она
будет основной формой обработки. В конструкторе форм нажмите кнопку
Далее. В появившемся окне можно указать автоматическое формирование
полей таблицы отчета. Для этого установите напротив каждого реквизита
ссылку на элемент управления, как это показано на рис. 35.3.
После этого откроется готовая форма с заданными полями. Удалите сверху
панель инструментов и добавьте новое поле выбора для отбора необходимого
Глава 35. Создание базы и подключение к 1С
487
сотрудника. Назовите его ПолеВыбораСотрудники и выберите в качестве типа
значения объект СписокЗначений. Внешний вид готовой формы отчета показан на рис. 35.4.
Рис. 35.3. Формирование полей таблицы
Рис. 35.4. Внешний вид основной формы
488
Часть VIII. Взаимодействие 1С и баз данных
Теперь, когда все необходимые действия выполнены, осталось только написать код, чтобы все это заработало. Поскольку наша процедура отчета принимает идентификатор сотрудника, мы будет передавать его из поля выбора.
Заполнение этого поля будет происходить перед открытием формы посредством
процедуры sp_GetEmployee. После того как будет выбран (или не выбран)
нужный сотрудник, пользователь должен будет нажать кнопку Выполнить.
В обработчике нажатия этой кнопки мы будем вызывать процедуру
sp_GetEmployeeReport для заполнения табличного поля отчета. Как это сделать, вы узнаете из следующего раздела.
Подключение базы данных
Откройте диалоговое окно свойств основной формы и установите обработчик
ПередОткрытием. Перейдите в модуль формы и добавьте код, представленный
в листинге 35.10.
Листинг 35.10. Обработчик формы ПередОткрытием
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
// Вызываем процедуру заполнения поля выбора
ЗаполнитьСписокСотрудников();
КонецПроцедуры
В обработчике мы вызываем собственную процедуру, которая заполняет поле
выбора данными из базы. Текст этой процедуры показан в листинге 35.11.
Листинг 35.11. Процедура заполнения списка сотрудников
Процедура ЗаполнитьСписокСотрудников ()
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Глава 35. Создание базы и подключение к 1С
489
Команда.ActiveConnection = Соединение;
// Заполняем имя хранимой процедуры
ИмяХранимойПроцедуры = "sp_GetEmployee";
// Передаем имя процедуры
Команда.CommandText = ИмяХранимойПроцедуры;
// Устанавливаем тип команды
Команда.CommandType = 4; // adCmdStoredProc
// Создаем объект для хранения результата запроса
Данные = СоздатьОбъект("ADODB.Recordset");
// Выполняем команду и обрабатываем результат
Данные = Команда.Execute();
// Создаем список значений
Список = Новый СписокЗначений;
// Если есть данные, обрабатываем их
Если Данные.RecordCount > 0 Тогда
// Позиционируем курсор на первую запись
Данные.MoveFirst();
Пока НЕ Данные.EOF Цикл
// Добавляем данные в список значений
Список.Добавить(Данные.Fields("EmployeeID"),
Данные.Fields("EmployeeName"));
// Переходим к следующей записи
Данные.MoveNext();
КонецЦикла;
// Закрываем набор
Данные.Close();
КонецЕсли;
// Передаем в наше поле выбора ссылку на список значений
ЭлементыФормы.ПолеВыбораСотрудники.СписокВыбора = Список;
КонецПроцедуры
Теперь при открытии основной формы автоматически будет загружаться
список сотрудников из базы на сервере. Отмечу, что если запрос не вернет
данные, то объект Recordset не будет открыт, поэтому рекомендую добавить
обработчик ошибок для исключения подобных ситуаций.
И последнее, что нам осталось, это заполнить отчет данными по нажатию на
кнопку Выполнить. Для этого создайте процедуру обработчика нажатия
и добавьте туда код, представленный в листинге 35.12.
490
Часть VIII. Взаимодействие 1С и баз данных
Листинг 35.12. Обработчик кнопки Выполнить
Процедура КнопкаВыполнитьНажатие(Элемент)
// Вызываем процедуру заполнения отчета
СформироватьОтчетПоСотрудникам();
КонецПроцедуры
В обработчике мы вызываем собственную процедуру, которая читает данные
с сервера, и заполняем табличное поле. Исходный текст процедуры показан
в листинге 35.13.
Листинг 35.13. Процедура получения данных по сотрудникам
Процедура СформироватьОтчетПоСотрудникам()
// Получаем текущее значение кода сотрудника из поля выбора
КодСотрудника = ЭлементыФормы.ПолеВыбораСотрудники[0];
// Очищаем табличную часть
ЭтотОбъект.ОтчетПоСотрудникам.Очистить();
// Создаем новый объект подключения
Соединение = СоздатьОбъект("ADODB.Connection");
// Формируем строку подключения
Соединение.ConnectionString =
"Provider=SQLOLEDB;server=MyServer;database=MyDatabase;uid=sa;pwd=";
// Открываем канал в синхронном режиме
Соединение.Open();
// Создаем новый объект Command
Команда = СоздатьОбъект("ADODB.Command");
// Передаем ссылку на текущее подключение
Команда.ActiveConnection = Соединение;
// Заполняем имя хранимой процедуры
ИмяХранимойПроцедуры = "sp_GetEmployeeReport";
// Передаем имя процедуры
Команда.CommandText = ИмяХранимойПроцедуры;
// Устанавливаем тип команды
Команда.CommandType = 4; // adCmdStoredProc
// Создаем объект Parameter
Параметр = СоздатьОбъект("ADODB.Parameter");
// Формируем параметр процедуры
Параметр = Команда.CreateParameter("@EmployeeID", 3, 1, 8);
Глава 35. Создание базы и подключение к 1С
491
// Добавляем параметр в объект Command
Команда.Parameters.Append(Параметр);
// Записываем значение параметра
Команда.Parameters("@EmployeeID").Value = КодСотрудника;
// Создаем объект для хранения результата запроса
Данные = СоздатьОбъект("ADODB.Recordset");
// Выполняем команду и обрабатываем результат
Данные = Команда.Execute();
// Заполняем табличное поле отчета
Если Данные.RecordCount > 0 Тогда
// Позиционируем курсор на первую запись
Данные.MoveFirst();
Пока НЕ Данные.EOF Цикл
// Добавляем данные в табличную часть
Строка = ЭтотОбъект.ОтчетПоСотрудникам.Добавить();
// Читаем имя сотрудника
Строка.Имя = Данные.Fields("EmployeeName");
// Должность
Строка.Должность = Данные.Fields("PostName");
// Наименование организации
Строка.Организация = Данные.Fields("OrgName");
// Департамент
Строка.Департамент =
Данные.Fields("DepartmentName");
// Адрес
Строка.Адрес = Данные.Fields("EmployeeAddress");
// Домашний телефон
Строка.ДомашнийТелефон =
Данные.Fields("EmployeeHomePhone");
// Мобильный телефон
Строка.ДомашнийТелефон =
Данные.Fields("EmployeeGSMPhone");
// Переходим к следующей записи
Данные.MoveNext();
КонецЦикла;
// Закрываем набор
Данные.Close();
КонецПроцедуры
492
Часть VIII. Взаимодействие 1С и баз данных
Результатом выполнения данной процедуры будет заполнение табличного
поля информацией по сотруднику (или по всем сотрудникам). Перед выполнением кода не забудьте скорректировать строку подключения в соответствии с вашими настройками (имя сервера и базы, имя и пароль пользователя).
Как видите, процесс чтения данных с сервера посредством хранимых процедур не представляет собой ничего сверхъестественного. Аналогичным образом можно организовать запись и обновление данных. Для этого достаточно
создать необходимые процедуры на сервере, а на клиенте работа с ними ничем не отличается от уже рассмотренных примеров. Замечу, что скорость работы через интерфейс ADO существенно быстрее, чем обычное выполнение
запросов в 1С. Трудно сказать, с чем это связано, но факт есть факт.
Как уже говорилось, клиентская часть выполнена в виде внешней обработки.
Преимуществом такого варианта служит независимость от основной конфигурации, что дает возможность вносить изменения в исходный код в любой
момент и в короткие сроки. При желании вы можете сохранить внешнюю
обработку в виде стандартной обработки и поместить в конфигурацию.
В этом случае ваша программа станет частью общей системы, для которой
можно задавать права доступа, создавать меню и др.
На этом, думаю, можно завершить тему подключения локальных и удаленных баз данных к системе 1С. Поверьте, при наличии действующей базы SQL
на сервере гораздо удобнее применять именно такой способ работы, чем
переносить все данные в 1С. Вы выиграете не только в скорости работы и
суммарной производительности, но и сохраните независимость структуры
данных базы от очередных изменений и обновлений конфигураций 1С, проводимых достаточно часто и далеко не всегда гладко. Думаю, информация,
полученная здесь, поможет вам в профессиональной работе и сэкономит
время и денежные средства.
Приложение
Описание компакт-диска
Диск содержит исходные коды примеров книги.
Примеры расположены в каталоге Examples, по главам книги.
Нумерация примеров совпадает с соответствующими номерами листингов.
494
Часть VIII. Взаимодействие 1С и баз данных
Ïðåäìåòíûé óêàçàòåëü
A
ADO 452
Коллекция Fields 452
Коллекция Parameters 452
Коллекция Properties 452
Метод Append 461
Метод BeginTrans 456
Метод Close 454
Метод CommitTrans 456
Метод CreateParameter 460
Метод Execute 455
Метод Open 454, 463
Метод RollBackTrans 456
Объект Command 452
Объект Connection 452
Объект Field 452
Объект Parameter 452
Объект Property 452
Свойство ActiveConnection 453
Свойство BOF 465
Свойство CommandText 457
Свойство CommandType 458
Свойство ConnectionString 452
Свойство ConnectionTimeout 453
Свойство EOF 465
Свойство Fileds 463
Свойство MoveFirst 464
Свойство MoveLast 464
Свойство MoveNext 464
Свойство Prepared 459
Свойство Previous 464
Свойство RecordCount 464
Свойство Status 453
S
SQL 466
@@error 471
ADD 468
ALTER TABLE 467
BEGIN TRANSACTION 470
COMMIT TRANSACTION 470
CREATE PROCEDURE 471
CREATE TABLE 467
DELETE 469
DROP COLUMN 468
FROM 469
INSERT 468
LIKE 470
ROLLBACK TRANSACTION 470
SELECT 471
SET 469
VALUES 468
WHERE 470
Б
Базовые объекты:
ДокументВыборка 10, 17
ДокументМенеджер 10, 17, 55
ДокументОбъект 10, 37, 39, 55
ДокументСписок 10, 42
ДокументСсылка 10
ДокументыМенеджер 10
ОтчетМенеджер 330
ОтчетОбъект 330
ОтчетыМенеджер 329
ПеречислениеМенеджер 401
496
Базовые объекты:
ПеречислениеСсылка 401
ПланВидовХарактеристик
Выборка 408
ПланВидовХарактеристик
Менеджер 407
ПланВидовХарактеристикОбъект 407
ПланВидовХарактеристикСписок 407
ПланВидовХарактеристик
Ссылка 408
ПланыВидовХарактеристик
Менеджер 407
РегистрНакопленияВыборка 102, 130
РегистрНакопленияНабор
Записей 137
РегистрНакопленияСписок 130
РегистрСведенийВыборка 225
РегистрСведенийЗапись 225
РегистрСведенийМенеджер 225, 235
РегистрСведенийНабор
Записей 225
РегистрСведенийСписок 225
СправочникВыборка 383
СправочникМенеджер 383
СправочникиМенеджер 383
СправочникОбъект 383
СправочникСписок 383
СправочникСсылка 383
Блокировка документа 14
В
Встроенные функции:
ВосстановитьЗначение 352
НачалоМесяца 63
ПолучитьМакетОформления 350
СохранитьЗначение 352
ТипЗнч 83, 89
Функция НачалоГода 64
Функция ТипЗнач 27
Выборка документов 17
Г
Глобальный контекст
ПолучитьОбщуюФорму 34
Ïðåäìåòíûé óêàçàòåëü
Д
ДокументВыборка:
Метод ПолучитьОбъект 18, 36
Свойство Ссылка 24
Следующий 18
ДокументМенеджер:
Метод Выбрать 18
Метод Записать 13
Метод НайтиПоНомеру 15, 21
Метод НайтиПоРеквизиту 21, 22
Метод ПолучитьОбъект 15
Метод ПолучитьСсылку 25
Метод ПолучитьФорму 30
Метод ПолучитьФормуВыбора 32
Метод ПолучитьФормуНового
Документа 32
Метод ПолучитьФормуСписка 32
Метод СоздатьДокумент 11, 46
Метод УстановитьНовыйНомер 12
Метод УстановитьПометку
Удаления 16
ПолучитьФорму 12
Свойство ПометкаУдаления 16
ДокументОбъект:
Метод Заполнить 45, 46
Метод ПолучитьСсылкуНового 25
Метод ПолучитьФорму 33
Метод Скопировать 22
Метод УстановитьСсылкуНового 25
Свойство Движения 40
Свойство Ссылка 41
Свойство ЭтотОбъект 41
Событие Обработка
Заполнения 49, 52
Событие Обработка
Проведения 49, 53
Событие ОбработкаУдаления
Проведения 49, 54
Событие ПередЗаписью 49, 51
Событие ПередУдалением 49, 52
Событие ПриЗаписи 49
Событие ПриКопировании 49
Событие ПриКопировании 50
Событие ПриУстановкеНового
Номера 49, 51
Ïðåäìåòíûé óêàçàòåëü
ДокументОбъекта:
Событие ПриЗаписи 50
ДокументСписок:
Отбор 42
Свойство Колонки 43
Свойство Порядок 43
ДокументСсылка:
Метод ПолучитьФорму 33
Свойство Метаданные 25
Документы:
ВнутреннийЗаказ 198
ВозвратТоваровОтПокупателя 168
ЗаказПокупателя 144, 148, 151
ЗаказПоставщику 105, 152, 154
КорректировкаЗаказаПокупателя 102
КорректировкаЗаказа
Поставщику 105, 154
ПеремещениеТоваров 102
ПоступлениеТоваровУслуг 105
З
Запись:
в регистр накопления 137
в регистр сведений 251
документа 13
К, М
Корректировка значений реквизитов
регистров накопления 221
Макет 417
О
Общие объекты:
ПостроительОтчета 335
РегистраНакопленияМенеджер 98
РегистрНакопленияВыборка 98
Объект ВыборкаИзРезультата
Запроса 59
Метод Следующий 59
Метод
СледующийПоЗначениюПоля 63
Объект Запрос:
Метод Выполнить 55
Метод НайтиПараметры 56
497
Метод УстановитьПараметр 56,
57, 256
НайтиПараметры 256
Свойство Текст 55
Объект Отбор:
Свойство Использование 42
Объект РезультатЗапроса:
Метод Выбрать 57
Метод Выгрузить 57
Метод Пустой 57
Объект ТаблицаЗначение:
Метод Сортировать 69
Объект ТаблицаЗначений:
Метод ВыгрузитьКолонку 67
Метод Добавить 48
Метод Свернуть 69
Объект ТабличныйДокумент:
Метод ПолучитьОбласть 127
Свойство ВысотаТаблицы 128
Объект Форма:
Метод Активизировать 31
Метод Открыт 31, 237
Метод ОткрытьМодально 31
Свойство ЗакрыватьПриЗакрытии
Владельца 32
Объекты:
Граница 98
Диаграмма 220
Запрос 55, 143
МоментВремени 98
ОбластьЯчеекТабличного
Документа 128
Порядок 43
РезультатЗапроса 56
ТабличныйДокумент 126
УникальныйИдентификатор 25,
32, 237
Оперативное проведение 14
Основная форма документа 28
Отбор:
Метод Количество 340
Метод Сбросить 340
Метод Удалить 340
498
ОтчетМенеджер:
Метод Создать 330
ОтчетОбъект:
Метод ПолучитьФорму 331
Отчеты 329
П
Перечисления 401
ВедениеВзаиморасчетов
ПоДоговорам 402
ВидыОперацийЗаказПокупателя 11
КодыОпераций 402
ОтветственныеЛица
Организации 403
СтатусыПартий 402
ПланВидовХарактеристикВыборка
Метод ПолучитьОбъект 410
Свойство Предопределенный 410
ПланВидовХарактеристикМенеджер
Метод Выбрать 409
Метод НайтиПоКоду 410
Метод НайтиПоНаименованию 409
Метод ПолучитьФорму 411
Метод ПолучитьФормуНовой
Группы 411
Метод ПолучитьФормуСписка 411
Метод СоздатьЭлемент 408
НайтиПоНаименованию 410
НайтиПоРеквизиту 410
ПланВидовХарактеристикОбъект:
Метод Записать 408
Метод Удалить 408
ПланВидовХарактеристикСсылка:
Метод Пустая 409
Планы видов характеристик 407
Выборка элементов 409
Использование языка запросов 412
Поиск элементов 410
Получение свойства 408
Получение форм 411
Создание нового элемента 408
Поиск документов 21
Получение макета регистра
накопления 126
Ïðåäìåòíûé óêàçàòåëü
Получение макета регистра
сведений 240
Получение нового экземпляра
формы 237
Получение отборов 42
Получение ссылки на документ 23
Получение форм регистра
сведений 235
Получение формы документа 30
Получение формы регистра
накопления 122
ПоляПостроителяОтчета:
Метод Добавить 339
Метод Найти 339
Метод Очистить 339
Метод Удалить 339
Построитель отчета 335
ПостроительОтчета:
ИсточникДанных 342
Метод Вывести 336
Метод Выполнить 336
Метод ЗаполнитьНастройки 336, 351
Метод ОформитьМакет 350
Метод ПолучитьЗапрос 351
Метод ПолучитьНастройки 351
Метод Установить
Настройки 351, 352
Свойство ВыбранныеПоля 338
Свойство ВыводитьЗаголовок
Отчета 341
Свойство ВыводитьОбщие
Итоги 341
Свойство ВыводитьПодвал
Отчета 341
Свойство ВыводитьПодвал
Таблицы 341
Свойство ВыводитьШапку
Таблицы 341
Свойство Заполнение
Расшифровки 341
Свойство Макет 344
Свойство МакетДетальных
Записей 348
Свойство МакетЗаголовка
Отчета 348
Ïðåäìåòíûé óêàçàòåëü
Свойство МакетОбщихИтогов 348
Свойство МакетОформления 348
Свойство МакетПодвалаОтчета 348
Свойство МакетШапкиТаблицы 348
Свойство ОбрабатыватьПрерывание
Пользователя 351
Свойство Отбор 338
Свойство ОтображатьСостояние 351
Свойство Порядок 338
Свойство Результат 351
Свойство Текст 336
ТекстЗаголовка 342
Проведение документа 13
Р
Расширение формы списка регистра
накопления:
Свойство ПараметрОтборПо
Регистратору 124
Регистр оборотов 115
Регистр остатков 108
РегистрНаколенияМенеджер:
Метод Выбрать 98
РегистрНакопленияВыборка:
Метод Слудующий 102
Период 133
Регистратор 134
Свойство Активность 130
Свойство ВидДвижения 131
Свойство НомерСтроки 132
РегистрНакопленияМенеджер:
Метод ВыбратьПоРегистратору 101
Метод Обороты 115
Метод Остатки 108
Метод ПолучитьМакет 126
Метод ПолучитьФормуСписка 122
Метод СоздатьНаборЗаписей 137
ПолучитьФорму 122
РегистрНакопленияНаборЗаписей:
Метод Выгрузить 140
Метод ВыгрузитьКолонку 120
Метод ДобавитьПриход 140
Метод ДобавитьРасход 140
Метод Загрузить 140
499
Метод Записать 137, 139
Метод Итог 109
Метод Очистить 139
Метод Получить 139
Метод УстановитьАктивность 130
РегистрНакопленияСписок:
Колонки 136
Свойство Отбор 134
Свойство Порядок 135
РегистрСведенийМенеджер
Метод Выбрать 226, 227, 229
Метод ВыбратьПоРегистратору 231
Метод Получить 232
Метод ПолучитьМакет 240
Метод ПолучитьПоследнее 229
Метод ПолучитьФорму 235
Метод ПолучитьФорму
РедактированияЗаписи 235, 238
Метод ПолучитьФормуСписка 239
Метод СоздатьМенеджерЗаписи 252
Метод СрезПервых 244
Метод СрезПоследних 245
ПолучитьПервое 229
РегистрСведенийМенеджерЗаписи:
Метод Выбран 253
Метод Записать 252
Метод Прочитать 253
Метод Удалить 253
РегистрСведенийНаборЗаписей:
Метод Записать 254
Метод Количество 254
Регистры накопления 97
Взаиморасчеты
СКонтрагентами 199, 200, 201
ВнутренниеЗаказы 174, 195, 196, 198
ДвижениеДенежныхСредств 160
ДенежныеСредства
Компании 161, 162
ЗаказыПокупателей 98, 102, 123,
151, 175, 177, 205, 214, 222
ЗаказыПоставщикам 98, 105,
109, 126, 153, 154, 156, 205,
214, 217, 218
ЗаказыПоставщику 108
Закупки 205
500
КонтрагентыВзаиморасчеты
Компании 157, 158, 202, 203, 204
КотрагентыВзаиморасчеты
Компании 205
ОстаткиТоваровКомпании 171,
172, 213, 214, 219, 220
ПартииТоваровКомпании 169, 171,
172, 183, 193
ПартииТоваровКомпании
Отданные 183
ПартииТоваровНаСкладах 100,
106, 111, 165, 167, 168, 188, 189,
190, 191, 205
ПодотчетныеЛицаВзаиморасчеты
Компании 209, 210
Продажи 115, 127, 205
ПродажиКомпании 164
ПродажиСебестоимость 205
РазмещениеЗаказов
Покупателей 151, 177, 205
РеализованныеТовары 181, 182, 183
СуммыЗаказов 211, 212, 213
ТоварыВРезервеНаСкладах 151,
174, 175, 176, 177, 179, 180, 196,
205, 208
ТоварыКПередачеОрганизаций 205
ТоварыКПередачеСо
Складов 176, 205
ТоварыКПолучениюНаСклады 205
ТоварыНаСкладах 176, 187, 191,
193, 205
ТоварыОрганизаций 185, 186, 187
Регистры сведений 225
Адреса 247, 256
ЗначенияСвойствОбъектов 413
Комплектующие
Номенклатуры 177, 260
КурсыВалют 199, 226, 228, 229,
232, 235, 237, 252, 258
ОтветственныеЛица
Компании 233, 246
ПаспортныеДанныеФизЛиц 233,
236, 238, 248, 251
ПраваДоступаПользователей 259
ЦеныКомпании 231, 238, 249, 254
Ïðåäìåòíûé óêàçàòåëü
ЦеныКонтрагентов 250
РегистрыНакопления:
ЗаказыПокупателей 143, 144, 145,
147, 148, 149
Продажи 150
ТоварыНаСкладах 149
С
Системные перечисления:
ВидСравнения 404
КодВозвратаДиалога 406
НаправлениеСортировки 405
ОбходРезультатаЗапроса 404
РежимДиалогаВопрос 406
Создание нового документа 11
Создание нового отчета 330
СправочникВыборка:
Метод УровеньВВыборке 390
СправочникМенеджер:
Метод Выбрать 387
Метод Выбрать
Иерархически 387, 389
Метод НайтиПоКоду 391
Метод НайтиПо
Наименованию 386, 391, 392
Метод НайтиПоРеквизиту 391, 392
Метод ПолучитьФорму 393
Метод ПолучитьФорму
Выбора 393, 394
Метод ПолучитьФормуНового
Элемента 393
Метод ПолучитьФормуНовой
Группы 393
Метод ПолучитьФорму
Списка 393, 394
Метод ПустаяСсылка 386
Метод СоздатьЭлемент 384
НайтиПоНаименованию 37
СправочникОбъект:
Метод Заблокировать 390
Метод Записать 384
Метод Разблокировать 390
Метод Удалить 385
Метод УстановитьПометку
Удаления 386
Ïðåäìåòíûé óêàçàòåëü
СправочникОбъект:
Свойство Владелец 394
Свойство ПометкаУдаления 386
Свойство Предопределенный 395
Свойство ЭтоГруппа 395
Событие Обработка
Заполнения 396, 398
Событие ПередЗаписью 396
Событие ПередУдалением 396, 397
Событие ПриЗаписи 396, 397
Событие ПриКопировании 396, 397
Событие ПриУстановкеНового
Кода 396, 398
СоздатьГруппу 385
СправочникСсылка:
Метод ПолучитьОбъект 386
Т
ТабличныйДокумент 418
Метод Вывести 425
Метод ВывестиВертикальный
РазделительСтраниц 428
Метод ВывестиГоризонтальный
РазделительСтраниц 428
Метод Записать 429
Метод Заполнить 427
Метод НайтиТекст 345
Метод Напечатать 430
Метод Область 344
Метод Показать 420
Метод ПолучитьМакет 425
Метод ПолучитьОбласть 425
Метод Прочитать 429
Свойство АвтоМасштаб 419
Свойство ВыделенныеОбласти 420
Свойство ИмяПараметров
Печати 419
Свойство ИмяПринтера 419
Свойство ИмяСохранения
ПоложенияОкна 419
Свойство Количество
Экземпляров 419
501
Свойство МасштабПечати 419
Свойство Области 422
Свойство ОриентацияСтраницы 419
Свойство Параметры 423
Свойство ПолеСверху 419
Свойство ПолеСлева 419
Свойство ПолеСнизу 419
Свойство ПолеСправа 419
Свойство ТекущаяОбласть 420
Свойство ТолькоПросмотр 419
Свойство ЧерноБелыйПросмотр 419
Свойство Экземпляров
НаСтранице 419
У
Удаление документа 16
Установка периодичности 159
Ф, Ч
Форма выбора документа 28
Форма списка документа 28
Чтение реквизитов документа 35
Я
Язык запросов:
В 79, 81
ВЫРАЗИТЬ 89, 176
ИЕРАРХИЯ 63, 218
ИМЕЮЩИЕ 203
ИТОГИ 90
ИТОГИ ПО 61
ОБЩИЕ 172
ОБЪЕДИНИТЬ 76
ПЕРВЫЕ 78
ПОДОБНО 81
РАЗЛИЧНЫЕ 199
РАЗРЕШЕННЫЕ 72
СОЕДИНЕНИЕ 89
УПОРЯДОЧИТЬ ПО 79
Язык запросов РАЗЛИЧНЫЕ 85
Download