Загрузил Виноградная Лоза

Методическое пособие к СпецКурсу

реклама
Методическое пособие к курсу
"СпецКурс"
Для редакторов и писателей
Используем только стандартные стили, сами стили не меняем.
Картинки должны вписываться в ширину листа, картинки не масштабируем.
Специфические стили (к примеру для расцветки кода) будут добавлены дополнительно.
Работаем в режиме "Советовать":
Пишем обычно, в режиме "Советовать" — не эффективно
Букву ‘ё’ в словах русского языка пишем или не пишем, по желанию; на ‘е’ не исправляем. В
синтаксисе языка 1С буква ‘ё’ не используется никогда.
Для ввода знака тире можно использовать сочетание клавиш ALT-0151.
Для добавления пункта в оглавление необходимо задать заголовок соответствующего
уровня в теле текста. После добавления вернитесь на страницу с оглавлением, кликните по
нему. В левом верхнем углу появится зацикленная стрелка. Кликнув по ней, вы выполните
обновление оглавления. Ваш пункт будет добавлен в оглавление.
Если добавляете материал к занятию, которого ещё нет, копируйте туда шаблон, а затем
редактируйте его.
Здесь, мне кажется, важно принять следующее соглашение: Отвергая — предлагай, предлагая
— действуй. Что значит: вносите правки самостоятельно, не удаляя материал предыдущего
автора и не ждите ответа.
ут же будут добавлены дополнительные соглашения о совместной работе.
Шаблон
Занятие <НомерЗанятия>
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
+Оглавление
Для редакторов и писателей
Шаблон
1
Занятие <НомерЗанятия>
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
2
Оглавление
2
Краткое оглавление (по описаниям из видео)
8
Занятие 001
Краткое содержание
Детальное описание
Понятие “Платформа” и “Конфигурация”
12
2
2
2
2
2
2
2
2
12
12
12
Установка платформы
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Домашнее задание / Самостоятельная работа
Решение.
14
Занятие 002
Краткое содержание
Детальное описание
За что отвечают прикладные объекты
Классификация модулей
Структура модуля
Основы синтаксиса
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
14
Занятие 003
Краткое содержание
19
Детальное описание
20
14
14
14
14
14
14
15
15
16
16
18
18
18
19
19
Самые главные виды объектов в 1С — это документы и отчёты. Вводимые документы
наполняют систему исходными данными. Отчёты позволяют эти данные извлечь и
представить в удобном для восприятия виде.
20
Для повышения эффективности работы системы совместно с этими видами объектов
используются ещё два — регистры и справочники.
20
При создании справочника лучше не ставить галку “ограничение количества уровней”.
Если количество уровней всё-таки задано, то при работе с этим справочником 1С будет
всё время проверять — а не превышено ли это количество, соответственно, это
приведет к лишним обращениям к базе данных и нагрузке на систему. 20
Вопросы к занятию (для самоконтроля)
24
Домашнее задание / Самостоятельная работа
24
Постановка задачи
24
Домашнее задание / Самостоятельная работа
24
Решение.
24
Занятие 004 (Виды хранения справочной информации)
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
24
Занятие 005
Краткое содержание
Детальное описание
27
24
24
27
27
27
27
27
27
27
Вопросы к занятию (для самоконтроля)
43
Домашнее задание / Самостоятельная работа
43
Постановка задачи
Ошибка! Закладка не определена.
Домашнее задание / Самостоятельная работа
43
Постановка задачи
43
Решение.
43
Занятие 006
43
Краткое содержание
43
Детальное описание
44
Понятие документа
44
Нумерация документов
44
Зачем нужно разделять события записи документа и проведения документа?
Вопросы к занятию (для самоконтроля)
45
Домашнее задание / Самостоятельная работа
45
Постановка задачи
45
Домашнее задание / Самостоятельная работа
45
Решение.
45
Занятие 007
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
46
Занятие 008
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
47
44
46
46
46
46
46
46
46
47
47
50
51
51
51
51
Занятие 009
52
Краткое содержание
52
Детальное описание
52
Задача: Создать печатную форму документа Счет на оплату.
52
Модуль менеджера
54
Вопросы к занятию (для самоконтроля)
55
Домашнее задание / Самостоятельная работа
55
Сделайте проверку на наличие строк в таблице документа "Счет на оплату".
Если строк нет, то печатать документ не нужно.
55
Реализуйте это в обеих процедурах, и Печать1() и Печать2().
55
Создайте процедуру печати для расходной накладной. Непроведённые документы не
должны иметь возможность быть распечатанными.
55
Домашнее задание / Самостоятельная работа
Решение.
55
Занятие 010
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Домашнее задание / Самостоятельная работа
Решение.
56
Занятие 011
Краткое содержание
Детальное описание
Оптимизация проведения Расходной накладной
Расчет себестоимости
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
62
Занятие 012
Краткое содержание
Детальное описание
Оптимизация проведения расходной накладной
Блокировки
Разделение итогов:
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
70
Занятие 013
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
80
Занятие 014
Краткое содержание
Детальное описание
Характеристики
Вопросы к занятию (для самоконтроля)
90
Занятие 015
Краткое содержание
Работа с формой: Сохраняем файлы и картинки.
55
56
56
56
56
56
56
62
62
62
67
69
69
70
70
70
70
70
73
75
79
79
80
80
80
80
90
90
90
90
90
91
99
107
107
107
107
Понятие конвертации данных формы, отображение картинок, временное хранилище,
навигационные ссылки.
107
Детальное описание
107
Вопросы к занятию (для самоконтроля)
115
Домашнее задание/Самостоятельное
115
Домашнее задание/Самостоятельное
116
Решение
116
Занятие 016
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
116
Занятие 017
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
123
Занятие 018
Краткое содержание
Детальное описание
Регистр бухгалтерии
Ручные операции
ОСВ (Оборотно-сальдовая ведомость)
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
123
Занятие 019
Краткое содержание
Детальное описание
Регистр бухгалтерии
Механизм субконто
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
136
Занятие 020
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
142
116
116
123
123
123
123
123
123
123
123
123
123
123
123
123
124
125
131
131
131
131
136
136
136
136
142
142
142
142
142
142
153
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
153
Занятие 021
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
156
Занятие 022
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
170
Занятие 023
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
181
Занятие 024
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
192
Занятие 025
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
197
Занятие 026
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
203
153
153
156
156
169
169
169
169
170
170
179
179
180
180
181
181
192
192
192
192
193
193
197
197
197
197
197
198
203
203
203
203
203
203
204
209
210
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
210
Занятие 027
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
210
Занятие 028
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание 10 / Самостоятельная работа
Решение.
218
Занятие 029
Краткое содержание
Детальное описание
Вопросы к занятие (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
229
210
210
210
210
217
218
218
218
218
218
218
226
226
226
226
226
229
229
241
241
241
241
Краткое оглавление (по описаниям из видео)
Занятие 001
Что такое 1С, платформа, конфигурация
Варианты функционирования: файловый, клиент-сервер, прочие (обзорно)
Создание новой базы, знакомство с конфигуратором
Понятие "прикладной объект", стандартное поведение системы
Занятие 002
Модули
Примитивные типы данных
Синтаксис модуля
Объектная сущность
События системы
Занятие 003
Связь прикладных объектов
Константы
Общие модули
Справочники
Синтаксис Если
Вызов серверных модулей
Занятие 004
Справочники. Подчинение элементов ТОЛЬКО.
Подчиненные справочники. Владельцы и Родитель.
Табличные части справочников.
Регистр сведений как объект хранящий дополнительные данные о элементах
справочника.
Периодика сведений.
Занятие 005
Знакомство с конструктором управляемых форм
Обход табличных частей
Команды формы
Клиент-серверная архитектура
Занятие 006
Документы
Формы
Директивы компиляции
Занятие 007
Проведение документов
Регистры накопления
Основы запросов
Создание простейшего отчёта на СКД
Занятие 008
Обусловленное проведение
Работа с отладчиком
Понятие оперативной методики проведения
Запросы: Таблицы, Условия
Занятие 009
Печать документов
Модуль менеджера (Печать2())
Макеты, табличный документ
Занятие 010
Складской учёт Себестоимость
Обход результата запроса
Занятие 011
Оптимизация проведения Расходной накладной (ДокТЧ, дальнейшее многократное
использование Врем. табл. в качестве отборов в Виртуальных таблицах Регистров
накопления + вложенные запросы)
Расчет себестоимости, неоперативное проведение
Запросы: Группировки, Условия
Занятие 012
Оптимизация проведения расходной накладной
Блокировки
Занятие 013
Партионный учет
Классический пример
Методы расчета себестоимости FIFO, LIFO
Занятие 014
Регистры накопления оборотов
Виртуальная таблица регистров накопления оборотов
Агрегаты
Планы видов характеристик
Отчёты с доп. характеристиками
Составные виды характеристик
Занятие 015
Работа с формой: Сохраняем файлы и картинки.
Понятие конвертации данных формы, отображение картинок, временное хранилище,
навигационные ссылки.
Занятие 016
ДанныеЗаполнения.
Ввод на основании.
Параметры формы.
Занятие 017
Конструирование интерфейса
Подсистемы
Занятие 018
Регистр бухгалтерии
Ручные операции
ОСВ
Занятие 019
Регистр бухгалтерии
Механизм субконто
Занятие 020
Регистр бухгалтерии
Признаки учета
Количественный учет
Занятие 021
Регистр бухгалтерии
Признаки учета
Субконто
Занятие 022
Регистр бухгалтерии
Валютный учёт
Занятие 023
Регистр бухгалтерии
Трёх-валютный учёт
Оборотные субконто
Занятие 024
Резюме по механизмам бухгалтерии
Многофирменный учет
Занятие 025
Механизмы расчета
Основы
Регистр расчета
План видов расчета
Занятие 026
Механизмы расчета
Получение данных базы
Перерасчеты
Занятие 027
Механизмы расчета
Получение данных графика
Занятие 028
Механизмы расчета
Расчет данных "по среднему"
Сторно записи
Занятие 029
Механизмы расчета
Разбор теста ДЗ
Резюме основных свойств механизмов расчета
Занятие 030
Механизмы расчета
Графики работы
Расчет по табелю
Занятие 031
Механизмы расчета
Измерения регистра расчета
Получение базы по периоду действия
Занятие 032
Механизмы расчета
Оптимизация хранения данных в регистрах расчета
Приоритеты
Занятие 033
Механизмы расчета. Оптимизация хранения данных в регистрах расчета. Приоритеты
расчета.
Занятие 001
Краткое содержание
На этом занятии мы изучим, что такое платформа и конфигурация. Познакомимся с
установкой 1С:Предприятия на наш компьютер. Научимся работать со списком
информационных баз. Посмотрим размещение основных файлов 1С в файловой системе
Windows. Посмотрим, как выглядит 1С в пользовательском режиме и изучим базовые приемы
работы с конфигуратором.
1. Что такое 1С, платформа, конфигурация
2. Варианты функционирования, файловый, клиент-сервер, прочие (обзорно)
3. Создание новой базы, знакомство с конфигуратором
4. Понятие "прикладной объект", стандартное поведение системы
Детальное описание
Понятие “Платформа” и “Конфигурация”
1С Предприятие — это универсальная система. Это одновременно и среда исполнения, и
среда разработки, включающая в себя различные инструменты и сервисы.
В состав системы входит:
● платформа — это среда для разработки и одновременно среда исполнения
прикладных решений;
● прикладные решения (конфигурации).
Конфигурация представляет собой набор объектов и алгоритмов, описывающих определенную
область автоматизации бизнеса.
Конфигурация — законченное прикладное решение для выполнения определенных учетных
задач.
Конфигурации могут поставляться как типовые (разработаны фирмой 1С), так и разработанные
сторонними разработчиками. Конфигурацию можно изменять. Исключения составляют
конфигурации, изменения в которых запрещены лицензионной политикой разработчика.
У конфигураций 1С есть одно удобное свойство — конфигурируемость, то есть возможность
адаптации типового решения к различным условиям хозяйственной жизни фирмы.
Релиз конфигурации — это небольшая модификация конфигурации, связанная с исправлением
ошибок,
выходом
новых
форм
документов
и
отчетов,
некоторыми
изменениями
в
законодательстве. Редакция выпускается, когда меняется методология программы.
Файловый вариант установки 1С Предприятие — установка происходит в каталог на локальный
ПК с возможностью подключения к каталогу по локальной сети. По завершению установки
создается файл базы данных с расширением *.1CD.
Клиент-серверный вариант — установка происходит на сервере, на этапе установки необходимо
дополнительно устанавливать сервер 1С Предприятие. Необходимо также устанавливать
дополнительное стороннее программное обеспечение “Система управления базами данных”
(СУБД), на текущий момент поддерживается 4 СУБД: MS SQL, PostgreSQL, IBM DB2, Oracle
Database.
1С Предприятие может запускаться в нескольких режимах работы: Тонкий клиент,
Толстый клиент, Web-Клиент.
Конфигурирование возможно только в режиме Толстого клиента.
Толстый клиент — полнофункциональный режим работы, постоянно синхронизируется с
Центральным сервером, для работы в данном режиме необходимо иметь непрерывную связь
с сервером 1С Предприятие.
Тонкий клиент — это своеобразный браузер 1С Предприятие, данный режим также позволяет
работать в полном доступе, кроме конфигурирования. При работе в тонком клиенте нет
необходимости в непрерывной связи, синхронизация между тонким клиентом и сервером
осуществляется с помощью “Данных сеансов”.
Основное отличие тонкого клиента от Web-клиента — возможность подключения внешних
компонент (расширение встроенного функционала) и взаимодействие с оборудованием, что на
данном этапе не всегда возможно в режиме Web-клиента.
Установка платформы
В рамках нашего курса нам достаточно будет скачать дистрибутив для обучения.
Он доступен по адресу в интернете http://online.1c.ru/catalog/free/learning.php. Также можно
скачать дистрибутив, предназначенный для обучения, с сайта для англоязычных
разработчиков https://1cdn.com/user/updates/1c_enterprise_platform_training_version/8_3_12_1714
Вопросы к занятию (для самоконтроля)
Что такое платформа 1С Предприятие?
Что такое конфигурация 1С Предприятие?
Что такое конфигурируемость?
Домашнее задание / Самостоятельная работа
Домашнее задание / Самостоятельная работа
Решение.
Занятие 002
Краткое содержание
1. Модули
2. Примитивные типы данных
3. Синтаксис модуля
4. Объектная сущность
5. События системы
Детальное описание
Стандартное поведение системы не всегда закрывает потребности, поэтому имеет смысл
переопределять поведение системы, добавлять новый функционал, которого нет в системе,
реальным программированием.
1С Предприятие используют для хранения, обработки и получения информации об объекте
учёта. Под объектом учёта следует понимать область автоматизации.
Дерево метаданных — это сведения об объектах, используемых системой.
За что отвечают прикладные объекты
Константы — сущность в 1С предприятии, которая позволяет сохранять условно-постоянные
величины.
Справочники — это открытые каталоги. Аналоги реальных списков в жизни. Для понимания,
нужен ли нам справочник, есть 2 признака: это список в реальной жизни (список контактов),
второй неявный признак — использование значения элемента справочника в других объектах
системы. Если хотя бы один из этих признаков присутствует, то это справочник.
Перечисления — закрытые каталоги.
Документы — это основа жизни любого предприятия. Это фиксация какого-то
совершившегося действия в бизнесе. Документы меняют различные показатели. Они очень
схожи с документами в реальной жизни.
Журналы документов хранят списки документов.
Регистры хранят показатели, которые изменяются документами.
Планы видов чего-то. Планы чего угодно привязываются как вспомогательные штуки к
регистрам. Регистр бухгалтерии — план счетов
Отчёты — это то, что реально выходит из базы данных, агрегированная информация,
представленная в удобном для восприятия виде.
Обработки выполняют различные действия с данными.
Задачи чем-то похожи на документы, но если документы фиксируют факт свершившийся
операции, то задачи планируют будущие действия.
Бизнес-процессы — это логика.
Внешние источники данных — эта штука сделана специально для того, чтобы разработчики
могли подключать, например, Excel-файл для анализа данных и оперировать его данными в
терминологии 1С.
Ветка Общие — это общие штуки.
У всех этих прикладных объектов есть стандартные реквизиты, стандартное поведение. Но мы
его можем переопределить.
Смотрим справочник. У него есть модуль объекта и модуль менеджера.
Зачем нужны модули? Их много. Даже в корне конфигурации есть модули.
Модуль нам нужен, чтобы описывать две вещи:
1.Работа с поведением пользователя
2.Бизнес-логика
Классификация модулей
Модули
Модули конфигурации
Модуль приложения — что делается при старте конфы
Модуль сеанса
Модуль внешнего соединения — обработка данных, получаемых из внешних
источников, например, от кассы
Общие модули
Репозитории общих процедур и функций — библиотеки
Хранилища подписок на события
Функции веб-сервисов
Объектные модули
Модуль объекта — действия с элементами объекта
Модуль менеджера
Модули формы
Модули команд
Структура модуля
Переменные модуля
Перем А;
Описание процедур и функций
Процедура
КонецПроцедуры
Б()
Область инициализации переменных
А=1;
Модуль — это просто текст.
Система 1С Предприятие является интерпретатором с предварительной компиляцией.
Предусмотрена предварительная компиляция модуля
Работа компилятора
Таблица переменных
Таблица процедур и функций
Основы синтаксиса
Самое главное в модуле — это комментарий.
Задача — познакомиться с примитивными типами значений и базовым синтаксисом системы 1С
Предприятия.
Одно из самых простых типов значений — булево.
Перем А,Б; //тип значения неопределенно — неинициализированное значение переменной
В
А
А
=
Неопределено;
=
=
//Булево
Истина;
Ложь;
В
=
Неопределено;
//Любой другой тип значения можно сравнивать с типом значения неопределено
//Неявное
преобразование
типов
А
=
НЕ
Истина;
//
И
ИЛИ
//Число
А
=
10;
А
=
10;
//число
неявное
преобразование
типов
А
=
-10;
А
=
0.987;
А
=
10
+
1;
А
=
10
%
3;
//остаток
1
//
булево
//
операцией
сравнения
получить
булево
А
=
10>3;
//
<
>
<=
>=
<>
А
=
10
>
3
и
8
=
1;
//строка (в двойных кавычках)
А = "Строка1";
//конкатенация
(слияние)
строк
А = "Строка1" + "Строка2";
//многострочная строка (комментарий в разрыв вставлять нельзя, иначе эти символы
войдут
в
//строку)
А
=
"Строка1
| Строка2";
А
=
"Строка1"
"Строка2";
Осталось
=
10;
А
=
"Осталось"
+
Осталось
+
"яблок";
А = Осталось + "яблок"; //Ошибка. Здесь не конкатенация, а операция сложения (т.к.
первый
//операнд число). Строку “яблок” система не может привести
к
числу
А
=
100
+
Осталось;
//110
//Дата
А
=
’20181027142400’;
А
=
’2018-10-27
14:24:00’;
А = ’2018 10 27’; //20018 10 27 00:00:00 (у даты время существует всегда, хоть мы
его и
//не
пишем)
А
=
ТекущаяДата();
Б = А + 86400; //добавили сутки (количество секунд в сутках)
А
=
ТекущаяДата()
—
‘19591222’;
Б
=
-1222
//NULL. Это когда в принципе не может быть ни какого значения (дата рождения у
папки
сотрудники)
А = NULL;
Есть еще тип данных: Тип типа тип
События описываются в виде процедур. Эти События могут быть предопределены системой 1С
Предприятие. Обработчики событий — процедуры и функции.
Процедура
А
КонецПроцедуры
=
Рассчитать(Парам1,
Парам1
+
Парам2)
Парам2;
Процедура ничего не возвращает в точку вызова, а функция возвращает
Функция
А
Возврат
КонецФункции
=
Рассчитать(10,20);
П
Разница(Парам1,
парам1
-
=
Процедура
Сообщить
КонецПроцедуры
Парам2)
парам2;
А;
Разница(40,30);
ПриНачалеРаботыСистемы()
(ТекущаяДата());
Домашнее задание / Самостоятельная работа
Постановка задачи
Вывести при запуске программы, сколько дней осталось до следующего дня рождения и
сколько секунд прошло с рождения.
Домашнее задание / Самостоятельная работа
Процедура ПриНачалеРаботыСистемы()
Сообщить("Добро пожаловать, сегодня " + ТекущаяДата());
НазваниеЗаполнено = ВызовСервера.КонстантаЗаполнена();
Если НЕ НазваниеЗаполнено Тогда
Сообщить("Введите сведения об организации!");
КонецЕсли;
ДР = '19801112';
Сегодня = НачалоДня(ТекущаяДата());
СекундПрошло = Сегодня - ДР;
Сообщить("Со дня рождения прошло: " + СекундПрошло);
СледующийДР = '20181112';
ДнейДО = Цел((СледующийДР - Сегодня) / 86400);
Сообщить("До следующего ДР осталось " + ДнейДО + " дней");
КонецПроцедуры
Решение.
Занятие 003
Краткое содержание
1. Связь прикладных объектов
2. Константы
3. Общие модули
4. Справочники
5. Синтаксис Если
6. Вызов серверных модулей
Детальное описание
Самые главные виды объектов в 1С — это документы и отчёты. Вводимые документы
наполняют систему исходными данными. Отчёты позволяют эти данные извлечь и
представить в удобном для восприятия виде.
Для повышения эффективности работы системы совместно с этими видами объектов
используются ещё два — регистры и справочники.
При создании справочника лучше не ставить галку “ограничение количества уровней”.
Если количество уровней всё-таки задано, то при работе с этим справочником 1С будет всё
время проверять — а не превышено ли это количество, соответственно, это приведет к
лишним обращениям к базе данных и нагрузке на систему.
Связь прикладных объектов
Диаграмма взаимодействия прикладных объектов
Самое главное у нас — отчёты.
Отчёт создается на основе чего-то. Чего? Что-то должно менять показатели.
Для ввода данных в базу есть документы.
Т.е. мы вводим документы, на основе документов получаем отчёты.
Это нормальная рабочая схема, но она не очень эффективна.
Поэтому для накопления показателей используются регистры. Т.е. регистры — это по сути
агрегаторы, они накапливают информацию с помощью документов, т.е. документ накапливает
записи в регистр, а на основе регистров мы формируем отчёты. Это рабочая схема системы
1С Предприятия.
Кроме того, регистры позволяют абстрагироваться от документов. Отчёты абсолютно
абстрагированы от документов, и они формируют данные только из регистров. А как уж в
регистры попадет информация, это уж как мы придумаем. Это основная логика поведения
учетной системы, в нашем случае 1С Предприятия.
Документы — это регистраторы изменений показателей, регистры — это агрегаторы,
накапливающие сами показатели, хранятся в табличках, удобных для извлечения данных,
отчеты — механизм для получения данных. Отчёты — это то, что мы с вами будем
вытаскивать.
У нас ещё есть куча всяких метаданных, справочники там, планы чего-то, ещё что-то.
Я попробую вкратце пояснить. (Я — Павел)
Справочники нам зачем нужны — в документах, регистрах, отчетах нужно вывести, например,
название товара — для этих целей накапливается справочная информация — справочники,
перечисления — используются и для документов и для регистров и для отчётов — справочная
сущность.
Различные планы чего угодно — это вспомогательные объекты для регистров; для
бухгалтерии — это план счетов, по сути это тоже справочники, они нужны для специального
функционирования регистров. Для отчётов есть механизмы — СКД, диаграммы.
На переднем плане схемы — документы, регистры, отчёты. Справочники, перечисления,
планы чего угодно на заднем плане.
Свойства прикладных объектов на примере констант и справочников.
Константа — условно-постоянная величина.
Настройка вывода свойств — закладками или списком — выбор в контекстном меню.
Каждая константа хранится в отдельной таблице. Это уменьшило проблему блокировок.
Для всех объектов, которые мы будем заполнять, существуют обязательные свойства: имя,
синоним, комментарий.
Имя — строка 250 символов.
Если у константы тип символьный, допустимая длина может быть переменной или
фиксированной. При использовании фиксированной длины читаемое значение константы
дополняется пробелами до этой самой длины.
Допустимая длина — настройка, которая влияет на чтение данных из базы. Неограниченная
длина — это значит не будет в константе хранится само значение, оно будет храниться в
другой таблице. Таблица “длинные строки” выглядит примерно так:
ID объекта
N записи
Значение
Спр.Сотрудники.Иванов 0
Новосибирск
Спр.Сотрудники.Иванов 1
Красный проспект
Т.е. это три колонки, в первой ID того объекта, для которого сохраняется значение, будет
индекс записи и значение. Поле “значение” по сути и хранит нам значение константы. Если
вдруг здесь не хватило одной строки, система создаст ещё одну. Поле Значение хранит строки
длиной 100 знаков. С одной стороны это хорошо, с другой — накладывает определённые
ограничения. Ограничения такие — строка не индексируется никоим образом, запросы по ним
построить можно, только если такие константы типизировать, привести к конкретной строке.
Плюс в следующем — ни одного байта в базе храниться не будет, если поле не заполнено.
Закладка представление — отображение константы в форме.
Многострочный режим, например, для ввода адреса. Расширенное редактирование позволит
пользователю вводить знаки табуляции и использовать поиск (Ctrl+F) по этому полю.
Как константы читать?
Откуда запускать проверку по константе? — Модуль управляемого приложения.
Он работает на стороне клиента и доступа к константам на его стороне нет. Нужно вызвать
сервер. Как вызвать сервер? Для этого можно использовать общий модуль.
Создадим один общий модуль Вызов сервера.
Свойства сервер, вызов сервера галочки поставить.
И именно в нём проверю, заполнена константа НаименованиеОрганизации или нет
Функция КонстантаЗаполнена() Экспорт
А = Константы.НазваниеОрганизации.Получить();
Если ПустаяСтрока(А) Тогда
Результат = Ложь;
Иначе
Результат = Истина;
КонецЕсли;
Возврат Результат;
КонецФункции
Теперь нам нужно вызвать эту функцию из модуля управляемого приложения.
Это будет со стороны клиента запрос к серверу, чтобы система вернула на основании этого
запроса какой-нибудь результат. По сути, если это сравнивать с браузером, это запрос к Webсерверу.
Процедура ПриНачалеРаботыСистемы()
НазваниеЗаполнено = ВызовСервера.КонстантаЗаполнена();
//Вызов+Ctrl+пробел- система сама подсказочку дает, дальше название
//функции заполнит
Если НЕ НазваниеЗаполнено Тогда
Сообщить("Введите сведения об организации!");
КонецЕсли;
КонецПроцедуры
МенеджерЗначений Прочитать создаём, если хотим изменять эту константу — установится
блокировка, когда вы будете работать с этим значением. Не нужно напрягать систему, если
изменение вносить не требуется. Чтение это одно, а изменение — другое. Если необходимо
менять что-то, тогда нужен менеджер значений Прочитать. Тогда можно и прочитать и
поменять. Если внесение изменений не требуется, достаточно только Получить.
Дата — это число, количество секунд с 01.01.1980
Директивы компиляции есть только у форм и команд.
ПустаяСтрока и «» — есть разница. Если в пустой строке будут незначащие символы
(пробелы, знаки табуляции ) — метод ПустаяСтрока вернет ИСТИНА, а если проверять «» —
вернёт ИСТИНУ только если реально там ничего нет, а если там есть пробел и т.п., то вернёт
ЛОЖЬ.
Справочники
Создам два справочника. Первый — Контрагенты. Т.е. наши покупатели, поставщики,
учредители и др. И посмотрим, что за таблица создаётся.
Представление, расширенное представление объекта, представление списка, расширенное
представление списка — всё то, что будет отображаться в управляемом интерфейсе при
автогенерации форм.
Представление объекта — Контрагент. Тогда Справочник Контрагенты, Создать кого —
Контрагент.
Иерархия
Контрагенты — иерархия групп и элементов.
Иерархия элементов, классика — справочник Подразделения.
Ограничение количества уровней иерархии — если поставить галку, это будет тормозить
систему. Система при записи, при копировании будет проверять — а не превышено ли
количество уровней. Поэтому, если не нужно, не ставьте. Если нужно, ставьте.
Из чего сейчас состоит справочник. Покажу. Кнопочка “Стандартные реквизиты”. Если нажать
эту кнопочку и перевернуть содержимое на 90 градусов — это структура нашей таблицы. Это
так выглядит наш справочник с его предопределенными стандартными реквизитами. Ссылка,
код, наименование, владелец, родитель… Это всё стандартно, что есть в любом справочнике.
Код, наименование настраиваются в свойствах справочника.
Родитель — ссылка на группу, которой принадлежит текущий элемент.
Можно изменить наименование стандартного реквизита, которое увидит пользователь. Для
этого изменим синоним реквизита. Например, пропишем Синоним стандартного реквизита
Родитель как Группа.
Предопределенный — мы можем создать предопределенные элементы, которые будут
созданы в момент запуска
В Реквизиты добавим Телефон — символьное, размер 14. В свойствах можно заполнить Маску
: +9(999)999-9999.
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 004 (Виды хранения справочной информации)
Краткое содержание
1. Справочники. Подчинение элементов ТОЛЬКО.
2. Подчиненные справочники. Владельцы и Родитель.
3. Табличные части справочников.
4. Регистр сведений как объект, хранящий дополнительные данные о элементах
справочника.
5. Периодика сведений.
Детальное описание
У справочника может быть табличная часть (общепринятое сокращение — ТЧ).
ТЧ похожа на подчинённый справочник, но с существенными отличиями:
- невозможно напрямую обратиться к элементу ТЧ справочника, а только указав сначала
элемент справочника, которому принадлежит запись ТЧ;
- записи в ТЧ можно начать вводить, даже не сохранив сам “родительский” элемент
справочника.
Регистры расчёта хранят 2 показателя:
1)Показатель базы
2)Показатель данных графика.
Регистр расчёта хранит показатель состояния, единственный регистр который не исчисляется
числом, может быть любой. (напр. Настрой сотрудника)
Непериодический = никогда не меняется.
Периодический = имеет период.
На прошлом занятии мы остановились на такой штуке, как справочники, но мы как-то не очень,
если честно, их и посмотрели. Давайте попробуем посмотреть все возможные варианты
справочников и все их свойства разберём более детально. Кодить будем, но совсем чуть-чуть.
И посмотрим один оператор, оператор цикла, точнее их несколько. Т.е. он один, но их
несколько.
Контрагентов мы сделали, теперь добавим еще справочников. Справочник Подразделения,
справочник с иерархией по элементам. Элементарный справочник, кроме наименований, в нем
ничего и не будет. И мы посмотрим свойства этого справочника, посмотрим, чем же они будут
отличаться от свойств справочника с контрагентами.
Подразделения:
Администрация
Бухгалтерия
АХО
Производство
Цех №1
Цех №2.
Обратите внимание, всё это — элементы, но элементы подчинены другим элементам.
Цех №1 — его родитель — это Производство.
У контрагентов это выглядит совсем по-другому. Так вот, там есть колонки “родитель” и
“группа”. Таблица одна. И элементы и группы это одно и тоже. Это просто строки в таблице.
Мы можем посмотреть список стандартных реквизитов справочника, и если эту таблицу
перевернем на 90 градусов, у нас получится таки перечень колонок, которые сейчас в этом
справочнике существуют. Есть колоночки Родитель и ЭтоГруппа. ЭтоГруппа — булево и как
раз и отвечает, группа это или элемент и второе — какие дополнительные реквизиты,
например, телефон, будут относиться к элементам или группам. Справочник.Контрагенты у
элемента реквизит Телефон — в свойствах этого реквизита на закладке Использование есть
одноименное поле Использование — для элемента, для группы, для группы и элемента.
Так мы определяем, реквизит заполняется только для элемента, только для группы или для
обоих этих уровней (для элемента и группы).
Для чего нужно использовать подчинение элементов элементом. Это интересная вещь,
например, в сборке комплектов. Когда один агрегат состоит из других агрегатов, которые
состоят из других узлов, которые состоят из еще чего-то, но каждый из них является объектом
аналитического учета.
Все взаиморасчеты с контрагентами будут в разрезе договоров. Для этого я (Павел) хочу
создать справочник Договоры и наделить именно подчинению контрагентам. На закладке
Владельцы мы можем владельца указать. Т.е. кто у нас владельцы — Контрагенты будут у нас
владеть этим справочником и у справочника Договоры появляется поле Владелец, в нем будет
ID элемента другого справочника. Для договоров могут быть владельцы не только из
справочника контрагенты, но и из других справочников. К примеру может быть справочник
ФизЛица. Т.е. Владелец — это ссылка на элемент другого справочника, которому принадлежат
договоры и этих владельцев может быть много, но для конкретного договора владелец может
быть только один.
Так работает режим подчинения. При указании владельцев справочника можно указать
использование подчинения.
В стандартный интерфейс справочника-владельца Контрагенты (в его форме элемента
Контрагент) добавилась стандартная параметризуемая команда Договоры:
Ссылка — это уникальный идентификатор ID.
ID есть не только у элементов. Ссылка — название строки табличной части справочника или
документа, объектной сущности. Не все объекты обладают ссылками.
Если у пользователя не будет прав, он увидит не договор, а ID.
Еще один справочник сделаем с сотрудниками. На закладке данные появится табличная часть
— дети сотрудников. Справочник — это таблица. Дети сотрудников будет еще одна таблица,
которая будет принадлежать этому же самому справочнику. У детей будет ссылка на сам
элемент справочника. Фишка в том, что в отличие от подчинения контрагенты+договоры
договор в качестве ссылки имеет свой собственный ID, а вот у детей сотрудников
собственного ID не будет, они принадлежат сотрудникам и сослаться конкретно на детей гдето в объектах аналитики невозможно будет. Т.е. это просто дополнительные сведения о
сотруднике, никак не отдельная логическая сущность.
В стандартных реквизитах справочника можно указывать синоним наименования.
Регистры.
Регистры накапливают показатели. Их много, четыре типа регистров и хранят они много
показателей. Для нас важно разделить показатели по категориям, важно знать в каком типе
регистра хранить какой показатель. Что такое показатель? Возьмем самое простое — остатки
товаров. Сколько товаров где у меня осталось. Это хранится в регистре накопления. Как мы
поймем что именно это надо сохранять в регистре накопления? Что такое остаток? Есть
показатель остатков, он характеризуется всегда числом. И этот показатель всегда зависит от
прошлых показаний. Если показатель зависит от прошлых показаний, значит это показатель
остатков. Бывает еще показатель оборотов. Тоже в числе измеряется. Но при этом не зависит
от прошлых показаний и у него нет приращения — «+», «-». И это тоже будет регистр
накопления. Регистр накопления и регистр бухгалтерии хранят эти показатели — остатки и
обороты, в регистре бухгалтерии просто в специфическом виде. Регистры расчета хранят два
показателя — показатель базы, показатель данных графика.
Регистры сведений хранят показатель состояния. Это единственный регистр, показатель
которого может не исчисляется числом. Он может быть любой. Может быть непериодическим,
никогда не меняется, так и иметь какую-то периодику.
Будем для каждого сотрудника сохранять его адрес в регистре сведений.
РС СведенияОСотрудниках.
Периодичность — в пределах дня. Основной отбор по периоду — если включен, входит в
индексируемые поля. Это будет влиять на скорость поиска данных по периоду.
То, что мы храним в регистре — это Ресурсы.
Ресурс — Адрес
То, в разрезе чего мы храним регистр — называется Измерение.
Измерение — Сотрудник.
Основной отбор — поле Сотрудник тоже попадет в индекс. Поле ведущее это довольно
интересная штука — значение этого измерения владеет записями регистра. Теперь по-русски.
8 записей адресов сотрудника. Сотрудника удаляем, система автоматом удаляет его адреса.
Если эта галка не стоит, значит записи регистра являются самостоятельными и не удаляются
автоматом при удалении сотрудника. Сотрудника не сможете удалить, пока не очистите
принудительно его адреса.
Регистры сведений позволяют хранить историю изменений реквизитов.
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 005
Краткое содержание
1. Знакомство с конструктором управляемых форм
2. Обход табличных частей
3. Команды формы
4. Клиент-серверная архитектура
Детальное описание
Формы предназначены для отображения и редактирования информации.
Управляемые формы, в отличие от старых Обычных форм, адаптивны, т.е. подстраиваются под
различные факторы, такие как настройка ролей пользователя и настройки самого пользователя.
По умолчанию формы объектов создаются системой автоматически. Вид созданной системой
формы зависит от настроек объекта.
Форма нигде не хранится. Она генерируется каждый раз при обращении к ней.
Пользователь может в режиме 1С:Предприятия изменить форму по своему желанию, вызвав
меню настройки формы через меню Еще — Изменить форму.
Можно добавлять или убирать с
то элементы, упорядочивать их,
группы, изменять оформление и
изменения
будут
применяться
интерфейсе
конкретного
пользователя.
Важно:
Чтобы
вернуть
первоначальные настройки формы,
меню настройки формы нажать
Установить
стандартные
формы какиесоздавая
т.д.
Эти
только
в
необходимо в
Ещё
—
настройки:
;
Для изменения стандартного поведения формы необходимо создать форму в режиме
конфигуратора.
Форма — это отдельный объект системы, он не принадлежит никакому другому объекту 1С.
Принадлежность формы, указанная в ветви конфигурации, носит информативный логический
характер. Форму можно создавать как у объекта конфигурации, например справочника или
документа, так и в ветви «Общие»:
При создании формы сначала открывается Конструктор формы объекта — это помощник
выбора первоначальных настроек. Здесь можно выбрать тип формы, который определяет её
стандартное поведение. Если нужно создать форму с «нуля», то необходимо выбрать тип
«Произвольная форма»:
Флаг «Назначить форму основной» означает, что форма будет открываться по умолчанию. Если
основная форма для объекта не выбрана, то никакая созданная форма не будет вызвана.
Откроется автогенерируемая форма.
Важно: Чтобы удалить основную форму, необходимо на закладке «Формы» предварительно
очистить поле с указанием этой формы, иначе система не позволит это сделать:
Работа с формами.
Выполнять различные действия с формами(создавать, удалять, изменять …) можно с
помощью панели или контекстного меню вызываемого по щелчку правой кнопкой мыши.
При созданий формы ее можно сделать «основной», в этом случае она будет открываться,
как форма по умолчанию для данного объекта, если разработчик не предусмотрел иного
поведения.
Основной формой элемента справочника Договоры в данном примере является
ФормаЭлемента1, если пользователь будет открывать элемент справочника, он будет
видеть именно эту форму. Для форм Списка и Выбора основная форма не выбрана, и они
будут генерироваться системой автоматически.
Если не выбрана основная форма, то при нажатии кнопки «лупа» она будет создана.
И, в отличие от «простого» создания формы, флаг «Назначить форму основной» будет
взведён, и его нельзя снять.
При попытке удалить основную форму мы получим сообщение об ошибке «Объект не может
быть удален, так как на него имеется ссылка в других объектах!», а также в «Служебные
сообщения» будет выведено подробное пояснение.
Для того, чтобы форма перестала быть основной, нужно нажать на кнопку «X». Сама форма
при этом удалена не будет.
Либо можно назначить другую форму основной, выбрав её из списка, который появится при
нажатии на кнопку «…».
Если основная форма заполнена, то при нажатии на кнопку «лупа» она откроется в
редакторе форм.
После нажатия на кнопку «готово» будет открыт конструктор управляемой формы:
В нижнем окне конструктора расположен образец формы (можно открыть отдельно
Ctrl + R). В зависимости от различных условий форма в режиме 1С:Предприятия может
выглядеть по другому.
Состав формы описан на пяти вкладках:
❏ Элементы
❏ Командный интерфейс
❏ Реквизиты
❏ Команды
❏ Параметры
На закладке “Реквизиты” есть колонки:
● Реквизит
● Значок формы
● Использовать всегда
● Тип
Тип
Некоторые типы заключены в скобки. Это значит, что этот тип значения не может быть передан
клиенту со стороны сервера. В таком случае система произведет конвертацию этого типа в тип
формы.
Использовать всегда
Флаг определяет, будет ли доступен на стороне клиента этот реквизит.
Если флаг снят, то когда пользователь в режиме 1С:Предприятия отключит на форме этот
реквизит, программно обратиться к этому реквизиту будет нельзя.
Это используется для оптимизации трафика.
Значок формы
Показывает, что реквизит отображается на форме.
Чтобы отобразить реквизит на форме, его нужно перенести мышкой из закладки «Реквизиты»
на закладку «Элементы». На закладке «Элементы» можно настраивать взаимное расположение
элементов, создавая группы и меняя их свойства.
Для быстрого перехода между связанными элементами и реквизитами формы нужно, выделив
нужный элемент или реквизит, нажать клавишу F12.
Система 1С:Предприятия событийная, поэтому поведение формы настраивается с помощью
событий.
События формы описываются в свойствах элементов формы на закладке «События».
При нажатии на значок «лупа» возле поля события (например ПриИзменении) система
предложит создать необходимые процедуры в модуле формы. При создании процедур
указываются необходимые директивы компиляции:
● &НаКлиенте
● &НаСервере
● &НаСервереБезКонтекста
Советы при написании процедур в модуле формы:
Все обработчики событий должны иметь свою автоматически сгенерированную функцию в
модуле формы. Если логика одинаковая, то она описывается отдельно и вызывается.
Если в нескольких обработчиках событий вызывается одна и та же логика, её нужно описать в
виде отдельной процедуры или функции и вызывать из того или иного обработчика событий.
Имена обработчиков, например КонтрагентПриИзменении, КонтрагентСоздание и т. д.
следует оставлять по умолчанию, чтобы другим разработчикам было сразу понятно назначение
процедуры.
Согласно стандартам разработки фирмы «1С» у каждого события должен быть свой обработчик.
Не следует назначать одну и ту же процедуру разным событиям. Если при разных событиях
необходимо выполнить одинаковые действия, то для каждого события создается свой
обработчик и из него вызывается общая процедура.
Например, если необходимо автоматически изменить поле формы «ФИО» (Наименование), при
изменении полей формы «Фамилия», «Имя» или «Отчество», то в этом случае следует
поступать следующим образом:
– создается отдельная процедура
ОбновитьФИО(), которая меняет поле «ФИО»
(Наименование):
&НаКлиенте
Процедура ОбновитьФИО()
Объект.Наименование = Объект.Фамилия + “ “ + Лев(Объект.Имя, 1) + “.” +
Лев(Объект.Отчество, 1) + “.”;
КонецПроцедуры
– для каждого из полей «Фамилия», «Имя» и «Отчество» создается своя процедураобработчик:
– из каждой процедуры-обработчика вызывается процедура ОбновитьФИО()
&НаКлиенте
Процедура ФамилияПриИзменении(Элемент)
ОбновитьФИО();
КонецПроцедуры
&НаКлиенте
Процедура ИмяПриИзменении(Элемент)
ОбновитьФИО();
КонецПроцедуры
&НаКлиенте
Процедура ОтчествоПриИзменении(Элемент)
ОбновитьФИО();
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьФИО()
Объект.Наименование = Объект.Фамилия + “ “ + Лев(Объект.Имя, 1) + “.” +
Лев(Объект.Отчество, 1) + “.”;
КонецПроцедуры
В данном примере назначать одну и ту же процедуру ОбновитьФИО() обработчиком события
одновременно для полей «Фамилия», «Имя» или «Отчество», будет нарушением стандартов
разработки фирмы «1С», хотя ошибок при отработке событий не возникнет:
К элементам формы обращаются, если хотят изменить «дизайн»:
● Доступность
● Отображение
● Найти поле, с которым работает пользователь
Чтобы на клиенте получить какое-либо предопределённое значение, удобно использовать
Метод ПредопределенноеЗначение(…). Он возвращает предопределённое значение,
переданное (как строка) в качестве аргумента. Можно использовать в форме, на клиенте.
Подробнее об использовании метода ПредопределенноеЗначение() можно прочитать в синтакспомощнике: Глобальный контекст — Функции обращения к конфигурации.
Клиент-серверная архитектура
Процесс создания формы:
1. Из таблиц базы данных создается копия необходимых данных
2. Из копии данных создается Прикладной объект
3. Из Прикладного объекта создается Прикладной объект формы
4. Из Прикладного объекта формы создаются элементы, реквизиты, параметры Формы и
сама Форма
5. Форма передается на клиент
6. Уничтожаются Прикладные объекты
Процесс записи формы:
1. Данные формы передаются с клиента на сервер (и синхронизирются?)
2. На сервере из переданных данных конструируется Прикладной объект
3. Из Прикладного объекта создаются записи
4. Созданные записи записываются в базу данных
Данные формы ≠ Прикладной объект
При создании формы на сервере все данные объекта (~Справочник.Объект) конвертируются в
примитивные типы. Для непримитивных типов в данные формы передаётся структура.
Например, перечисление пол Мужской:
Ключ
Значение
ID
5fb664df5g1d6fg61
Представление
"Мужской"
Поэтому данные формы содержат не данные объекта, а сконвертированные значения.
После формирования формы на сервере с данными формы она передаётся на клиент. После
передачи форма существует одновременно на клиенте и сервере.
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
320
Домашнее задание / Самостоятельная работа
Постановка задачи
Описание, ссылка на решение в конце документа
Необходимо по окончанию отчества, в форме элемента справочника "Сотрудники" попытаться
определить его пол.
"ич" — Мужской
"на" — Женский
при других вариантах пол оставить пустым.
Для присвоения пола используйте метод ПредопределенноеЗначение(), к примеру так:
ПредопределенноеЗначение("Перечисление.ПолСотрудника.Мужской");
Для работы со строками изучите методы Лев(), Прав, СокрЛ() и другие методы работы со
строками.
Решение.
Занятие 006
Краткое содержание
В этом занятии проходим создание простейшего документа с реквизитами и табличной частью.
Создаём форму документа, изучаем понятие “контекст формы” и использование директив
компиляции форм.
1. Документы
2. Формы
3. Директивы компиляции
Детальное описание
Понятие документа
Документ регистрирует событие и, в отличие от справочника, имеет отметку времени —
стандартный реквизит “Дата” и отметку на оси времени “МоментВремени”. Документ имеет
важное свойство — возможность проведения, т.е. отражение факта хозяйственной операции в
регистрах. Непроведённый документ фактически является черновиком.
Документ — это регистратор изменений показателей учета. Он имеет полную аналогию с
бумажным документом на предприятии, документ регистрирует события.
Документу важно, когда он существует, в отличие, например, от справочника, элементы
которого просто существуют или не существуют.
Документ имеет дату, время, момент времени и ссылку.
Документ проводится — это значит, что он регистрирует движения и меняет регистры.
Документы на оси времени чётко упорядочены относительно друг друга (даже в пределах 1
секунды).
По своей сути в базе данных документ — это та же таблица, но имеющая свойство хронологии
во времени. Если у документа есть табличная часть, то это ещё одна таблица в базе данных.
Стандартные реквизиты табличной части документа: номер строки, ссылка. Ссылка ссылается
на сам документ, которому она принадлежит.
Нумерация документов
Задаётся в свойствах конфигурации на закладке «Совместимость», пункт «Режим
автонумерации». Может принимать два значения:
● Не освобождать автоматически
В момент создания документа номер присвоился и не освобождается, даже если документ
потом не был записан (используется в типовых конфигурациях по умолчанию).
● Освобождать автоматически
Если документ не сохраняется при создании, то его номер освобождается (имеем
пересортицу).
Во всех типовых конфигурациях номер документа имеет строковый тип.
Если хотим, чтобы документы разных видов имели единую нумерацию, тогда им нужно
присвоить один и тот же предварительно созданный нумератор в поле «Нумератор».
Зачем нужно разделять события записи документа и проведения документа?
По сути во время записи можно делать всё то же самое, что и при проведении документа. Но
эти события нужно разделять, чтобы можно было сохранить черновик документа, не меняя
показатели (регистры) системы.
При использовании директивы компиляции &НаКлиенте управляемая форма на клиенте будет
содержать содержит лишь данные формы. Для получения детальной информации из базы
данных необходимо обращаться на сервер, желательно с директивой
&НаСервереБезКонтекста. Директиву &НаСервере используем в случаях необходимости
работы с контекстом формы и доступом к базе данных одновременно. Директива компиляции
&НаКлиентеНаСервереБезКонтекста используется редко, в основном, чтобы не дублировать
одинаковые процедуры и для возможности вызова как из клиентских процедур, так и из
серверных.
Для создания дополнительных кнопок на форме используются команды формы.
«Форма Объекта» (документа, справочника и т.д) — нигде не хранится (ни на жёстком
диске ни в базе данных), а открывается интерактивно* и служит для взаимодействия
пользователя с базой данных (БД).
При этом «Форма Объекта» одновременно существует в оперативной памяти компьютера
&НаКлиенте и &НаСервере и служит для чтения и записи данных в/из БД.
«Форму Объекта» можно не создавать, если нет необходимости описывать (программировать)
какое-либо действие, которое будет происходить при работе пользователя с БД через Форму
Объекта. С помощью «Формы Объекта» также можно убрать/добавить видимость «Элементов
формы» (команд, табличных частей и т.д). Действия «Формы Объекта» описывают в «Модуле
формы».
* «Интерактивный» означает «содержащий элемент взаимодействия с пользователем. Само
слово «интерактивный» состоит из «интер» (между-) и «активный» (действенный).
Если не создавать ФормуОбъекта, то она все равно будет сгенерирована* системой. В
этом случае на «Форме объекта» будут отображены все «Реквизиты» и «Табличные части»
Объекта из «Дерева конфигурации».
“Объект” конфигурации имеет «Стандартные реквизиты», которые уже заранее созданы
разработчиками, но мы можем ещё добавить свои «Реквизиты». У каждого вида Объекта
(Справочники, Документы, Перечисления и т.д.) свой состав «Стандартных реквизитов».
«Табличные части» — создаются для ввода данных в БД пользователями.
* «Сгенерировано» означает сформировано автоматически
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 007
Краткое содержание
Регистры накопления, проведение документов. Основы запросов. Создание простейшего
отчёта на СКД.
1. Проведение документов
2. Регистры накопления
3. Основы запросов
4. Отчёты
Детальное описание
Регистр накопления может быть двух видов: остаточный и оборотный. В отличие от регистра
сведений, регистр накопления обязательно должен иметь документ-регистратор. Ресурс
регистра накопления может быть только числовым.
Дата основных (актуальных) итогов — 30.11.3999.
Свойство документа “Движения” содержит коллекцию движений документа по регистрам.
Оперативное проведение не позволяет проводить документ будущим временем.
Несмотря на то, что обработка проведения может занимать длительное время (например,
пересчитывать что-то или скачать фильм из интернета, чтобы сохранить его вместе с
движениями), движения документа сохраняются на конкретный момент времени. То есть
состояние данных с точки зрения 1С изменяется мгновенно.
Проведение документа обрабатывается в пределах одной транзакции.
Транзакция — это как бы атомарное действие с данными. Внутри транзакции может быть
сколько угодно манипуляций с данными, которые будут применены только в момент успешного
завершения транзакции. А в случае невозможности выполнить транзакцию, либо при её
принудительной отмене, никакие данные не будут изменены.
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 008
Краткое содержание
Знакомство с запросами
Обусловленное проведение документов
1. Обусловленное проведение
2. Работа с отладчиком
3. Понятие оперативной методики проведения
4. Запросы: Таблицы, Условия
Детальное описание
Для извлечения данных из базы данных используется механизм запросов. Запрос — это
объект, который имеет много методов и несколько свойств. Одно из свойств Запроса —
Запрос.Текст — описывает логику извлечения данных. Полученные при выполнении запроса
данные представляют из себя объект РезультатЗапроса.
Запрос можно писать вручную, но удобнее конструктором. Для отладки запроса используется
специальная обработка — консоль запросов. Открывать консоль нужно в режиме
1С:Предприятие (не в Конфигураторе).
Текст запроса состоит из предложений. Единственное обязательное предложение —
предложение ВЫБРАТЬ. После ключевого слова ВЫБРАТЬ в тексте запроса задается список
полей выборки, разделяемых запятыми. Полям можно назначать синонимы (или псевдонимы
поля), делается это с помощью ключевого слова КАК.
Список полей можно заменить символом *, в этом случае увидим все поля таблицы, к которой
обращается запрос, кроме виртуальных полей.
Таблицы информационной базы, данные которых извлекаются запросом, указываются после
ключевого слова ИЗ. Иными словами, задача предложения ИЗ состоит в том, чтобы
обозначить список источников данных.
Условия в запросах можно накладывать с помощью предложения ГДЕ.
По умолчанию результат запроса выводится отсортированным в том порядке, в каком данные
физически хранятся в базе данных, то есть хаотически. Отсортировать результат запроса
можно с помощью конструкции УПОРЯДОЧИТЬ ПО.
Получать данные запросами гораздо оптимальнее, чем через точку. Например, ранее в
модуле формы документа Счет на оплату мы создали функцию:
Функция ПолучитьЦену(Товар)
Возврат Товар.ЦенаПродажи;
КонецФункции
Здесь точка в выражении Товар.ЦенаПродажи — это полноценный запрос к базе данных,
который система сформулирует следующим образом:
ВЫБРАТЬ
*
ИЗ
Справочник.Номенклатура
ГДЕ
Ссылка = &Товар1
Результатом запроса будут все поля справочника Номенклатура, хотя нам необходимо только
одно поле — ЦенаПродажи. Запрос можно оптимизировать, заменив “звёздочку” полем
ЦенаПродажи.
Маленький лайфхак для проверки наличия синтаксических ошибок в тексте запроса. При
создании текста запроса вручную или при его правке можно из текста запроса по правой
кнопке мыши попытаться открыть конструктор запроса. Если конструктор открывается, значит,
синтаксических ошибок нет.
Для передачи переменных в запрос используется метод Запрос.УстановитьПараметр(<Имя>,
<Значение>), в скобках указываются соответственно имя и значение передаваемого
параметра. В нашем случае команда будет выглядеть как
Запрос.УстановитьПараметр("Товар1", Товар);
Следующий шаг — выполнение запроса и получение результата с помощью метода
Выполнить() объекта Запрос. Этот метод возвращает другой объект РезультатЗапроса,
содержащий выбранные данные из базы данных:
РезультатЗапроса = Запрос.Выполнить();
С результатом запроса можно поступать по-разному: например, можно получить из него
выборку, или можно выгрузить его в таблицу значений. Выборка наиболее экономно расходует
оперативную память, в большинстве случаев использовать нужно именно её.
Полученный нами объект РезультатЗапроса имеет метод Выбрать(), который возвращает
новый объект ВыборкаИзРезультатаЗапроса. Далее выборку можно обойти с помощью цикла
Пока Выборка.Следующий() Цикл, а в теле цикла производятся какие-то действия. В нашем
случае мы знаем, что в выборке всего одна строка, и мы можем получить её, написав:
Выборка.Следующий();
Желательно для оптимизации перед выборкой проверять результат запроса на пустоту
методом Пустой(), так как выборка занимает оперативную память.
Итак, можно переписать нашу функцию ПолучитьЦену() следующим образом:
Функция ПолучитьЦену(Товар)
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
|
ЦенаПродажи
|ИЗ
|
Справочник.Номенклатура
|ГДЕ
|
Ссылка = &Товар1";
Запрос.УстановитьПараметр("Товар1", Товар);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат 0;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
Возврат Выборка.ЦенаПродажи;
КонецФункции
Для отладки программного кода удобно использовать метод “Вычислить выражение” (SHIFTF9) и “Табло” (CTRL-ALT-W).
Откроем конструктор запросов и в разделе База данных на закладке “Таблицы и поля”
выберем РегистрыНакопления. Здесь видим, что для созданного нами ранее регистра
ОстаткиТоваров доступно четыре таблицы: одна реальная (ОстаткиТоваров) и три
виртуальные (ОстаткиТоваров.Обороты, ОстаткиТоваров.Остатки и
ОстаткиТоваров.ОстаткиИОбороты), смотри рис. Для виртуальных таблиц в разделе Таблицы
доступна кнопка “Параметры виртуальной таблицы”. С помощью этой кнопки можно задавать
параметр запроса, например, &ДатаОтчета.
Точка получения остатков в виртуальной таблице остатков регистра накопления не включает
саму себя на временной оси.
Используя запрос, создадим обусловленное проведение документа Расходная накладная.
Ранее в модуле объекта мы сформировали движения документа. Сейчас с помощью запроса
проверим, не приведёт ли проведение документа к отрицательным остаткам товара, чтобы в
этом случае отказаться от проведения.
Запишем движения документа и построим запрос к остаткам по регистру накопления. Строим
запрос с помощью Конструктора запросов:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|ОстаткиТоваровОстатки.Номенклатура КАК Номенклатура,
|ОстаткиТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|РегистрНакопления.ОстаткиТоваров.Остатки КАК ОстаткиТоваров
|ГДЕ
|ОстаткиТоваровОстатки.КоличествоОстаток < 0";
РезультатЗапроса = Запрос.Выполнить();
Если результат этого запроса не пустой, значит, имеются отрицательные остатки товаров и
документ проводить нельзя. Проверяем:
Если НЕ РезультатЗапроса.Пустой() Тогда
Отказ = Истина;
КонецЕсли;
Предполагается, что образование отрицательных остатков — редкая ситуация, поэтому
сначала записали движения, потом делаем проверку. Если сначала проверять остатки —
нужно было бы заблокировать таблицы регистра, чтобы с ними никто не мог работать.
Смотри рис.
Рис.
Вопросы к занятию (для самоконтроля)
Познакомиться с конструктором запросов.
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 009
Краткое содержание
1. Печать документов
2. Модуль менеджера
3. Макеты, табличный документ
Детальное описание
Задача: Создать печатную форму документа Счет на оплату.
Создадим печатную форму документа Счет на оплату.
В занятии разобраны 2 способа создания печатной формы — вручную в модуле формы
документа и при помощи конструктора.
Способ №1 — создание вручную в Модуле Формы Документа
В форме документа СчетНаОплату создаем новую команду Печать1.
Кнопку перетаскиваем на форму в Командную панель.
В Свойствах команды назначим Действие с директивой “Создать на клиенте и процедуру на
сервере”.
В Документе СчетНаОплату создаем Макет Печатной формы с типом Табличный документ.
Процесс создания макета печатной формы можно посмотреть на YouTube. Ключевые моменты
— создание именованных областей ОблШапка, ОблСтрока и ОблПодвал, создание
<параметров> (названы точно так же, как реквизиты табличной части) и шаблонов, сочетающих
обычный текст и параметры. Главное отличие Мокселя от Excel — возможность создания ячеек
с различной шириной в одном и том же столбце. Для этого нужно выделить нужные строки и
изменить ширину столбца. При этом система уведомит о создании нового формата строк.
В Модуле формы в процедуре &НаСервере создаём табличный документ ТабДок — то, куда
будут выводиться данные печатной формы:
ТабДок = Новый ТабличныйДокумент;
На данном этапе он пустой, и нам нужно заполнить его данными.
Получим только что созданный макет:
Макет
=
Документы.СчетНаОплату.ПолучитьМакет(“Макет”);
Из полученного макета нужно получить именованные области:
ОблШапка
=
Макет.ПолучитьОбласть(“ОблШапка”);
ОблСтрока
=
Макет.ПолучитьОбласть(“ОблСтрока”);
ОблПодвал
=
Макет.ПолучитьОбласть(“ОблПодвал”);
Теперь нужно заполнить Параметры Шапки Макета:
ОблШапка.Параметры.Номер
=
ОблШапка.Параметры.Дата
=
Объект.Номер;
Формат(Объект.Дата,
ОблШапка.Параметры.Контрагент
=
"ДЛФ=DD");
Объект.Контрагент;
ОблШапка.Параметры.Договор = Объект.Договор;
Чтобы вывести дату в нужном формате (например, чтобы получить месяц прописью), нужно
Объект.Дата обернуть в функцию Формат. Подробнее о Форматной строке, втором параметре
функции Формат, можно посмотреть здесь.
Далее следует записать параметры шапки в ТабДок:
ТабДок.Вывести(ОблШапка);
Создадим цикл (Для каждого Из) для вывода строк табличной части в ТабДок:
Для
каждого
Стр
Из
Объект.Товары
Цикл
ОблСтрока.Параметры.Заполнить(Стр);
ТабДок.Вывести(ОблСтрока);
КонецЦикла;
Выведем параметры Подвала в ТабДок, при этом, при помощи функции ЧислоПрописью,
сделаем так, чтобы сумма выводилась в виде текста:
СуммаИтого = Объект.Товары.Итог("Сумма");
ОблПодвал.Параметры.СуммаПрописью = ЧислоПрописью(СуммаИтого,,"рубль, рубля,
рублей,
м,
копейка,
копейки,
копеек,
ж,
2");
ТабДок.Вывести(ОблПодвал);
Чтобы скрыть сетку, группировки и заголовки в табличном документе следует применить
следующие свойства:
ТабДок.ОтображатьГруппировки
=
Ложь;
ТабДок.ОтображатьЗаголовки
=
Ложь;
ТабДок.ОтображатьСетку = Ложь;
Чтобы иметь возможность вернуть результат на Клиент, нужно процедуру &НаСервере
переименовать в Функцию. Функция на Сервере будет возвращать на Клиент
ТабДок=
Печать1НаСервере();
ТабДок = Показать();
Вышеописанный способ создания печатной формы плох тем, что он позволяет выводить на
печать данные, не записанные в базу данных. Печатать не сохранённый документ — это не
очень хорошо, потому что всегда есть вероятность ошибочных действий пользователя, и в итоге
может получится так, что данные в программе и на распечатанном документе будут не
идентичны. Но самый главный минус в том, что при выводе строк в ТабДок создается запрос в
цикле к базе данных!!! За это снижают баллы на экзамене 1С Специалист.
Модуль менеджера
Повторим назначение различных модулей в конфигураторе. То, что мы делаем в форме — это,
простыми словами, реакция на действия пользователя. В модуле объекта мы реагируем на
события записи объекта и его проведение. В модуле менеджера, наряду со стандартными,
можно создавать свои собственные дополнительные методы, расширяющие контекст.
Способ №2 — создание печатной формы в Списке Документов при помощи конструктора.
В свойствах документа СчетНаОплату, на вкладке макеты, можно воспользоваться
конструктором печати: процесс создания печатной формы с помощью конструктора
Конструктор печати создает команду Печать2() в модуле менеджера. Создание печатной
формы при помощи конструктора не идеально, потому что также создается запрос в цикле.
Для того, чтобы решить проблему запроса в цикле, в запросе нужно вместо Ссылки получать
Представление:
Запрос.Текст
=
|
СчетНаОплату.Дата,
|
СчетНаОплату.Договор.Представление
|
СчетНаОплату.Контрагент.Представление
|
СчетНаОплату.Номер,
|
СчетНаОплату.Товары.(
|
НомерСтроки,
|
Номенклатура.Представление
|
Цена,
|
Количество,
|
Сумма
|
)
|ИЗ
"ВЫБРАТЬ
КАК
КАК
КАК
Договор,
Контрагент,
Номенклатура,
|
Документ.СчетНаОплату
|ГДЕ
|
СчетНаОплату.Ссылка В (&Ссылка)";
КАК
СчетНаОплату
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Сделайте проверку на наличие строк в таблице документа "Счет на оплату". Если строк нет, то
печатать документ не нужно.
Реализуйте это в обеих процедурах, и Печать1() и Печать2().
Создайте процедуру печати для расходной накладной. Непроведённые документы не должны
иметь возможность быть распечатанными.
Домашнее задание / Самостоятельная работа
Решение.
Занятие 010
Краткое содержание
1. Складской учёт
2. Себестоимость
3. Обход результата запроса
Детальное описание
Виртуальные таблицы не хранятся в БД, они генерируются при запросе. Параметры
виртуальных таблиц задаются НЕ в секции запроса “ГДЕ”, а в круглых скобках после имени
виртуальной таблицы, аналогично параметрам функции.
Для получения результата запроса к регистру, включая движения самого документарегистратора, необходимо использовать МоментВремениВключаяДокумент = Новый
Граница(МоментВремени(), ВидГраницы.Включая)
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Для учёта расчётов с покупателями необходимо сохранять информацию об их долгах. Долг
увеличивается при проведении документа "Расходная накладная", сумма документа — это и
есть сумма долга. Другие документы в увеличении долга не участвуют.
Для этого необходимо спроектировать регистр и добавить реквизиты в документ. Валюта
взаиморасчётов одна (не важно какая).
Домашнее задание / Самостоятельная работа
Решение.
Ход решения.
1.
В Объект «Расходная накладная» добавляем:
Реквизиты: «СуммаПоДокументу» V Только просмотр (пользователь не сможет изменять
данные); «Контрагент».
Реквизиты Табличной части: «Цена»; «Сумма»
P.S. » т.к. «СуммаПоДокументу» — это Реквизит Объекта. Значит, действия с ним описываем в
«Модуле Объекта»
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
СуммаПоДокументу = Товары.Итог("Сумма");
КонецПроцедуры
2.
Для отображения на ФормеДокумента «Итогов» по Колонке «Сумма»
Свойства Таблицы на закладке Использования V показывать подвал
В свойствах Колонки «Сумма» V Путь к данным подвала
3.
Рассчитываем «Сумму» по каждой строке табличной части.
В «Модуле Формы» при Событиях «При Изменении» колонки «Цена» или «Количество»
&НаКлиентеНаСервереБезКонтекста
Процедура РассчитатьСумму(Стр)
Стр.Сумма = Стр.Цена * Стр.Количество;
КонецПроцедуры
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
РассчитатьСумму(Элементы.Товары.ТекущиеДанные);
КонецПроцедуры
&НаКлиенте
Процедура ТоварыЦенаПриИзменении(Элемент)
РассчитатьСумму(Элементы.Товары.ТекущиеДанные);
КонецПроцедуры
4.
Создаём РегистрНакопления «Долги Покупателей»:
Выбрали «Вид регистра» — «Остатки» т.к. он хранит в себе значения на начало Периода (т.е.
остаток) и движения «+ Приход» и «- Расход».
Измерения (в разрезе чего)– «Контрагент» V Запрет Изменения Значений (проверка на верный
тип данных заполнения)
Ресурс (всегда число) — «Сумма»
5.
V «Проведение документа» — это значит, что документ сделал «Движения» по
Регистрам которые выбраны в свойствах Объекта «Движения». Значит, действия
«Проведения» описываем в «Модуле Объекта» в предопределённой Процедуре
«ОбработкаПроведения». Можно использовать «Конструктор движений»
В Модуле Объекта
#Область ДЗ_4 // Новая форма комментария
Движения.ДолгиПокупателей.Записывать = Истина; // записываем движения РН
Движение = Движения.ДолгиПокупателей.ДобавитьПриход(); //Формируем
Движения РН “+”
Движение.Контрагент = Контрагент; // Движения РН в Измерения Формируем
Движения РН
Движение.СуммаДокумента = Товары.Итог("Сумма"); // Движения РН в Ресурсы
Формируем Движения РН
Движение.Период = Дата;
#КонецОбласти
6. На Форме Объекта (документа «Расходная накладная») устанавливаем
«Параметризируемую команду» т.е гиперессылку в Регистр Накопления «Долги
покупателей».
Вид формы “Расходная накладная” ДО.
1. На вкладке с Элементами формы перейти на вкладку “Командный
интерфейс”
2. Раскрыть список “Перейти”
3. Установить флаг “Видимость” у тех регистров ссылку на которые
необходимо отобразить на форме.
Вид формы “Расходная накладная” ПОСЛЕ.
Занятие 011
Краткое содержание
1. Оптимизация проведения Расходной накладной
2. Расчет себестоимости, неоперативное проведение
3. Запросы: Группировки, Условия
Детальное описание
Оптимизация проведения Расходной накладной
Продолжаем рассматривать обработку проведения документа «Расходная накладная» и
попробуем его оптимизировать.
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.ОстаткиТоваров.Записывать = Истина;
#Область ДЗ_4
Движения.Взаиморасчеты.Записывать = Истина;
Движение = Движения.Взаиморасчеты.ДобавитьПриход();
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Сумма = СуммаИтого;
#КонецОбласти
ТЗ=Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура","Количество");
Для Каждого ТекСтрокаТовары Из ТЗ Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрокаТовары.Количество;
КонецЦикла;
Движения.Записать();
Запрос = Новый Запрос;
Запрос.Текст="ВЫБРАТЬ
|
ОстаткиТоваровОстатки.Номенклатура КАК Номенклатура,
|
ОстаткиТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(&МоментВремени, Склад =
&Склад) КАК ОстаткиТоваровОстатки
|ГДЕ
|
ОстаткиТоваровОстатки.КоличествоОстаток < 0";
|
Граница = Новый Граница(МоментВремени(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("МоментВремени",Граница);
Запрос.УстановитьПараметр("Склад",Склад);
РезультатЗапроса=Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Отказ = Истина;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Мало товара "+Выборка.Номенклатура+", нужно еще "+(Выборка.КоличествоОстаток));
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Решаем первую задачу (обозначена в комментарии к запросу выше)
Хотим получить табличную часть документа, сгруппированную по номенклатуре, т.е. нам
нужны две колонки «Номенклатура» и «Количество». В консоли запросов получаем следующий
запрос и ниже его результат:
Фактически то же самое делает метод ТЗ.Свернуть("Номенклатура","Количество"). Теперь
заменим строчки кода
ТЗ = Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура","Количество");
на только что полученный запрос (только сделаем его уже в конструкторе запроса, поэтому он
будет немного отличаться от запроса выше наличием синонимов) и немного скорректируем
движения по регистру накоплений ОстаткиТоваров:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|
РасходнаяНакладнаяТовары.Номенклатура КАК Номенклатура,
|
СУММА(РасходнаяНакладнаяТовары.Количество) КАК Количество
|ИЗ
|
Документ.РасходнаяНакладная.Товары КАК РасходнаяНакладнаяТовары
|ГДЕ
|
РасходнаяНакладнаяТовары.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
|
РасходнаяНакладнаяТовары.Номенклатура";
Запрос.УстановитьПараметр("Ссылка",Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = Выборка.Количество;
КонецЦикла;
Решаем вторую задачу
Следующий запрос в обработке проведения получает остатки, хотим в качестве параметра
виртуальной таблицы передать ту номенклатуру, по которой нам нужно остатки получить.
Запрос = Новый Запрос;
Запрос.Текст="ВЫБРАТЬ
|
ОстаткиТоваровОстатки.Номенклатура КАК Номенклатура,
|
ОстаткиТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ОстаткиТоваров.Остатки(&МоментВремени, Склад = &Склад
и Номенклатура В (&МассивТоваров)) КАК ОстаткиТоваровОстатки
|ГДЕ
|
ОстаткиТоваровОстатки.КоличествоОстаток < 0";
Граница = Новый Граница(МоментВремени(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("МоментВремени",Граница);
Запрос.УстановитьПараметр("Склад",Склад);
Запрос.УстановитьПараметр("МассивТоваров",Товары.ВыгрузитьКолонку("Номенклатур
а"));
Теперь виртуальная таблица рассчитает остатки только по тем товарам, которые есть в
табличной части документа. Но метод ВыгрузитьКолонку(), создаёт нам массив и это
нехорошо, поэтому в качестве параметра «МассивТоваров» попробуем передать запрос,
полученный при решении первой задачи.
Сначала разберём понятие временных таблиц в запросах.
Чтобы сохранить запрос где-то для его дальнейшего использования, используются временные
таблицы. В запросе для этого используется слово ПОМЕСТИТЬ
<НазваниеВременнойТаблицы>.
Причём при выполнении запроса ничего не отобразится, но на самом деле там есть одна
колонка с количеством строк, помещенных во временную таблицу. При пакетном запросе
(один запрос следует за другим), запросы разделяются символом «;».
По факту мы получили тот же самый запрос из первой задачи, но с использованием
временной таблицы ДокТЧ.
То же самое сделаем уже в конфигураторе конструктором запроса (поместим первый запрос
во временную таблицу и выберем из нее количество и номенклатуру). Чтобы временная
таблица «жила», нужно предварительно создать МенеджерВременныхТаблиц:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
По факту МенеджерВременныхТаблиц — это такой «бокс», который хранит в себе временные
таблицы какого-то запроса и который можно передавать из одного модуля в другой. Получим
следующее:
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|
РасходнаяНакладнаяТовары.Номенклатура КАК Номенклатура,
|
СУММА(РасходнаяНакладнаяТовары.Количество) КАК Количество
|ПОМЕСТИТЬ ДокТЧ
|ИЗ
|
Документ.РасходнаяНакладная.Товары КАК РасходнаяНакладнаяТовары
|ГДЕ
|
РасходнаяНакладнаяТовары.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
|
РасходнаяНакладнаяТовары.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество
|ИЗ
|
ДокТЧ КАК ДокТЧ";
Запрос.УстановитьПараметр("Ссылка",Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = Выборка.Количество;
КонецЦикла;
Теперь эту временную таблицу ДокТЧ будем использовать для решения второй задачи, мы
вместо параметра виртуальной таблицы «МассивТоваров» передадим запрос «ВЫБРАТЬ
ДокТЧ.Номенклатура ИЗ ДокТЧ КАК ДокТЧ». Получим:
Запрос.Текст="ВЫБРАТЬ
|
ОстаткиТоваровОстатки.Номенклатура КАК Номенклатура,
|
ОстаткиТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ОстаткиТоваров.Остатки(
|
&МоментВремени,
|
Склад = &Склад
|
И Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК ОстаткиТоваровОстатки
|ГДЕ
|
ОстаткиТоваровОстатки.КоличествоОстаток < 0";
Замечание: Объект Запрос тут тот же, т.е. повторную строчку инициализации запроса (Запрос
= Новый Запрос;) мы убрали.
Временная таблица «жива», пока «жив» объект Запрос, а он существует пока не закончилась
процедура, ну или пока мы не удалили непосредственно временную таблицу. Временная
таблица реально создается во временных таблицах на сервере баз данных, поэтому это
медленная операция, с одной стороны; но если она используется несколько раз, то это уже
оптимальнее, чем использование вложенного запроса. Ну и ещё запросы с использованием
временных таблиц читабельнее.
Теперь обработка проведения документа максимально оптимальна.
Расчет себестоимости
Сначала так же передадим запрос в качестве второго параметра виртуальной таблицы
СтоимостьТоваров.Остатки( , ), чтобы остатки считались только по той номенклатуре,
которая находится в табличной части нашего документа (про Период — первый параметр
виртуальной таблицы пока не говорим).
Получили две таблицы:
Теперь хотим соединить эти таблицы, т.е. взять всю табличную часть документа и
присоединить к ней таблицу из регистра накоплений СтоимостьТоваров. Предварительно
запрос из регистра СтоимостьТоваров поместим во временную таблицу Остатки (чисто для
методических целей).
То же самое сделаем в конфигураторе конструктором запроса.
Запрос.Текст=
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Номенклатура,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки(
|
,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
|
|
|ИЗ
|
|
|
ДокТЧ.Количество КАК Количество,
Остатки.КоличествоОстаток КАК КоличествоОстаток,
Остатки.СтоимостьОстаток КАК СтоимостьОстаток
ДокТЧ КАК ДокТЧ
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура";
Теперь непосредственно посчитаем себестоимость на основе данных, полученных из этого
запроса. Формула описана ниже.
Получаем:
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Себестоимость = Выборка.Количество / Выборка.КоличествоОстаток *
Выборка.СтоимостьОстаток;
Движение = Движения.СтоимостьТоваров.ДобавитьРасход();
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Количество = Выборка.Количество;
Движение.Стоимость = Себестоимость;
КонецЦикла;
Движения.СтоимостьТоваров.Записывать = Истина;
Формула немного отличается от той, что представлена на рисунке выше, это нужно для
решения проблемы зависания последней копейки при полном списании товара:
Себестоимость=Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СтоимостьОстаток
;
Мы сначала находим отношение списываемого к остатку и потом пропорцией получаем
стоимость. Т.е. если мы все продаём,
Выборка.Количество / Выборка.КоличествоОстаток = 1,
и никакая копейка не зависнет при полном списании товара.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Добавьте в справочник Сотрудники реквизит "ДатаРождения".
Запросом выберите всех сотрудников у которых реквизит не заполнен.
Всех у кого ДР в текущем месяце.
Всех кто старше 18 лет.
Для этого используйте функции работы с датами (Справка — Содержание справки —
Встроенный язык — Работа с запросами — Синтаксис текста запросов — Ключевые слова и
функции — Функции — Функции работы с датами)
Запросом выберите из справочника Номенклатура все товары с единицей измерения "Литр", и
отсортируйте записи по цене.
Домашнее задание/Самостоятельное
Решение
Занятие 012
Краткое содержание
1. Оптимизация проведения расходной накладной
2. Блокировки
Детальное описание
Оптимизация проведения расходной накладной
Попробуем списать все имеющиеся ложки документом «Расходная накладная», документ
проведётся и все спишется, но, если перепровести этот же документ, вылезет ошибка.
Хотя если сначала отменить, а затем провести, документ проведётся, все ложки спишутся, и
ошибки не возникнет. В чём же причина возникновения ошибки именно при перепроведении
документа?
Дело в том, что в запросе для виртуальной таблицы Остатки регистра накоплений
СтоимостьТоваров не указан период вычисления остатков, а значит система берет основные
итоги (оперативные) на 30.11.3999.
Запрос.Текст=
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Номенклатура,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки(
|
,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|//////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
Остатки.КоличествоОстаток КАК КоличествоОстаток,
|
Остатки.СтоимостьОстаток КАК СтоимостьОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
|
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура";
Но старые движения этого документа у нас уже есть, так как мы их не удаляем при начале
формирования движений. За удаление или нет старых движений при перепроведении
документа отвечает следующая настройка, в нашем случае старые движения удаляются
только при отмене проведения (подробнее в модуле 007).
Добавим первый параметр виртуальной таблицы Остатки регистра накоплений
СтоимостьТоваров — &МоментВремени, в качестве значения параметра передадим
МоментВремени(), чтобы указать, что итоги нам нужно посчитать НА момент времени
документа, не включая саму эту точку (т.е. движения самого документа учтены не будут при
расчете остатка). Это отличается от того, что мы передавали в качестве параметра
&МоментВремени в виртуальную таблицу регистра ОстаткиТоваров, там по логике
проведения нам нужно было учитывать движения самого документа, поэтому использовали
объект Граница.
Вернёмся к ошибке: мы продали все ложки и перепроводим расходную накладную.
В строке
Себестоимость
=Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СтоимостьОстаток;
Выборка.КоличествоОстаток имеет тип NULL, потому что в запросе мы к ДокТЧ присоединяем
таблицу Остатки, в которой ничего нет (мы же все ложки продали).
Чтобы ошибка не возникала, нужно в запросе использовать функцию
ЕСТЬNULL(<ПроверяемоеЗначение>, <ВозвращаемоеЗначение>), в нашем случае
ЕСТЬNULL(Остатки.КоличествоОстаток,0) ,т.е. если Остатки.КоличествоОстаток будет NULL,
то функция нам вернет 0. А дальше просто реализуем проверку на равенство нулю
Остатки.КоличествоОстаток, чтобы исключить деление на 0.
Пока Выборка.Следующий() Цикл
Если Выборка.КоличествоОстаток=0 Тогда
Сообщить("Что-то пошло не так, остаток стоимости не совпадает с остатками по
складу");
Отказ=Истина;
Возврат;
КонецЕсли;
Себестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СтоимостьОстаток;
Движение = Движения.СтоимостьТоваров.ДобавитьРасход();
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Количество = Выборка.Количество;
Движение.Стоимость = Себестоимость;
КонецЦикла;
Так как ЕСТЬNULL() довольно медленная операция, то не надо проверять все
присоединяемые поля, достаточно тех, по которым делается проверка.
Теперь попробуем тот же документ перепровести текущей датой (оперативно), он снова как и
сначала не проведётся и выведется сообщение «Что-то пошло не так, остаток стоимости не
совпадает с остатками по складу». Дело в том, что при перепроведении оперативно дата
документа будет всегда больше, чем дата этого же документа на тот момент, когда он был
проведен до этого. А это значит, что движения этого документа при расчете остатков будут
учитываться. Чтобы решить эту проблему, перед запросом вставим следующее:
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Движения.СтоимостьТоваров.Записать();
КонецЕсли;
Таким образом, при оперативном проведении мы записываем в регистр пустой набор записей,
тем самым очищая движения, сделанные самим документом по регистру СтоимостьТоваров.
Блокировки
Итак, прочитаем, что же происходит у нас с Расходной накладной с самого начала процедуры
ОбработкаПроведения, будем рассматривать только обусловленные проведения (т.е. про
движения по регистру накоплений Взаиморасчеты говорить не будем).
Начнем разговор с проведения документа по регистру ОстаткиТоваров.
Движения.ОстаткиТоваров.Записывать = Истина; //поставили маркер, что нужно записать
движения по регистру ОстаткиТоваров
#Область ДЗ_4
Движения.Взаиморасчеты.Записывать = Истина;
Движение = Движения.Взаиморасчеты.ДобавитьПриход();
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Сумма = СуммаИтого;
#КонецОбласти
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|
РасходнаяНакладнаяТовары.Номенклатура КАК Номенклатура,
|
СУММА(РасходнаяНакладнаяТовары.Количество) КАК Количество
|ПОМЕСТИТЬ ДокТЧ
|ИЗ
|
Документ.РасходнаяНакладная.Товары КАК РасходнаяНакладнаяТовары
|ГДЕ
|
РасходнаяНакладнаяТовары.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
|
РасходнаяНакладнаяТовары.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество
|ИЗ
|
ДокТЧ КАК ДокТЧ";
Запрос.УстановитьПараметр("Ссылка",Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий()Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = Выборка.Количество;
КонецЦикла;
//1
Движения.Записать(); //записали движения (только по регистрам ОстаткиТоваров и
Взаиморасчеты)
Запрос.Текст="ВЫБРАТЬ
|
ОстаткиТоваровОстатки.Номенклатура КАК Номенклатура,
|
ОстаткиТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
|
РегистрНакопления.ОстаткиТоваров.Остатки(
|
&МоментВремени,
|
Склад = &Склад
|
И Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК ОстаткиТоваровОстатки
|ГДЕ
|
ОстаткиТоваровОстатки.КоличествоОстаток < 0";
Граница = Новый Граница(МоментВремени(), ВидГраницы.Включая);
Запрос.УстановитьПараметр("МоментВремени",Граница);
Запрос.УстановитьПараметр("Склад",Склад);
Запрос.УстановитьПараметр("МассивТоваров",Товары.ВыгрузитьКолонку("Номенклатура"));
РезультатЗапроса=Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Отказ = Истина;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Мало товара "+Выборка.Номенклатура+", нужно еще "+(Выборка.КоличествоОстаток));
КонецЦикла;
КонецЕсли;
Теперь представим такую ситуацию: два пользователя в одну и ту же миллисекунду нажали
кнопку «Провести» у двух разных расходных, в которых есть ложка, а она последняя, которая
осталась на складе. И первый, и второй пользователи получат информацию по остаткам, но
без учета того, что другая транзакция еще активна. Таким образом, может получиться, что мы
продадим один и тот же товар два раза. Поэтому нужно системе как-то «сказать», чтобы она
не давала читать параллельно, пока эта транзакция не будет закончена, другими словами нам
нужно установить блокировку на чтение записей по таблице регистра ОстаткиТоваров.
Поэтому принудительно поставим блокировку на чтение, следующей строчкой кода (вставим
вместо //1 в коде сверху):
Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина; — это не установка
блокировки, а установка флага «надо бы заблокировать», сама блокировка произойдет в
момент записи движения (Движения.Записать();) и закончится при выходе из процедуры
проведения. Если блокировка установлена на какой-то товар, то другой пользователь
повторно установить блокировку не сможет, он встанет в транзакционную очередь на чтение.
Потом он либо дождется своей очереди, либо вывалится с ошибкой, что данные
заблокированы и получить их он не смог.
Система сейчас заблокирует остатки в регистрах для этих товаров, что находятся в табличной
части расходной накладной (по сочетанию Номенклатура+Склад). Теперь до окончания
транзакции никто параллельно читать остатки товаров по той самой ложке не сможет (по тому
складу, что указан в нашей расходной накладной).
Разделение итогов:
В регистрах накоплений на вкладке «Прочее» есть галочка «Разрешить разделение итогов».
Если она стоит, то в пользовательском режиме можно управлять этой настройкой (Все
функции — Стандартные — Управление итогами — колонка «Разделение итогов»). Что же это
такое?
Проводятся, например, две приходные накладные, в которых есть одна и та же ложка.
Таблица итогов регистра ОстаткиТоваров до проведения этих приходных накладных
выглядит следующим образом:
Товар
Количество
Ложка
5 + 1=6
Проводится первая приходная накладная, в ней мы купили 1 ложку. Количество в таблице
итогов по ложке стало 6. И пока первая приходная накладная проводится, таблица итогов по
ложке на запись заблокирована (нельзя же в таблице Excel одну и ту же ячейку править двум
пользователям одновременно, и тут так же). Соответственно, вторая приходная накладная
параллельно с ней будет висеть в транзакционной очереди (будет ждать, пока строчку с
ложкой освободят). А чтобы этого не было, разработчики придумали механизм разделения
итогов. Если данный механизм включен, вторая приходная накладная, увидев, что строка с
ложкой заблокирована в таблице итогов, задвоит эту строку. А таблица итогов будет
выглядеть следующим образом:
Товар
Индекс Итогов
Количество
Ложка
0
6
Ложка
1
4
Т.е. итого будет все равно 10, но в таблице итогов будет две записи по ложке. Если
параллельно будут 5 приходных накладных с ложкой проводиться, значит, в таблице итогов
будет 5 таких строчек. Когда-нибудь потом система их схлопнет. Таким образом, данный
механизм позволяет увеличить параллельность записи тех документов, в которых есть
необусловленное проведение. Но, с другой стороны, это же плохо, когда одновременно
проводятся и приходная, и расходная накладные (с одной и той же ложкой) или, например, две
расходные накладные. Так вот, строчка Движения.ОстаткиТоваров.БлокироватьДляИзменения
= Истина кроме установки блокировок, ещё запрещает параллельное использование
механизма разделения итогов, т. е. когда установлена блокировка по методу
Движения.Записать(); (см. код выше), ни приходная, ни расходная накладные параллельно
обновить таблицу итогов не смогут (по нашей ложке), только последовательно.
Рассмотрим ещё момент «старых» движений, например, пользователь открыл уже
проведённую расходную накладную с ложкой, поменял ложку на вилку и снова проводит
документ. Благодаря той же строчке
Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина система заблокирует итоги и
по ложке, и по вилке, т.е. старые движения автоматически войдут в эту блокировку (будет
установлена блокировка ещё и на тот набор записей, который удаляется из документа).
Основной смысл блокировки — быстро принять решение при минимуме
заблокированных таблиц.
И пока что, кроме записей таблицы регистра ОстаткиТоваров, больше ничего не
заблокировано (естественно таблицы самого документа заблокированы, так как туда идет
запись).
Теперь посмотрим, что будет дальше при проведении документа по регистру накоплений
СтоимостьТоваров.
Если Отказ Тогда
Возврат;
КонецЕсли;
//2
Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Движения.СтоимостьТоваров.Записать();
КонецЕсли;
Запрос.Текст=
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Номенклатура,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК КоличествоОстаток,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки(&МоментВремени
|
,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
ЕСТЬNULL(Остатки.КоличествоОстаток,0) КАК КоличествоОстаток,
|
Остатки.СтоимостьОстаток КАК СтоимостьОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
|
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура";
Запрос.УстановитьПараметр("МоментВремени",МоментВремени());
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.КоличествоОстаток=0 Тогда
Сообщить("Что-то пошло не так, остаток стоимости не совпадает с
остатками по складу");
Отказ=Истина;
Возврат;
КонецЕсли;
Себестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СтоимостьОстаток;
Движение = Движения.СтоимостьТоваров.ДобавитьРасход();
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Количество = Выборка.Количество;
Движение.Стоимость = Себестоимость;
КонецЦикла;
Движения.СтоимостьТоваров.Записывать = Истина;
Таким образом, в данном случае мы сначала читаем остатки, а уже потом формируем
движения, поэтому заблокировать при помощи строчки кода
Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина в данном случае не
получится, т.к. на момент получения остатков движений ещё нет, нет информации о том, что
же будет записано (нет набора фильтров для установки блокировки). Нам самим придётся
установить блокировку и указать, что и где мы будем блокировать. Для этого есть
специальный объект БлокировкаДанных. Синтаксис выглядит следующим образом:
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.СтоимостьТоваров");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = Товары;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура","Номенклатура");
Блокировка.Заблокировать();
(вставим данный код вместо //2 в коде выше)
Блокировка — это таблица, каждая строка этой таблицы описывает что и где мы будем
блокировать. ЭлементБлокировки как раз и есть та самая строка таблицы, методом
Добавить() мы говорим, что будем блокировать таблицу регистра СтоимостьТоваров. Режим
блокировки Исключительный говорит, что ни читать, ни писать параллельно со мной никто не
сможет в этот регистр. Есть ещё режим Разделяемый, он позволяет параллельно читать
нескольким пользователям, но не писать. В свойстве ИсточникДанных указываем, откуда
нужно взять те данные по товарам, по которым мы хотим установить блокировку. Они хранятся
в табличной части Товары документа Расходная накладная (ну или можно предварительно
выбрать эти данные запросом). В методе ИспользоватьИзИсточникаДанных() мы
устанавливаем соответствие между названием поля в табличной части Товары и названием
измерения регистра СтоимостьТоваров (в нашем случае они называются одинаково —
«Номенклатура»). Таким образом, мы установили блокировку на список товаров в табличной
части Товары в регистре СтоимостьТоваров. Сейчас мы сделали всё то же самое, что за нас
делало свойство БлокироватьДляИзменения при установке блокировки по регистру
ОстаткиТоваров. Объектом Блокировка можно установить блокировку только внутри
транзакции, блокировать можно любые таблицы, а не только таблицы регистров.
Блокировку мы установили по тем товарам, что есть в табличной части документа, но как быть
со старыми движениями при перепроведении, их же тоже нужно заблокировать? Блокируем
точно так же, как и в случае с регистром ОстаткиТоваров, т.е. добавим после блокировки при
помощи объекта БлокировкаДанных еще следующую строку:
Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;
Но это только флаг «надо бы заблокировать», а сама блокировка старых движений произойдёт
при записи движений, поэтому пустые движения будем записывать безусловно
//Если Режим = РежимПроведенияДокумента.Оперативный Тогда
Движения.СтоимостьТоваров.Записать();
//КонецЕсли;
Помимо блокировки старых движений при перепроведении, данный флаг запретит
использование механизма разделения итогов.
Различия между Движения.Записать() и Движения.СтоимостьТоваров.Записать():
Метод Движения.СтоимостьТоваров.Очистить() очищает данные в оперативной памяти,
принадлежащей этому документу, а не в базе, так как записи данных в базе ещё нет.
Теперь поговорим о настройке «Режим управления блокировкой данных» в свойствах
Конфигурации.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Необходимо разработать механизм резервирования товаров.
Резервирование производится при проведении документа "Счет".
Резервируется товар на конкретном складе.
Товар резервируется для конкретного покупателя.
Товар резервируется на 3 дня.
Проверку наличия товаров на складе на момент резервирования делать не нужно.
Списание резервов реализовывать не нужно, только увеличение резерва. Списание мы
реализуем совместно позже.
Блокировок в задании не предусматривается.
В отчёте ОстаткиТоваров, необходимо выводить информацию по зарезервированным
товарам: Колонки:
Товар | КоличествоОстаток | ВРезерве
Домашнее задание/Самостоятельное
Решение
Занятие 013
Краткое содержание
1. Партионный учет
2. Классический пример
3. Методы расчета себестоимости FIFO, LIFO
Детальное описание
Методы партионного списания FIFO, LIFO
FIFO — First-In-First-Out (“первый пришел, первый ушел”)
LIFO — Last-In-First-Out (“последний пришел, первый ушел”)
Это не всегда деньги, могут быть сроки годности или долги покупателей.
Товар
Партия
Количество
Стоимость
Ложка
1
10
100
Ложка
2
10
200
Ложка
3
10
300
Хотим списать 17 по FIFO:
Ложка
1
10
100
Ложка
2
7
200/10*7=140
Осталось:
Ложка
1
Ложка
2
3
60
Ложка
3
10
300
По среднему было бы: 600/30*17=340 и остаток соответственно 13 ложек на сумму 260.
Хотим списать 17 по LIFO:
Ложка
1
10
300
Ложка
2
7
200/10*7=140
Ложка
1
10
100
Ложка
2
3
60
Ложка
3
Осталось:
Метод FIFO –максимально адекватная оценка при расчете себестоимости (приближена к
текущим рыночным ценам).
Метод LIFO-списываем сначала товар по максимальной стоимости, тем самым увеличиваем
себестоимость и уменьшаем прибыль, а значит, уменьшаем и налог на прибыль. Поэтому на
территории РФ и некоторых других стран расчет себестоимости списания товара по данному
методу запрещен по налоговому законодательству. Но в управленческих целях считать так
можно.
Нам нужно ввести понятие «Партия» в нашу конфигурацию. Добавим измерение «Партия» в
регистр накоплений «СтоимостьТоваров». Признаком партии (тип измерения «Партия») в
нашем случае будет документ «ПриходнаяНакладная», но можно завести и справочник партий
или номер таможенной декларации тоже может быть признаком партии.
Также скорректируем движения документа Приходная накладная по регистру
СтоимостьТоваров.
Движение = Движения.СтоимостьТоваров.ДобавитьПриход();
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Партия = Ссылка; — добавили строку
Движение.Количество = ТекСтрокаТовары.Количество;
Движение.Стоимость = ТекСтрокаТовары.Сумма;
Теперь сделаем отчёт по остаткам товаров с их стоимостью с отображением партий. Сделаем
его без помощи СКД.
Создаем отчёт, добавляем в него макет, создает форму отчёта. Она будет пустой, в ней
добавляем реквизит формы — ТабДок с типом Табличный документ и перетаскиваем на
форму. Создаем команду «Сформировать» и тоже перетаскиваем на форму, создаем
обработчик команды форма. Где его создаем? На клиенте нельзя, так как нам нужно доставать
запросом данные, которых на клиенте нет. Остаётся на клиенте и на сервере без контекста
или с ним? Сейчас у нас контекст формы — это считай ничего, только табличный документ,
который мы будем заполнять, поэтому создаем на клиенте и сервере с контекстом, иначе нам
самим пришлось бы передавать табличный документ на сервер в качестве параметра.
Код отчёта будет следующим:
&НаСервере
Процедура СформироватьНаСервере()
Макет = Отчеты.ОстаткиПартийТоваров1.ПолучитьМакет("Макет");
ОблШапка = Макет.ПолучитьОбласть("ОблШапка");
ОблСтрока = Макет.ПолучитьОбласть("ОблСтрока");
ТабДок.Очистить();
ТабДок.Вывести(ОблШапка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Товар,
|
СтоимостьТоваровОстатки.Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК Количество,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК Стоимость
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки КАК СтоимостьТоваровОстатки";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий()Цикл
ОблСтрока.Параметры.Заполнить(Выборка); // в макете названия параметров и
названия значений в выборке одинаковые, мы в запросе специально синонимы сделали
ТабДок.Вывести(ОблСтрока);
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура Сформировать(Команда)
СформироватьНаСервере();
КонецПроцедуры
Получили:
Как у нас данные отсортированы сейчас? Никак. Хотим отсортировать по Товару и по Партиям.
Запрос поменяем на следующий:
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Товар,
|
СтоимостьТоваровОстатки.Партия КАК Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК Количество,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК Стоимость
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки КАК СтоимостьТоваровОстатки
|
|УПОРЯДОЧИТЬ ПО
|
Товар,
|
Партия";
Но ничего не поменяется, так как сортировка по Товару и по Партии сейчас — это сортировка
по внутреннему представлению.
Есть в конструкторе свойство «Автоупорядочивание», оно полезно именно для отчётов.
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Товар,
|
СтоимостьТоваровОстатки.Партия КАК Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК Количество,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК Стоимость
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки КАК СтоимостьТоваровОстатки
|
|УПОРЯДОЧИТЬ ПО
|
Товар,
|
Партия
|АВТОУПОРЯДОЧИВАНИЕ";
Теперь товар упорядочен по основному представлению (причём если справочник
иерархический, то отсортировано будет еще и с учётом иерархии), но партия тоже
отсортировалась по представлению, а не по дате. Но нам нужна сортировка по хронологии,
поэтому отсортируем по моменту времени.
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Товар,
|
СтоимостьТоваровОстатки.Партия КАК Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК Количество,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК Стоимость
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки КАК СтоимостьТоваровОстатки
|
|УПОРЯДОЧИТЬ ПО
|
Товар,
|
СтоимостьТоваровОстатки.Партия.МоментВремени УБЫВ
|АВТОУПОРЯДОЧИВАНИЕ";
Теперь добавим итоги в отчёт: хотим видеть, сколько всего вилок, сколько ложек. Для этого в
макет добавим область итогов и добавим итоги в запрос. Заметим, что группировки
уменьшают количество строк запроса, тогда как итоги их, наоборот, увеличивают.
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Товар,
|
СтоимостьТоваровОстатки.Партия КАК Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК Количество,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК Стоимость
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки КАК СтоимостьТоваровОстатки
|
|УПОРЯДОЧИТЬ ПО
|
Товар,
|
СтоимостьТоваровОстатки.Партия.МоментВремени УБЫВ
|ИТОГИ
|
СУММА(Количество),
|
СУММА(Стоимость)
|ПО
|
Товар
|АВТОУПОРЯДОЧИВАНИЕ";
Как только появилась в запросе строка ИТОГИ ПО, массив результата запроса стал
многомерным.
Но мы хотим красивый отчёт, чтобы итоги были другим шрифтом, для этого обойдем
результат запроса с учетом тех измерений, которые добавились строчкой ИТОГИ ПО.
Вся процедура теперь будет выглядеть так:
Макет = Отчеты.ОстаткиПартийТоваров1.ПолучитьМакет("Макет");
ОблШапка = Макет.ПолучитьОбласть("ОблШапка");
ОблСтрока = Макет.ПолучитьОбласть("ОблСтрока");
ОблИтоги = Макет.ПолучитьОбласть("ОблИтоги");
ТабДок.Очистить();
ТабДок.Вывести(ОблШапка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура КАК Товар,
|
СтоимостьТоваровОстатки.Партия КАК Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток КАК Количество,
|
СтоимостьТоваровОстатки.СтоимостьОстаток КАК Стоимость
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки КАК СтоимостьТоваровОстатки
|
|УПОРЯДОЧИТЬ ПО
|
Товар,
|
СтоимостьТоваровОстатки.Партия.МоментВремени УБЫВ
|ИТОГИ
|
СУММА(Количество),
|
СУММА(Стоимость)
|ПО
|
Товар
|АВТОУПОРЯДОЧИВАНИЕ";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаИтоги =
РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаИтоги.Следующий()Цикл
Выборка = ВыборкаИтоги.Выбрать();
Пока Выборка.Следующий() Цикл
ОблСтрока.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОблСтрока);
КонецЦикла;
ОблИтоги.Параметры.Заполнить(ВыборкаИтоги); // в макете названия параметров
и названия значений в выборке одинаковые, мы в запросе специально синонимы
сделали
ТабДок.Вывести(ОблИтоги);
КонецЦикла;
Примечание: такой же отчёт можно сделать при помощи СКД.
Данный отчёт нам поможет в расчёте себестоимости списания по методу FIFO или LIFO.
Мы хотим списывать товары с учетом этой самой иерархии. Например, хотим списать 27 вилок
(не важно по FIFO или LIFO-метод отличаться будет только упорядочиванием в запросе).
Сначала нужно узнать количество вилок в итоге (в нашем случае 60), если их больше
списываемого количества, то переходить к партиям (в нашем случае эту проверку можно не
делать, так как до этого мы делали проверку по регистру ОстаткиТоваров, и, если б
количества не хватило, мы не дошли бы до списания стоимости по регистру
СтоимостьТоваров). Затем списываем 20 штук по цене 6000 и уменьшаем количество
списываемого товара на 20. И оставшиеся 7 штук списываем от следующей партии по цене
5000/20*7=1750 (внутри партии списываем по среднему).
Перейдем в документ Расходная накладная и попробуем реализовать данное списание
(реализуем по методу FIFO).
Поправим запрос для выбора данных из регистра СтоимостьТоваров в обработке
проведения, добавив измерение Партия в выбираемые поля. И посмотрим на результат
запроса при отладке, чтобы понять, какие именно итоги нам нужно получить.
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура,
|
СтоимостьТоваровОстатки.Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток,
|
СтоимостьТоваровОстатки.СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки(
|
&МоментВремени,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура,
|
ДокТЧ.Количество,
|
Остатки.Партия,
|
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
|
Остатки.СтоимостьОстаток
|ИЗ
|
|
|
ДокТЧ КАК ДокТЧ
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура";
Так как количество партий по ложке в регистре СтоимостьТоваров у нас три (три приходные
накладные с ложкой), то в результате запроса видим три строки с различными количеством и
стоимостью, но в расходной накладной у нас всего одна строка с ложкой и она при левом
соединении таблиц дублируется для каждой строки таблицы Остатки. Следовательно, в
итогах по столбцу КоличествоОстаток нам нужна сумма, а по столбцу Количество либо
минимум, либо максимум (это одно и то же в данном случае).
Получили:
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура,
|
СтоимостьТоваровОстатки.Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток,
|
СтоимостьТоваровОстатки.СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки(
|
&МоментВремени,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
Остатки.Партия,
|
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
|
Остатки.СтоимостьОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
|
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура
|ИТОГИ
|
МИНИМУМ(Количество),
|
СУММА(КоличествоОстаток)
|ПО
|
Номенклатура";
Для обхода результата у нас будет два цикла: первый — по товарам (у нас может быть не
только ложка, но и еще что-то в документе расходная накладная), а второй цикл по партиям,
где мы будем уже списывать стоимость.
Запрос.Текст =
"ВЫБРАТЬ
|
СтоимостьТоваровОстатки.Номенклатура,
|
СтоимостьТоваровОстатки.Партия,
|
СтоимостьТоваровОстатки.КоличествоОстаток,
|
СтоимостьТоваровОстатки.СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
|
РегистрНакопления.СтоимостьТоваров.Остатки(
|
&МоментВремени,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
Остатки.Партия,
|
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
|
Остатки.СтоимостьОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
|
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
|
Остатки.Партия.МоментВремени //по FIFO
|ИТОГИ
|
МИНИМУМ(Количество),
|
СУММА(КоличествоОстаток)
|ПО
|
Номенклатура";
Запрос.УстановитьПараметр("МоментВремени", МоментВремени() );
Результат = Запрос.Выполнить();
ВыборкаТовар = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаТовар.Следующий() Цикл
Если ВыборкаТовар.КоличествоОстаток < ВыборкаТовар.Количество Тогда
Сообщить("Что-то пошло не так, остаток стоимости не совпадает с
остатками по складу");
Отказ = Истина;
Возврат;
КонецЕсли;
ВыборкаПартия = ВыборкаТовар.Выбрать();
ОсталосьСписать = ВыборкаТовар.Количество; // наши 11 из расходной
накладной
Пока ВыборкаПартия.Следующий() И ОсталосьСписать<>0 Цикл
Списать = МИН(ОсталосьСписать,ВыборкаПартия.КоличествоОстаток); //
минимум сколько осталось списать, либо сколько в партии есть
Себестоимость = Списать / ВыборкаПартия.КоличествоОстаток *
ВыборкаПартия.СтоимостьОстаток;
Движение = Движения.СтоимостьТоваров.ДобавитьРасход();
Движение.Период = Дата;
Движение.Партия = ВыборкаПартия.Партия;
Движение.Номенклатура = ВыборкаПартия.Номенклатура;
Движение.Количество = Списать;
Движение.Стоимость = Себестоимость;
ОсталосьСписать = ОсталосьСписать - Списать;
КонецЦикла;
КонецЦикла;
Спишем 24 ложки.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
В отчёт "ОстаткиПартийТоваров" (любой) добавьте поле "Средняя цена за единицу". Расчет
элементарный, "СуммаОстаток/КоличествоОстаток". Помните что деление на ноль приводит к
ошибке.
Посмотрите описание условного предложения в языке запросов ВЫБОР КОГДА.
Измените алгоритм проведения расходной, так, чтобы самые дорогие товары, независимо от
партий, списывались первыми.
Домашнее задание/Самостоятельное
Решение
Занятие 014
Краткое содержание
1. Регистры накопления оборотов
2. Виртуальная таблица регистров накопления оборотов
3. Агрегаты
4. Планы видов характеристик
5. Отчёты с доп. характеристиками
6. Составные виды характеристик
Детальное описание
Зачем мы считали себестоимость на прошлых занятиях? Чтобы в дальнейшем посчитать
прибыль.
Товар
За сколько купили
За сколько продали
Профит
Ложка
10
1000
990
Можем ли мы такой отчёт сформировать на основе тех данных, что уже есть в нашей базе?
В принципе да — можем, данные по продажам у нас есть в расходных накладных, а
себестоимость в регистре СтоимостьТоваров. Сделаем такой отчёт, назовем
АнализПродаж1. Получили следующий запрос в СКД:
ВЫБРАТЬ
РасходнаяНакладнаяТовары.Номенклатура,
СУММА(РасходнаяНакладнаяТовары.Сумма) КАК Сумма
ПОМЕСТИТЬ ДанныеОПродажах
ИЗ
Документ.РасходнаяНакладная.Товары КАК РасходнаяНакладнаяТовары
ГДЕ
РасходнаяНакладнаяТовары.Ссылка.Проведен
И РасходнаяНакладнаяТовары.Ссылка.Дата МЕЖДУ &НачалоПериода И
&КонецПериода
СГРУППИРОВАТЬ ПО
РасходнаяНакладнаяТовары.Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СтоимостьТоваровОбороты.Номенклатура,
СтоимостьТоваровОбороты.СтоимостьРасход
ПОМЕСТИТЬ СтоимостьПродаж
ИЗ
РегистрНакопления.СтоимостьТоваров.Обороты(&НачалоПериода, &КонецПериода, , )
КАК СтоимостьТоваровОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДанныеОПродажах.Номенклатура,
ДанныеОПродажах.Сумма КАК Выручка,
СтоимостьПродаж.СтоимостьРасход КАК Себестоимость
ИЗ
ДанныеОПродажах КАК ДанныеОПродажах
ЛЕВОЕ СОЕДИНЕНИЕ СтоимостьПродаж КАК СтоимостьПродаж
ПО ДанныеОПродажах.Номенклатура = СтоимостьПродаж.Номенклатура
Добавим колонку «Прибыль» при помощи поля в пользовательском режиме.
Добавляем это поле в выводимые поля и получаем:
Так же можем посчитать Прибыль в самом запросе (вычисляется на стороне sql сервера) или в
вычисляемых полях СКД (вычисляется на стороне сервера 1С Предприятия).
Но что же не так с этим отчётом?
1. Данные собираются по табличным частям реальных документов (их может быть очень и
очень много с большими табличными частями).
2. Мы получаем в запросе СтоимостьТоваровОбороты.СтоимостьРасход — это обороты, а
регистр накоплений СтоимостьТоваров — это регистр остатков. Обороты у регистра
остатков в агрегированном состоянии не хранятся, т.е. система будет бегать по реальной
таблице движений регистра, чтобы эти обороты посчитать.
3. Что будет если добавиться еще какой-то документ, который будет делать движения в
регистре СтоимостьТоваров, будем отчёт переписывать?
4. Для чего нужны регистры? Чтобы абстрагироваться от документной сущности, т.е. отчёты
должны делаться только на основе регистров.
Попробуем тогда изменить регистр СтоимостьТоваров и добавить туда то, что нам не хватает
для вычисления прибыли. Можем добавить реквизит СуммаПродажи (или ЦенаПродажи) и из
реальной таблицы регистра СтоимостьТоваров сможем данные для прибыли получить. И все
будет работать, но это будет не прибыль.
Потому что ресурс СтоимостьРасход в регистре СтоимостьТоваров — это не всегда продажа
(украли, сгорел и т.п.).
Это хороший пример «имитационного получения показателя»: у нас показатель нигде не
хранится, но нам кажется, что хранится. Получается у нас сейчас нигде не хранится
себестоимость именно продаж. Создадим такой регистр: это будет регистр накоплений
оборотов «Продажи».
Регистр накоплений оборотов отличается от регистра накопления остатков тем, что у него есть
специальные служебные таблицы, хранящие обороты за месяц.
Регистр оборотов Продажи: Измерения — Номенклатура
- Контрагент
Ресурсы
-Количество
-Себестоимость
-Выручка
Сформируем движения:
Но для начала поправим немного запрос. Теперь нам нужно получать сумму продажи из
документа, поэтому в самый первый запрос (где мы создаем временную таблицу ДокТЧ)
добавим сумму в выбираемые поля.
Запрос.Текст =
"ВЫБРАТЬ
| РасходнаяНакладнаяТовары.Номенклатура,
| СУММА(РасходнаяНакладнаяТовары.Количество) КАК Количество,
| СУММА(РасходнаяНакладнаяТовары.Сумма) КАК Сумма
|ПОМЕСТИТЬ ДокТЧ
|ИЗ
| Документ.РасходнаяНакладная.Товары КАК РасходнаяНакладнаяТовары
|ГДЕ
| РасходнаяНакладнаяТовары.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| РасходнаяНакладнаяТовары.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ДокТЧ.Номенклатура,
| ДокТЧ.Количество
|ИЗ
| ДокТЧ КАК ДокТЧ";
Теперь поправим запрос, в котором получаем данные для расчёта себестоимости.
"ВЫБРАТЬ
| СтоимостьТоваровОстатки.Номенклатура,
| СтоимостьТоваровОстатки.Партия,
| СтоимостьТоваровОстатки.КоличествоОстаток,
| СтоимостьТоваровОстатки.СтоимостьОстаток
|ПОМЕСТИТЬ Остатки
|ИЗ
| РегистрНакопления.СтоимостьТоваров.Остатки(
|
&МоментВремени,
|
Номенклатура В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК СтоимостьТоваровОстатки
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ДокТЧ.Номенклатура КАК Номенклатура,
| ДокТЧ.Количество КАК Количество,
| ДокТЧ.Сумма КАК Выручка,
| Остатки.Партия,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
| Остатки.СтоимостьОстаток
|ИЗ
| ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
|
ПО ДокТЧ.Номенклатура = Остатки.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| Остатки.Партия.МоментВремени
|ИТОГИ
| МИНИМУМ(Количество),
| МИНИМУМ(Выручка),
| СУММА(КоличествоОстаток)
|ПО
| Номенклатура";
Тогда движения примут следующий вид (располагаться они будут внутри цикла по товарам, но
после цикла по партиям).
Движение = Движения.Продажи.Добавить();// только один вид движения, т.к. регистр
оборотов
Движение.Период = Дата;
Движение.Контрагент = Контрагент;
Движение.Номенклатура = ВыборкаТовар.Номенклатура;
Движение.Количество = ВыборкаТовар.Количество;
Движение.Себестоимость = СебестоимостьИтого;// накопленная себестоимость, внутри
цикла по партиям
Движение.Выручка = ВыборкаТовар.Выручка;
Проведение по регистру Продажи ничем не обусловлено, поэтому блокировки ставить не
нужно.
После проведения нашей расходной накладной в регистре Продажи получили:
Теперь осталось сделать отчёт по продажам с использованием нового регистра Продажи,
назовем АнализПродаж2.
Запрос для отчёта в СКД будет совсем простым:
ВЫБРАТЬ
ПродажиОбороты.Номенклатура,
ПродажиОбороты.Контрагент,
ПродажиОбороты.КоличествоОборот,
ПродажиОбороты.СебестоимостьОборот,
ПродажиОбороты.ВыручкаОборот
ИЗ
РегистрНакопления.Продажи.Обороты КАК ПродажиОбороты
Заметим, что у регистра оборотов есть всего одна виртуальная таблица Обороты. У данной
таблицы два параметра, указывающих за какой период нужно посчитать обороты (в отличие от
виртуальной таблицы Остатки регистра накоплений остатков, там один параметр-НА какое
число нужно остатки посчитать). В СКД эти параметры система автоматически определяет на
вкладке «Параметры».
Но мы хотим, чтобы пользователь формировал отчёт по стандартным периодам, потому что за
каждый месяц у нас обороты рассчитаны. Поэтому запретим пользователю в качестве
параметра выбирать произвольные даты, для этого создадим параметр ПериодОтчета,
который будет управлять параметрами НачалоПериода и КонецПериода. И пользователю
разрешим управлять только параметром ПериодОтчета.
Вот такой отчёт получили (вилка появилась, потому что в расходную добавили еще вилку).
Зачем нам может быть нужен такой отчёт? Например, для определения плана закупок,
зарплаты менеджерам, вычисления скидки контрагенту и т.д. А для каждого такого сценария
нужны все поля этого отчёта? Нет. Например, сценарий — планируем закупки, для этого нам
нужен анализ продаж за аналогичный период прошлого года. Для этого сценария разрез по
контрагентам нам не нужен.
Таблица итогов регистра Продажи сейчас у нас выглядит следующим образом:
Т.е. сейчас во всех возможных измерениях накапливаются обороты за месяц. Но по сценарию
выше нам не нужна информация в разрезе контрагента, так зачем эта информация в таблице
итогов хранится? А если бы в разрезах контрагента информация не хранилась, то таблица итогов
схлопнулась бы (представим, что ложку покупают 500 контрагентов, то таблица итогов в 500 раз
станет меньше). Для этого можно убрать галку «Использование в итогах» в свойствах
измерения «Контрагент» регистра Продажи. Но тогда, если вдруг обороты по контрагенту
понадобятся, то система будет «бегать» по таблице движений для получения оборотов по
контрагенту.
Но очень часто мы заранее не знаем, какие измерения нам нужны в таблице итогов, а какие нет
(сценарии работы не знаем заранее). Для таких случаев в 1с есть «искусственный интеллект»,
который сам анализирует, какие измерения нам нужны в таблице итогов, а какие нет. Данный
механизм называется Агрегаты (вкладка «Данные» регистра накоплений оборотов), этот
механизм есть только у регистров накоплений оборотов. По факту агрегаты — это та же самая
таблица итогов, только мы сами определяем, за какой период мы хотим агрегировать данные и
по каким измерениям.
Каждая строка в конструкторе агрегатов — это новая таблица. Но система сама решит для каждой
таблицы нужно ее использовать или нет, т.к. у всех трех таблиц стоит в поле Использование
значение Авто. Решения принимаются на основе запросов, насколько часто и по каким разрезам
данные читаются из таблицы итогов.
Самое оптимальное, что мы можем сделать — это создать агрегаты по всем возможным
сочетаниям:
Сейчас мы запланировали, какие таблицы в принципе могут быть. Т.е. создадутся 4 таблицы, но
пустые. А вот будут они накапливаться и когда зависит от собранной статистики (какие данные
выбираются при формировании отчётов, из запросов при проведении документов и т д).
В режиме 1с Предприятие зайдём в Все функции-Стандартные-Управление итогами-вкладка
«Агрегаты» и включим режим агрегатов по кнопке «Использование».
Можно либо использовать режим агрегатов, либо использовать режим итогов. Нажмем
перестроить. Получим
Эффект от использования таблицы агрегатов 4% по сравнению с использованием таблицы
итогов, то есть отчёт АнализПродаж будет строиться на 4 % быстрее.
Главная особенность отличия агрегатов от итогов заключается в том, что агрегаты сами не
перестраиваются и не обновляются никогда. Т.е. если мы документ задним числом провели,
система сама автоматически остатки товаров в итогах пересчитала, а в агрегатах такого нет. В
агрегатах происходит так: мы месяц закрыли, обновили агрегаты по кнопке Обновить, дальше
мы вводим данные задним числом, система считает данные агрегата невалидными и агрегаты не
использует (если по ложке агрегат стал невалидным, система «бежит» по таблице движений). Но
если опять нажать кнопку Обновить, агрегат снова станет актуальным (и да, можно сделать
регламентную операцию, которая будет обновлять агрегаты хоть раз в секунду). Обновление
агрегатов операция быстрая, а вот перестроение агрегатов медленная, поэтому при
перестроении можно поставить ограничение, например, не надо пересчитывать таблицы, если
прирост производительности будет небольшим.
Оптимальные агрегаты — по факту на основе статистики автоматически загружают в
конфигуратор те таблицы, которые мы сами описывали в конструкторе агрегатов, т.е. помогают
создать эти таблицы.
Если большая нагрузка на оборотные показатели, то лучше использовать агрегаты.
Характеристики
У нас ложка, которую мы продаем, сделана из серебра и вилка тоже из серебра. Хотим отчёт
АнализПродаж строить в разрезе материалов.
Хотим иметь возможность описать номенклатуру в различных свойствах, для одной
номенклатуры это будет размер и цвет, для другой — страна происхождения и основной
поставщик и т.д. Ещё мы хотим, чтобы пользователь сам имел возможность эти свойства описать.
Таким образом, нам нужно для номенклатуры хранить ее описание в универсальном виде.
Предположим, что описания номенклатуры мы будем хранить в подчиненном справочнике.
Вопрос такой: что такое у нас зеленый, 26, медь? Добавим в тот же справочник колонку «Вид
свойства».
Теперь нужно определиться с типами, зеленый — наверное, строка, 26 –число, медь — допустим
тоже строка, ну а если еще основной поставщик появится, то это уже из справочника
Контрагенты выбирать бы хотелось. Вот так мы будем хранить характеристики. А вот в каком
виде и как будем разбираться.
Итак, основного поставщика мы храним в справочнике Контрагенты, «зеленый», наверное, тоже
хотим в каком -то справочнике хранить и «медь» еще в одном.
Но сколько таких справочников у нас должно быть, мы заранее ведь не знаем. Поэтому создадим
справочник свойств, подчиненный справочнику «ВидСвойства».
Вся эта сложная схема сводится к двум подчиненным справочникам.
Для номенклатуры нам нужно где-то хранить значения свойств (столбец «Наименование»), но
эти значения зависят от вида свойств. Т.е. у нас есть справочник «Значения свойств», справочник
«Виды свойств» и еще одна «штука», где будет указаны все возможные значения (свойства) для
каждого вида свойства.
Перед реализацией, подумаем как мы будем хранить следующее
Можно конечно сделать реквизит справочника с составным типов данных, но мы заранее не
можем знать все типы данным (потом добавлять придется и отчёты переписывать). Для таких
целей в 1с есть «План видов характеристик», по сути это следующая таблица
Коллекция типа значения (все что там в принципе может храниться) образует «Характеристику».
По факту это составной тип данных с такими галками. Нужна характеристика, чтобы вручную не
наследовать типы значений при изменении этой коллекции. Она сама за нас это сделает.
Итак, создадим План видов характеристик, он практически ничем не отличается от обычного
справочника, назовем ВидыСвойствНоменклатуры. И в режиме 1с Предприятие добавим
несколько свойств
Теперь хотим, чтобы тип значения был не строка, а СправочникСсылка.Контрагенты для
основного поставщика и т.п. Настраивается это здесь
Получили
По факту мы сейчас создали вот этот справочник, он у нас описывает виды свойств.
Теперь хотим хранить информацию о наших ложке и вилке, какой у них цвет, какой основной
поставщик.
Хранить
эту
информацию
будем
в
регистре
сведений
ОписаниеСвойствНоменклатуры:
Измерения: Номенклатура (тип СправочникСсылка.Номенклатура),
ПланВидовХарактеристикСсылка.ВидыСвойствНоменклатуры)
ВидСвойства
(тип
Ресурсы: Значение (тип Характеристика.ВидыСвойствНоменклатуры) — тут указаны все типы,
которые мы выбрали при создании нашего плана видов характеристик в поле «Тип значения
характеристик». И если нам какой-то еще тип туда нужно добавить, мы это делаем только у плана
видов характеристик, а в регистре в тип Характеристика.ВидыСвойствНоменклатуры
добавленный тип автоматически подтянется. Собственно для этого этот отдельный объект
(плана видов характеристик) и сделан.
Еще хотим, чтобы в регистре сведений Значение выбиралось в зависимости от типа поля
ВидСвойства, т.е. если выбираем ВидСвойства — Размер, то в значении мы не можем
контрагента выбрать. Это делается в свойстве ресурса «Значение» на вкладке
«Представление», поле «Связь по типу» (хотим ограничить выбор типа значений, тем который
указан в виде свойства).
Теперь сделаем, чтобы цвет был все-таки не строкой. Для этого добавим подчиненный
справочник
к
плану
видов
характеристик
и
назовем
этот
справочник
ДополнительныеСвойстваНоменклатуры и добавим его в поле «Тип значения характеристик»
в свойствах нашего плана видов характеристик (мы же хотим выбирать что-то из этого
справочника при описании свойств номенклатуры). Перейдем в режим 1с Предприятие и
переопределим тип Вида свойства Цвет, теперь он будет не строкой, а будет браться из
справочника ДополнительныеСвойстваНоменклатуры.
А теперь еще нам захотелось добавить новый вид свойств Страна происхождения, создадим
новый вид свойства и укажем, что он тоже будет храниться в справочнике
ДополнительныеСвойстваНоменклатуры. Если попробовать задать для вилки страну
происхождения, то в форме выбора значения будут, кроме стран происхождения, еще и цвета.
Поэтому нам хочется сделать отбор: когда выбираем страну происхождения, нужно чтобы вид
свойства
влиял
на
открывающуюся
форму
выбора
справочника
ДополнительныеСвойстваНоменклатуры. Настраивается это, как и связь по типу, в свойствах
ресурса Значение регистра сведений ОписаниеСвойствНоменклатуры. То есть мы хотим,
чтобы от вида свойства еще зависело то, как система фильтрует подчиненные элементы
справочника. Это свойство называется «Связи параметров выбора».
Теперь при выборе страны происхождения цвета в форму выбора не попадают.
А собственно зачем мы все это делали? Для отчётов, чтобы в них можно было фильтры делать,
сортировки, группировки. Нам нужно три таблицы связать: справочник Номенклатура, план видов
характеристик
ВидыСвойствНоменклатуры
и
регистр
сведений
ОписаниеСвойствНоменклатуры. И делается это просто в свойствах справочника
Номенклатура, на вкладке Данные кнопка Характеристики.
Теперь в пользовательском режиме мы можем изменять отчёт АнализПродаж и выбрать в нем
созданные свойства для номенклатуры: цвет, страна происхождения, основной поставщик,
размер (делать по ним группировки, отборы и т п).
Таким образом, механизм характеристик позволяет делать «псевдореквизиты», теперь мы
можем в разрезе характеристики построить любой отчёт, сделанный на СКД , а не только
АнализПродаж.
Вопросы к занятию (для самоконтроля)
Занятие 015
Краткое содержание
1. Работа с формой: Сохраняем файлы и картинки.
2. Понятие конвертации данных формы, отображение картинок, временное хранилище,
навигационные ссылки.
Детальное описание
Хотим в справочнике Номенклатура иметь возможность выбора картинки товара и чтобы она
отображалась в форме элемента номенклатуры. Первый вопрос, который возникает, где эти
файлы (картинки) хранить? Тут есть несколько вариантов:
1. Хранить в системе где-то в файле, и в справочник прикладывать путь на этот файл.
Этот вариант плохой, потому что 1с умеет работать в гетерогенной среде. Получается один
пользователь будет открывать с компьютера с Windows, другой с MacOS, еще кто-то вообще с
мобильного приложения и тогда по какому стандарту пути к файлу писать? Непонятно. А вдруг
эти файлы вообще удалили или переместили.
2. Будем хранить картинку в виде реквизита этого самого справочника Номенклатура.
Данный вариант хорош тем, что теперь картинка связана с самим элементом справочника,
любое изменение картинки будет изменением элемента справочника.
Минус в том, что не всегда картинки нужны для сохранения именно бизнес-сущностей, это
дополнительные данные.
3. Сохранение данных в отдельном справочнике или регистре сведений (более
правильный).
Тут тоже есть плюсы и минусы. Плюсы: с точки зрения обмена данных, когда из одной базы
данных информация летит в другую, картинками можно и не обмениваться (они же в другой
таблице у нас)
.
4. Картинки хранятся в отдельной базе данных и в справочнике Номенклатура есть ссылки
на них (самый правильный).
В этом случае нерушимая сущность немного рушится (картинка же уже не в нашей базе
данных хранится). Но при создании бэкапов нашей базы у нас картинки храниться в бэкапах не
будут, а это более правильно ведь они к бизнес-процессу отношения не имеют.
Но сейчас мы будем рассматривать не с точки зрения того как лучше хранить и передавать
картинки, а с точки зрения работы с формой, поэтому мы будем рассматривать второй
вариант, а именно хранение картинке в справочнике Номенклатура. Мы будем говорить не
про работу с внешними файлами, а про работу с формами и картинками, а также про
ВременноеХранилище.
Итак, добавим картинку в качестве нового реквизита справочника Номенклатура. Назовем его
«ДанныеКартинки» и выберем тип «ХранилищеЗначения» (по сути это двоичные данные). В
принципе в этот реквизит можно загрузить все что угодно, а не обязательно картинку.
Откроем форму элемента справочника Номенклатура, почему-то данные картинки не
отображаются на ней.
Видим что в колонке Тип у реквизита ДанныеКартинки написано «Недоступен в данных
формы». Почему? Система не знает, что это за тип значения, он не сериализуется и не может
сконвертироваться и перелететь с сервера на сторону клиента.
Таким образом, ДанныеКартинки клиенту в прямом виде не передаются никогда, они будут
отображены, если клиент, после того, как форма ему передалась, запросит сервер показать
ему какую-то картинку.
Чтобы показать пользователю картинку, добавим реквизит формы АдресКартинки строкового
типа и перенесем его на форму. У элемента формы АдресКартинки в свойстве Вид укажем
вместо «Поле ввода» –«Поле картинки». По факту вид «Поле картинки» указывает системе,
что при выводе формы нужно обратиться к серверу и проверить не картинка ли там выводится
и, если да (если это картинка одного из тех форматов, которые поддерживаются 1с), то
система передаст бинарные данные, которые интерпретатор форм (клиент) получит и покажет
в виде реальной картинки.
Теперь добавим команду «ЗагрузитьФото», чтобы пользователь мог выбирать эту самую
картинку. Но сначала исправим режим совместимости и модальности (зачем скажем позже и
исправим на правильный вариант):
После создания команды, сделаем ее обработчик. Вопрос такой: где мы этот обработчик
будем делать, на клиенте или на сервере?
Нажатие на кнопку происходит на клиенте, а данные картинки должны быть где-то на сервере.
Но мы не можем ее сразу в базу данных записать, потому что пользователь открыл форму,
открыла ложку, поменял картинку ложки на картинку вилки и потом нажал кнопку Отмена, т.е.
в этом случае никакого сохранения не будет. Следовательно, сразу мы не можем картинку
записать в реквизиты, а в данных формы картинки у нас тоже нет. Поэтому картинка
записывается куда-то на сервере (круг с надписью картинка ниже) и ждет события сохранения
формы, тогда система увидит, что у нас есть картинка и в данные ее тоже запишет. Но
записать ее нужно из формы, так как пользователь производит выбор картинки. Вот для таких
событий, как одно из сценариев его применения, является объект ВременноеХранилище. Т.е.
временное хранилище — это данные, которые хранятся на сервере 1с Предприятия, но
которые мы можем записывать и читать со стороны клиента и со стороны сервера. Временное
хранилище существует либо до первого клиент-серверного вызова (когда форма
переинициализируется, тогда все что сделала пользователь будет уничтожено), либо до конца
сеанса пользователя. Как это время объекта настраивается мы посмотрим дальше. Но
абсолютно точно оно живет не дольше сеанса пользователя, когда пользователь закончил
работу, все временные хранилища, им порожденные, будут уничтожены; также временные
хранилища можно привязать к какой-то форме и, если форма перестает существовать
(пользователь ее закрывает), то все временные хранилища связанные с этой формой
уничтожаются.
Мы сделаем следующее: пользователь нажимает кнопку «Загрузить фото», этот файл
перелетает во временное хранилище, мы запоминаем его имя в реквизит АдресКартинки и,
если пользователь сохраняет данные, то временное хранилище в качестве данных картинки
записывается в базу данных. Таким образом, мы на клиенте создадим обработчик команды и
запишем данные во временное хранилище.
Для записи картинки во временное хранилище будем использовать метод ПоместитьФайл():
ПоместитьФайл (<Адрес>, <НачальноеИмяФайла>, <ВыбранноеИмяФайла>, <Интерактивно>,
<УникальныйИдентификаторФормы>) возвращает Истину, если пользователь что-то выбрал,
или Ложь, если нет.
Обработчик команды будет выглядеть следующим образом:
&НаКлиенте
Процедура ЗагрузитьФото(Команда)
АдресВыбраннойКартинки = "";
Если ПоместитьФайл(АдресВыбраннойКартинки,,,Истина,УникальныйИдентификатор)
Тогда //АдресВыбраннойКартинки-адрес временного хранилища, в который система поместит
выбранный файл;Интерактивно-Истина,УникальныйИдентификатор - id формы, с которым
будет связано временное хранилище
АдресКартинки = АдресВыбраннойКартинки;
КонецЕсли;
КонецПроцедуры
Но пока мы только помещаем картинку во временное хранилище, но не сохраняем ее. Также
надо заметить, что при выборе картинки не появляется маркер модифицированности формы
(«звездочка»). Его можно поставить, поставив галку «Изменяет сохраняемые данные» в
свойствах команды «ЗагрузитьФото», тогда при нажатии команды проставлялся маркер
модифицированности у формы. Но это неправильно, так как тогда маркер будет проставляться
безусловно, вне зависимости выбрал пользователь картинку или нет.
Поэтому проставлять маркер будет программно, строкой Модифицированность = Истина;
Теперь осталось записать данные из временного хранилища в базу данных.
Для этого нам нужно определиться с обработчиком какого события мы работаем. Откроем
список всех обработчиков, нам нужны те, которые связаны с записью, но их тоже много.
Еще есть обработчики записи в модуле объекта справочника. Давайте определимся с чем мы
имеем дело.
Во-первых, мы точно работаем с формой, т.е. нам нужны события формы, так как временное
хранилище существует, пока существует форма (т.е. выбираем из тех, что на картинке выше).
Теперь для начала определимся какой обработчик нам нужен «перед», «при» или «после»
записи. Все обработчики с префиксом «После» записи нам не подходят, ведь запись уже
произошла. Все обработчики с префиксом «При» записи — запись уже произошла, но вы
находитесь в транзакции записи еще и гипотетически можете отказаться от самой записи,
следовательно, это нам тоже не подходит. Нам нужно «перед» записью, тогда запись еще не
произошла, но транзакция уже началась. Теперь осталось выбрать между «ПередЗаписью» и
«ПередЗаписьюНаСервере». В событии «ПередЗаписью» на стороне клиента мы перейдем
на сторону сервера, получим копию прикладного объекта, запишем его в базу, а потом
начнется событие «ПередЗаписьюНаСервере», в котором система сама опять получит копию
прикладного объекта и сделает все то же самое. Таким образом нам нужен обработчик
события «ПередЗаписьюНаСервере»: пусть система сама получит прикладной объект, и мы
запишем данные в базу. Получим следующее:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
ТекущийОбъект.ДанныеКартинки = Новый
ХранилищеЗначения(ПолучитьИзВременногоХранилища(АдресКартинки));
КонецПроцедуры
Тогда картинка запишется, но при последующем открытии не будет отображаться. Для этого
ПриСозданииНаСервере (а не ПриОткрытии), когда форма еще конструируется, нам нужно
получить ссылку на данные, которые хранятся в базе данных.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
АдресКартинки = ПолучитьНавигационнуюСсылку(Объект.Ссылка,"ДанныеКартинки");
КонецПроцедуры
Теперь при открытии картинка отображается. Поменяем у ложки цену, попробуем записать и
вылетит ошибка.
Ошибка возникает потому, что в процедуре ПередЗаписьюНаСервере при изменении цены мы
пытаемся получить адрес временного хранилища в строке ТекущийОбъект.ДанныеКартинки =
Новый ХранилищеЗначения(ПолучитьИзВременногоХранилища(АдресКартинки));, но он
пустой. Чтобы ошибка не возникала сделаем проверку:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЭтоАдресВременногоХранилища(АдресКартинки) Тогда
ТекущийОбъект.ДанныеКартинки = Новый
ХранилищеЗначения(ПолучитьИзВременногоХранилища(АдресКартинки));
КонецЕсли;
КонецПроцедуры
Теперь все работает хорошо. Осталось разобраться зачем мы меняли в самом начале режим
совместимости. Вернем режим совместимости в первоначальное значение «Не
использовать» и запустим систему, тогда и нажатии кнопки «Загрузить фото» вылетит
ошибка.
Для метода ПоместитьФайл() есть аналог НачатьПомещениеФайла(), который работает в
немодальном режиме, собственно для всех таких методов есть аналоги, работающие в
немодальном режиме. Теперь нужно переписать код с использование данного метода.
Получим:
&НаКлиенте
Процедура ЗагрузитьФото(Команда)
Оповещение = Новый ОписаниеОповещения("ЗакончилиВыбиратьФайл", ЭтотОбъект);
НачатьПомещениеФайла(Оповещение,,,Истина,УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ЗакончилиВыбиратьФайл(Результат,Адрес,Файл,ДопПараметры) Экспорт
Если Результат Тогда
АдресКартинки = Адрес;
Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
Чтобы не переписывать код, разработчики 1с придумали Рефакторинг (автозамену), система
ищет все функции, использующие модальный режим, и меняет их на аналоги, работающие в
немодальном режиме (но для сложных случаев автозамена не работает, придется самим
описывать). Делается по кнопке Текст-Рефакторинг-Нерекомендуемые синхронные вызовыНайти вызовы модуля/Преобразовать вызовы модуля. Вот так система нам поменяет код (это
тоже самое, что и код выше):
&НаКлиенте
Процедура ЗагрузитьФото(Команда)
АдресВыбраннойКартинки = "";
НачатьПомещениеФайла(Новый ОписаниеОповещения("ЗагрузитьФотоЗавершение",
ЭтотОбъект, Новый Структура("АдресВыбраннойКартинки", АдресВыбраннойКартинки)),
АдресВыбраннойКартинки,,Истина,УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьФотоЗавершение(Результат, Адрес, ВыбранноеИмяФайла,
ДополнительныеПараметры) Экспорт
АдресВыбраннойКартинки = ДополнительныеПараметры.АдресВыбраннойКартинки;
Если Результат Тогда
АдресКартинки = АдресВыбраннойКартинки;
Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
Обращаем Ваше внимание на то, что любая автоматическая замена кода без понимания логики
работы разрабатываемого приложения может привести к неработоспособности его отдельных
блоков. Потому после каждого автоматического рефакторинга, необходимо самостоятельно
просмотреть код. Не доверяйте слепо автоматическим системам, они тоже совершают ошибки.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Необходимо создать два документа, Приходный и Расходный кассовые ордера (деньги в кассу
поступили или выданы). ПКО уменьшает долг контрагента за товары.
В момент записи РКО, при отсутствии денег в кассе, нужно информировать пользователя, но
документ должен проводиться. Учета валют нет. Для вывода информации пользователю
используйте метод "ПоказатьПредупреждение()".
Реквизитный состав документов и другие данные остаются на Ваше усмотрение.
Домашнее задание/Самостоятельное
Решение
Занятие 016
Краткое содержание
ДанныеЗаполнения
Ввод на основании
Параметры формы
Детальное описание
Механизм ввода на основании позволяет «на основании» одних объектов создавать
заполненные объекты другого типа. К примеру, на основании документа «Счет на оплату»
создавать документ «Расходная накладная», заполненного данными из счета на оплату.
Или на основании элемента справочника «Сотрудники» можем создавать документ «Приказ о
приеме» и т. п. (т. е. объекты могут быть разных типов, необязательно только документы
заполняются на основании документов, а справочники на основании справочников).
Мы создадим документ «Расходная накладная» на основании «Счета на оплату».
Как только мы добавили эту строку (как заполняться еще не описывали) в форме списка и в
форме самого документа «Счет на оплату» появится новая кнопка «Ввести на основании». Т.е.
на самом деле это параметризованная команда, а выбранный документ «Счет на оплату»
является ее параметром.
Как же работает «Ввод на основании»? «Ввод на основании» запускает обработчик события
«ОбработкаЗаполнения» (в модуле документа «Расходная накладная»).
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения,
СтандартнаяОбработка)
// Вставить содержимое обработчика.
КонецПроцедуры
Если хотим отключить стандартное поведение системы, то ставим параметр
СтандартнаяОбработка в Ложь.
ДанныеЗаполнения так как мы рассматриваем механизм «ввода на основании», то в данный
параметр прилетит сам документ (либо какой-то другой объект), на основании которого мы
создаем новый.
Объектов-источников данных может быть много (на основании которых мы документ
заполняем) и у них может быть разный реквизитный состав, поэтому предварительно нужно
проверять, какой тип значения у ДанныхЗаполнения и в зависимости от этого,
«определенным» образом заполнять документ. Но сейчас «предположим», что
ДанныеЗаполнения это один документ.
Итак, заполним документ РасходнаяНакладная данными из документа СчетНаОплату.
Совсем просто заполнение выглядит так:
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения,
СтандартнаяОбработка)
Контрагент = ДанныеЗаполнения.Контрагент;
Для каждого Стр Из ДанныеЗаполнения.Товары Цикл
НоваяСтрока =Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока,Стр);// можем так написать так как реквизиты
табличных частей документов полностью совпадают
КонецЦикла;
КонецПроцедуры
Сделаем то же самое конструктором ввода на основании, получим:
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения,
СтандартнаяОбработка)
//проверка на тип объекта-основания
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.СчетНаОплату") Тогда
// Заполнение шапки
Контрагент = ДанныеЗаполнения.Контрагент;
Для Каждого ТекСтрокаТовары Из ДанныеЗаполнения.Товары Цикл
НоваяСтрока = Товары.Добавить();
НоваяСтрока.Количество = ТекСтрокаТовары.Количество;
НоваяСтрока.Номенклатура = ТекСтрокаТовары.Номенклатура;
НоваяСтрока.Сумма = ТекСтрокаТовары.Сумма;
НоваяСтрока.Цена = ТекСтрокаТовары.Цена;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
!!! Но тип ДанныхЗаполнения не всегда ссылочный, он может быть еще и структурой.
ОбработкаЗаполнения также вызывается при создании документа без ввода на основании.
Приведем пример.
В свойствах реквизита Контрагент документа РасходнаяНакладная, есть галочка «Заполнять
из данных заполнения», поставим ее.
Теперь в пользовательском режиме зайдем в список документов «Расходная накладная» и с
помощью отбора найдем все документы с контрагентом «ТОО МИР».
Таких документов не нашлось. Создадим такой и увидим, что контрагент автоматически
заполнится значением «ТОО МИР».
Как раз из-за той галочки, а в ОбработкуЗаполнения при этом прилетит структура.
Таким образом, эту галочку можно поставить для любых реквизитов, тогда при создании
нового документа с включенными отборами его реквизиты будут заполнены данным отборов
при условии, что СтандартнаяОбработка принудительно не отключена (нет такой строки в
коде СтандартнаяОбработка = Ложь).
Поэтому в ОбработкеЗаполнения и нужно делать проверку - на основе чего мы заполняем
документ (Если ТипЗнч(ДанныеЗаполнения)=Тип("ДокументСсылка.СчетНаОплату")…).
Но механизм заполнения реквизитов из данных заполнения используется редко, так как
пользователь чаще всего не умеет делать такие отборы, а пользуется поиском по строке в
форме списка, а он на данные заполнения никак не влияет.
Механизм ввода на основании может быть двусторонним, т е мы можем создавать как
«Расходную накладную» на основании «Счет на оплату», так и наоборот.
Куда же дальше ДанныеЗаполнения «полетят», после процедуры ОбработкаЗаполнения
модуля объекта?
Вспомним, что управляемые формы нигде не хранятся, а конструируются в момент их вызова
на основании различных параметров, настроек ролей и т.п. Конструируется она в событии
ПриСозданииНаСервере (т. е. в данной процедуре формы еще нет) на стороне сервера.
Посмотрим, какие параметры есть у формы при ее конструировании с помощью отладчика.
Создадим «Расходную накладную» на основании «Счета на оплату».
Форма конструируется на основании этих параметров. Как видим их много, но все они
доступны только в момент создания формы на стороне сервера и здесь их можно
переопределять. Теперь посмотрим, параметры при открытии формы.
Теперь параметров намного меньше, т.к. система удаляет все неключевые параметры формы
после того, как событие ПриСозданииНаСервере отработало. Поясним что такое ключевой
параметр: когда конструируется форма она на основании одного или нескольких параметров
определяется уникальность формы, такие параметры и являются ключевыми. Т.е. задав
значение параметра Ключ формы можно открыть форму документа программно.
Таким образом, в момент жизни формы существуют только ключевые параметры. И мы можем
создавать свои собственные параметры, в том числе ключевые.
Итак, у формы есть предопределенные параметры, параметры, созданные на вкладке
«параметры» самой формы (см. рис выше) и плюс еще параметры, заданные при вызове
формы (когда форма будет открываться). Соответственно коллекция параметров, которая
прилетит при создании формы, заранее нам неизвестна. Переопределяя параметры, мы
изменяем стандартное поведение формы. Покажем это на примере: сымитируем ввод на
основании при помощи кнопки. В форме документа «Счет на оплату» создадим команду «Ввод
на основании вручную».
&НаКлиенте
Процедура ВводНаОснованииВручную(Команда)
Парам = Новый Структура;
Парам.Вставить("Основание",Объект.Ссылка);
ОткрытьФорму("Документ.РасходнаяНакладная.Форма.ФормаДокумента",парам);
КонецПроцедуры
Теперь при нажатии на кнопку «Ввод на основании вручную» создается «Расходная
накладная» с заполненными значениями. И если отладчиком посмотреть значение
ДанныхЗаполнения при нажатии на эту кнопку, то там будет этот документ «Счет на оплату».
Мы также можем еще передать и нестандартные, какие-то свои параметры. Передадим
параметр Скидка, мы его только что придумали.
&НаКлиенте
Процедура ВводНаОснованииВручную(Команда)
Парам = Новый Структура;
Парам.Вставить("Основание",Объект.Ссылка);
Парам.Вставить("Скидка", 10);
ОткрытьФорму("Документ.РасходнаяНакладная.Форма.ФормаДокумента",парам);
КонецПроцедуры
При отладке в обработчике события ПриСозданииНаСервере формы документа расходная
накладная видим, что появился наш параметр Скидка с значением 10.
Этот параметр мы не описывали на закладке «Параметры» конструктора управляемых форм,
он не является стандартным параметром, просто мы его придумали и передали в функцию
ОткрытьФорму. Причем как только отработает процедура ПриСозданииНаСервере этот
параметр перестанет существовать, так как он не является ключевым.
Итак, параметры влияют на создание формы, на создание объектов, которые будут
создаваться во время конструирования формы, если эта форма новая; если форма
существующая, то на основе параметров можно определить, как она будет себя вести- будет
ли это форма выбора, будет ли открываться какой-то конкретный документ и т. д.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
Занятие 017
Краткое содержание
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
Занятие 018
Краткое содержание
Регистр бухгалтерии
Ручные операции
ОСВ
Детальное описание
Регистр бухгалтерии
Создадим регистр бухгалтерии РегистрБухгалтерии. На вкладке «Основные» в графе «План
счетов» задается тип измерения «Счет» регистра бухгалтерии.
Флаг «Корреспонденция» отвечает за то, в каком виде мы будем вводить данные в регистр
бухгалтерии. Принцип двойной записи соблюдается с флагом и без. В первом случае это
контролируется системой:
с поставленной галкой (флагом)
без галки
В чем отличие наличие и отсутствие флага «Корреспонденция»? Отличие этих записей в том,
что только в первом случае (с галкой) можно прочитать корреспондирующие обороты, во
втором случае (без галки) изменения показателей разорваны друг от друга.
Мы галку «Корреспонденция» поставим. На вкладке «Данные» создадим один ресурс
«Сумма». У ресурса есть свойство «Балансовый», если эта галка стоит то изменение будет
сразу по обоим счетам (СчетДт и СчетКт):
А если эту галку снять, то система автоматически сделает измерения для ввода данных в виде
двух полей, и никакого баланса накапливаться не будет, т.е. мы сможем по дебету и по
кредиту записать разные суммы:
Мы галку «Балансовый» у ресурса «Сумма» поставим.
Реквизит у любых регистров- это дополнительный комментарий к движению. Так как мы
предполагаем, что бухгалтер сам будет создавать проводки в регистре бухгалтерии, то
создадим реквизит «СодержаниеПроводки» типа Строка неограниченной длины.
Ручные операции
Сделаем документ «ОперацияБух», который позволит вводить данные о любых операциях с
точки зрения ведения бухгалтерского учета прямо в регистр (т.е. позволит самостоятельно
заполнить колонки СчетДт, СчетКт, Сумма, СодержаниеПроводки). Для этой цели мы
пользователю покажем саму таблицу движений регистра бухгалтерии, и пусть он
непосредственно в нее, минуя алгоритм проведения, все данные руками вносит. На вкладке
«Движения» документа ОперацияБух поставим свойство Проведение в значение
Запретить, но при этом отметим галкой, что движения по регистру бухгалтерии
РегистрБухгалтерии он делать будет.
И вот эту таблицу движений (эту галочку которую мы поставили) мы пользователю в форме и
покажем. Создадим форму документа и перенесем движения ДвиженияРегистрБухгалтерии
на форму.
Но если пользователь в настройках уберет из формы отображение движений, то движения не
прочитаются, т. к. галка «Использовать всегда» не стоит и считывание данных происходит
только тогда, когда они отображаются на форме.
Поле Период с формы в Движениях уберем, так как туда будем записывать дату документа в
модуле объекта. Делать это будем в обработчике события ПередЗаписью (а не ПриЗаписи,
так как тут уже запись физически произошла).
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Для Каждого Проводка из Движения.РегистрБухгалтерии Цикл
Проводка.Период = Дата;
КонецЦикла;
КонецПроцедуры
Введем несколько документов ОперацияБух и в РБ РегистрБухгалтерии получим
следующие движения.
ОСВ (Оборотно-сальдовая ведомость)
Теперь на основе этих данных сделаем отчет «Оборотно-сальдовая ведомость» (ОСВ).
Делать его будем с помощью СКД, запросом данные выберем из виртуальной таблицы
остатков и оборотов РБ РегистрБухгалтерии.
Поговорим поподробнее про ресурсы данной виртуальной таблицы. Реально в базе хранятся
только СуммаНачальныйОстаток, СуммаКонечныйОстаток и СуммаОборот (на самом
деле немного привираем тут). Что же все остальное? Система проверяет, что лежит в
таблице, например в реальном остатке и проверяет вид счета (активный или пассивный). Если
счет активный, то система эти данные запишет в остаток Дт (даже если он отрицательный). В
остаток Кт система выведет данные, если счет является пассивным, причем знак суммы
реальной таблицы изменит на противоположный (если в реальной таблице
СуммаНачальныйОстаток = -1000 и счет пассивный, то СуммаНачальныйОстатокКт =
1000). И тут нужно обратить внимание вот на что: система при выборе полей с припиской Дт
или Кт будет делать еще один запрос к таблице ПланаСчетов, чтобы проверить, какой это
счет – активный или пассивный. Отсюда такой вывод: если заранее известно, к какому счету
обращаемся, не нужно использовать Дт/Кт, используем просто СуммаНачальныйОстаток
(или там СуммаКонечныйОстаток в зависимости от задачи). В нашем случае нам нужны
данные по всем счетам, поэтому мы будем использовать СуммаНачальныйОстатокДт и
СуммаНачальныйОстатокКт. Что такое СуммаНачальныйРазвернутыйОстатокДт,
СуммаНачальныйРазвернутыйОстатокКт и т.п.? Развернутый остаток – это всегда итоги:
итоги по счету-группе, общие итоги; система будет их рассчитывать по смыслу бухгалтерского
учета. Например, если один поставщик нам должен 1000, а мы другому поставщику должны
300, то развернутый остаток нам так и покажет 1000 и 300 отдельно. А свернутый остаток
покажет 700 по тому поставщику, который нам должен. Нам для ОСВ развернутые остатки не
нужны (при работе в СКД развернутые остатки вообще не нужны, так как у СКД свои
настройки).
В результате получим следующий запрос:
ВЫБРАТЬ
РегистрБухгалтерииОстаткиИОбороты.Счет КАК Счет,
РегистрБухгалтерииОстаткиИОбороты.СуммаНачальныйОстатокДт КАК
СуммаНачальныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаНачальныйОстатокКт КАК
СуммаНачальныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.СуммаОборотДт КАК СуммаОборотДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаОборотКт КАК СуммаОборотКт,
РегистрБухгалтерииОстаткиИОбороты.СуммаКонечныйОстатокДт КАК
СуммаКонечныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаКонечныйОстатокКт КАК
СуммаКонечныйОстатокКт
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.ОстаткиИОбороты КАК
РегистрБухгалтерииОстаткиИОбороты
Получим ОСВ следующего вида:
Не нравится, что нет итогов, так как СКД видит, что выбираются остатки по смыслу бух. учета и
считает их как Дт - Кт, а это всегда 0, если выводить остатки по всем счетам. Как получить
итоги виртуальной таблицы остатки в математическом смысле, а не бух. учета? Для того,
чтобы система считала просто математические итоги (а не по смыслу бух. учета), в СКД нужно
убрать роли у СуммаНачальныйОстатокДт, СуммаНачальныйОстатокКт,
СуммаКонечныйОстатокДт, СуммаКонечныйОстатокКт.
Роль Счет тоже можно убрать, но она уже ни на что не влияет. Получили следующее:
Изменим немного заголовок – сделаем группы и дадим возможность задавать период отчета.
Если мы хотим в дальнейшем управлять папками, то нужно их прям создавать кнопкой, но в
нашем случае мы их «создадим», изменив Путь таким образом: для поля
СуммаНачальныйОстатокДт – путь «СальдоНачальное.Дебет», где до точки - это название
папки, после точки - название поля.
Получили:
Поменяем названия колонок с СальдоНачальное.Дебет на просто Дебет. Делается это в
СКД, в Настройках на вкладке «Другие настройки»: свойство «Тип заголовка полей» установим
в значение «Краткий» и зададим оформление. Получили:
Заметим, что в отчете мы не делали отбор по виду счета, т.е. если будет забалансовый счет,
то в ОСВ дебет с кредитом не сойдутся. Поэтому в пользовательском режиме сделаем такой
отбор на всякий случай. Осталось добавить отбор по периоду.
Отчет в итоговом варианте будет выглядеть следующим образом:
Напоследок решим задачу, не связанную с бухгалтерией, а связанную с работой с формами.
Задача заключается в следующем: хотим, чтобы в форме списка документа ОперацияБух под
табличной частью с документами была табличная часть с теми проводками (движениями по
регистру), которые принадлежат выделенному документу в настоящий момент.
Для ее решения создадим форму списка документа ОперацияБух и добавим новый реквизит
формы Проводки типа ДинамическийСписок. Далее у реквизита формы Проводки для
свойства «ОсновнаяТаблица» выберем значение РегистрБухгалтерии (в дальнейшем когда
в конфигурацию добавим субконто, то нужно будет выбрать
РегистрБухгалтерии.ДвиженияССубконто).
Это значит: выбрать все из таблицы РегистрБухгалтерии. Перетащим реквизит Проводки на
форму и уберем поля Период и Регистратор.
Теперь по идее вверху у нас документы, внизу проводки выделенного документа. Привяжем
настроенный механизм к событиям формы. Для этого нужно задействовать обработчик
события ПриАктивизацииСтроки табличной части Список (когда пользователь выделяет
новый документ) и обработчик события ПриСозданииНаСервере (когда пользователь
открывает форму документа) в модуле формы списка.
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
Док = Элементы.Список.ТекущаяСтрока;
Проводки.Параметры.УстановитьЗначениеПараметра("Регистратор", Док);
КонецПроцедуры
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Док = Элементы.Список.ТекущаяСтрока;
Проводки.Параметры.УстановитьЗначениеПараметра("Регистратор", Док);
КонецПроцедуры
Также в свойствах реквизита формы Проводки, на вкладке «Объект», поставим галку
«ПроизвольныйЗапрос. И теперь данные будут браться не из поля ОсновнаяТаблица
«РегистрБухгалтерии» как мы указывали в начале, а из запроса, который мы напишем.
Получили:
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Сделать отбор в динамическом списке "Проводки" через отбор, а не через параметр запроса.
Домашнее задание/Самостоятельное
Решение
Отбор динамического списка.
Для начала установим отбор в режиме 1С Предприятие. У динамического списка в меню
«Еще» выбираем пункт «Настроить список …».
Откроется окно настройки списка. На вкладке «Отбор» создадим новый элемент, и заполним
для него необходимые свойства.
Теперь сделаем тоже самое, только с помощью программного кода. Рассмотрим реквизит с
типом Динамический список подробнее. У него есть кроме всего прочего свойство Отбор. Не
будем вдаваться в подробности и добавим отбор именно сюда. Если же внимательно изучить
свойства динамического списка, то свойство Отбор можно найти так же в Компоновщике
настроек, причем как в Настройках, так и в Пользовательских настройках и в Фиксированных
настройках. Отбор из корня реквизита динамического списка соответствует отбору
фиксированных настроек.
Для начала смотрим, что Отбор имеет тип ОтборКомпоновкиДанных. Ищем его в Синтакс
помощнике.
Далее следуем по цепочке. И даже если бы мы не знали что нам нужно создать элемент
отбора, просто изучая описание каждого свойства и метода можно найти нужный. Открываем
Элементы.
Как видим это коллекция у которой есть метод Добавить. Продолжаем.
У метода есть один обязательный параметр Тип. Тип элемента может быть 2 видов из которых
нас интересует ЭлементОтбораКомпоновкиДанных. И в результате возвращается значение с
типом ЭлементОтбораКомпоновкиДанных.
В результате мы можем написать первую строчку программного кода, в которой добавим к
коллекции элементов отбора новый элемент.
Далее нужно заполнить свойства элемента. Переходим в синтакс помощнике к
ЭлементОтбораКомпоновкиДанных.
Для заполнения обязательны Использование, ЛевоеЗначение, ПравоеЗначение и
ВидСравнения. Использование, ПравоеЗначение и ВидСравнения не вызывает трудностей.
Рассмотрим ЛевоеЗначение в синтакс помощнике.
Видим что ЛевомуЗначению нужно присвоить ПолеКомпоновкиДанных который создается
конструктором.
Если наш код будет вызываться многократно, то в самом простом случае можно очищать
коллекцию элементов перед добавлением. В итоге код примет вид.
Занятие 019
Краткое содержание
Регистр бухгалтерии
Механизм субконто
Детальное описание
Регистр бухгалтерии
Продолжим рассматривать регистр бухгалтерии. Мы сделали документ ОперацияБух,
который позволяет добавлять проводки в ручном режиме. Запрограммируем наши документы,
чтобы они делали проводки в РБ РегистрБухгалтерии автоматически.
Рассмотрим документ ПКО (Приходный кассовый ордер). На вкладке Движения, укажем, что
он будет делать движения по РБ РегистрБухгалтерии, и сформируем движения в
обработчике события ОбработкаПроведения.
Отличие от регистров накоплений. Порядок записей из набора записей регистров накоплений
мы могли использовать любой. Например, такие значения как ВидДвижения, Период и др.
измерения и ресурсы. Но для записей набора записей регистра бухгалтерии порядок важен.
Но в регистрах бухгалтерии сначала нужно задать СчетДт, СчетКт.
Почему счета указываются сначала?
Потому что они обладают определенными свойствами, из-за которых можно или нельзя будет
указывать те или иные ресурсы.
Сейчас у нас ресурс один – это Сумма, но в дальнейшем у нас может появиться ресурс
ВалютнаяСумма. В зависимости от того, есть ли у нас понятие касса в валюте и
взаиморасчеты с покупателем в валюте или нет, будет доступен ресурс ВалютнаяСумма или,
наоборот, недоступен. Поэтому сначала указываем значения СчетДт и СчетКт, а затем
значения всех остальных ресурсов и измерений, иначе система вывалится с ошибкой.
Движения.РегистрБухгалтерии.Записывать = Истина;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Касса;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Покупатели;
Проводка.Сумма = Сумма;
В пользовательском режиме перепроведем один ПКО и увидим проводку в регистре
бухгалтерии.
Механизм субконто
Все хорошо, за исключением одного НО. Сейчас у нас только суммовой учет (синтетический
учет), а в ПКО указывается контрагент.
Каким образом (как и в регистрах накоплений) вести учет в разрезе конкретных
покупателей, поставщиков и т.д?
Перейдем к понятию аналитики в регистрах бухгалтерии. И тут возникает проблема. Счетов у
нас много, и мы не можем для разных счетов указать разные измерения.
В регистрах накопления было все просто, у каждого регистра — свои измерения. А в регистрах
бухгалтерии — для каждого счета свои измерения, хотя регистр — один.
Так как от счета зависит, перечень аналитики на этом счете, а счета и аналитику какую-то
может придумывать сам пользователь то нам нужен универсальный механизм, который
позволит это реализовать.
Какой универсальный механизм позволит реализовать для разных счетов указать свой
перечень аналитики?
В итоге мы хотим, чтобы остатки регистре бухгалтерии хранились в таком виде:
Какой тип значения будет у столбцов с аналитикой (2 и 3 столбцы)?
Ответ – составной тип, потому что мы не знаем, какой счет выберет пользователь, и, какая
аналитика по этому счету ему нужна.
Мы знаем, что существует объект, который позволяет создавать виды аналитического учета –
это план видов характеристик (механизм характеристик), что в регистрах бухгалтерии
называется понятием субконто. Мы создадим план видов характеристик, чтобы пользователь
сам мог создавать виды аналитики, назовем его ВидыСубконто. Зададим типы значения
характеристик. Он будет составного типа: для наших счетов выберем только типы, указанные
ниже (в реальной жизни примитивных типов не должно быть, на экзамене 1с Специалист
можно).
Создадим предопределенный вид характеристик: контрагенты.
Как субконто привязать к счетам?
Это настраивается в плане счетов, именно тут настраивается, какая аналитика к каким счетам
привязывается. А регистр бухгалтерии будет наследовать свойства плана счетов и добавит,
столько полей для формирования записи (субконто) – сколько мы заранее предусмотрим.
Как плане счетов у счета добавить аналитику (в предопределенных)?
Для того, чтобы в плане счетов у счета добавить аналитику (в предопределенных), нужно
указать ее источник. Это указывается на вкладке Субконто в свойстве «Виды субконто».
Выберем в данном поле только что созданный план видов характеристик ВидыСубконто.
На что повлияет свойство «Максимальное количество субконто»?
Оно влияет на то, сколько строчек видов субконто (в предопределенной таблице) мы сможем
указать для каждого счета. Т.е. насколько детально мы хотим разбить счет.
В нашей задаче максимальное количество для всех субконто будет равно 2, а в реальной
жизни не больше 3 (чуть позже скажем почему).
Какое максимальное значение позволяет установить платформа? - 50!!!
После этого таблице остатков регистра бухгалтерии появятся два новых поля: Субконто1 и
Субконто2, как мы и хотели.
Добавим аналитику по покупателям и по поставщикам.
Теперь сформируем движения по регистру бухгалтерии тем же самым приходным кассовым
ордером. Получим следующее в ОбработкеПроведения:
Движения.РегистрБухгалтерии.Записывать = Истина;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Касса;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Покупатели;
//Проводка.СубконтоКт.Контрагенты = Контрагент;// то же самое, что и строка ниже,
только тут система будет пытаться строить запрос к базе и найти предопределенный элемент
по наименованию "Контрагент"
Проводка.СубконтоКт [ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент; // тут мы указываем предопределенный вид характеристики вручную, работает
чуть быстрее, но при современных мощностях разница не особо заметна
Проводка.Сумма = Сумма;
Перепроведем ПКО, у нас появились СубконтоДт1, СубконтоДт2 и СубконтоКт1,
СубконтоКт2:
Причем для всех счетов таблица одна, но тип значений в этих полях будет разный.
Почему же тип значений в этих полях будет разный? - Из-за того, что субконто наследует
тип значения, который указан в плане видов характеристик.
Как хранятся таблицы в базе данных?
Теперь поподробнее рассмотрим структуру хранения таблиц в БД. До введения понятия
субконто в нашу базу движения и остатки выглядели следующим образом:
Что у нас произошло после добавления субконто?
В верхней таблице (в таблице с движениями только по синтетике) ничего не поменялось.
Система просто создала еще одну таблицу, так как количество субконто по различным счетам
разное, так называемую таблицу «Движения субконто (аналитики)».
По сочетанию Регистратор + НомерСтроки делается соответствие с таблицей «Движения
синтетики». Строчка кода Проводка.СубконтоКт
[ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] = Контрагент; - это по сути
обращение к таблице «Движения субконто». Никаких сумм, количества в таблице
«Движения субконто» нет. Есть просто информация о накоплении информации для
аналитического учета.
Как система будет выбирать данные, когда мы будем какие-нибудь запросы строить?
Система будем обращаться в таблицу «Движения субконто», вытаскивать оттуда данные и
связывать с реальной таблицей.
Что у нас произошло после добавления субконто с таблицей остатков?
В системе всегда будет таблица с синтетическими остатками. Но таблица остатков также
останется. А для хранения информации об аналитике появится новая таблица. В нашем
случае для счета Товары появится таблица, с двумя субконто. А для счета Поставщики в
базе будет хранится еще одна таблица только уже с одним субконто.
Сколько будет хранится в базе таблиц Остатки по субконто?
Таблиц у нас будет столько, сколько максимальное количество субконто мы сделаем (в нашем
случае их 2).
Таким образом, у нас в базе хранится таблица с синтетическими остатками без всякой
аналитики, таблица с остатками по тем счетам, по которым указан один уровень субконто, и
таблица, по тем счетам, по которым указаны остатки по двум субконто.
Таблица остатков должна быть максимально проиндексирована, посчитаем, сколько полейиндексов сейчас уже занято, например, у таблицы остатков с двумя субконто. Поле Период
имеет тип Дата и занимает 1 индексное поле, Счет это ссылочное поле (ПланСчетовСсылка)
и тоже занимает 1 индексное поле; с полем Субконто сложнее, оно составного типа и если как
у нас состоит только из ссылочных типов, то занимает 3 индексных поля (Вид-ТипЗначение/Справочник-Номенклатура-Ложка); если в составной тип добавить еще
примитивные типы, то система на каждый из них будет хранить еще одно поле.
Так как до какой-то версии Microsoft SQL Server не поддерживал индексацию больше 16
полей, то, если добавить все примитивные типы (+4 ) и/или увеличить максимальное
количество субконто, то мы можем выйти за границу индексации. Поэтому максимальное
количество субконто 50 никем никогда не используется, даже больше 3 никем, никогда не
используется!!! Если нужна какая-то аналитика по большинству счетов, которые не входят в
примитивные типы, то проще добавить измерение в регистр бухгалтерии. Итак: 1.
Примитивные типы в коллекцию субконто добавлять не надо (в реальной жизни), это на
порядок замедлит работу системы 2. Просто так делать много субконто в свойстве
«Максимальное количество субконто» Плана счетов не надо.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
Занятие 020
Краткое содержание
Регистр бухгалтерии
Признаки учета
Количественный учет
Детальное описание
Рассмотрим количественный учет на примере купли-продажи товара, но для начала добавим
два новых вида - аналитики Номенклатура и Склады в план видов характеристик
ВидыСубконто в качестве предопределенных элементов.
Пользователь тоже может создавать новые виды аналитики, создадим Учредители в режиме
1С Предприятие
Теперь добавим предопределенные виды аналитики к предопределенным счетам в Плане
счетов.
Теперь мы сможем сформировать движения (купля-продажа товара), но пока что только
суммовые. Сначала опишем движения в документе «Приходная накладная», предварительно
поставив галку, что документ делает движения по РБ РегистрБухгалтерии на вкладке
«Движения» документа. ОбработкаПроведения документа ПриходнаяНакладная с
добавленными движениями по регистру бухгалтерии выглядит следующим образом:
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.ОстаткиТоваров.Записывать = Истина;
Движения.СтоимостьТоваров.Записывать = Истина;
Движения.РегистрБухгалтерии.Записывать = Истина;// не забывать ставить маркер
"надо бы записать"
ТЗ = Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура","Количество, Сумма");
Для Каждого ТекСтрокаТовары Из ТЗ Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение = Движения.СтоимостьТоваров.ДобавитьПриход();
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Партия = Ссылка;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение.Стоимость = ТекСтрокаТовары.Сумма;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Товары;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Поставщики;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
ТекСтрокаТовары.Номенклатура;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Склад] = Склад;
Проводка.Сумма = ТекСтрокаТовары.Сумма;
КонецЦикла;
КонецПроцедуры
Но тут нам не хватает количественного учета, он нам нужен, например, чтобы себестоимость
посчитать. Себестоимость в механизмах бухгалтерии будем считать двумя способами: 1.
Себестоимость будет считаться в разрезе склада (сейчас будем делать) 2. Себестоимость
будет считаться без учета складов (реализуем позже).
Почему сразу не реализовать без учета складов? Дело в том, что в плане счетов по счету
Товар два разреза аналитики – Номенклатура и Склад. В регистрах накопления мы
количество хранили в одном регистре, стоимость в другом, в котором не было измерения
Склад. А сейчас у нас счет один, но по сути это «один регистр накопления» и нельзя один счет
вести только в количественном выражении, а другой только в суммовом. И пока мы реализуем
ситуацию, как будто бы нет возможности без разреза по складу посчитать себестоимость.
Итак, что же будем делать с количеством?
СчетДт
СчетКт
Сумма
Количество
Товары
Поставщики
1000
10
Если мы добавим в регистр бухгалтерии новый ресурс Количество как в таблице выше. Если
по Количество теперь сформировать движения, то это значение ресурса будет менять данные
по обоим счетам, но что у счета Поставщики будет равно 10, у нас нет аналитики
Номенклатура по поставщикам? Поэтому нужно будет создать два поля: КоличествоДт и
КоличествоКт.
СчетДт
СчетКт
Сумма
КоличествоДт
Товары
Поставщики
1000
10
КоличествоК
т
И тогда мы сможем по счету Товары накапливать количество (КоличествоДт), а по счету
Поставщики количество не будет накапливаться (КоличествоКт будет пустым). Итак,
создадим в регистре бухгалтерии ресурс Количество и уберем галку «Балансовый» - это как
раз и повлияет на появление КоличествоДт и КоличествоКт, т.е. баланс по количеству не
накапливается.
Но системе теперь нужно знать, по каким счетам нужно накапливать количество, а по каким
нет. Для этого в плане счетов существуют «Признаки учета», находится это свойство на
вкладке «Данные». Сам «Признак учета» - это булево, и ничем он от обычных реквизитов не
отличается (просто дополнительный реквизит). Просто именно из списка признаков учета
можно будет определенные действия делать, поэтому их выделили «отдельным пунктом».
Добавим признак учета «Количественный». Теперь для каждого счета кроме стандартных
реквизитов (Код, Наименование, Порядок, Вид и т .п.) мы будем еще задавать признак
Количественный.
И еще свяжем счета с признаками учета, в те счета, по которым нужно вести количественный
учет, мы добавим соответствующую галочку.
Осталось связать ресурс Количество регистра бухгалтерии с признаком учета
Количественный плана счетов (чтобы система поняла, за какой ресурс по факту этот признак
учета (эта галочка) отвечает). Делается это в свойствах ресурса Количество регистра
бухгалтерии, свойство называется «Признак учета».
Теперь при попытке сделать движение по ресурсу Количество по счету, у которого нет
признака Количественный, система вылетит с ошибкой.
Есть «такое правило», если галка «Балансовый» не стоит у ресурса регистра бухгалтерии,
надо указать свойство «Признак учета»; если галка «Балансовый» стоит, то свойство
«Признак учета» остается пустым.
Добавим в ОбработкуПроведения документа ПриходнаяНакладная еще и движение по
количеству, получим следующий код:
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.ОстаткиТоваров.Записывать = Истина;
Движения.СтоимостьТоваров.Записывать = Истина;
Движения.РегистрБухгалтерии.Записывать = Истина;
ТЗ = Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура","Количество, Сумма");
Для Каждого ТекСтрокаТовары Из ТЗ Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение = Движения.СтоимостьТоваров.ДобавитьПриход();
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Партия = Ссылка;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение.Стоимость = ТекСтрокаТовары.Сумма;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Товары;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Поставщики;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
ТекСтрокаТовары.Номенклатура;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.Сумма = ТекСтрокаТовары.Сумма;
Проводка.КоличествоДт = ТекСтрокаТовары.Количество;
//Проводка.КоличествоКт = ТекСтрокаТовары.Количество; //ОШИБКА, у счета
Поставщики нет признака учета Количественный
Проводка.СодержаниеПроводки = "Поступили товары";
КонецЦикла;
КонецПроцедуры
Перепровели приходную накладную и получили проводки:
Добавили новый ресурс, хотим сделать отчет. Назовем его «Остатки ТМЦ».
Нам нужны остатки, поэтому в запросе в СКД будем данные выбирать из таблицы остатков
регистра бухгалтерии. Поговорим немного о параметрах виртуальной таблицы
РегистрБухгалтерииОстатки: нам нужны данные только по счету Товары, это задается в
поле «Условие счета» строкой Счет = & Счет (тогда параметр появится на вкладке
«Параметры» СКД, где надо будет указать значение), либо указать явно значение Счет =
ЗНАЧЕНИЕ(ПланСчетов.ПланСчетов.Товары). В параметре «Субконто» виртуальной
таблицы задается типизация выбора полей из регистра бухгалтерии. Поясним эту фразу:
неважно как в счете указаны субконто (Номенклатура и Склад) или (Склад и Номенклатура),
выбрать данные мы можем как номенклатуру в разрезе складов, так и склады в разрезе
номенклатуры. Именно параметром Субконто определяется, что мы будем выбирать. В него
можно передать либо один вид субконто &Номенклатура, тогда система выберет данные по
этому виду субконто по заданному счету, если он указан (или по всем счетам с данным видом
субконто, если счет не указан); либо в параметр Субконто можно передать массив и в массив
передать два вида субконто – Номенклатура, Склады. Тогда система выберет по всем счетам
(если условие счета пустое) остатки (в нашем случае) и первым субконто будет называться
Номенклатура, а вторым субконто будет называться Склады. Но мы можем передать и в
обратном порядке – Склады, Номенклатура. Для выборки данных неважно в каком порядке в
плане счетов субконто указано, выбрать можно в любом порядке. Единственное, если массив
в параметрах таблицы будет совпадать с порядком следования субконто у счета, то система
выберет это быстрее. Поэтому порядок следования субконто у счета должен совпадать с
самым популярным сценарием извлечения данных. Параметром «Условие» мы можем
установить условие по субконто, например, НЕ Субконто1.ЭтоГруппа: так как субконто - это
составной тип данных (в него обычно входят почти все справочники и некоторые документы),
то такое обращение «через точку» приведет к огромному запросу, который будет соединять
все таблицы, которые входят в этот составной тип, для того чтобы выбрать оттуда поле
ЭтоГруппа. Поэтому обращаться к субконто через точку, равно как и к регистратору (он тоже
составного типа) какого-либо регистра через точку это ЖЕСТЬ! Если нужно, например,
отобрать субконто, не являющееся группой, используем функцию ВЫРАЗИТЬ: НЕ ВЫРАЗИТЬ
(Субконто1 КАК Справочник.Номенклатура).ЭтоГруппа –теперь запрос будет построен с
одним левым соединением к справочнику Номенклатура. Причем параметр «Субконто» никак
не влияет на параметр «Условие». Даже если в Субконто указан параметр Номенклатура, то
в Условии все равно будет запрос из 5 левых соединений (в нашем случае 5), т.е. при
накладывании условий на субконто нужно обязательно делать типизацию. Параметр Период
задавать не будем, в СКД он автоматически попадет в параметры.
Пока что зададим только Счет. Из этой таблицы выберем несколько полей, получим
следующий запрос:
ВЫБРАТЬ
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
РегистрБухгалтерииОстатки.Субконто2 КАК Субконто2,
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
РегистрБухгалтерииОстатки.КоличествоОстаток КАК КоличествоОстаток
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(, Счет = &Счет, , ) КАК
РегистрБухгалтерииОстатки
Система СКД автоматически для Субконто1 и Субконто2 настроила роли (см. картинку ниже).
Во-первых, субконто является измерением и если они друг от друга зависят, то можно их
каким-то образом связать (кто является Родителем, кто Владельцем). У нас они независимы, и
пользователь может настроить отчет и в одном измерении, и в другом. Галка «Игнорировать
значение NULL» - мы же можем не делать отбор по счетам и выводить данные по колонкам
Субконто1 и Субконто2. Но не у всех счетов есть 2 субконто, тогда вместо Субконто2 будут
пустые строки; в том случае, когда эта галка стоит, пустые значения выводиться не будут.
склад
Укажем в СКД, что ресурс Количество будем суммировать только по Субконто1.
Параметры заполним пока следующим образом:
Получим такой отчет:
Все хорошо, но сейчас этот отчет не подходит под понятие остатки ТМЦ, потому что
пользователь может создать свой новый счет, например, счет 01.4 Материалы –активный,
количественный с одним Субконто –Номенклатура. Потом пользователь операцией мог был
оприходовать материалы на этот счет и по идее отчет «Остатки ТМЦ» должен был бы
отразить эти данные. Поэтому нам нужно как-то переделать наш отчет. Что такое остатки
ТМЦ? Это, во-первых, остатки количественные, следовательно, мы можем поставить отбор по
признаку счета Количественный. Во-вторых, остатки ТМЦ это всегда активный счет. И, втретьих, по этому счету должна вестись аналитика по Номенклатуре (&ВидыСубконто).
Субконто2 уберем, так как мы теперь выбрать его не можем из-за того, что в параметре
Субконто установили один параметр. В настройках СКД можно было делать необязательные
поля, но мы опустим это. Запрос получится следующий:
ВЫБРАТЬ
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
//РегистрБухгалтерииОстатки.Субконто2 КАК Субконто2,
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
РегистрБухгалтерииОстатки.КоличествоОстаток КАК КоличествоОстаток,
РегистрБухгалтерииОстатки.Счет КАК Счет
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
,
Счет.Количественный
И Счет.Вид = &Активный,
&ВидыСубконто,
) КАК РегистрБухгалтерииОстатки
На вкладке Параметры СКД зададим следующие параметры и дадим пользователю право
добавлять свои виды субконто (убрав галку «Ограничение доступности»).
Результат тот же в отчете, но теперь по любому количественному счету с субконто
номенклатура мы увидим данные.
Теперь хотим продать товары, сделаем движения по регистру бухгалтерии в документе
«Расходная накладная». Поставим галку, что документ делает движения в регистре
бухгалтерии. Прежде чем делать движения, нужно выбрать запросом данные из регистра
бухгалтерии, чтобы себестоимость посчитать. Получили такой запрос:
Запрос.Текст =
"ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
|
ISNULL(РегистрБухгалтерииОстатки.КоличествоОстаток,0) КАК
КоличествоОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
|
&МоментВремени,
|
Счет = &Счет,
|
&МассивСубконто,
|
Субконто1 В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)
|
И Субконто2 = &Склад) КАК РегистрБухгалтерииОстатки
|
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1";
Запрос.УстановитьПараметр("Счет", ПланыСчетов.ПланСчетов.Товары);
Массив = Новый Массив(2);
Массив[0] = ПланыВидовХарактеристик.ВидыСубконто.Номенклатура;
Массив[1] = ПланыВидовХарактеристик.ВидыСубконто.Склады;
Запрос.УстановитьПараметр("МассивСубконто", Массив);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить("Остатки по БУХ: "+Выборка.КоличествоОстаток);
КонецЦикла;
Параметры Склад и МоментВремени мы задавали уже ранее при проведении документа по
регистрам накопления (про параметр МоментВремени попозже еще поговорим). То что
Субконто1- это номенклатура, а Субконто2 –склады определяется как раз порядком
элементов в параметре МассивСубконто.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
1.Необходимо в документ Приходная накладная, добавить поле "Контрагент" и описать
движение по счету "Поставщики" с учетом указанного контрагента.
2. В документе Расходная накладная" необходимо описать списание товаров и учет выручки от
покупателей.
Проводки на собственное усмотрение. Данные для списания получены на занятии в запросе.
Себестоимость списания товаров рассчитывается как средняя по складу.
Домашнее задание/Самостоятельное
Решение
1.Добавляем реквизит Контрагент в документ ПриходнаяНакладная, добавляем его на форму.
В ОбработкуПроведения добавляем одну строку и получаем такой код цикла:
Для Каждого ТекСтрокаТовары Из ТЗ Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение = Движения.СтоимостьТоваров.ДобавитьПриход();
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Партия = Ссылка;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение.Стоимость = ТекСтрокаТовары.Сумма;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Товары;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Поставщики;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
ТекСтрокаТовары.Номенклатура;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.СубконтоКт [ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент;// к ДЗ_№7
Проводка.Сумма = ТекСтрокаТовары.Сумма;
Проводка.КоличествоДт = ТекСтрокаТовары.Количество;
//Проводка.КоличествоКт = ТекСтрокаТовары.Количество; //ОШИБКА, у счета
Поставщики нет признака учета Количественный
Проводка.СодержаниеПроводки = "Поступили товары";
КонецЦикла;
2.
//очищаем регистр бухгалтерии, перед чтением итогов, чтобы старые движения документа не
влияли на получение остатков
Движения.РегистрБухгалтерии.Записать();
Запрос.Текст =
"ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
|
ISNULL(РегистрБухгалтерииОстатки.КоличествоОстаток,0) КАК КоличествоОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
|
&МоментВремени,
|
Счет = &Счет,
|
&МассивСубконто,
|
Субконто1 В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)
|
И Субконто2 = &Склад) КАК РегистрБухгалтерииОстатки
|
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1";
Запрос.УстановитьПараметр("Счет", ПланыСчетов.ПланСчетов.Товары);
Массив = Новый Массив(2);
Массив[0] = ПланыВидовХарактеристик.ВидыСубконто.Номенклатура;
Массив[1] = ПланыВидовХарактеристик.ВидыСубконто.Склады;
Запрос.УстановитьПараметр("МассивСубконто", Массив);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
//Сообщить("Остатки по БУХ: "+Выборка.КоличествоОстаток);
//Проверяем на отрицательные остатки. Можно было бы этого и не делать, так как в
бухучете часто проводят в минус.
//Мы делаем для удобства дальнейшего изложения.
Если Выборка.Количество > Выборка.КоличествоОстаток Тогда
Сообщить("Мало товара " + Выборка.Номенклатура+" не хватает
"+(Выборка.Количество-Выборка.КоличествоОстаток));
Отказ = Истина;
КонецЕсли;
Если Отказ Тогда
Продолжить;
КонецЕсли;
Себестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СуммаОстаток; //множители
поменяны местами, так решается проблема списания копейки, см. расчет себестоимости в
оперативном учете.
// Списание товаров (себестоимость), Проводка "Прибыль-Товары"
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Товары;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
Выборка.Номенклатура;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.КоличествоКт = Выборка.Количество;
Проводка.Сумма = Себестоимость;
Проводка.СодержаниеПроводки = "Списание себестоимости";
//Регистрация долга покупателя, Проводка "Покупатели-Прибыль"| Можно было бы
вынести за цикл этот блок, но в будущем надо будет возвращать, поэтому оставим так.
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Покупатели;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент;
Проводка.Сумма = СуммаИтого; //Товары.Итог("Сумма");-если бы не было реквизита
СуммаИтого
Проводка.СодержаниеПроводки = "Учтена прибыль";
КонецЦикла;
// вначале документа в нашем случае нельзя, так как есть строка Движения.Записать() в
середине обработки проведения,
// она бы записала просто пустые движения (на тот момент, до строки Движения.Записать()
движений по регистру бухгалтерии еще не было бы)
Движения.РегистрБухгалтерии.Записывать = Истина;
Также в разборе ДЗ№7 в форму документа «Операция бух» добавили элементы СубконтоДт1,
СубконтоДт2, КоличествоДт, СубконтоКт1, СубконтоКт2 и КоличествоКт просто перетаскиванием
элементов.
В форме списка документа «Операция бух» сделала тоже самое (перетащили элементы в ТЧ
Проводки), но до этого предварительно поменяли основную таблицу динамического списка
Проводки на РегистрБухгалтерии.РегистрБухгалтерии.ДвиженияССубконто.
Занятие 021
Краткое содержание
Регистр бухгалтерии, признаки учета субконто.
Детальное описание
Рассмотрим механизм, который называется «Признаки учета по субконто» для решения
задачи расчета себестоимости без учета складов. Напомним, что сейчас при списании
себестоимости по регистру бухгалтерии документом «Расходная накладная», себестоимость
считается только в разрезе одного конкретного склада. Напомним текст запроса, который
получает данные для дальнейшего расчета себестоимости:
Запрос.Текст =
"ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
|
ISNULL(РегистрБухгалтерииОстатки.КоличествоОстаток,0) КАК
КоличествоОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
|
|
|
|
|
|
|
|
|
|
&МоментВремени,
Счет = &Счет,
&МассивСубконто,
Субконто1 В
(ВЫБРАТЬ
ДокТЧ.Номенклатура
ИЗ
ДокТЧ КАК ДокТЧ)
И Субконто2 = &Склад) КАК РегистрБухгалтерииОстатки
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1";
Допустим у нас по Ложке такая ситуация:
Себестоимость 1 ложки – 15. И в принципе мы можем сейчас запросом вытащить такие
данные, уберем из запроса Субконто2 и не будем делать отбор по складу в параметрах
виртуальной таблицы. Но что станет с таблицей остатков при таком списании?
Спишем 10 ложек со склада Юг по себестоимости (без учета складов):
Если теперь еще списать ложки со склада Север получим:
Без разреза складов конечно у нас остаток по сумме уйдет в ноль, но все равно это плохо,
плюс эти минусы будут вечно храниться. Отсюда вопрос, как же сделать правильно? В
регистрах накопления мы создавали отдельный регистр СтоимостьТоваров, где убирали
лишний разрез учета (Склад). Но в регистре бухгалтерии так сделать нельзя: у нас есть счет,
который по сути как один регистр накопления, и мы не можем пользователя заставить
количество товара в разрезе складов хранить на одном счета, а стоимость товара хранить на
другом счете без учета по складам. Поэтому нам нужно каким-то образом сделать так, чтобы
стоимость накапливалась по товарам (Субконто1) и не накапливалась по складам (Субконто2).
Для этого в системе есть механизм «Признаки учета по субконто». Покажем, как это
работает на примере табличной модели:
Продадим теперь 1 ложку со склада Юг:
Теперь реализуем это при помощи механизма «Признаки учета по субконто».
Сформулируем задачу: мы хотим отключить расчет остатков по Сумме в разрезе аналитики
Склад (по Субконто2) по некоторым счетам.
Зайдем на вкладку «Субконто» нашего плана счетов. Так как мы хотим в разрезе аналитики
накапливать или не накапливать какой-либо ресурс, мы добавим «Признак учета по
субконто» и назовем «Суммовой».
Добавим еще один предопределенный счет (просто чтобы оба варианта расчета
себестоимости у нас сохранились), назовем его ТоварыОпт.
Свяжем ресурс Сумма регистра бухгалтерии с Признаком учета субконто «Суммовой».
Как раз эта связь и делает «разрез» в таблице движений и в таблице итогов регистра
бухгалтерии.
В документе ПриходнаяНакладная сделаем возможным поступление и на склад розничный, и
на оптовый. Для этого в документ добавим реквизит «Вид поступления», числового типа
длины 1, принимать будет значения 0 или 1. Добавим этот реквизит на форму с видом поля
«Поле переключателя».
И добавим значения переключателей.
Теперь осталось проверить, какое значение стоит в реквизите ВидПоступления, и проставить
правильный счет в проводке, сделаем это. ОбработкаПроведения документа
ПриходнаяНакладная примет вид:
Процедура ОбработкаПроведения(Отказ, Режим)
Если ВидПоступления = 0 Тогда
СчетУчетаТоваров = ПланыСчетов.ПланСчетов.Товары;
Иначе
СчетУчетаТоваров = ПланыСчетов.ПланСчетов.ТоварыОпт;
КонецЕсли;
Движения.ОстаткиТоваров.Записывать = Истина;
Движения.СтоимостьТоваров.Записывать = Истина;
Движения.РегистрБухгалтерии.Записывать = Истина;
ТЗ = Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура","Количество, Сумма");
Для Каждого ТекСтрокаТовары Из ТЗ Цикл
Движение = Движения.ОстаткиТоваров.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение = Движения.СтоимостьТоваров.ДобавитьПриход();
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
Движение.Партия = Ссылка;
Движение.Количество = ТекСтрокаТовары.Количество;
Движение.Стоимость = ТекСтрокаТовары.Сумма;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = СчетУчетаТоваров; // поменяли эту строку
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Поставщики;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
ТекСтрокаТовары.Номенклатура;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.СубконтоКт [ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент;// к ДЗ_№7
Проводка.Сумма = ТекСтрокаТовары.Сумма;
Проводка.КоличествоДт = ТекСтрокаТовары.Количество;
//Проводка.КоличествоКт = ТекСтрокаТовары.Количество; //ОШИБКА, у счета
Поставщики нет признака учета Количественный
Проводка.СодержаниеПроводки = "Поступили товары";
КонецЦикла;
КонецПроцедуры
При проведении ПриходнойНакладной в регистре бухгалтерии проводки визуально не
изменились, но на самом деле сумма приходуется одной строкой (без складов), а количество в
другой (с учетом складов).
Теперь нужно сделать списание себестоимости без учета складов документом
РасходнаяНакладная. Прежде чем его реализовывать распроведем документ
РасходнаяНакладная, проведем два документа ПриходнаяНакладная (для прихода на
разные склады) и подготовим запрос в консоли запросов.
Сформируем запрос к таблице остатков с отбором по счету ТоварыОпт и пока что выберем
все поля. Получим:
Как раз в данном запросе видно, что по складам у нас ведется количественный учет, а по
номенклатуре (без складов) ведется суммовой учет. Но нам нужно и остаток товара, и
стоимость товара получить в одной строке для расчета себестоимости. Сформулируем задачу:
нам нужно написать запрос, результатом которого будет таблица вида:
Товар
КоличествоДок
КоличествоОстато
к
СуммаОстаток
Даже КоличествоДок пока опустим, потом получим это значение, т.е. вот такую таблицу
хотим получить:
Товар
Ложка
КоличествоДок
КоличествоОстато
к
СуммаОстаток
20
300
Для начала уберем ненужные нам поля: Счет (не нужен, так как по нему мы сделали отбор),
СуммаОстатокДт, СуммаОстатокКт, СуммаРазвернутыйОстатокДт,
СуммаРазвернутыйОстатокКт, КоличествоОстатокДт, КоличествоОстатокКт,
КоличествоРазвернутыйОстатокДт, КоличествоРазвернутыйОстатокКт. Результатом запроса
будет такая таблица:
Субконто
1
Субконто2
Ложка
СуммаОстат КоличествоОст
ок
аток
2 950
Ложка
Основной
склад
10
Ложка
Север
13
Вилка
9 000
Вилка
Основной
склад
20
Вилка
Север
20
Но нам нужно, чтобы СуммаОстаток и КоличествоОстаток были в одной строке, при этом
для контроля отрицательных остатков нам нужно понять какой остаток на конкретном складе, а
для расчета себестоимости нам нужна сумма и общее количество. Чтобы это реализовать
будем из таблицы выше КоличествоОстаток получать два раза, только второй раз будем
получать его по условию: ВЫБОР КОГДА Субконто2 = &Склад ТОГДА
КоличествоОстаток КОНЕЦ КАК КолПоСкладу. Полный текст запроса будет таким:
ВЫБРАТЬ
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
РегистрБухгалтерииОстатки.Субконто2 КАК Субконто2,
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
РегистрБухгалтерииОстатки.КоличествоОстаток КАК КоличествоОстаток,
ВЫБОР КОГДА Субконто2 = &Склад ТОГДА КоличествоОстаток КОНЕЦ КАК
КолПоСкладу
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(, Счет=&Счет, , ) КАК
РегистрБухгалтерииОстатки
А результат запроса выглядит так:
Субконто1
Ложка
Субконто2
СуммаОстато КоличествоОстато КолПоСклад
к
к
у
2 950
Ложка
Основной
склад
10
Ложка
Север
13
Вилка
13
9 000
Вилка
Основной
склад
20
Вилка
Север
20
20
Теперь у нас есть еще одна колонка, где выводится остаток только по указанному в параметре
складу (отбор по складу у нас не в параметрах виртуальной таблицы).
Теперь сгруппируем строки по Субконто1, а Субконто2 вообще уберем так как оно нам не
нужно (сейчас мы его выбирали для демонстрации данных). Получим такой запрос:
ВЫБРАТЬ
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
СУММА(РегистрБухгалтерииОстатки.СуммаОстаток) КАК СуммаОстаток,
СУММА(РегистрБухгалтерииОстатки.КоличествоОстаток) КАК КоличествоОстаток,
СУММА(ВЫБОР
КОГДА РегистрБухгалтерииОстатки.Субконто2 = &Склад
ТОГДА РегистрБухгалтерииОстатки.КоличествоОстаток
КОНЕЦ) КАК КолПоСкладу
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(, Счет = &Счет, , ) КАК
РегистрБухгалтерииОстатки
СГРУППИРОВАТЬ ПО
РегистрБухгалтерииОстатки.Субконто1
Получится такой результат запроса (по счету ТоварыОпт и складу Север):
Субконто1
СуммаОстаток
КоличествоОстаток
КолПоСкладу
Ложка
2 950
23
13
Вилка
9 000
40
20
Теперь нужно все ввести в документ РасходнаяНакладная. Для начала, как и в документ
ПриходнаяНакладная, добавим реквизит ВидСписания и перенесем его на форму документа
в виде поля переключателя, а вид переключателя установим в значение «Тумблер».
Специально списание себестоимости с расчетом стоимости списания только в пределах
склада оставим, для этого сделаем формирование движений по условию:
В обоих областях Бух и БухОПТ пока что одинаковый код, осталось в БухОПТ поменять текст
запроса (Строку Движения.РегистрБухгалтерии.Записывать = Истина; вынесла за пределы
обоих областей для наглядности условия). Начнем редактировать запрос:
Для начала уберем из параметров виртуальной таблицы отбор по Субконто2 (по Складу),
теперь мы можем получить общее количество по всем складам и суммовые значения.
Выберем поля как указано на картинке ниже (вычисляемое выражение такое же, что и в
запросе рассматриваемом до этого):
Но как соединяться таблицы ДокТЧ и РегистрБухгалтерииОстатки? Они соединяются по
равенству Номенклатуры, но во второй таблице у нас столько строк, сколько у нас складов с
остатками по данной номенклатуре (у нас же таблица РегистрБухгалтерииОстатки не
сгруппирована еще). Поэтому группировку сделаем следующим образом:
Получим следующий текст запроса:
ВЫБРАТЬ
ДокТЧ.Номенклатура КАК Номенклатура,
МИНИМУМ(ДокТЧ.Количество) КАК Количество,
СУММА(РегистрБухгалтерииОстатки.СуммаОстаток) КАК СуммаОстаток,
СУММА(РегистрБухгалтерииОстатки.КоличествоОстаток) КАК КоличествоОстаток,
СУММА(ВЫБОР
КОГДА РегистрБухгалтерииОстатки.Субконто2 = &Склад
ТОГДА РегистрБухгалтерииОстатки.КоличествоОстаток
КОНЕЦ) КАК ОстатокПоСкладу
ИЗ
ДокТЧ КАК ДокТЧ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
&МоментВремени,
Счет = &Счет,
&МассивСубконто,
Субконто1 В
(ВЫБРАТЬ
ДокТЧ.Номенклатура
ИЗ
ДокТЧ КАК ДокТЧ)) КАК РегистрБухгалтерииОстатки
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1
СГРУППИРОВАТЬ ПО
ДокТЧ.Номенклатура
Установим параметры: счет поменяем на ТоварыОпт и в движениях СчетКт установим в
ТоварыОпт. При проверке остатков теперь будем использовать ОстатокПоСкладу вместо
общего остатка КоличествоОстаток.
Область БухОПТ выглядит теперь так:
//очищаем регистр бухгалтерии, перед чтением итогов, чтобы старые движения документа не
влияли на получение остатков
Движения.РегистрБухгалтерии.Записать();
Запрос.Текст =
"ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
МИНИМУМ(ДокТЧ.Количество) КАК Количество,
|
СУММА(РегистрБухгалтерииОстатки.СуммаОстаток) КАК СуммаОстаток,
|
СУММА(РегистрБухгалтерииОстатки.КоличествоОстаток) КАК КоличествоОстаток,
|
СУММА(ВЫБОР
|
КОГДА РегистрБухгалтерииОстатки.Субконто2 = &Склад
|
ТОГДА РегистрБухгалтерииОстатки.КоличествоОстаток
|
КОНЕЦ) КАК ОстатокПоСкладу
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
|
&МоментВремени,
|
Счет = &Счет,
|
&МассивСубконто,
|
Субконто1 В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)) КАК РегистрБухгалтерииОстатки
|
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1
|
|СГРУППИРОВАТЬ ПО
|
ДокТЧ.Номенклатура";
Запрос.УстановитьПараметр("Счет", ПланыСчетов.ПланСчетов.ТоварыОпт);
Массив = Новый Массив(2);
Массив[0] = ПланыВидовХарактеристик.ВидыСубконто.Номенклатура;
Массив[1] = ПланыВидовХарактеристик.ВидыСубконто.Склады;
Запрос.УстановитьПараметр("МассивСубконто", Массив);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
//Сообщить("Остатки по БУХ: "+Выборка.КоличествоОстаток);
//Проверяем на отрицательные остатки. Можно было бы этого и не делать, так как в
бухучете часто проводят в минус.
//Мы делаем для удобства дальнейшего изложения.
Если Выборка.Количество > Выборка.ОстатокПоСкладу Тогда
Сообщить("Мало товара " + Выборка.Номенклатура+" не хватает
"+(Выборка.Количество-Выборка.ОстатокПоСкладу)+ " на складе "+Склад);
Отказ = Истина;
КонецЕсли;
Если Отказ Тогда
Продолжить;
КонецЕсли;
Себестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СуммаОстаток; //множители
поменяны местами, так решается проблема списания копейки, см. расчет себестоимости в
оперативном учете.
// Списание товаров (себестоимость), Проводка "Прибыль-Товары"
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.ТоварыОпт;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
Выборка.Номенклатура;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.КоличествоКт = Выборка.Количество;
Проводка.Сумма = Себестоимость;
Проводка.СодержаниеПроводки = "Списание себестоимости";
//Регистрация долга покупателя, Проводка "Покупатели-Прибыль"
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Покупатели;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент;
Проводка.Сумма = СуммаИтого; //Товары.Итог("Сумма");-если бы не было реквизита
СуммаИтого
Проводка.СодержаниеПроводки = "Учтена прибыль";
КонецЦикла;
Проверим, проведем две приходные накладные по ложке на разные склады по разной
стоимости и проведем расходную, по таблице движений регистра бухгалтерии видим, что
себестоимость считается верно (ВидПоступления и ВидСписания в документах равен
значению Опт): 3000/20 =150, 150*10=1500.
Рассмотрим другую задачу. У нас есть два кассовых документа –ПКО и РКО. Документ ПКО
делает проводку «Касса-Покупатель». Со счетом дебета Касса мы согласны, но счет кредита
Покупатель может быть и другим, ведь деньги могут поступать в кассу не только от
покупателя, но и от поставщиков (возврат авансов), от наших сотрудников и т.д. Поэтому
дадим пользователю возможность менять счет кредита, для этого в документ добавим
реквизит КорСчет, Субконто1 и Субконто2. Формы у документа нет, поэтому добавлять
новые реквизиты на форму нам не надо (система сама это сделает за нас). Теперь в режиме
1С Предприятие выберем КорСчет –Покупатели, но при выборе Субконто1 нам система
предлагает выбрать тип значения, а хотелось бы, чтобы сразу открывался справочник
Контрагенты. Также можно выбрать и Субконто2, хотя у счета Покупатели есть только одно
субконто. Для решения первой задачи в свойстве Субконто1 «Связь по типу» укажем, что
Субконто1 связано по типа с КорСчет, элемент связи по типу 1.
Что это значит? Система сейчас «залезет» в карточку счета, найдет там таблицу с видами
субконто и присвоит тип значения полю Субконто1 по первому субконто в таблице. То же
самое сделаем и для Субконто2, тут уже связь по типу будет по полю 2. Теперь в режиме 1С
Предприятие при установке в документе ПКО, к примеру, КорСчет –Покупатели Субконто1
предлагается выбрать из справочника Контрагенты. Субконто2 у счета Покупатели нет,
поэтому система не знает, какую связь по типу установить, и предлагает выбрать тип самим.
Если бы у счета была два субконто, то тип автоматически определялся бы у обоих субконто.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Необходимо "причесать" форму ПКО, при выборе счета, по которому не ведется
аналитический учет (Субконто) поля на форме должны быть недоступны для редактирования,
при выборе счета с одним субконто должно быть доступно одно поле, для счета с двумя
субконто - оба. Ну и проводки, естественно надо переписать с учетом выбранного счета и
аналитики.
Домашнее задание/Самостоятельное
Решение
Решение из разбора ДЗ (управление видимостью субконто в ПКО):
Создаем форму документа ПКО. Код модуль формы документа ПКО:
&НаКлиенте
Процедура КорСчетПриИзменении(Элемент)
УправлениеВидимостью()
КонецПроцедуры
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
УправлениеВидимостью()
КонецПроцедуры
&НаСервере
Процедура УправлениеВидимостью()
КолСубконто = Объект.КорСчет.ВидыСубконто.Количество();
Для Ном = 1 По 2 Цикл
Элементы["Субконто" + Ном].Видимость = КолСубконто >= Ном;
КонецЦикла;
КонецПроцедуры
Занятие 022
Краткое содержание
Регистр бухгалтерии, валютный учет.
Детальное описание
Многовалютный учет можно реализовать не только в регистрах бухгалтерии, а, например, в
регистрах накопления, но в регистрах бухгалтерии это будет более интересно рассмотреть по
причине экономии на количестве субконто.
Для начала создадим справочник Валюты с предопределенным элементом БазоваяВалюта
и независимый периодический (в пределах дня) регистр сведений КурсыВалют c измерением
Валюта (тип СправочникСсылка.Валюты, ведущее) и ресурсом Курс (тип Число длина 10
точность 4 - для более серьезных конфигураций хорошо бы ввести еще понятие кратности).
Введем курсы валют:
Теперь перейдем в механизм бухгалтерии: в бухучете хотим по некоторым счетам, например,
по счету Касса накапливать сумму не только в базовой валюте (в нашем случае в рублях), но
еще и в долларах, и в евро. Для этой цели создадим еще один счет КассаВВалюте (01.11) и
по нему хотим накапливать информацию в разрезе различных валют. С ходу хочется это
реализовать при помощи нового субконто Валюта, и по этому субконто будем разрезать
остатки по счету КассаВВалюте. Но у нас сейчас накапливается баланс в разрезе Суммы,
поэтому сумму просто так мы накапливать не можем, а то Сумма получиться «в долларахевро», т.е. нельзя чтобы это новое субконто влияло на получение данных из регистра по
Сумме. Тогда в принципе можно сделать еще один ресурс валютная сумма ВалСумма,
сделаем такой ресурс.
Именно здесь будут храниться доллары, евро (сама сумма) и т.д., а в ресурс Сумма мы
запишем сумму рублевого покрытия. Так все таки, где будем хранить саму валюту, в субконто?
В принципе да, можно создать вид субконто Валюта и связать ВидСубконто с признаком
учета субконто Валютный (создадим такой) и связать его с созданным ресурсом регистра
бухгалтерии ВалСумма. Работать это будет, но валютная сумма будет накапливаться не
целиком по счету, а по конкретному виду аналитики (по валюте), т.е. по счету целиком мы срез
по валюте построить не сможем. И если вдруг у нас появиться потребность взаиморасчеты с
контрагентами в валюте вести, у нас появится уже два разреза Контрагент, Валюта; а для
более сложных счетов разрезов, соответственно, будет еще больше (Контрагенты, Договоры,
ВидСделки, Бюджет). И, понимая, что по всем разрезам у нас появляется еще и Валюта, и
она у нас может быть по многим счетам, мы можем говорить, что: в таких ситуациях, когда по
множеству счетов ведется одинаковый разрез учета, который разрезает счет (вообще
полностью) во всех других детальных разрезах, эффективнее использовать не аналитикусубконто, а измерение регистра бухгалтерии. Измерение регистра бухгалтерии будет
разрезать нам все счета (если мы не настроим так, чтобы не все счета разрезались). Итак,
измерение – это глобальный разрез по всем счетам, который используется в том случае, если
мы хотим сэкономить на субконто. Нужно заметить, что субконто это у нас составной тип
данных, а измерение, которое мы сейчас добавим, будет иметь типизированное значение (в
нашем случае будет СправочникСсылка.Валюты), т.е. мы еще экономим индексные поля. Что
бывает часто в измерениях регистра бухгалтерии? Часто бывают Организация (реализация
консолидированного учета в регистрах бухгалтерии), Подразделение (подразделение часто
по всем счетам проходит), ВидДеятельности (если организация все свои затраты и доходы
считает в разрезе видов деятельности).
Добавим измерение регистра бухгалтерии Валюта. Если ресурс ВалСумма оставить
балансовым, то продажа/покупка валюта у нас будет недоступной:
Валюта
СчетДт
СчетКт
Сумма
ВалСумма
Операция
USD
Касса
Банк
5000
100
Покупка
валюты
Получается, если ВалСумма балансовый ресурс, то в по счету Касса будет ВалСумма=100,
но по счету Банк мы ведем расчет по базовой валюте, и нам валютная сумма по банку не
нужна (по факту все то же самое, что и по ресурсу Количество было). Поэтому ресурс
ВалСумма сделаем небалансовым и нужно сделать связь с признаком учета. Создадим
признак учета Валютный:
Поставим признак учета в валюте у счета КассаВВалюте.
И в регистре бухгалтерии в свойствах ресурса ВалСумма указать признак учета Валютный.
Разделили счета, теперь по одним счетам у нас ведется валютный учет, а по другим нет. И
таблица примет вид:
Валют
а
СчетД
т
СчетКт
Сумм
а
ВалСуммаД
т
ВалСуммаК
т
Операци
я
USD
Касса
Банк
5000
100
000000
Покупка
валюты
Что же будет с самой валютой? В принципе ее можно оставить так, как она есть балансовым,
тогда система будет записывать данные по остаткам в валюте по банку доллары равные 0. Но
предположим, что можно покупать доллары за евро (для всех бух. систем так нельзя), для
надуманной этой цели разрежем измерение Валюта регистра бухгалтерии на ВалютаДт и
ВалютаКт. Тогда движение будет записано следующим образом:
Валюта
Дт
Валюта
Кт
Счет
Дт
Счет
Кт
Сум
ма
ВалСумм
аДт
ВалСумм
аКт
Операц
ия
USD
null
Касса
Банк
5000
100
000000
Покупка
валюты
Этот разрез тоже должен быть связан с каким-то признаком учета (с тем же самым
валютным), тогда null и 0000 по банку писаться не будут (а если Валюта будет балансовой,
то в ВалСуммаКт будет писаться 0 в этом примере). Итак, у измерения Валюта снимем
признак Балансовый и установим признак учета в значение Валютный.
Теперь поправим форму документа ОперацияБух, чтобы в проводки можно было данные по
валютам вносить, и внесем операцию покупки валюты в режиме 1С Предприятие.
Теперь мы сможем остатки посмотреть в разрезе валюты, для этого немного поменяем отчет
ОСВ. Получим такой запрос:
ВЫБРАТЬ
РегистрБухгалтерииОстаткиИОбороты.Счет КАК Счет,
РегистрБухгалтерииОстаткиИОбороты.СуммаНачальныйОстатокДт КАК
СуммаНачальныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаНачальныйОстатокКт КАК
СуммаНачальныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.СуммаОборотДт КАК СуммаОборотДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаОборотКт КАК СуммаОборотКт,
РегистрБухгалтерииОстаткиИОбороты.СуммаКонечныйОстатокДт КАК
СуммаКонечныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаКонечныйОстатокКт КАК
СуммаКонечныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.Валюта КАК Валюта,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаНачальныйОстатокДт КАК
ВалСуммаНачальныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаНачальныйОстатокКт КАК
ВалСуммаНачальныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаОборотДт КАК ВалСуммаОборотДт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаОборотКт КАК ВалСуммаОборотКт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаКонечныйОстатокДт КАК
ВалСуммаКонечныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаКонечныйОстатокКт КАК
ВалСуммаКонечныйОстатокКт
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.ОстаткиИОбороты КАК
РегистрБухгалтерииОстаткиИОбороты
Уберем для валютной суммы роли, так же как делали для суммы (в модуле 20), чтобы все
считалось по математическим принципам, а не по принципу бух. учета. Получим такой отчет:
Теперь сформируем движения программно при помощи документа ПКО.
Добавим реквизит Валюта в документ ПКО. ВалСумму добавлять не будем, считаем, что
если указан КорСчет валютный, то сумма, указанная в документе, это именно сумма в
указанной валюте. В модуле объекта опишем формирование движений:
Процедура ОбработкаПроведения(Отказ, Режим)
********//здесь движения по регистрам накопления
//////////////БУХ. УЧЕТ/////////////////////////
Если Валюта.Пустая() Тогда
Касса = ПланыСчетов.ПланСчетов.Касса;
Курс = 1;
Иначе
Касса = ПланыСчетов.ПланСчетов.КассаВВалюте;
СрезПоследних = РегистрыСведений.КурсыВалют.СрезПоследних(Дата, Новый
Структура("Валюта",Валюта)); // можно было бы использовать запрос вместо объектной
модели
//результатом будет ТаблицаЗначений, состоящая из тех же полей, которые есть в
регистре сведений, у нас это Валюта и Курс (см. рисунок ниже).
Курс =СрезПоследних[0].Курс;
КонецЕсли;
Движения.РегистрБухгалтерии.Записывать = Истина;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = Касса;
#Область ДопДЗМодуль21
Проводка.СчетКт = КорСчет;
Если ЗначениеЗаполнено(Субконто1) Тогда
Проводка.СубконтоКт[КорСчет.ВидыСубконто[0].ВидСубконто] = Субконто1;
КонецЕсли;
Если ЗначениеЗаполнено(Субконто2) Тогда
Проводка.СубконтоКт[КорСчет.ВидыСубконто[1].ВидСубконто] = Субконто2
КонецЕсли;
#КонецОбласти
Если Касса.Валютный Тогда
Проводка.ВалСуммаДт = Сумма;
Проводка.ВалютаДт = Валюта;
КонецЕсли;
Если КорСчет.Валютный Тогда
Проводка.ВалСуммаКт = Сумма;
Проводка.ВалютаКт = Валюта;
КонецЕсли;
Проводка.Сумма = Сумма*Курс;
КонецПроцедуры
Создадим такой документ ПКО:
Получим такую проводку:
И в ОСВ:
Теперь представим такую ситуацию. Кто-то поменял курс валют, теперь то, что мы видим в
отчете правильно только в валюте, но в рублевом покрытии нет. Поэтому нам нужно делать
переоценку, т.е. делать какую-то запись, что валюта подешевела (или наоборот) и вместо 5000
осталось 4200 (курс стал 42). Какие счета могут быть валютными? Те, где реально деньги
хранятся (Банк, Касса) и счета взаиморасчетов с покупателями, поставщиками, дебиторами,
кредиторами и т. д.; по этим счетам будут накапливаться валютные остатки. Поэтому здесь
нужно будет принять следующее решение: переоценку валютных счетов будем делать всегда
перед формированием отчетов, т.е. в начале и конце каждого месяца. Что такое переоценка
валютных счетов? По сути нам нужно сформировать операцию в которой мы спишем (или
наоборот начислим) денег в рублевого остатка (остатка в базовой валюте) на счет Прибыль.
Напишем универсальный документ, который пересчитает все валютные счета и произведет
переоценку валютных средств (Проводка КассаВВалюте – Прибыль либо наоборот) при его
проведении. Назовем этот документ ПереоценкаВалютныхСчетов, проводится документ
будет неоперативно. Чтобы произвести переоценку валюты, нам нужно получить таблицу
вида:
Счет
Валют
а
КУРСОВАЯ РАЗНИЦА
=500042*100=800
СуммаОстато
к
ВалСуммаОстат
ок
Курс
5000
100
42
Но еще у нас есть Субконто1 и Субконто2 и их тоже нужно получать, чтобы осуществлять
переоценку по самой детальной аналитике. Единственное, что мы не будем трогать по счетам
это Количество и ВалСуммаОстаток.
Запрос для получения этих данных будет таким:
ВЫБРАТЬ
РегистрБухгалтерииОстатки.Счет КАК Счет,
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
РегистрБухгалтерииОстатки.Субконто2 КАК Субконто2,
РегистрБухгалтерииОстатки.Валюта КАК Валюта,
РегистрБухгалтерииОстатки.ВалСуммаОстаток *
ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1) - РегистрБухгалтерииОстатки.СуммаОстаток
КАК Отклонение
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(&МоментВремени, Счет.Валютный, , )
КАК РегистрБухгалтерииОстатки
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрСведений.КурсыВалют.СрезПоследних(&МоментВремени, ) КАК
КурсыВалютСрезПоследних
ПО РегистрБухгалтерииОстатки.Валюта = КурсыВалютСрезПоследних.Валюта
ГДЕ
РегистрБухгалтерииОстатки.ВалСуммаОстаток *
ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1) - РегистрБухгалтерииОстатки.СуммаОстаток
<> 0
Осталось сформировать движения, их можно сформировать, бегая по счету и по валюте.
ОПТИМИЗИРОВАТЬ ЗАПРОС самостоятельно
Получается, что если в запрос данные попали, то значит, у нас есть отклонение, и мы получим
счет, чтобы это отклонение в проводке сформировать. Получили следующую
ОбработкуПроведения документа ПереоценкаВалютныхСчетов:
Процедура ОбработкаПроведения(Отказ, Режим)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|
РегистрБухгалтерииОстатки.Счет КАК Счет,
|
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
|
РегистрБухгалтерииОстатки.Субконто2 КАК Субконто2,
|
РегистрБухгалтерииОстатки.Валюта КАК Валюта,
|
РегистрБухгалтерииОстатки.ВалСуммаОстаток *
ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1) - РегистрБухгалтерииОстатки.СуммаОстаток
КАК Отклонение
|ИЗ
|
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(&МоментВремени,
Счет.Валютный, , ) КАК РегистрБухгалтерииОстатки
|
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрСведений.КурсыВалют.СрезПоследних(&МоментВремени, ) КАК
КурсыВалютСрезПоследних
|
ПО РегистрБухгалтерииОстатки.Валюта = КурсыВалютСрезПоследних.Валюта
|ГДЕ
|
РегистрБухгалтерииОстатки.ВалСуммаОстаток *
ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1) - РегистрБухгалтерииОстатки.СуммаОстаток
<> 0";
Запрос.УстановитьПараметр("МоментВремени",МоментВремени());
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Если Выборка.Отклонение > 0 Тогда
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетКт = Выборка.Счет;
Проводка.ВалютаКт = Выборка.Валюта;
Для каждого СтрВидСубконто Из Выборка.Счет.ВидыСубконто Цикл // запрос в
цикле, так писать нельзя! но оставим для упрощения понимания
Проводка.СубконтоКт[СтрВидСубконто.ВидСубконто] =
Выборка["Субконто"+СтрВидСубконто.НомерСтроки];
КонецЦикла;
Иначе
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетДт = Выборка.Счет;
Проводка.ВалютаДт = Выборка.Валюта;
Для каждого СтрВидСубконто Из Выборка.Счет.ВидыСубконто Цикл // запрос в
цикле, так писать нельзя! но оставим для упрощения понимания
Проводка.СубконтоДт[СтрВидСубконто.ВидСубконто] =
Выборка["Субконто"+СтрВидСубконто.НомерСтроки];
КонецЦикла;
КонецЕсли;
Проводка.Сумма = МАКС(Выборка.Отклонение,-Выборка.Отклонение);//получаем
модуль числа, так как Сумма всегда положительна в проводках
Проводка.СодержаниеПроводки = "Переоценка валюты";
КонецЦикла;
Движения.РегистрБухгалтерии.Записывать = Истина;
КонецПроцедуры
В режиме 1С Предприятие создадим документ ПереоценкаВалютныхСчетов от 31.01.2019 и
получим следующие проводки в регистре бухгалтерии (до этого мы сделали ОперациюБух от
31.01.2019 на покупку 100 долларов по курсу 50, затем курс поменяли на 42).
Данный документ нужно будет создавать всегда в конце месяца и, если происходят какие-либо
операции (проценты по кредиту начисляем и т.п.) по валютным счетам, то на дату этой
операции перед ней нужно формировать такой документ.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Документ "Переоценка валюты" являясь универсальным, должен выбирать все валютные
счета и производить по ним переоценку.
Находить отклонение в рублевом покрытии и списывать его на счет "Прибыль".
В случае если счет активный и отклонение положительное - это наша прибыль.
Если счет активный и отклонение отрицательное - убыток.
И все зеркально с пассивными счетами.
Для оптимизации проведения документа, необходимо все данные по субконто, виду счета и
пр. получать в запросе.
Домашнее задание/Самостоятельное
Решение
Решение в разборе ДЗ№8:
Процедура ОбработкаПроведения(Отказ, Режим)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|
РегистрБухгалтерииОстатки.Счет КАК Счет,
|
РегистрБухгалтерииОстатки.Субконто1 КАК Субконто1,
|
РегистрБухгалтерииОстатки.Субконто2 КАК Субконто2,
|
РегистрБухгалтерииОстатки.Валюта КАК Валюта,
|
РегистрБухгалтерииОстатки.ВалСуммаОстаток *
ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1) - РегистрБухгалтерииОстатки.СуммаОстаток
КАК Отклонение,
|
РегистрБухгалтерииОстатки.Счет.Вид КАК СчетВид,
|
РегистрБухгалтерииОстатки.Счет.ВидыСубконто.(
|
НомерСтроки КАК СчетНомерСтроки,
|
ВидСубконто КАК СчетВидСубконто
|
) КАК ВидыСубконто
|ИЗ
|
РегистрБухгалтерии.РегистрБухгалтерии.Остатки(&МоментВремени,
Счет.Валютный, , ) КАК РегистрБухгалтерииОстатки
|
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрСведений.КурсыВалют.СрезПоследних(&МоментВремени, ) КАК
КурсыВалютСрезПоследних
|
ПО РегистрБухгалтерииОстатки.Валюта = КурсыВалютСрезПоследних.Валюта
|ГДЕ
|
(ВЫРАЗИТЬ(РегистрБухгалтерииОстатки.ВалСуммаОстаток *
ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1) - РегистрБухгалтерииОстатки.СуммаОстаток
КАК ЧИСЛО(15, 2))) <> 0";
Запрос.УстановитьПараметр("МоментВремени", МоментВремени() );
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
//Отклонение = Окр(Выборка.отклонение,2);
Если (Выборка.Отклонение > 0 И Выборка.СчетВид <> ВидСчета.Пассивный) ИЛИ
(Выборка.Отклонение < 0 И Выборка.СчетВид = ВидСчета.Пассивный) Тогда
//Хорошо!
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетДт = Выборка.Счет;
Проводка.ВалютаДт = Выборка.Валюта;
ВыборкаСубконто = Выборка.ВидыСубконто.Выбрать();
Пока ВыборкаСубконто.Следующий() Цикл
Проводка.СубконтоДт[ВыборкаСубконто.СчетВидСубконто]= Выборка["Субконто"
+ ВыборкаСубконто.СчетНомерСтроки] ;
КонецЦикла;
Иначе
//Плохо!
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетКт = Выборка.Счет;
Проводка.ВалютаКт = Выборка.Валюта;
ВыборкаСубконто = Выборка.ВидыСубконто.Выбрать();
Пока ВыборкаСубконто.Следующий() Цикл
Проводка.СубконтоКт[ВыборкаСубконто.СчетВидСубконто]= Выборка["Субконто"
+ ВыборкаСубконто.СчетНомерСтроки] ;
КонецЦикла;
КонецЕсли;
Проводка.Сумма = МАКС(Выборка.Отклонение, - Выборка.Отклонение);
Проводка.СодержаниеПроводки = "Переоценка валюты";
КонецЦикла;
Движения.РегистрБухгалтерии.Записывать = Истина;
КонецПроцедуры
Занятие 023
Краткое содержание
Регистр бухгалтерии, трех-валютный учет, оборотные субконто.
Детальное описание
Продолжим рассмотрение темы учета валют. Иногда в конфигурацию необходимо внести
такое понятие, как трехвалютный учет (учет в какой-то параллельной валюте). Например, в
совместном предприятии России и Германии необходимо оценивать активы и долги какие-то
сразу же в двух валютах. Это не будет учет в том понимании, что мы рассматривали в
предыдущем модуле. В данном случае нам нужно вести валютный учет по всем счетам, т.е.
нам нужно в регистр бухгалтерии помимо ресурсов Сумма, ВалСумма, добавить еще один
ресурс, который будет оценивать сумму в другой валюте. В предыдущем модуле мы
занимались переоценкой валюты и говорили, что ее нужно делать перед формированием
отчетов и любыми операциями с валютными долгами. Почему мы это делали? Потому что мы
учет вели только в суммовом учете, а валютная сумма это всего лишь вспомогательное
значение (а сколько еще это стоит в валюте), но с точки зрения бух. учета нас интересовала
только сумма, потому что мы в национальной валюте все высчитываем. А теперь у нас должна
появиться валюта, которая не будет меняться в принципе и не будет зависеть от переоценок.
Поясним: к примеру, мы купили стул, в национальной валюте он стоит 1000, и в момент
покупки в евро он стоит 20; если говорить про уже реализованный валютный учет, то рублевое
покрытие стула будет меняться с изменением курса, но с точки зрения трехвалютного учета,
покупка стула останется неизменной с течением времени, и курс валюты не будет влиять на
сумму рублевого покрытия. То есть мы будем накапливать для одного вида учета в
национальной валюте, а для другого вида учета мы будем накапливать в какой-то другой
(третьей) валюте и здесь уже не будет мультивалютности (будет всегда учет только в какой-то
одной определенной валюте), можно ее назвать валютой управленческого учета. Балансы
будут абсолютно разные: по национальной валюте будет один баланс, по управленческой другой. Попробуем реализовать трехвалютный учет.
Нам нужно добавить еще один ресурс в регистр бухгалтерии и произвести расчет этого
ресурса при записи в регистр бухгалтерии. Добавим ресурс УпрСумма, ресурс будет
балансовым с признаком учета по субконто Суммовой, т.е. по своим настройкам он такой же,
как и ресурс Сумма. И эту сумму нужно добавить во все документы. Если мы не хотим ресурс
УпрСумма описывать в каждом документе, то можно делать расчет ресурса УпрСумма в
модуле набора записей регистра бухгалтерии в событии ПередЗаписью. Там можно
перебрать текущие проводки:
Процедура ПередЗаписью(Отказ, РежимЗаписи)
Для каждого Проводка Из ЭтотОбъект Цикл
Проводка.УпрСумма = ...;
КонецЦикла;
КонецПроцедуры
Мы так делать не будем. так можно делать (так не придется описывать в каждом документе),
но те документы, в которых есть обусловленное проведение, там необходимо рассчитывать
ресурс УпрСумма отдельно. Это мы и сделаем, но предварительно опишем поступление с
управленческой суммой в документе ПриходнаяНакладная.
Для начала добавим реквизит УпрСумма в табличную часть Товары документа
ПриходнаяНакладная, сразу не забываем перетащить новый реквизит на форму, и
откорректируем движения по регистру бухгалтерии:
Процедура ОбработкаПроведения(Отказ, Режим)
Если ВидПоступления = 0 Тогда
СчетУчетаТоваров = ПланыСчетов.ПланСчетов.Товары;
Иначе
СчетУчетаТоваров = ПланыСчетов.ПланСчетов.ТоварыОпт;
КонецЕсли;
Движения.ОстаткиТоваров.Записывать = Истина;
Движения.СтоимостьТоваров.Записывать = Истина;
Движения.РегистрБухгалтерии.Записывать = Истина;
ТЗ = Товары.Выгрузить();
ТЗ.Свернуть("Номенклатура","Количество, Сумма, УпрСумма");//суммируем еще и
УпрСумма
Для Каждого ТекСтрокаТовары Из ТЗ Цикл
………// здесь движения по регистрам накопления
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = СчетУчетаТоваров;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Поставщики;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
ТекСтрокаТовары.Номенклатура;
Проводка.СубконтоДт [ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.СубконтоКт [ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент
Проводка.Сумма = ТекСтрокаТовары.Сумма;
Проводка.УпрСумма = ТекСтрокаТовары.УпрСумма;//добавили строку
Проводка.КоличествоДт = ТекСтрокаТовары.Количество;
Проводка.СодержаниеПроводки = "Поступили товары";
КонецЦикла;
КонецПроцедуры
И теперь опишем расчет управленческой суммы в документе РасходнаяНакладная. В какой
валюте у нас будет УпрСумма будем хранить в константе ВалютаУправленческогоУчета,
создадим такую:
Заметка: на экзамене 1С Специалист из формулировки задания не всегда понятно
необходимо просто валюту добавить или нужен двойной баланс (трехвалютный учет); чтобы
разобраться, нужно посмотреть на отчеты, если там по всем счетам вторая сумма проходит, то
это будет трехвалютный учет.
Для формирования движений в документе РасходнаяНакладная получать данные нам нужно
по остаткам регистра, хотя, казалось бы, мы можем на дату документа получить
СуммуОстаток (сумма в национальной валюте) и, получив курс валюты, посчитать УпрСумму
и УпрСебестоимость. Но так делать нельзя, так как УпрСумма - это балансовый ресурс, и его
нужно точно так же получать. А вот движение по долгу покупателя можно сделать расчетом с
помощью курса валюты. Курс валюты получим при помощи объектной модели: система сама
напишет за нас запрос такой же, как если бы мы получали курс сами запросом (метод, который
сам пишет запрос, которым мы можем управлять лишь косвенно). Движения получились
следующие (приводим ниже только область Бух, в области БухОПТ тоже все это дублируем):
#Область Бух
//очищаем регистр бухгалтерии, перед чтением итогов, чтобы старые движения
документа не влияли на получение остатков
Движения.РегистрБухгалтерии.Записать();
Запрос.Текст =
"ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
|
РегистрБухгалтерииОстатки.УпрСуммаОстаток КАК УпрСуммаОстаток,
|
ISNULL(РегистрБухгалтерииОстатки.КоличествоОстаток,0) КАК
КоличествоОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
|
&МоментВремени,
|
Счет = &Счет,
|
&МассивСубконто,
|
Субконто1 В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)
|
И Субконто2 = &Склад) КАК РегистрБухгалтерииОстатки
|
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1";
Запрос.УстановитьПараметр("Счет", ПланыСчетов.ПланСчетов.Товары);
Массив = Новый Массив(2);
Массив[0] = ПланыВидовХарактеристик.ВидыСубконто.Номенклатура;
Массив[1] = ПланыВидовХарактеристик.ВидыСубконто.Склады;
Запрос.УстановитьПараметр("МассивСубконто", Массив);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
//Проверяем на отрицательные остатки. Можно было бы этого и не делать, так как в
бухучете часто проводят в минус.
//Мы делаем для удобства дальнейшего изложения.
Если Выборка.Количество > Выборка.КоличествоОстаток Тогда
Сообщить("Мало товара " + Выборка.Номенклатура+" не хватает
"+(Выборка.Количество-Выборка.КоличествоОстаток));
Отказ = Истина;
КонецЕсли;
Если Отказ Тогда
Продолжить;
КонецЕсли;
Себестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СуммаОстаток; //множители
поменяны местами, так решается проблема списания копейки, см. расчет себестоимости в
оперативном учете.
УпрСебестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.УпрСуммаОстаток; //Добавили
строку
// Списание товаров (себестоимость), Проводка "Прибыль-Товары"
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Товары;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
Выборка.Номенклатура;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.КоличествоКт = Выборка.Количество;
Проводка.Сумма = Себестоимость;
Проводка.УпрСумма = УпрСебестоимость;
Проводка.СодержаниеПроводки = "Списание себестоимости";
//Регистрация долга покупателя, Проводка "Покупатели-Прибыль"
Структура =
РегистрыСведений.КурсыВалют.ПолучитьПоследнее(Дата, Новый Структура("Валюта",
Константы.ВалютаУправленческогоУчета.Получить()));// метод, который сам пишет запрос,
которым мы можем управлять лишь косвенно
Курс = Структура.Курс;
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Покупатели;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент;
Проводка.Сумма = СуммаИтого; //Товары.Итог("Сумма");-если бы не было реквизита
СуммаИтого
Проводка.УпрСумма = СуммаИтого / Курс; // считаем что курс <>0, а вообще здесь
проверку сделать нужно конечно
Проводка.СодержаниеПроводки = "Учтена прибыль";
КонецЦикла;
#КонецОбласти
Добавим в отчет ОСВ новые поля с управленческой суммой, так же, как и с Суммой и
ВалСуммой уберем роли у новых полей, чтобы остаток считался по математическому
принципу, и сделаем «папку», проставив точку в Пути.
Проверим то, что сделали в режиме 1С Предприятие. Установим доллар в качестве значения
константы ВалютаУправленческогоУчета. Перепроведем приходные накладные, указав
значение УпрСуммы, а затем перепроведем расходную накладную (не забываем проверить,
что задан курс на дату документа) и получим следующие проводки:
А отчет ОСВ выглядит так (отображение Оборотов и ВалСуммы убрали):
Мы видим, что по УпрСумме хранится баланс, в отличие от ВалСуммы, где по одному счету
может вестись валютный учет, а по другому счету нет. И естественно УпрСумма должна
накапливаться по каждому счету, т. е. при формировании любой операции она либо автоматом
рассчитывается в модуле набора записей регистра бухгалтерии, либо устанавливается
документом. Документом она может устанавливаться в двух сценариях: 1. Пользователь
упр.сумму сам вводит (это все приходные документы); 2. Списания (рассчитываем
себестоимость, делаем перемещение и т.п.).
Вернемся к коду обработки проведения документа РасходнаяНакладная, расположенному
выше, в которым мы делаем движения по регистру бухгалтерии и оптимизируем его, а именно
поработаем со следующими строками:
Создадим специальный общий модуль ВозвращаемыеЗначения, исполняться он будет на
сервере, вызов сервера с клиента разрешим. Также укажем свойство «Повторное
использование возвращаемых значений» равное «на время сеанса» (надеемся, что
пользователи не будут работать во время смены курса валют, хотя конечно это
маловероятно!).
Что делает это свойство? Если в данном модуле мы опишем какую-либо функцию, например,
ПолучитьКурсВалюты(), передадим в эту функцию дату и валюту, и будем вызывать эту
функцию, то при первом вызове система будет выполнять эту функцию полностью (строить
запросы и т .д.), а при повторном вызове система проверит, если такие входящие параметры
уже были в предыдущих вызовах, то она не будет выполнять функцию, а вернет ранее
возвращаемое значение. Получается такой «кэш», который будет накапливаться либо на
время сеанса (пользователь открыл базу и это его сеанс), либо на время вызова (если из
какой-либо другой процедуры уже были вызовы этой функции в этом модуле, тогда будет
повторно возвращаться значение; если это другой вызов, то функция будет выполняться
заново) в зависимости от значения свойства. И опишем функцию в данном модуле:
Функция ПолучитьКурсУпрВалюты(Дата) Экспорт
Структура = РегистрыСведений.КурсыВалют.ПолучитьПоследнее(Дата, Новый
Структура("Валюта", Константы.ВалютаУправленческогоУчета.Получить()));
Возврат ?(Структура.Курс = 0, 1 , Структура.Курс) ;
КонецФункции
А в обработке проведения расходной накладной вместо тех двух строчек напишем:
Курс = ВозвращаемыеЗначения.ПолучитьКурсУпрВалюты(НачалоДня(Дата));
При проведении документов одной датой секунды у них будут разные, поэтому Дату передаем
на начало дня, чтобы значения курса брались из кэша.
Напомним, что в свойствах модуля мы поставили возвращаемые значения на время сеанса, т.
е. если курс изменится в середине дня, то система будет возвращать нам старые значения.
Это можно «обойти», сбросив кэши при изменении курса валюты. В регистре сведений
КурсыВалют в модуле набора записей в обработчике события ПриЗаписи запишем
следующий код:
Процедура ПриЗаписи(Отказ, Замещение)
ОбновитьПовторноИспользуемыеЗначения();
КонецПроцедуры
Но опять же значения сбросятся для текущего сеанса, но если кто-то другой курс поменяет, то
текущий сеанс не узнает, что он его поменял.
Следующая наша задача, это получить отчет по прибыли. Когда мы делали отчет
АнализПродаж на регистрах накопления, мы специально для него создавали новый
оборотный регистр накопления Продажи и оттуда получали Количество, Себестоимость,
Выручку и Прибыль.
Такой отчет по регистрам бухгалтерии у нас не получится. Посмотрим на движения регистра
бухгалтерии при продаже товара:
Количество есть, себестоимость есть, выручка тоже есть, но не в разрезе продаваемого
товара. Следовательно, надо бы добавить разрез по товару в выручку. Выручка у нас где? Она
накапливается по счету Прибыль, поэтому в плане счетов у счета Прибыль добавим
субконто Номенклатура, а чтобы по этому субконто не накапливались остатки, поставим
галочку «Только обороты», а также галочку «Суммовой», чтобы сумма по этому субконто
записывалась.
Осталось в обработку проведения расходной накладной там, где мы регистрируем долг
покупателя, добавить аналитику по продаваемому товару и откорректировать проводки с
суммой (теперь у нас проводки по каждой строчке табличной части Расходная накладная, а
не по табличной части в целом). Получили следующий код (приводим только область Бух, для
области БухОПТ тоже самое делаем):
#Область Бух
//очищаем регистр бухгалтерии, перед чтением итогов, чтобы старые движения
документа не влияли на получение остатков
Движения.РегистрБухгалтерии.Записать();
Запрос.Текст =
"ВЫБРАТЬ
|
ДокТЧ.Номенклатура КАК Номенклатура,
|
ДокТЧ.Количество КАК Количество,
|
ДокТЧ.Сумма КАК Сумма,
|
РегистрБухгалтерииОстатки.СуммаОстаток КАК СуммаОстаток,
|
РегистрБухгалтерииОстатки.УпрСуммаОстаток КАК УпрСуммаОстаток,
|
ISNULL(РегистрБухгалтерииОстатки.КоличествоОстаток,0) КАК
КоличествоОстаток
|ИЗ
|
ДокТЧ КАК ДокТЧ
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.РегистрБухгалтерии.Остатки(
|
&МоментВремени,
|
Счет = &Счет,
|
&МассивСубконто,
|
Субконто1 В
|
(ВЫБРАТЬ
|
ДокТЧ.Номенклатура
|
ИЗ
|
ДокТЧ КАК ДокТЧ)
|
И Субконто2 = &Склад) КАК РегистрБухгалтерииОстатки
|
ПО ДокТЧ.Номенклатура = РегистрБухгалтерииОстатки.Субконто1";
Запрос.УстановитьПараметр("Счет", ПланыСчетов.ПланСчетов.Товары);
Массив = Новый Массив(2);
Массив[0] = ПланыВидовХарактеристик.ВидыСубконто.Номенклатура;
Массив[1] = ПланыВидовХарактеристик.ВидыСубконто.Склады;
Запрос.УстановитьПараметр("МассивСубконто", Массив);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Количество > Выборка.КоличествоОстаток Тогда
Сообщить("Мало товара " + Выборка.Номенклатура+" не хватает
"+(Выборка.Количество-Выборка.КоличествоОстаток));
Отказ = Истина;
КонецЕсли;
Если Отказ Тогда
Продолжить;
КонецЕсли;
Себестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.СуммаОстаток; //множители
поменяны местами, так решается проблема списания копейки, см. расчет себестоимости в
оперативном учете.
УпрСебестоимость =
Выборка.Количество/Выборка.КоличествоОстаток*Выборка.УпрСуммаОстаток;
// Списание товаров (себестоимость), Проводка "Прибыль-Товары"
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Товары;
Проводка.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
Выборка.Номенклатура; //добавили субконто
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
Выборка.Номенклатура;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
Проводка.КоличествоКт = Выборка.Количество;
Проводка.Сумма = Себестоимость;
Проводка.УпрСумма = УпрСебестоимость;
Проводка.СодержаниеПроводки = "Списание себестоимости";
//Регистрация долга покупателя, Проводка "Покупатели-Прибыль
Курс = ВозвращаемыеЗначения.ПолучитьКурсУпрВалюты(НачалоДня(Дата));
Проводка = Движения.РегистрБухгалтерии.Добавить();
Проводка.Период = Дата;
Проводка.СчетДт = ПланыСчетов.ПланСчетов.Покупатели;
Проводка.СчетКт = ПланыСчетов.ПланСчетов.Прибыль;
Проводка.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] =
Выборка.Номенклатура; //добавили субконто
Проводка.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Контрагенты] =
Контрагент;
Проводка.Сумма = Выборка.Сумма; //изменили СуммаИтого на Выборка.Сумма
Проводка.УпрСумма = Выборка.Сумма / Курс; //изменили СуммаИтого на
Выборка.Сумма
Проводка.СодержаниеПроводки = "Учтена прибыль";
КонецЦикла;
#КонецОбласти
Перепроведем расходную накладную, получим проводки:
Теперь у нас по счету Прибыль есть разрез по проданному товару и по дебету, и по кредиту.
Причем в остатках разреза номенклатуры не будет, а вот по оборотам есть. Поговорим о
новой для нас таблице оборотов регистра бухгалтерии.
Таблица корреспондирующих оборотов регистра бухгалтерии накапливается следующим
образом (приводим основные поля таблицы), например, при списании товара:
Счет
Субконто1
Субконт
о2
СуммаО
борот
КорСчет
КорСубконт
о1
КорСубконт
о2
Приб
ыль
Ложка
Товар
ы
Ложка
Юг
100
Товары
Ложка
-100
Прибыль
Ложка
Юг
Так накапливается информация по каждому движению в разрезе самой детальной аналитики,
т.е. для одной проводки пишется две строки в таблице оборотов. За наличие КорСчета и
КорСубконто в данной таблице, отвечает галка «Корреспонденция», стоящая на вкладке
«Основные» регистра бухгалтерии (если бы галка не стояла, движения имели бы вид: СчетВидДвижения-Сумма). Благодаря этому мы можем вытащить корреспондирующие суммы, т.
е. анализируя счет Прибыль, мы можем увидеть, что со счетом Товары была
корреспонденция и какая аналитика была по корсчету, и, наоборот, анализируя счет Товары,
увидим корреспонденцию со счетом Прибыль.
У регистра бухгалтерии есть две виртуальные таблицы оборотов:
РегистрБухгалтерии.Обороты и РегистрБухгалтерии.ОборотыДтКт. В первой таблице мы
задаем счет, по которому хотим получить обороты по дебету и по кредиту, и система выведет
все корреспонденции к этому счету. А в таблице РегистрБухгалтерии.ОборотыДтКт сводные проводки: мы можем получить обороты по дебету какого-то счета со всеми
кредитовыми корреспонденциями и обороты по кредиту со всеми дебетовыми
корреспонденциями. Посмотрим, как выглядит виртуальная таблица оборотов регистра
бухгалтерии в консоле запросов:
На основании этого запроса создадим отчет «ПрибыльПоБУ». Запрос для отчета будет
следующим:
ВЫБРАТЬ
РегистрБухгалтерииОбороты.Субконто1 КАК Субконто1,
РегистрБухгалтерииОбороты.СуммаОборотДт КАК Себестоимость,
РегистрБухгалтерииОбороты.СуммаОборотКт КАК Выручка,
РегистрБухгалтерииОбороты.КоличествоКорОборотДт КАК Количество
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.Обороты(, , , Счет = &Счет, , , КорСчет
В(&КорСчета), ) КАК РегистрБухгалтерииОбороты
Зададим параметры:
Получили следующий отчет:
В том случае, если у нас взаиморасчеты с покупателями ведутся в валюте и соответственно
данный счет в переоценку тоже попадет, тогда нужно вводить новый разрез учета –«что это за
операция»-продажа, покупка, переоценка и т.д. Это будет дополнительное субконто, по
которому мы сможем сделать отборы.
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
Занятие 024
Краткое содержание
Резюме по механизмам бухгалтерии,
Многофирменный учет
Детальное описание
Нужно ли в документе ОперацияБух прописывать очищение движений регистра при пометке
документа на удаление? Не нужно, потому что пользователь движения руками ввел и считает,
что это данные документа. А пометил он его на удаление или нет уже «дело десятое». У
движений регистра бухгалтерии (и у других регистров) есть свойство «Активность» типа
Булево, и, если движение неактивно, то в таблице итогов, оборотов эти движения не
участвуют. Получается движение есть, но на итоги, обороты (на виртуальные таблицы) они не
влияют.
Соответственно поменять активность можно любым способом, можно вручную (на форме
документа), можно менять с помощью какой-то кнопки или поменять активность в событии:
Процедура ПриЗаписи(Отказ)
НаборЗаписей = РегистрыБухгалтерии.РегистрБухгалтерии.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Ссылка);
НаборЗаписей.Прочитать();
НаборЗаписей.УстановитьАктивность(Не ПометкаУдаления);
НаборЗаписей.Записать();
КонецПроцедуры
Теперь при пометке на удаление документа ОперацияБух движения станут неактивными
(значок «ДтКт» становится серым), а при отмене пометки активными.
Рассмотрим балансовое измерение регистра бухгалтерии.
Задача у нас следующая: мы хотим вести учет в базе в разрезе юридических лиц (наших
собственных).
Для этой цели создадим справочник «ЮрЛица» с предопределенным элементом
«ОсновнаяОрганизация». Мы будем рассматривать только на примере документа
ОперацияБух, остальные документы переписывать не будем, но понятно, что, вообще говоря,
теперь реквизит ЮрЛицо нужно добавить во все документы, во многие регистры.
В регистр бухгалтерии добавим измерение ЮрЛицо типа СправочникСсылка.ЮрЛица, это
измерение будет балансовым. Балансовые измерения должны располагаться выше
небалансовых в регистре бухгалтерии, т.к. по балансовым измерениям мы будем всегда
выбирать данные при получении итогов. Свойство «Запрет незаполненных значений»
измерения ЮрЛицо вообще ставить нужно, но мы не будем, иначе документы не будут
проводиться, ведь движения мы не переписывали.
В документе ОперацияБух добавим ЮрЛицо в качестве реквизита документа, чтобы он в
движениях в каждой строчке ЮрЛицо не вводил, и перетащим реквизит на форму документа.
Заметка: чтобы в каждый документ не добавлять реквизит ЮрЛицо, можно создать общий
реквизит ЮрЛицо. Вообще говоря, общие реквизиты не для этого существуют, но так их тоже
можно использовать.
Мы добавлять общий реквизит не будем.
В модуле документа ОперацияБух в обработчике события ПередЗаписью заполним
измерение ЮрЛицо в движениях регистра бухгалтерии (также как мы записывали Период):
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Для Каждого Проводка из Движения.РегистрБухгалтерии Цикл
Проводка.Период = Дата;
Проводка.ЮрЛицо = ЮрЛицо;
КонецЦикла;
КонецПроцедуры
А чтобы во всех других документах по бух. учету ничего не сломалось, мы поставим заглушку:
если ЮрЛицо не заполнено, поставим ЮрЛицо по умолчанию. В модуле набора записей
регистра бухгалтерии в обработчике события ПередЗаписью:
Процедура ПередЗаписью(Отказ, РежимЗаписи)
Для каждого Проводка Из ЭтотОбъект Цикл
Если Проводка.ЮрЛицо.Пустая() Тогда
Проводка.ЮрЛицо = Справочники.ЮрЛица.ОсновнаяОрганизация;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
И теперь поставим отбор в отчете ОСВ, условно-безусловный отбор: пользователь сможет
либо установить отбор по нужному ЮрЛицу, либо его отключить. В СКД, если мы установим
условие в параметрах виртуальной таблицы РегистрБухгалтерииОстаткиИОбороты, то это
условие будет обязательным для СКД и его нужно будет всегда задавать.
А мы хотим, чтобы пользователь либо имел возможность его задать, либо без отбора отчет
выводил. Для этих целей параметры виртуальной таблицы указываются в фигурных скобках
{}, это называется дополнение текста запроса для языка схемы компоновки данных.
ВЫБРАТЬ
РегистрБухгалтерииОстаткиИОбороты.Счет КАК Счет,
РегистрБухгалтерииОстаткиИОбороты.СуммаНачальныйОстатокДт КАК
СуммаНачальныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаНачальныйОстатокКт КАК
СуммаНачальныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.СуммаОборотДт КАК СуммаОборотДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаОборотКт КАК СуммаОборотКт,
РегистрБухгалтерииОстаткиИОбороты.СуммаКонечныйОстатокДт КАК
СуммаКонечныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.СуммаКонечныйОстатокКт КАК
СуммаКонечныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.Валюта КАК Валюта,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаНачальныйОстатокДт КАК
ВалСуммаНачальныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаНачальныйОстатокКт КАК
ВалСуммаНачальныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаОборотДт КАК ВалСуммаОборотДт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаОборотКт КАК ВалСуммаОборотКт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаКонечныйОстатокДт КАК
ВалСуммаКонечныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.ВалСуммаКонечныйОстатокКт КАК
ВалСуммаКонечныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.УпрСуммаНачальныйОстатокДт КАК
УпрСуммаНачальныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.УпрСуммаНачальныйОстатокКт КАК
УпрСуммаНачальныйОстатокКт,
РегистрБухгалтерииОстаткиИОбороты.УпрСуммаОборотДт КАК УпрСуммаОборотДт,
РегистрБухгалтерииОстаткиИОбороты.УпрСуммаОборотКт КАК УпрСуммаОборотКт,
РегистрБухгалтерииОстаткиИОбороты.УпрСуммаКонечныйОстатокДт КАК
УпрСуммаКонечныйОстатокДт,
РегистрБухгалтерииОстаткиИОбороты.УпрСуммаКонечныйОстатокКт КАК
УпрСуммаКонечныйОстатокКт
ИЗ
РегистрБухгалтерии.РегистрБухгалтерии.ОстаткиИОбороты(, , , , , , {ЮрЛицо =
&ЮрЛицо}) КАК РегистрБухгалтерииОстаткиИОбороты
Это означает, что параметр будет задан в том случае, если пользователь его установит, а
если нет, то параметр будет проигнорирован, как будто параметра нет.
На вкладке «Параметры» СКД снимем ограничения доступности у параметра ЮрЛицо.
И покажем этот параметр пользователю
В режиме 1С Предприятие создадим документы ОперацияБух с разными ЮрЛицами и
посмотрим ОСВ. ОСВ без отбора:
С отбором по Орг1 (создали еще две организации помимо основной):
Вопросы к занятию (для самоконтроля)
Домашнее задание/Самостоятельное
Разобраться с копированием документа ОперацияБух, сейчас скопированный документ
пустой.
Домашнее задание/Самостоятельное
Решение
В модуле документа ОперацияБух в обработчике события ПриКопировании() пишем
следующее:
Процедура ПриКопировании(ОбъектКопирования)
НаборЗаписей = РегистрыБухгалтерии.РегистрБухгалтерии.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ОбъектКопирования.Ссылка);
НаборЗаписей.Прочитать();
Движения.РегистрБухгалтерии.Загрузить(НаборЗаписей.Выгрузить());
КонецПроцедуры
Занятие 025
Краткое содержание
Добавляем:
1. подсистему Расчет,
2. план видов расчета,
3. регистр расчета,
4. универсальный документ, который будет производить движения по регистру расчета,
5. общий модуль для расчета ресурса Сумма регистра расчета.
В модуле документа прописываем код, для записи исходных данных в регистр расчета.
Принудительно записываем их в регистр.
Вызываем экспортную процедуру общего модуля, передавая в неё Ссылку на данный
документ.
В общем модуле считываем из регистра расчета набор записей, соответствующий документу
(идет поиск по Ссылке).
Рассчитываем для каждой записи из Набора записей Сумму.Набор записей с уже
рассчитанными данными вновь записываем в регистр.
Детальное описание
Добавляем план видов расчета (ПВР) Начисления. Отличается от справочника только
закладкой Расчет. Пока сделаем вид, что этой закладки нет и, в таком случае, наш ПВР от
обычного справочника никак не отличается.
Необходим для хранения этой информации:
ПВР Начисления -- вкладка Прочее -- Предопределенные добавляем вид расчета
ОкладСуммой:
Закладку Ведущие пока не заполняем.
Создаем подсистему Расчет и помещаем в нее ПВР Начисления.
В созданный ПВР Начисления не только мы в конфигураторе можем добавлять планы видов
расчета (как сделали это для ОкладСуммой), но сможет добавлять их и пользователь.
Тестируем.
Создадим регистр расчета (РР) ЖурналНачислений.
-------------------------------------------------------------------------------------------------------------------Назначение РР: хранение данных для расчета и хранение результата расчета. То есть вся эта
таблица:
-------------------------------------------------------------------------------------------------------------------На вкладке Основное РР ЖурналНачислений указываем
● в поле План видов расчета наш ПВР Начисления,
● в поле Периодичность -- Месяц.
Заполняем Данные:
РР ЖурналНачислений включаем в подсистему Расчет и в командном интерфейсе этой
подсистемы включаем его видимость.
Создаем универсальный документ ВводПроизвольныхНачислений, из которого будет попадать
информация в РР ЖурналНачислений.
В Данных документа добавляем ТЧ ЖурналНачислений. Все наименования реквизитов ТЧ
полностью совпадают с наименованиями данных РР ЖурналыНачислений (кроме
ВидРасчета):
В качестве периода регистрации в РР попадет Дата документа.
Добавляем документ в подсистему Расчет.
В движениях документа указываем движение по РР ЖурналНачислений. Оперативное
проведение запрещаем, проведение разрешаем:
В модуле документа прописываем код проведения по РР ЖурналНачислений:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Для каждого Стр Из ЖурналНачислений Цикл
Запись = Движения.ЖурналНачислений.Добавить();
ЗаполнитьЗначенияСвойств(Запись, Стр);
Запись.ПериодРегистрации = Дата;
КонецЦикла;
Движения.ЖурналНачислений.Записывать = Истина;
КонецПроцедуры
Тестируем. В документе ВводПроизвольныхНачислений от 13.01.19г. Иванюхину назначаем
ОкладСуммой с параметром 100.000
Колонка Сумма остается пустой -- она будет рассчитана.
Проверяем: в РР ЖурналНачислений введенная информация отражена. Причем заметим,
несмотря на то, что в коде модуля документа он прописан равным дате документа-источника,
а Дата -- это 13.01.19г. приведен к началу месяца -- так работает система.
Считаем сумму. Где и как? Должна быть единая точка для вхождения всех документов по
зарплате -- общий модуль.
Добавляем общий модуль Расчет (Серверный; вызов сервера пока запретим;
Привилегированный):
Почему вызов сервера пока запрещен? Его разрешение пока лишнее. Еще будут моменты,
когда вызывать общий модуль будем из форм документа, тогда разрешим.
Для чего Привилегированный? Работать будет быстрее (т.к. отменена большая часть
проверок).
В модуле общего модуля Расчет прописываем экспортную процедуру РассчитатьЗаписи:
Процедура РассчитатьЗаписи() Экспорт
КонецПроцедуры
Теперь вернемся в модуль документа ВводПроизвольныхНачислений и допишем 2 строки
кода:
Здесь можно скопировать эти 2 строки:
Движения.Записать();
Расчет.РассчитатьЗаписи(Ссылка);
Записываем движения документа в РР ЖурналНачислений (команда 1).
Передаем ссылку в процедуру РассчитатьЗаписи общего модуля Расчет на набор данного
документа для получения в общем модуля их копии.
По сути это так. Представим картину. Слева есть документ. Документ записывает данные в
регистр, а потом мы в общем модуле читаем данные уже из регистра, притом те данные,
которые соответствуют документу, ссылка на который передана. И дальше на основе
полученных данных рассчитываем результат и опять движения записываем в регистр.
В модуле общего модуля прописываем:
Скопировать код общего модуля можно здесь:
Процедура РассчитатьЗаписи(Регистратор) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Для каждого Запись Из НаборЗаписей Цикл
Запись.Сумма = Запись.Параметр;
КонецЦикла;
НаборЗаписей.Записать();
КонецПроцедуры
Тестируем. В ранее сформированный документ Ввод произвольных начислений от 13.01.19г. с
Иванюхиным добавим Камменского с видом расчета тоже ОкладСуммой и параметром
500.000. Перепроведем. В РР ЖурналНачислений видим: суммы для обоих сотрудников
рассчитаны.
Рассказаны ситуации, когда РР используется не только для начисления зарплаты.
Вопросы к занятию (для самоконтроля)
● Что хранится в регистре расчета?
● Почему расчетные показатели правильно рассчитывать в общем модуле?
● Регистр расчета может быть использован не только для расчета заработной платы. Что
общего у областей, для которых удобно использовать регистр расчета?
● Почему в общем модуле в данном случае включение возможности вызова сервера было
необязательным? В каком случае включение этой возможности будет обязательным?
● Что такое режим общего модуля или документа Привилегированный? Для чего мы его
используем в нашем случае?
● С чем надо быть особенно внимательным, оформляя документ с движением по регистру
расчета?
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 026
Краткое содержание
Расчет, учитывая базу
Для того, чтобы работать с базой было возможно, нажали галочку в РР базовый период, в
плане видов расчета включили зависимость от базы и указали базовый вид расчета для
ПремииПроцентом.
В общем модуле рассчитали сумму базы и использовали ее для расчета премии.
Перерасчеты
Отчет по перерасчетам
Детальное описание
На прошлом занятии рассчитали ОкладСуммой.
На этом рассчитаем ПремиюПроцентом.
От какого периода считать? Пока в нашем РР нет периода базы:
Добавим базовый период в регистр расчета ЖурналНачислений (в виде двух полей
БазовыйПериодНачало и БазовыйПериодКонец), нажав одну галочку Базовый период.
Поля базового периода не обязательны к заполнению. Например, зачем их указывать для
расчета ОкладаСуммой?
Базовый период установить уже сможем. А какой вид расчета считать как базовый?
ОкладСуммой, Больничный? Что именно?
Для возможности выбора некоторого вида расчета в качестве базового в плане видов расчета
Начисления на закладке Расчет устанавливаем или Зависит по периоду действия, или Зависит
по периоду регистрации (пункт 1 скрина).
При выборе этих пунктов в плане видов расчетов появилась дополнительная табличная часть
-- Базовые.
Но в этой таблице Базовые не будут доступны никакие поля, если не указать из какого
конкретного плана видов расчета выбирается база. А ведь их может быть много, это у нас
сейчас один. Ставим галочку на вкладке Расчет плана видов расчета в поле Базовые планы
видов расчета для ПВР Начисления (пункт 2 скрина).
Добавляем в ПВР Начисления вид начисления ПремияПроцентом, для него устанавливаем
базовым ОкладСуммой.
В докумен ВводПроизвольныхНачислений добавляем поля БазовыйПериодНачало и
БазовыйПериодКонец (вспоминаем, что заполняются записи движений документа в регистре
через ЗаполнитьЗначенияСвойств, поэтому имена в регистре и в документе должны
совпадать, а стандартные поля базового периода в регистре расчета называются именно так).
Тестируем. Создаем второй документ Ввод произвольных начислений:
Проводим. В РР ЖурналНачислений движения по новому документу отобразились, в т.ч. и
премия. Конечно, пока суммы премии странные, но мы же помним, как пока считается сумма (в
общем модуле как Запись.Сумма = Запись.Параметр).
Изменим расчет. Будем теперь рассчитывать записи, исходя из условия по виду расчета.
В общем модуле Расчет:
Для каждого Запись Из НаборЗаписей Цикл
Если Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладСуммой Тогда
Запись.Сумма = Запись.Параметр;
ИначеЕсли Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ПремияПроцентом Тогда
?
КонецЕсли;
КонецЦикла;
Если у конкретной записи из НабораЗаписей вид расчета указан как ПремияПроцентом, надо
получить сумму базы, от которой считать % премии.
У записи регистра расчета есть всего два метода: ПолучитьБазу и ПолучитьДанныеГрафика.
Нас интересует метод ПолучитьБазу.
Строка в ТЗ_База одна, поэтому будем обращаться к этой строке как нулевому элементу
таблицы значений и значение колонки сумма этой строки поместим в переменную База
(находимся в общем модуле Расчет):
Базу = ТЗ_База[0].Сумма;
И теперь ПремиюПроцентом для конкретной записи посчитаем по формуле:
Запись.Сумма = База * Запись.Параметр / 100;
В общем модуле Расчет сейчас:
Процедура РассчитатьЗаписи(Регистратор) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Для каждого Запись Из НаборЗаписей Цикл
Если Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ОкладСуммой Тогда
Запись.Сумма = Запись.Параметр;
ИначеЕсли Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ПремияПроцентом Тогда
МассивРесурсов = Новый Массив(1);
МассивРесурсов[0] = "ЖурналНачислений.Сумма";
СтруктураИзмерений = Новый Структура();
СтруктураИзмерений.Вставить("Сотрудник",
"ЖурналНачислений.Сотрудник");
ТЗ_База = Запись.ПолучитьБазу(МассивРесурсов,
СтруктураИзмерений);
База = ТЗ_База[0].Сумма;
Запись.Сумма = База * Запись.Параметр / 100;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
КонецПроцедуры
Обратим здесь внимание на строку
ТЗ_База = Запись.ПолучитьБазу(МассивРесурсов, СтруктураИзмерений);
Мы же понимаем, что это запрос. И в нашем случае он в цикле. Запрос в цикле - это очень
плохо! В дальнейшем научимся делать по-другому.
Тестируем. Перепроводим документ от 14.01.19г. и видим, что в РР Журнал начислений
премия рассчиталась.
Введем новый документ от 01.01.19г с Иванюхиным с видом расчетаОкладСуммой с
параметром 33.333. Теперь у Иванюхина в январе 2 оклада. Перепроведем документ, где ему
начислялась премия и в РР Журналначислений видим, что для ПремииПроцентом учтены оба
оклада.
Запоминаем: записи или только по одному сотруднику вносим в документ, или только по
одному виду расчета.
Необходимость перерасчета
Организуем механизм фиксирования информации о необходимости пересчета определенного
перечня зависимых видов расчета для определенных сотрудников при изменении записей
ведущих видов расчета для этих сотрудников.
Настраивается практически всё вручную.
Регистр расчета ЖурналНачислений -- вкладка Перерасчеты. Добавим таблицу перерасчетов
Перерасчет1, а в ней измерение Сотрудник.
Определим связь:
Т.е. при изменение данных в РР ЖурналНачислений по записям с ведущим видом расчета в
таблице Перерасчет1 будет сохранена информация о зависимых от этого вида видах расчетов
по данным сотрудникам.
Таблица перерасчетов состоит всегда из трех колонок:
1. документ, записи в котором стали неактуальны,
2. вид расчета,
3. сотрудник.
В плане видов расчета Начисления определим ведущие и зависимые виды расчета. При
изменении записей с видом расчета ОкладСуммой надо пересчитать записи с видом расчета
ПремияПроцентом.
Логика подсказывает, что чаще всего Базовые и Ведущие будут совпадать.
При изменении премии оклад пересчитывать не будем.
Сформируем отчет по перерасчетам
Добавим отчет Перерасчеты. СКД -- выбираем все поля из таблицы перерасчетов
ЖурналНачислений.Перерасчет1: ОбъектПерерасчета (это документ), видРасчета, Сотрудник.
На закладке Настройки добавляем эти 3 поля в выбранные поля и всё это выводим в виде
детальных записей:
Отчет добавляем в подсистему Расчет.
Тестируем. Формируем отчет -- изначально он пустой. Поменяем где-нибудь одну из ведущих
записей (а у нас она пока одна -- ОкладСуммой): в документе Ввод произвольных начислений
№1 от 13.01.19г. Иванюхину изменим параметр по виду расчета ОкладСуммой со 120.000 на
110.000. Перепроводим документ. Формируем отчет по перерасчетам.
В документе №1 были 2 сотрудника с видом расчета ОкладСумма. Система сделала так: все
записи, из набора записей, поступившие из документа, зависимые от вида расчета
ОкладСуммой, стали неактуальны.
Совпадение, что и в документе №1(с окладом), и в документе №2(с зависящей от него
премией) - именно эти 2 сотрудника.
Перепроводим документ, указанный в отчете. Вновь формируем отчет - он пустой.
В какой момент данный из таблицы перерасчетов “уходят”? Просто: когда перерасчет будет
произведен. Грамотно: Когда будет зарегистрирована успешная транзакция набора записей в
РР, которая попала в эту таблицу.
Вопросы к занятию (для самоконтроля)
Как добавить поля базового периода в регистр расчета?
Где в регистре расчета увидеть поля базового периода?
Как добавить в план видов расчета таблицу Базовые?
В каком случае в таблице Базовые плана видов расчета не отображаются поля?
Сколько и какие методы есть для записи регистра расчета?
Описать синтаксис метода ПолучитьБазу регистра расчета
Описать механизм формирования таблица перерасчетов
Сколько колонок и какие находятся в таблице перерасчеты?
Как в план видов расчета добавляется таблица Ведущие и для чего она предназначена?
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 027
Краткое содержание
1. Поняли, для чего нужен период действия.
2. Добавили регистр сведений Календарь. Для его заполнения -- обработку
ЗаполнениеКалендаря.
3. Узнали, как в РР включить период действия (галочка у одноименного поля) и как эта
галочка включает 3 вещи: добавляет в РР 3 стандартные поля, дает возможность
связать РР и РС с графиком, включает режим вытеснения.
4. Узнали, какую галочку надо поставить в РС с графиком, чтобы включенный РР механизм
вытеснения работал качественно (галочку Использует период действия).
5. В общем модуле посчитали ОкладПоДням и ОкладЗаМесяц, предварительно получив
ФактДней и НормаДней, используя метод записи РР ПолучитьДанныеГрафика.
6. Построили отчет ДиаграммаГанта и поняли, почему ПериодДействияКонец в документе
ВводПроизвольныхНачислений надо привести к концу дня.
Детальное описание
Получение данных графика
Необходимо знать, какой день рабочий, какой -- выходной. Для этого создадим регистр
сведений Календарь (в подсистему Расчет):
Для упрощения заполнения РС Календарь формируем обработку ЗаполнениеКалендаря (в
подсистему Расчет). В обработке данных нет, добавляем форму. На форме обработки
добавляем и выносим на форму:
● реквизит Период (тип Стандартный период),
● команду Заполнить (на Клиенте и на Сервере).
Модуль формы обработки:
&НаСервере
Процедура ЗаполнитьНаСервере()
ТекДата = Период.ДатаНачала;
НачатьТранзакцию();
Пока ТекДата <= Период.ДатаОкончания Цикл
Запись = РегистрыСведений.Календарь.СоздатьМенеджерЗаписи();
Запись.Дата = ТекДата;
Запись.Признак = ?(ДеньНедели(ТекДата) < 6, 1, 0);
Запись.Записать();
ТекДата = ТекДата + 86400;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
Иначе
Сообщить(ОписаниеОшибки());
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура Заполнить(Команда)
ЗаполнитьНаСервере();
ПоказатьОповещениеПользователя("Все ок");
КонецПроцедуры
В пользовательском режиме, используя обработку, заполняем РС Календарь графиком 2019г.
Можно вносить изменения вручную, например, изменили 01.01.19г. на выходной день.
Теперь знаем, какой день рабочий, какой выходной.
Теперь нужно для каждой записи знать, сколько сотрудник реально работал.
Сейчас мы знаем по каждому сотруднику только
1. к какому месяцу относится расчет (пункт 1 скрин колонка “Период регистрации”). А вдруг
сотрудник проработал только ½ месяца. Где это увидеть?
2. и базовый период (пункт 2 скрин поля “Дата начала базового периода” и “Дата
окончания базового периода”). Но база не прояснит, сколько сотрудник работал в этом
периоде реально.
Значит, в РР надо внести еще ПЕРИОД ДЕЙСТВИЯ.
Основное для этого -- галочка в РР в поле период действия (пункт 1 скрин).
Она вызывает 3 изменения:
1) в РР появляется 3 стандартных реквизита (пункт 2 скрин),
2) включается механизм вытеснения (пункт 3 скрин),
3) в РР на закладке Основное становятся активными 3 поля для связи РР и регистра
сведений с данными о графике работы (в нашем случае -- с РС Календарь) (пункт 4
скрин).
Подробнее о этих пунктах.
1)О вновь появившихся полях. ПериодДействия всегда будет началом месяца.
Например,внесли запись, где ПериодДействияНачало 15 янв., ПериодДействияКонец 31янв.
Система укажет для ПериодДействия 1 янв.
Как отличается ПериодРегистрации и ПериодДействия? Сотрудник болел и принес
больничный лист в феврале, но болел в январе. ПериодРегистрации - 1 февр,
ПериодДействия - 1 янв.
2) О механизме вытеснения. Механизм вытеснения означает, что система при каждой записи
данных в регистр будет проверять, а нет ли конкурирующих друг с другом записей.
Простым языком: вводим прогул, система ищет, а что этот прогул уменьшить должен, нет ли
окладов по этому же периоду, если есть -- их надо “урезать”.
Механизм тяжелый, он будет перебирать записи и пытаться найти все конкурирующие записи.
Например, из документа сейчас отправляется в РР 100 записей и в РР уже 200 записей есть.
Системе надо каждую запись с каждой сравнить. Медленно! Поэтому в таких регистрах
расчета, в которых включен период действия, нужно хранить только те записи, для которых
это вытеснение имеет смысл.
3) О связывании РС Календарь и РР ЖурналНачислений. Подробно на скрине (п.4)
После этого система может посчитать количество рабочих дней в периодах.
Для того, чтобы правильно работал механизм вытеснения, описанный в п.2, надо указать какой
вид расчета каким вытесняется (п.5 скрин) Например, оклад вытесняется больничным,
прогулом, др.
--------------------------------Создаем 2 вида расчета: Прогул (000003), ОкладПоДням (00005). Для ОкладПоДням
вытесняющий -- Прогул. Поправим вид расчета ПремияПроцентом: добавим для него в
базовые и ведущие ОкладПоДням.
Поправим документ.ВводПроизвольныхНачислений. Добавляем в его ТЧ ЖурналНачислений 2
реквизита: ПериодДействияНачало и ПериодДействияКонец, перенесем их выше и сделаем
обязательными полями (добавим проверку заполнения).
Сумму из реквизитов ТЧ документа удалим, она же здесь у нас всегда пустая и заполняется
только в РР. При этом система “ругается”, игнорируем предупреждения.
Тестируем. Все документы Ввод произвольных начислений перепроводим, предварительно
введя для каждой строкиТЧ информацию о периоде действия (начало и конец).
Добавляем новый документ
В РР ЖурналНачислений попали исходные данные, а сумма, конечно, не рассчиталась.
Рассчитаем. Идем в общий модуль Расчет.
Вспоминаем, у записей РР всего 2 метода: ПолучитьБазу и ПолучитьДанныеГрафика.
Теперь рассмотрим и используем второй.
Синтаксис:
ПолучитьДанныеГрафика(<ВидПериода>)
где ВидПериода -- 4 варианта ВидПериодаРегистраРасчета:
1. ПериодДействия,
2. БазовыйПериод,
3. ПериодРегистрации,
4. ФактическийПериодДействия.
Для первых трех пунктов. Это не сколько фактически отработал сотрудник в периоде
(действия, базовом или регистрации), а сколько рабочих дней в этом периоде согласно
графика.
4) ФактическийПериодДействия. Это самое интересное. Сколько дней между
ПериодДействияНачало и ПериодДействияКонец за вычетом вытесненных дней другими
записями. Например, зарегистрировали сотруднику запись с 1 по 31 янв., потом
зарегистрировали ему прогул 5 дней в середине января. Система будет считать за минусом
прогула 5 дней.
То есть, конечно именно ФактическийПериодДействия нам и надо использовать, чтобы
понять, за сколько же дней платить оклад.
Метод ПолучитьДанныеГрафика вернет таблицу значений (как и метод ПолучитьБазу).
Для записи РР всегда этим методом будет возвращена 1 строка.
Но метод ПолучитьДанныеГрафика есть не только для записи РР. Этот метод есть и у
менеджера РР, т.е. для всего набора записей. В этом случае метод ПолучитьДанныеГрафика
вернет данные сразу для всего набора записей, которые рассчитываются. В этом случае м.б.
не одна строка.
У нас же для конкретной записи РР всегда метод ПолучитьДанныеГрафика вернёт только 1
строку с колонкой, которая называется как ресурс РС, связанного с данным РР -- Признак:
Добавим в процедуре общего модуля Расчет:
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладПоДням
Тогда
ТЗ_ФактДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ФактическийПериодД
ействия);
ФактДней = ТЗ_ФактДней[0].Признак;
Запись.Сумма = ФактДней * Запись.Параметр;
Тестируем. Перепроводим документ с созданым чуть раньше документом с ОкладомПоДням.
Видим, что РР ЖурналНачислений расчиталось всё правильно: было 22 дня по 1.000, Сумма
22.000. Верно!
Посмотрим, работает ли вытеснение.
введем документ для Добаговой за январь с прогулом:
А теперь перепроведем документ с ее же окладом по дням. Смотрим в РР ЖурналНачислений.
Пересчиталось! Теперь ее сумма (ОкладПоДням) не 22.000, а 12.000.
Ведь для ОкладаПоДням Прогул указан как вытесняющий.
Посмотрим, что сейчас у нас с механизмом перерасчетов. В документе с прогулом
Добаговой изменим начало ее прогула, например, на 10 янв. По жизненной логике оклад надо
бы пересчитать. Но ни для Прогула, ОкладаПоДням ведущие виды оклада мы не указывали.
Формируем отчет Перерасчеты. В нем есть информация о необходимости перерасчета
ОкладаПоДням. Это, конечно, отлично, но почему так сработало, не смотря на то, что Прогул
ведущим для ОкладаПоДням не указан?
❖ Перерсчеты по вытеснениям работают автоматически!
Мы же указали, что Прогул является вытесняющим для ОкладаПоДням, в этом случае он
работает и как ведущий.
---------------------------------------Добавим вид расчета ОкладЗаМесяц(000006). Вытесняющим для него является Прогул.
Укажем ОкладЗаМесяц как базовый и ведущий для ПремииПроцентом.
Идём в общий модуль Расчёт. Добавляем новое условие для этого вида расчета. Знаем, как
получать ФактДней за месяц (получали для ОкладаПоДням). Получим НормаДней, то есть
сколько рабочих дней должно было бы попасть в ПериодДействия:
ТЗ_НормаДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ПериодДействия);
НормаДней = ТЗ_НормаДней[0].Признак;
Знаем, сколько должен был отработать сотрудник за период действия и сколько отработал
фактически и сколько должен был бы получить, если бы отработал все дни по норме.
Отсюда:
Запись.Сумма = ФактДней / НормаДней * Запись.Параметр;
Чтобы избежать в любом случае деления на ноль, введем проверку на ноль НормаДней. В
результате для расчета ОкладаЗаМесяц в общий модуль добавили:
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладЗаМесяц
Тогда
ТЗ_ФактДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ФактическийПериодД
ействия);
ФактДней = ТЗ_ФактДней[0].Признак;
ТЗ_НормаДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ПериодДействия);
НормаДней = ТЗ_НормаДней[0].Признак;
Если НормаДней = 0 Тогда
Запись.Сумма = 0;
Иначе
Запись.Сумма = ФактДней / НормаДней * Запись.Параметр;
КонецЕсли;
Тестируем. Работает!
Создадим отчет ДиаграммаГанта. Добавляем форму, на ней реквизит Диаграмм (тип
ДиаграммаГанта) и команду Сформировать (наКлиенте и на Сервере).
Модуль формы:
&НаСервере
Процедура СформироватьНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|
ЖурналНачисленийФактическийПериодДействия.ВидРасчета КАК
ВидРасчета,
|
ЖурналНачисленийФактическийПериодДействия.Сотрудник КАК
Сотрудник,
|
ЖурналНачисленийФактическийПериодДействия.ПериодДействияНачало
КАК ПериодДействияНачало,
|
ЖурналНачисленийФактическийПериодДействия.ПериодДействияКонец
КАК ПериодДействияКонец
|ИЗ
|
РегистрРасчета.ЖурналНачислений.ФактическийПериодДействия КАК
ЖурналНачисленийФактическийПериодДействия";
//Запрос.УстановитьПараметр("", );
РезультатЗапроса = Запрос.Выполнить();
Диаграмма.Очистить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Точка = Диаграмма.УстановитьТочку(Выборка.Сотрудник);
Серия = Диаграмма.УстановитьСерию(Выборка.ВидРасчета);
Значение = Диаграмма.ПолучитьЗначение(Точка, Серия);
Интервал = Значение.Добавить();
Интервал.Начало = Выборка.ПериодДействияНачало;
Интервал.Конец = Выборка.ПериодДействияКонец;
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура Сформировать(Команда)
СформироватьНаСервере();
КонецПроцедуры
Для корректного отображения в модуле документа ВводПроизвольныхНачислений к концу дня
приводим ПериодДействияКонец:
Вопросы к занятию (для самоконтроля)
В каком случае метод ПолучитьДанныеГрафика возвращает несколько строк?
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 028
Краткое содержание
Детальное описание
Раньше научились
1) рассчитывать Оклад, просто присваивая сумме параметр (для ОкладСуммой):
Запись.Сумма = Запись.Параметр;
2) узнавать базу (для ПремииПроцентом):
ТЗ_База = Запись.ПолучитьБазу(МассивРесурсов, СтруктураИзмерений);
База = ТЗ_База[0].Сумма;
Запись.Сумма = База * Запись.Параметр / 100;
3) рассчитывать ФактДней и НормаДней (для ОкладПоДням и ОкладЗаМесяц):
Для ОкладЗаМесяц:
ТЗ_ФактДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ФактическийПериод
Действия);
ФактДней = ТЗ_ФактДней[0].Признак;
ТЗ_НормаДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ПериодДействия);
НормаДней = ТЗ_НормаДней[0].Признак;
Если НормаДней = 0 Тогда
Запись.Сумма = 0;
Иначе
Запись.Сумма = ФактДней / НормаДней * Запись.Параметр;
КонецЕсли;
Познакомимся с расчетом по среднему на примере расчета больничного. Будем брать
количество дней болезни и умножать на среднюю ставку (среднее дневной оклад за последние
2 месяца).
Для этого узнаем:
● сколько сотрудник за последние 2 месяца заработал
● и сколько за этот же период отработал.
Создаем начисление Больничный. Его база (они же и ведущие) -- все виды окладов и Премия
(не включаем только Прогул). Больничный вытесняет ОкладПоДням и ОкладЗаМесяц.
Введем тестовые данные. Сотрудник будет болеть в марте, начнем с ввода оклада за
предыдущее 3 месяца (Пилюлькину за янв - март по 100.000):
Введем больничный за март. В параметры -- 60%, базовый -- за предыдущие 2 месяца:
Перепроводим документ с ОкладомЗаМесяц Пилюлькину за март. Смотрим в РР
ЖурналНачислений -- оклад за март был перерассчитан (71428.57), часть дней в марте была
вытеснена больничным.
Идем в общий модуль.
Больничный собираемся рассчитать по такой формуле:
Запись.Сумма = ФактДней / ДнейВБазе * База * Запись.Параметр / 100;
Базу считали выше (для ПремииПроцентом), скопируем. Могли бы, конечно, этот
повторяющийся фрагмент кода поместить в некую функцию, но не будем, т.к. скоро всё будет
изменено.
ФактДней получали тоже (для СуммаЗаМесяц и СуммаПоДням) -- копируем.
Осталось определить ДнейВБазе. Копируем фрагмент (2 строки кода) для нахождения
ФактДней и немного изменяем его (ДанныеГрафика ищем для базового периода:
получается:
Тестируем. Перепроводим документ с больничным. Рассчиталось. Для упрощения проверки
установим % больничного (параметр в документе) 100 и перепроводим документ с
больничным опять. Получается в РР ЖурналНачислений правильная сумма больничного
(28.571.43).
Но ведь какой-то же подвох есть!
Ставим точку останова в общем модуле в строке расчета больничного
Запись.Сум
ма =
ФактДн
ей
/ ДнейВБа * База
зе
* Запись.Пара
метр
/
100;
что “прилетело”
6
42
200.000
Но изменим документ с окладом Пилюлькиным за янв. Пусть он работал не с 1-31
янв, а с 20-31 янв. Видим в РР: оклад за янв пересчитался (40.909,09).
Больничный за март был 28.571.43 Перепроводим документ с этим больничным.
Стало 20.129,87
6
42
140.909,09
Дни в базе не пересчитались! Уменьшенную базу делим на неуменьшенное количество дней.
Что произошло:
ДнейВБазе считали от БазовогоПериода. А он ведь не уменьшился. Конечно, могли бы внести
изменения в базовый период и при этом рассчиталось бы правильно. Но, представим:
сотрудник прогулял в январе в середине месяца. Как же будем “вычленять” серединку
базового периода? Невозможно. Поэтому наш расчет (выше) принципиально неверен.
!!! ДнейВБазе надо рассчитывать не как количество рабочих дней в базовом периоде.
Надо накапливать количество реально отработанных дней в базовом периоде в РР
ЖурналНачислений (добавляем ресурс ОтработаноДней/ Число, 2, 0).
Для каких видов расчета накапливать отработанные дни? Вдруг в дальнейшем появится какойто вид, которого пока нет, по которому тоже надо накапливать количество отработанных дней?
Поэтому в плане видов расчета Начисления для своего удобства добавим реквизит
УчитываетОтработанныеДни (булево). Если Истина -- отработанные дни рассчитываем.
Добавим в общем модуле Расчет ниже строки с вычислением суммы больничного (на
сертификации так не писать!):
Накопили за каждый месяц количество фактически отработанных дней.
Тестируем.
В регистре сведений Начисления для ОкладЗаМесяц и ОкладПоДням ставим галочку
УчитыватьКоличество отработанных дней.
В РР ЖурналНачислений отберем записи только по Пилюлькину:
Это 4 документа (3 оклада и 1 больничный). Перепроведем документы с окладами (тем самым
накопим отработанные дни Пилюлькина).
В общем модуле комментируем ошибочный фрагмент кода (поиска количества отработанных
дней через их получение посредством метода записи РР ПолучитьДанныеГрафика в базовом
периоде):
//НЕПРАВИЛЬНО
//ТЗ_ДнейВБазе =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.БазовыйПериод);
//ДнейВБазе = ТЗ_ДнейВБазе[0].Признак;
Отработанные дни -- это ресурс нашего РР и его получим как ресурс (как получали и Сумму),
через метод записи ПолучитьБазу:
МассивРесурсов = Новый Массив(2); //в массив добавляем 2 ресурса РР
МассивРесурсов[0] = "ЖурналНачислений.Сумма";
МассивРесурсов[1] = "ЖурналНачислений.ОтработаноДней";// вновь добавленный
ресурс
СтруктураИзмерений = Новый Структура();
СтруктураИзмерений.Вставить("Сотрудник", "ЖурналНачислений.Сотрудник");
ТЗ_База = Запись.ПолучитьБазу(МассивРесурсов, СтруктураИзмерений);
База = ТЗ_База[0].Сумма;
ДнейВБазе = ТЗ_База[0].ОтработаноДней;// получили количество отработанных
дней
Тестируем. Перепроводим больничный -- рассчитан правильно: 29.153,60
Теперь введем другого сотрудника. Но у него произойдет принципиально все по-другому.
Вводим 4 документа с ОкладомЗаМесяц Чистову янв-апр с параметром 100.000, пустым
базовым периодом и периодом действия с 1-го по последнее число каждого соответствующего
месяца. Дата документа -- первое число соответствующего месяца.
Делаем отбор по Чистову в РР ЖурналНачислений. Видим 4 записи с рассчитанным окладом.
Работник в апреле принес больничный за март:
Проводим. Больничный в РР ЖурналНачислений не рассчитался.
Ставим в общем модуле Расчет точку останова здесь:
Запись.Сумма = ФактДней / ДнейВБазе * База * Запись.Параметр / 100;
и видим, что в ФактДней попадает 0.
Период регистрации (01 апреля) старше периода действия (12-20 марта). Больничный
является вытесняющим для ОкладаЗаМесяц:
Больничный пытается вытеснить ОкладЗаМесяц, причем записи с больничным имеют период
регистрации младше, чем период регистрации записей, которые больничный пытается
вытеснить. Это система не позволит.
Здесь автор (Павел Чистов) приводит такое мнемоническое правило. Суть: Зелен еще
вытеснять бывалых.
Необходимо в апреле сторнировать сумму оклада марта, приходящуюся на время мартовского
больничного.
Система знает при регистрации больничного, что ей не дает это сделать какой-то “кусок”
оклада. Система это знает и мы можем воспользоваться этой информацией при регистрации
больничного. Т.е. при расчете больничного можем получить информацию, что ему мешает
рассчитаться.
Идем в модуль объекта ВводПроизвольных начислений и получаем все, мешающие записи
(зона СТОРНО):
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Для каждого Стр Из ЖурналНачислений Цикл
Запись = Движения.ЖурналНачислений.Добавить();
ЗаполнитьЗначенияСвойств(Запись, Стр);
Запись.ПериодРегистрации = Дата;
Запись.ПериодДействияКонец =
КонецДня(Запись.ПериодДействияКонец);
КонецЦикла;
////////////////////////////////////////////////////////////////////////////////////////////
/////
ЗаписиСторно = Движения.ЖурналНачислений.ПолучитьДополнение(); //зона
СТОРНО
Для каждого Стр Из ЗаписиСторно Цикл
Запись = Движения.ЖурналНачислений.Добавить();
ЗаполнитьЗначенияСвойств(Запись, Стр);
Запись.ПериодРегистрации = Стр.ПериодРегистрацииСторно;
Запись.ПериодДействияНачало = Стр.ПериодДействияНачалоСторно;
Запись.ПериодДействияКонец =
КонецДня(Стр.ПериодДействияКонецСторно);
Запись.Сторно = Истина;
КонецЦикла;
//////////////////////////////////////////////////////////////////////////////конец зона
СТОРНО
Движения.ЖурналНачислений.Записывать = Истина;
Движения.Записать();
Расчет.РассчитатьЗаписи(Ссылка);
КонецПроцедуры
Пояснение к зона СТОРНО
Есть такой метод движений ПолучитьДополнение. Этот метод возвращает таблицу значений о
записях РР, конкурирующих с записями, проводимыми данным документа.
Таблица значений повторяет структуру регистра + там будут 3 доп.колонки
(ПериодДействияНачалоСторно, ПериодДействияКонецСторно, ПериодРегистрацииСторно):
Вот и возьмем эти 3 поля и сформируем запись по окладу с признаком Сторно, мешающему
провести больничный. Формируем цикл по ЗаписямСторно (их же м.б.несколько). В этом цикле
всё, что и в цикле выше по строкам ТЧ документа ЖурналНачислений. Скопируем этот цикл.
Различие: период действия начало, конец, регистрации берем из Стр (заполняем значениями
этих 3 доп.полей, возращенных методом ПолучитьДополнение()).
И добавляем:
Запись.Сторно = Истина;
Именно это свойства (это строка) и говорит, что теперь, т.к. мы по сути скопировали запись об
окладе (это же оклад у нас сейчас прилетел в ЗаписиСторно), то сейчас, если оклад на оклад
накладывается, то фактический период действия будет освобожден.
Тестируем. Перепроводим документ с больничным. В РР ЖурналНачислений видим:
больничный рассчитан (33.333,33). И видим: добавилась строка по окладу с признаком сторно
(с периодом регистрации 1апр). Но со знаком “+”!
Но его сминусовать. В общем модуле Расчет допишем:
Если Запись.Сторно Тогда
Запись.Сумма = -Запись.Сумма;
Запись.ОтработаноДней = -Запись.ОтработаноДней;
КонецЕсли;
Теперь понятна разница этих 2-ух свойств зависимости от базы в плане видов расчета:
Обратим внимание: если в модуле документа нет записей сторно, то в перерасчетах
необходимость сторнирования не зарегистрируется. Т.е.в текущем месяце если что-то
вводится -- в перерасчеты “влетает” информация по вытесняющим видам расчетов (на
прошлом уроке видели). В будущих -- “влетает” (мы об этом еще поговорим.
А вот задним числом -- ждет, чтобы мы задействовали описание механизма сторно в модуле
документа. Иначе в перерасчетах не отразится.
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание 10 / Самостоятельная работа
Решение.
1) Механизмы расчета предназначены для (4)
1. Может быть настроена зависимость по перерасчетам. Т.е.возможен каскадный
перерасчет.
2. Есть возможность автоматически получать базу. Конечно, это можно настроить и не с
помощью механизмов расчета, но понадобится программировать, а с помощью
механизмов расчета это делает система.
3. Возможность настраивать сложные периодические расчеты (благодаря 2 методам
записи РР: ПолучитьБазу и ПолучитьДанныеГрафика), учитывая механизмы
вытеснения, полученную базу, др.
4. ?
2) План видов расчета хранит данные по
● базе,
да
● вытеснению,
да
● перерасчетам.
да
3) Настройка “Зависимость по базе” включает возможность задавать
базовые виды расчета
да
базовый период
данные для перерасчета базы
Т.е. настроить зависимость по базе -- это указать, например, что ПремияПроцентом считается
от ОкладаСуммой (в плане видов расчета):
4) Перерасчеты автоматически регистрируются
Изменении записей РР с базовыми видами расчета
Изменении записей РР с базовыми видами расчета
да
Изменении записей РР с вытесняющими видами расчета в текущем периоде
да
Изменении записей РР с вытесняющими видами расчета в любом периоде
5) Метод "ПолучитьДанныеГрафика()" позволяет получить данные графика по следующим
периодам:
Период действия
да
Период регистрации
да
Период больничного
-
Период праздников
-
Фактический период действия
да
Базовый период
да
Промежуточный период
-
6) Данные графика хранятся в:
Регистр расчета
-
Регистр накопления оборотов
-
Регистр накопления остатков
-
Регистр сведений
Регистр бухгалтерии
да
-
7) Дополнение у набора записей регистра расчета (записи-сторно) могут появиться при
условии, что у записей:
Вид расчета вытесняет другой вид расчета
да
Период регистрации больше или равен периоду действия
Период регистрации больше периода действия
да
Период регистрации меньше или равен периоду действия
Период регистрации меньше периода действия
Совпадают измерения записываемой и вытесняемой записи регистра
да
Установлен признак сторно
8) Записи-сторно можно сформировать:
Только на основе полученных записей-дополнения
-
А никто не мешает мне ввести записи хоть вручную!
да
9) Для получения базы необходимо:
а) Указать базовые виды расчета
б) Указать ведущие виды расчета
в) Настроить в плане видов расчета зависимость по базе
г) Включить в плане видов расчета использование периода действия
д) Включить в регистре расчета базовый период
е) Связать регистр сведений и регистр расчета
10)
да
да
да
-
Занятие 029
Краткое содержание
Механизмы расчета. Оптимизация расчетов записей.
Детальное описание
Займемся оптимизацией расчета записей. Посмотрим на код общего модуля Расчет на
примере расчета Больничного:
Процедура РассчитатьЗаписи(Регистратор) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Для Каждого Запись Из НаборЗаписей Цикл
Если Запись.ВидРасчета = ……… Тогда
//условия для остальных видов расчета
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.Больничный Тогда
МассивРесурсов = Новый Массив(2);
МассивРесурсов[0] = "ЖурналНачислений.Сумма";
Массивресурсов[1] = "ЖурналНачислений.ОтработаноДней";
СтруктураИзмерений = Новый Структура();
СтруктураИзмерений.Вставить("Сотрудник", "ЖурналНачислений.Сотрудник");
ТЗ_База = Запись.ПолучитьБазу(МассивРесурсов, СтруктураИзмерений);
База = ТЗ_База[0].Сумма;
ДнейВБазе = ТЗ_База[0].ОтработаноДней;
ТЗ_ФактДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ФактическийПериодДействия);
ФактДней = ТЗ_ФактДней[0].Признак;
Запись.Сумма = ФактДней/ДнейВБазе * База * Запись.Параметр / 100;
КонецЕсли;
Если Запись.ВидРасчета.УчитываетОтработанныеДни Тогда
ТЗ_ФактДней =
Запись.ПолучитьДанныеГрафика(ВидПериодаРегистраРасчета.ФактическийПериодДейс
твия);
ФактДней = ТЗ_ФактДней[0].Признак;
Запись.ОтработаноДней = ФактДней;
КонецЕсли;
Если Запись.Сторно Тогда
Запись.Сумма = -Запись.Сумма;
Запись.ОтработаноДней = -Запись.ОтработаноДней;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
КонецПроцедуры
Получается для каждой записи регистра расчета с видом расчета Больничный у нас 3
запроса в цикле! А как нам нужно? Нам нужно, чтобы все данные, которые нам нужны для
расчета, были получены один раз для всего набора записей. Метод
ПолучитьДанныеГрафика() есть как у Записи (им мы до этого и пользовались), так и у
МенеджераРегистраРасчета:
Данный метод получит данные графика, но не для одной записи, а для всех записей по какомуто отбору. В качестве отбора мы должны, как минимум, указать Регистратор, для которого мы
записи получаем (т.е. как минимум нужно установить, для какого документы мы хотим
получить данные графика). Теперь метод ПолучитьДанныеГрафика() будет возвращать не
одну строку, а столько строк, сколько строк с таким отбором в НабореЗаписей есть. И тогда
внутри цикла нам не придется для каждой записи получать значения, мы будет просто
обращаться к уже рассчитанному значению. Аналогично метод ПолучитьБазу() есть не
только у записей, но и у МенеджераРегистраРасчета.
Оптимизированный общий модуль Расчет будет таким:
Процедура РассчитатьЗаписи(Регистратор) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Отбор = Новый Структура();
Отбор.Вставить("Регистратор", Регистратор);
//в ТЗ столько строк, сколько в НабореЗаписей строк. Индексы
ТЗ_ФактДней/ТЗ_НормаДней/ТЗ_РабДнейВБазе и НаборЗаписей совпадают
ТЗ_ФактДней = РегистрыРасчета.ЖурналНачислений.ПолучитьДанныеГрафика(Отбор,
ВидПериодаРегистраРасчета.ФактическийПериодДействия);
ТЗ_НормаДней =
РегистрыРасчета.ЖурналНачислений.ПолучитьДанныеГрафика(Отбор,
ВидПериодаРегистраРасчета.ПериодДействия);
ТЗ_РабДнейВБазе =
РегистрыРасчета.ЖурналНачислений.ПолучитьДанныеГрафика(Отбор,
ВидПериодаРегистраРасчета.БазовыйПериод);
МассивРесурсов = Новый Массив(2);
МассивРесурсов[0] = "ЖурналНачислений.Сумма";
Массивресурсов[1] = "ЖурналНачислений.ОтработаноДней";
СтруктураИзмерений = Новый Структура();
СтруктураИзмерений.Вставить("Сотрудник", "ЖурналНачислений.Сотрудник");
ТЗ_База = РегистрыРасчета.ЖурналНачислений.ПолучитьБазу(Отбор,
МассивРесурсов, СтруктураИзмерений);
Для Каждого Запись Из НаборЗаписей Цикл
Индекс = НаборЗаписей.Индекс(Запись); // система вернет индекс записи в
наборе
База = ТЗ_База[Индекс].Сумма;
ФактДней = ТЗ_ФактДней[Индекс].Признак;
НормаДней = ТЗ_НормаДней[Индекс].Признак;
ДнейВБазе = ТЗ_База[Индекс].ОтработаноДней;
Если Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладСуммой Тогда
Запись.Сумма = Запись.Параметр;
ИначеЕсли Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ПремияПроцентом Тогда
Запись.Сумма = База*Запись.Параметр/100;
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладПоДням
Тогда
Запись.Сумма = ФактДней * Запись.Параметр;
ИначеЕсли Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ОкладЗаМесяц Тогда
Если НормаДней = 0 Тогда
Запись.Сумма = 0;
Иначе
Запись.Сумма = ФактДней/НормаДней * Запись.Параметр;
КонецЕсли;
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.Больничный
Тогда
Запись.Сумма = ФактДней/ДнейВБазе * База * Запись.Параметр / 100;
КонецЕсли;
Если Запись.ВидРасчета.УчитываетОтработанныеДни Тогда
Запись.ОтработаноДней = ФактДней;
КонецЕсли;
Если Запись.Сторно Тогда
Запись.Сумма = -Запись.Сумма;
Запись.ОтработаноДней = -Запись.ОтработаноДней;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
КонецПроцедуры
Проверим в режиме 1С Предприятие, перепроведем документ
ВводПроизвольныхНачислений №16, который является регистратором для двух записей в
НабореЗаписей.
Поэтому в таблицах значений (ТЗ_ФактДней, ТЗ_НормаДней и т.д.) будет по две строчке
(столько же, сколько по данному документу записей в наборе записей).
Расчет в таком виде уже будет принят на экзамене 1С Специалист, и оценка снижена не будет.
Но, вообще говоря, этот код тоже не всегда оптимален, потому что нам не всегда нужно
получать и ФактДней, и НормуДней, и Базу, а мы на всякий случай получаем все.
Теперь попробуем получить данные не при помощи методов МенеджераРегистровРасчета, а
запросом. Какую таблицу нам нужно получить, чтобы гарантированно рассчитать все записи,
которые у нас есть?
НомерСтрок
и
УчитываетОтработанн
ыеДни
БазаДней
НормаДней ФактДней База
Такую таблицу мы будем собирать из нескольких таблиц: основой для таблицы должна быть,
та таблица, которая сейчас рассчитывается плюс несколько доп. колонок из других таблиц.
В виртуальную таблицу ЖурналНачислений.ДанныеГрафика входят все те же поля, что и в
реальную таблицу ЖурналНачислений плюс 4 поля: ПризнакПериодДействия,
ПризнакФактическийПериод, ПризнакБазовыйПериод, ПризнакПериодРегистрации. В
данной таблице строк всегда будет столько же, сколько и строк в реальной таблице.
Таблица ЖурналНачислений.БазаЖурналНачислений состоит из всех полей, что и
реальная таблица ЖурналНачислений плюс рассчитываемые поля (их будет столько, сколько
ресурсов у регистра расчета): СуммаБаза, ОтработаноДнейБаза. Про разрезы пока ничего не
говорим. Данная таблица будет содержать не все строки реальной таблицы, например по
больничному база будет, а по окладу (любому) нет, так как по нему не собирается база.
В нашем случае правильно соединить две таблицы ЖурналНачислений.ДанныеГрафика и
ЖурналНачислений.БазаЖурналНачислений и получить оттуда все нужные нам данные.
Заметка!: Соединять таблицы ЖурналНачислений.ДанныеГрафика и ЖурналНачислений
неправильно, так как они полностью повторяют друг друга, просто в первой есть еще доп.
поля, первая таблица более полная.
Итак, из таблицы ЖурналНачислений.ДанныеГрафика выберем поля указанные ниже и
установим отбор по регистратору:
Таблицы регистров расчета проиндексированы таким образом, что довольно эффективно
использовать составной индекс на период регистрации, поэтому добавим еще условие в
параметры виртуальной таблицы.
Если оставить отбор просто по Регистратору, то все будет работать, просто основной индекс
таблиц регистра расчета является составным: Регистратор + ПериодРегистрации, поэтому
так будет намного эффективнее.
Из таблицы ЖурналНачислений.БазаЖурналНачислений получим рассчитываемые поля
СуммаБаза, ОтработаноДнейБаза и также установим параметры виртуальной таблицы:
Параметр «Условие» - такое же, как и для предыдущей таблицы
Параметр «ИзмеренияОсновногоРегистра»: например, нужно нам базу получить только по
конкретному сотруднику, тогда в этом параметре нужно указать, откуда данные для отбора
брать. Т.е. в данном параметре нужно указать измерение, указать его нужно в виде массива. И
передадим мы туда название измерения, по которому у нас осуществляется отбор –
«Сотрудник». Данный параметр по сути, это откуда мы значение для отбора берем.
Параметр «ИзмеренияБазовогоРегистра» - это «в какую таблицу нужно залезть и отбор
поставить». В данный параметр тоже передается массив измерений. Данный параметр по
сути, это где мы значение для отбора устанавливаем.
Соединим эти таблицы по полю НомерСтроки, номер строки гарантированно будет везде
одинаковый (так как мы по одному документу данные берем).
Если нужно будет делать расчет сразу по нескольким документам, то в условиях соединения
таблиц будет еще связь по регистратору.
И поставим сортировку по НомеруСтроки, чтобы понимать, как у нас упорядочены записи,
потому что это для нас будет важно. Получим следующий запрос:
Процедура РассчитатьЗаписи(Регистратор) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|ЖурналНачисленийДанныеГрафика.НомерСтроки КАК НомерСтроки,
|ЖурналНачисленийДанныеГрафика.Сторно КАК Сторно,
|ЖурналНачисленийДанныеГрафика.Параметр КАК Параметр,
|ЖурналНачисленийДанныеГрафика.ПризнакПериодДействия КАК ПризнакПериодДействия,
|ЖурналНачисленийДанныеГрафика.ПризнакФактическийПериодДействия КАК
ПризнакФактическийПериодДействия,
|ЖурналНачисленийДанныеГрафика.ПризнакБазовыйПериод КАК ПризнакБазовыйПериод,
|ЖурналНачисленийДанныеГрафика.ПризнакПериодРегистрации КАК
ПризнакПериодРегистрации,
|ЖурналНачисленийДанныеГрафика.ВидРасчета.УчитываетОтработанныеДни КАК
ВидРасчетаУчитываетОтработанныеДни,
|ЖурналНачисленийБазаЖурналНачислений.СуммаБаза КАК СуммаБаза,
|ЖурналНачисленийБазаЖурналНачислений.ОтработаноДнейБаза КАК ОтработаноДнейБаза
|ИЗ
|
РегистрРасчета.ЖурналНачислений.ДанныеГрафика(
|
Регистратор = &Регистратор
|
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийДанныеГрафика
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрРасчета.ЖурналНачислений.БазаЖурналНачислений(
|
&МассивИзмерений,
|
&МассивИзмерений,
|
,
|
Регистратор = &Регистратор
|
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийБазаЖурналНачислений
|
ПО ЖурналНачисленийДанныеГрафика.НомерСтроки =
ЖурналНачисленийБазаЖурналНачислений.НомерСтроки
|
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
Теперь нам нужно установить параметр ПериодРегистрации, которого у нас нет. Мы его,
конечно, можем получить из Регистратора, но это будет дополнительный запрос, поэтому мы
будем передавать его в процедуру РассчитатьЗаписи() вторым параметром. Модуль объекта
документа ВводПроизвольныхНачислений будет:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Для Каждого Стр Из ЖурналНачислений Цикл
Запись = Движения.ЖурналНачислений.Добавить();
ЗаполнитьЗначенияСвойств(Запись, Стр);
Запись.ПериодРегистрации = Дата;
Запись.ПериодДействияКонец = КонецДня(Запись.ПериодДействияКонец);
КонецЦикла;
ЗаписиСторно = Движения.ЖурналНачислений.ПолучитьДополнение();
Для Каждого Стр Из ЗаписиСторно Цикл
Запись = Движения.ЖурналНачислений.Добавить();
ЗаполнитьЗначенияСвойств(Запись, Стр);
Запись.ПериодРегистрации = Стр.ПериодРегистрацииСторно;
Запись.ПериодДействияНачало= Стр.ПериодДействияНачалоСторно;
Запись.ПериодДействияКонец = КонецДня(Стр.ПериодДействияКонецСторно);
Запись.Сторно = Истина; // именно данная строка освобождает фактический период
действия при наложении периодов по окладу
КонецЦикла;
Движения.ЖурналНачислений.Записывать = Истина;
Движения.Записать();
Расчет.РассчитатьЗаписи(Ссылка, НачалоМесяца(Дата));//передаем ссылку на текущий
документ
КонецПроцедуры
Вернемся к коду общего модуля Расчет, установим параметры запроса, получим:
Процедура РассчитатьЗаписи(Регистратор, ПериодРегистрации) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|ЖурналНачисленийДанныеГрафика.НомерСтроки КАК НомерСтроки,
|ЖурналНачисленийДанныеГрафика.Сторно КАК Сторно,
|ЖурналНачисленийДанныеГрафика.Параметр КАК Параметр,
|ЖурналНачисленийДанныеГрафика.ПризнакПериодДействия КАК ПризнакПериодДействия,
|ЖурналНачисленийДанныеГрафика.ПризнакФактическийПериодДействия КАК
ПризнакФактическийПериодДействия,
|ЖурналНачисленийДанныеГрафика.ПризнакБазовыйПериод КАК ПризнакБазовыйПериод,
|ЖурналНачисленийДанныеГрафика.ПризнакПериодРегистрации КАК
ПризнакПериодРегистрации,
|ЖурналНачисленийДанныеГрафика.ВидРасчета.УчитываетОтработанныеДни КАК
ВидРасчетаУчитываетОтработанныеДни,
|ЖурналНачисленийБазаЖурналНачислений.СуммаБаза КАК СуммаБаза,
|ЖурналНачисленийБазаЖурналНачислений.ОтработаноДнейБаза КАК ОтработаноДнейБаза
|ИЗ
|
РегистрРасчета.ЖурналНачислений.ДанныеГрафика(
|
Регистратор = &Регистратор
|
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийДанныеГрафика
|
ЛЕВОЕ СОЕДИНЕНИЕ РегистрРасчета.ЖурналНачислений.БазаЖурналНачислений(
|
&МассивИзмерений,
|
&МассивИзмерений,
|
,
|
Регистратор = &Регистратор
|
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийБазаЖурналНачислений
|
ПО ЖурналНачисленийДанныеГрафика.НомерСтроки =
ЖурналНачисленийБазаЖурналНачислений.НомерСтроки
|
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
Запрос.УстановитьПараметр("Регистратор",Регистратор);
Запрос.УстановитьПараметр("ПериодРегистрации",ПериодРегистрации);
МассивИзмерений = Новый Массив(1);
МассивИзмерений[0] = "Сотрудник";
Запрос.УстановитьПараметр("МассивИзмерений",МассивИзмерений);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Дальше есть две технологии: либо по результату запроса бегать и внутри этого цикла в наборе
записей искать нужную строку, либо, наоборот, бегать по записям и внутри выборки искать
нужную запись с номером строки. Мы сделаем вторым вариантом. Получим:
Процедура РассчитатьЗаписи(Регистратор, ПериодРегистрации) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
|
ЖурналНачисленийДанныеГрафика.НомерСтроки КАК НомерСтроки,
|
ЖурналНачисленийДанныеГрафика.Сторно КАК Сторно,
|
ЖурналНачисленийДанныеГрафика.Параметр КАК Параметр,
|
ЖурналНачисленийДанныеГрафика.ПризнакПериодДействия КАК
ПризнакПериодДействия,
|
ЖурналНачисленийДанныеГрафика.ПризнакФактическийПериодДействия КАК
ПризнакФактическийПериодДействия,
|
ЖурналНачисленийДанныеГрафика.ПризнакБазовыйПериод КАК
ПризнакБазовыйПериод,
|
ЖурналНачисленийДанныеГрафика.ПризнакПериодРегистрации КАК
ПризнакПериодРегистрации,
|
ЖурналНачисленийДанныеГрафика.ВидРасчета.УчитываетОтработанныеДни КАК
ВидРасчетаУчитываетОтработанныеДни,
|
ЕСТЬNULL(ЖурналНачисленийБазаЖурналНачислений.СуммаБаза,0) КАК СуммаБаза,
|
ЕСТЬNULL(ЖурналНачисленийБазаЖурналНачислений.ОтработаноДнейБаза,0) КАК
ОтработаноДнейБаза
|ИЗ
|
РегистрРасчета.ЖурналНачислений.ДанныеГрафика(
|
Регистратор = &Регистратор
|
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийДанныеГрафика
|
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрРасчета.ЖурналНачислений.БазаЖурналНачислений(
|
&МассивИзмерений,
|
&МассивИзмерений,
|
,
|
Регистратор = &Регистратор
|
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийБазаЖурналНачислений
|
ПО ЖурналНачисленийДанныеГрафика.НомерСтроки =
ЖурналНачисленийБазаЖурналНачислений.НомерСтроки
|
|УПОРЯДОЧИТЬ ПО
|
НомерСтроки";
Запрос.УстановитьПараметр("Регистратор",Регистратор);
Запрос.УстановитьПараметр("ПериодРегистрации",ПериодРегистрации);
МассивИзмерений = Новый Массив(1);
МассивИзмерений[0] = "Сотрудник";
Запрос.УстановитьПараметр("МассивИзмерений",МассивИзмерений);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
СтруктураПоиска = Новый Структура("НомерСтроки",);
Для Каждого Запись Из НаборЗаписей Цикл
СтруктураПоиска.НомерСтроки = Запись.НомерСтроки;
Если НЕ Выборка.НайтиСледующий(СтруктураПоиска) Тогда
Запись.Сумма = 0;
Запись.ОтработаноДней = 0;
Иначе
База = Выборка.СуммаБаза;
ФактДней = Выборка.ПризнакФактическийПериодДействия;
НормаДней = Выборка.ПризнакПериодДействия;
ДнейВБазе = Выборка.ОтработаноДнейБаза;
УчитываетОтработанныеДни =Выборка.ВидРасчетаУчитываетОтработанныеДни;
Если Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладСуммой Тогда
Запись.Сумма = Запись.Параметр;
ИначеЕсли Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ПремияПроцентом Тогда
Запись.Сумма = База*Запись.Параметр/100;
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладПоДням
Тогда
Запись.Сумма = ФактДней * Запись.Параметр;
ИначеЕсли Запись.ВидРасчета =
ПланыВидовРасчета.Начисления.ОкладЗаМесяц Тогда
Если НормаДней = 0 Тогда //НормаДней может быть 0, если пользователь не
заполнил Календарь
Запись.Сумма = 0;
Иначе
Запись.Сумма = ФактДней/НормаДней * Запись.Параметр;
КонецЕсли;
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.Больничный
Тогда
Запись.Сумма = ФактДней/ДнейВБазе * База * Запись.Параметр / 100;
КонецЕсли;
Если УчитываетОтработанныеДни Тогда
Запись.ОтработаноДней = ФактДней;
КонецЕсли;
Если Запись.Сторно Тогда
Запись.Сумма = -Запись.Сумма;
Запись.ОтработаноДней = -Запись.ОтработаноДней;
КонецЕсли;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать();
КонецПроцедуры
Посмотрим в отладке, что у нас получилось в запросе:
Вопросы к занятие (для самоконтроля)
Домашнее задание/Самостоятельное
Домашнее задание/Самостоятельное
Решение
Занятие 030
Краткое содержание
Разобрали Графики (в т.ч. использование шаблонов) и механизм расчета по табелю
Детальное описание
Расчет по среднему дневному
Усложним работу с больничным. Сейчас рассчитываем: за каждый день болезни
рассчитываем по среднему дневному. Но за минусом выходных и праздников. Ведь мы
считаем:
Запись.Сумма = ФактДней / ДнейВБазе * База * Запись.Параметр / 100;
где ФактДней -- это рабочие дни согласно Календаря.
Хотим теперь: оплачивать за каждый день, включая выходные и праздники.
Так переходим к учету по разным графикам работы.
Дадим пользователю заполнять РС Календарь в зависимости от разных графиков работ.
1)Добавляем справочник ГрафикиРаботы (в подсистему Расчет).
2)В РС Календарь добавляем 2-е измерение -- График (тип - ссылка на справочник
ГрафикиРаботы, Ведущее, Основной отбор, Запрет незаполненных. Ведущее -- это значит,
если удалим график-пятидневку из справочника, то из РС удалится всё, что там есть.
Основной отбор -- это индексы.)
3)В обработке ЗаполнениеКалендаря удалим ранее введенные записи (2 строки):
&НаСервере
Процедура ЗаполнитьНаСервере()
ТекДата = Период.ДатаНачала;
НачатьТранзакцию();
Пока ТекДата <= Период.ДатаОкончания Цикл
Запись = РегистрыСведений.Календарь.СоздатьМенеджерЗаписи();
Запись.Дата = ТекДата;
Запись.Признак = ?(ДеньНедели(ТекДата) < 6, 1, 0);
//Запись.Записать();
///////////////////////Комментируем
Запись.Удалить();
////////////////////////Удаляем запись
ТекДата = ТекДата + 86400;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
Иначе
Сообщить(ОписаниеОшибки());
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура Заполнить(Команда)
ЗаполнитьНаСервере();
ПоказатьОповещениеПользователя("Все ок");
КонецПроцедуры
3)В пользовательском режиме запускаем обработку ЗаполнениеКалендаря (Расчет - Сервис Заполнение календаря - Стандартный период - Год - Этот год - Заполнить. Т.к. заполняли
именно на этот год. Теперь РС Календарь очищен).
4) Строки кода обработки возвращаем до состояния п.3.
5) На форме обработки добавляем реквизит График (тип: СправочникСсылка.ГрафикиРаботы).
Вытягиваем на форму.
Шаблон заполнения зададим в виде таблицы, где пользователи будут вводить “1” и “0”.
Добавляем реквизит ШаблонЗаполнения (тип: ТаблицаЗначений). В ШаблонЗаполнения
добавляем колонку реквизита Признак (Число,1, 0). Шаблон выносим на форму.
6)Посмотрим, что хотим сделать, в пользовательском режиме.Расчет - Сервис ЗаполнениеКалендаря.
Для января сначала сформируем, потом укажем графики Пятидневка, Календарные дни (все
дни будут рабочими), Два-через-два №1
Пятидневка
Все дни являются рабочими
Два-через-два
Начало заполнения (первый день ТЧ) приходится на первое число, указанное в периоде
обработки ЗаполнениеГрафика.Не каждый год начинается с понедельника, поэтому надо
внимательно следить, какой день периода устанавливаем первым.
6)В модуле формы обработки до цикла получаем количество строк в шаблоне (после
НачатьТранзакцию();):
КолСтрок = ШаблонЗаполнения.Количество();
Запись.Признак = ?(ДеньНедели(ТекДата) < 6, 1, 0);//в цикле вместо этого
Индекс = (ТекДата - Период.ДатаНачала) / 86400 % КолСтрок;// пропишем это,
(ТекДата - Период.ДатаНачала) - разница между текущ.датой и началом периода в
секундах. Делим разницу на кол-во сек в сутках, получаем количество дней и берем
остаток от деления количество прошедших дней с начала периода на количество
строк в шаблоне
Т.е.найдем разницу дат между текущей датой и датой начала периода и номер строки в
шаблоне, по которой взять признак.
--------------------------------------------Остаток от деления меньшего числа на большее есть меньшее число. Можно взять
за аксиому, а можно пересчитать по формуле, исходя из того, что любое число
представимо в виде:
делимое = частное * делитель + остаток,
где 0 <= остаток < делитель
--------------------------------------------Например, прошло 8 дней, в шаблоне 6 строк. Остаток от деления 2. Верно, 8 день
соответствует 2-й строке:
1 день
1
7
2 день
2
8
3 день
3
4 день
4
5 день
5
6 день
6
Еще пример.Прошло 3 дня, в шаблоне 7 строк. Остаток от деления меньшего на большее есть
меньшее число, значит, полученный индекс 3. То есть, 3-й день прошедший день со дня
начала периода соответствует 3-ему дню шаблона, что верно:
1 день
1
2 день
2
3 день
3
4 день
5 день
6 день
7 день
-------------------------------------------------------------Теперь в цикле для текущей даты вносим
Запись.Признак = ШаблонЗаполнения [Индекс].Признак ;
И для записи записываем используемый график:
Запись.График = График;
Тестируем. Расчет - Сервис - ЗаполнениеКалендаря. Начало периода установим не 1 янв., а
31дек.18г., т.к. хотим получить пятидневку (с понедельника).
Итак, Период 31.12.2018 - 31.01.2019, График Пятидневка.
Шаблон заполняем, как показано выше (1,1,1,1,1,0,0). Нажимаем Заполнить.
В РС Календарь заполнилось по пятидневкам.
Теперь пересчитаем какую-нибудь запись с окладом по дням (напр., с окладом Добаговой за
янв.) Было 18.000, стало 57.000. Почему?
Система не знает, по какому графику ей рассчитывать.
Нам стало мало этих 3-х полей для связи РР и РС Календарь:
Можно добавить либо измерение, либо реквизит РР. В нашем случае базу разрезать не
собираемся по графику, просто хотим добавить признак записи, поэтому добавим просто
реквизит РР. Добавляем реквизит График (тип: СправочникСсылка.ГрафикиРаботы). И на
закладке Данные этого реквизита укажем связь с измерением РС Календарь -- График. Так
установили доп.отбор.
То есть значение, которое попадет в реквизит РР График будет являться значением отбора в
РС по измерению График. Это делаем в том случае, если нам не нужно базу по графику
разбивать, а нам ее как раз разбивать сейчас не нужно. Нам же больничный, напр., надо
посчитать. И нам неважно, по какому графику сотрудник трудился в базовый период. Именно
поэтому у нас в РР График сейчас - это просто реквизит, а не измерение.
Чтобы значение в РР попало, поправим документ. В ТЧ документа
ВводПроизвольныхНачислений вводим реквизит ТЧ График (тип:
СправочникСсылка.ГрафикиРаботы. По именам в коде документа заполняется автоматически
(по ЗаполнитьЗначенияСвойств(Запись, Стр);), поэтому в коде ничего не дописываем.
Перепроведем теперь документ с окладом Добаговой за янв.(обязательно указываем график в
одноименном поле ТЧ, напр.,”Пятидневка”).
Смотрим в РР: Добагова в янв.проработала 19 дн., получила 19.000.
Поставим др.график: “Два-через-два”. В РР 13дн, 13.000.
“Календарные дни”. В РР 25дн, 25.000.
Расчет по табелю
Хотим, чтобы пользователь имел возможность рассчитывать зарплату для любого сотрудника
по табелю.
Появится 2-й документ -- Табель (в подсистему Расчет).
Тестируем. Заполняем Добаговой за янв. произвольным образом табель (от 1янв). В реальной
жизни в табеле могут указываться предпраздничные дни, больничный, др.
Накопим всю информацию по табелю в оборотном регистре накопления ДанныеТабеля (в
подсистему Расчет). В его данных График не имеет никакого значения, здесь мы его не
задаем, мы же просто фиксируем свершившийся факт. Данные РН ДанныеТабеля:
Регистратор этого РН документ
Табель.
Для документа Табель проведение, конечно, разрешаем, оперативное проведение здесь
бессмысленно -- его запрещаем.
В модуле этого документа напишем код по проведению этого документа.
Переберем строки и колонки ТЧ документа. Можно и запросом. Но мне (Павлу) проще это в
объекте модуля описать (так быстрее):
1
Перепроводим документ Табель с Добаговой за январь.В оборотном РН ДанныеТабеля
получили кол.дней 20.
В конфигураторе в плане видов расчета Начисления добавим предопределенный вид расчета
ОплатаПоТабелю (0000008). Как понимаем, если пользователь руками вносит данные в
табель, никаких вытеснений автоматизировать не нужно: пользователь самостоятельно
укажет, когда сотрудник работает, когда -- болеет и т.д. И эти данные вводятся в режиме
реального времени, никто их задним числом не вводит. Табель подписывается и сдается
всегда в режиме реального времени ( в конце недели или месяца - это уже неважно). Т.е. в
данном случае все наши данные для расчета хранятся не в РР, а в РС. То, что из РС внесем то и рассчитаем. Поэтому здесь никаких базовых, вытесняющих, др. не будет.
В общем модуле Расчет в запросе конструктором добавим еще 1 таблицу -- оборотов
ДанныеТабеля.Обороты:
Из нее выбираем КолДнейОборот. Связь с таблицей ЖурналНачисленийДанныеГрафика по
Сотруднику.
Добавляем в код строки, передающие в запрос появившиеся 2 параметра виртуальной
таблицы оборотов:
Запрос.УстановитьПараметр("НачалоМесяца", НачалоМесяца(ПериодРегистрации));
Запрос.УстановитьПараметр("КонецМесяца", КонецМесяца(ПериодРегистрации));
Прописываем условие по КолДнейПоТабелю (который мы выбрали сейчас в запросе:
ДанныеТабеляОбороты.КолДнейОборот КАК КолДнейПоТабелю).
По сути -- это ОкладПоДням, только оклад этот по табелю:
//копируем это
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОкладПоДням
Тогда
Запись.Сумма = ФактДней * Запись.Параметр;
//вставляем и изменяем вид расчета
ИначеЕсли Запись.ВидРасчета = ПланыВидовРасчета.Начисления.ОплатаПоТабелю
Тогда
Запись.Сумма = КолДнейПоТабелю * Запись.Параметр;
Добавляем строку:
КолДнейПоТабелю = Выборка.КолДнейПоТабелю;
Тестируем. За янв вводим документ ВводПроизвольныхНачислений Добаговой с периодом
действия с 1-31 янв, ОплатойПоТабелю, Параметр 10.000.
БазовыйПериод, График не заполняем.
ОплатаПоТабелю рассчиталась 200.000
Т.е. мы уже рассчитали не методом отклонения, а ввода табельных данных рассчитали.
Так можно рассчитывать и оплату труда менеджера по продажам, если премия считается как
% от продаж. Т.е. данные о продажах накапливаются в РН обороты, а потом одной записью
анализируем и вносим в РР.
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 031
Краткое содержание
Внутреннее совместительство.
Отличия в механизмах получения базы по периоду действия и периоду регистрации.
Причины необходимости добавления нескольких РР.
Что такое “Период действия является базовым периодом”
Детальное описание
Начнём с измерений РР. Сейчас у нас один -- Сотрудник. Почему же в каркасных
конфигурациях их много?
Зачем нужно делать несколько разных РР?
Измерения РР влияют еще и на механизм вытеснения. У нас, если оклад введен Иванову, а
прогуливать будет Петров, оклад Иванова от этого никак не уменьшится.
Так, если будет несколько измерений, механизм вытеснения будет использовать их все.
Сотрудники работают в разных подразделениях и допускается (или не допускается)
внутреннее совместительство. Т.е. сотрудник по 1-му месту работает по 1-му графику, по
другому - по другому графику. И прогулять может, напр., в одном из этих подразделений.
Добавим измерение в РР ЖурналНачислений -- Подразделение (тип:
СправочникСсылкаПодразделения, запрет незаполненных).
На вкладке Связи этого измерения галочка Базовые нужна для индексации этого измерения
для получения базы из другого регистра. Сейчас РР один, эта галка ни на что не влияет. Когда
будем делать 2-й РР, сюда вернемся и о этой галке поговорим еще. Про 2-ю опцию этой
вкладки - Связи с графиком - посмотрим сегодня.
Скопируем измерение Подразделение и вставим его ТЧ документа
ВводПроизвольныхНачислений и поднимем повыше (сразу после Сотрудника). Вспоминаем,
что движения в документе в РР заполняются автоматически по именам.
Идем в общий модуль Расчет. Видим, что в запросе вытаскиваем 2”штуки”: 1) данные графика
и 2) базу.
На что влияет измерение? Прежде всего -- на разрез базы. Ведь мы туда передаем
МассивИзмерений. Пока там только Сотрудник. Это означает, что хоть база и хранится сейчас
по 2-м измерениям, но получать ее будем пока только по 1-му.
Смотрим это на примере документов Пилюлькину. В 3 документа с ОкладомЗаМесяц за янвмарт добавим в документ информацию по подразделению -- Бухгалтерия, графику -Пятидневка. В документ с отпуском за март --Бухгалтерия, Календарные дни.
В РР все рассчиталось.
Пилюлькину за янв добавим документ с ОкладомЗаМесяц с периодом действия с 1-10 янв,
параметром 50.000, Пятидневка. Проводим. В РР рассчитался и этот оклад.
Для расчета больничного за март сейчас системе все равно, в каких подразделениях работал
Пилюлькин в янв-февр.
Если это не устраивает, то в МассивИзмерений, задаваемый для запроса, добавляем 2-е
измерение регистра:
МассивИзмерений = Новый Массив(2);
МассивИзмерений [0] = "Сотрудник";
МассивИзмерений [1] = "Подразделение";//добавили
Запрос.УстановитьПараметр("МассивИзмерений", МассивИзмерений);
Вспомним, что массив входим в запрос в качестве параметра для виртуальной таблицы для
выбора базы 2-жды:
Пока у нас РР один, эта разница неважна.
Но уже учтем, что порядок указания измерений д.б. такой: первый элемент основного
регистра должен равняться первому измерению базового регистра. Здесь речь не о порядке
расположения в дереве метаданных.
Тестируем. Перепроводим больничный Пилюлькина за март. Теперь в больничный попала
база только по Бухгалтерии.
С разрезами по вытеснению всё также. Хотим уменьшить оклад Пилюлькину по АХЧ за янв.
Введем ему в его документ с окладом по АХЧ второй строкой прогул с 7 по 9 янв (Пятидневка).
В РР его оклад по АХЧ уменьшился.
Это мы посмотрели внутреннее совместительство.
Механизм получения базы по периоду действия
Как считается база по периоду действия:
Т.е. база за период действия считается только по фактическому периоду действия.
Например, был установлен график работы Каждый день. Оклад за месяц 100.000.
Устанавливаем базовый период для расчета базы премии с 15-31 янв (31.23 видео).
Вся база целиком стоит 100.000. В базу вошли 31 день (график Каждый день
рабочий), а в базовый период взяли, напр., 15 дней. 100.000 / 31 *15 Если внутри
базового периода есть выходные, напр., 3, то 100.000 / 31 *12
В этом принципиальная разница в
Если была бы зависимость по периоду регистрации -- считалось бы иначе (за весь
месяц). В этом случае, если 1-е число в базу войдет -- вся база целиком войдет,
если базу будем получать со 2-го числа месяца -- ничего не получим (будет 0).
1) Иногда нужна зависимоть по периоду регистрации (напр., для налогов),
иногда -- по периоду действия. Но ведь для 1 РР можем указать только 1 вид
зависимости. Значит, надо несколько РР. Это 1-я причина создания
нескольких РР.
2) Оптимизация расчетов (чтобы быстрее работало). Как минимум те записи,
которые ничего не вытесняют и их ничто вытеснить не может, д.б. созданы в
отдельном РР (говорили об этом раньше)
Для каких-то записей не неважен базовый период -- их можно в отдельный
РР. Напр., “АвторскиеОтчислениеЗаРазработкуКурсов”Из них то можно базу
будет вытащить, а для них база не нужна. Тогда уже не нужны 2 поля
(базовый начало и конец), не нужна и связь с РС Календарь.
3) Можно делить и по смыслу, напр., “Удержания”/”Начисления”.
Период действия является базовым периодом (с 48.30 видео)
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 032
Краткое содержание
Детальное описание
1) Дать пользователю возможность вводить свои собственные виды расчета и
привязывать их к стандартным алгоритмам.
Все Начисления можно разделить на категории по неким общим признакам. Теперь в общем
модуле будем производить расчет в зависимости не от вида Начисления, а от некой общей
категории начислений.
Зачем? Чтобы пользователь мог сам ввести новое начисление, оно попало в определенную
категорию начислений и для этого начисления отработал определенный алгоритм по
алгоритму, определенному для данной категории номенклатур.
Создаем перечисление СпособыРасчета:
В план видов расчета Начисления добавляем реквизит СпособРасчета (тип
ПеречислениеСсылка.СпособыРасчета).
Теперь нам надо в общем модуле обнаружить, какая категория начисления пришла в записи.
Конечно, можем это сделать запросто:
Если Запись.ВидРасчета.СпособРасчета = ......
Но ведь это запрос, а еще и в цикле!
Поэтому выбираем его наряду со всем другими показателями в начале процедуры
РассчитатьЗаписи:
|
ЖурналНачисленийДанныеГрафика.ВидРасчета.СпособРасчета КАК
СпособРасчета,
Помещаем выбранной в запросе значение способа расчета в переменную СпособРасчета, а
во всех условиях изменяем сравнение вида начисления со значением, на сравнение общих
категорий расчета:
СпособРасчета = Выборка.СпособРасчета;
Если СпособРасчета = Перечисления.СпособыРасчета.ФиксированнойСуммой Тогда
Запись.Сумма = Запись.Параметр;
ИначеЕсли СпособРасчета = Перечисления.СпособыРасчета.Процентом Тогда
Запись.Сумма = База * Запись.Параметр / 100;
ИначеЕсли СпособРасчета = Перечисления.СпособыРасчета.ЗаОтработанныеДни
Тогда
Запись.Сумма = ФактДней * Запись.Параметр;
ИначеЕсли СпособРасчета = Перечисления.СпособыРасчета.ПоТабелю Тогда
Запись.Сумма = КолДнейПоТабелю * Запись.Параметр;
ИначеЕсли СпособРасчета = Перечисления.СпособыРасчета.ЗаМесяц Тогда
Если НормаДней = 0 Тогда
Запись.Сумма = 0;
Иначе
Запись.Сумма = ФактДней / НормаДней * Запись.Параметр;
КонецЕсли;
ИначеЕсли СпособРасчета = Перечисления.СпособыРасчета.ПоСреднему Тогда
Запись.Сумма = ФактДней / ДнейВБазе * База * Запись.Параметр / 100;
КонецЕсли;
В пользовательском режиме плане видов начислений Начисления указываем для каждого
начисления Способ расчета:
2 строкой -- Дежурство -- это вид начислений, придуманный и введенный пользователем.
Проверим, как работает расчет по начислению, введенному в план видов расчета в
пользовательском режиме, т.е.по дежурству.
В документ по расчету зарплаты Чистову за апрель:
Провели. В регистре РР Начисления отображена сумма за дежурства 3.500.
Т.е. теперь пользователь имеет возможность вводить свои собственные виды расчета
и привязывать их к стандартным алгоритмам.
2) Приоритеты
Добавляем в плане видов расчета Начисления реквизит Приоритет(тип Число/ 3/ 0).
Расставляем Приоритеты в ПВР Начисления в пользовательском режиме:
Десятками считаем, чтобы была возможность что-то вставить в диапазоны значений.
Базу в общем модуле в запросе теперь будем получать в зависимости от приоритета.
Для этого сначала получим список приоритет, которые в нашем наборе записей есть. Для
этого сделаем отдельную функцию:
Функция ПолучитьСписокПриоритетов(Регистратор)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|
ЖурналНачислений.ВидРасчета.Приоритет КАК ВидРасчетаПриоритет
|ИЗ
|
РегистрРасчета.ЖурналНачислений КАК ЖурналНачислений
|ГДЕ
|
ЖурналНачислений.Регистратор = &Регистратор
|
|УПОРЯДОЧИТЬ ПО
|
ВидРасчетаПриоритет";
Запрос.УстановитьПараметр("Регистратор", Регистратор);
РезультатЗапроса = Запрос.Выполнить();
Возврат
РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("ВидРасчетаПриоритет");
КонецФункции
В общем модуле Расчет в процедуре РассчитатьЗаписи() до запроса получим массив с
приоритетами записи:
СписокПриоритетов = ПолучитьСписокПриоритетов(Регистратор);
Изменим запрос в общем модуле:
Теперь наша процедура такая:
Процедура РассчитатьЗаписи(Регистратор, ПериодРегистрации) Экспорт
НаборЗаписей = РегистрыРасчета.ЖурналНачислений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Регистратор);
НаборЗаписей.Прочитать();
СписокПриоритетов = ПолучитьСписокПриоритетов(Регистратор);
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|
ЖурналНачисленийДанныеГрафика.НомерСтроки КАК НомерСтроки,
|
ЖурналНачисленийДанныеГрафика.Сторно КАК Сторно,
|
ЖурналНачисленийДанныеГрафика.Параметр КАК Параметр,
|
ЖурналНачисленийДанныеГрафика.ПризнакПериодДействия КАК
ПризнакПериодДействия,
|
ЖурналНачисленийДанныеГрафика.ПризнакФактическийПериодДействия КАК
ПризнакФактическийПериодДействия,
|
ЖурналНачисленийДанныеГрафика.ПризнакБазовыйПериод КАК
ПризнакБазовыйПериод,
|
ЖурналНачисленийДанныеГрафика.ПризнакПериодРегистрации КАК
ПризнакПериодРегистрации,
|
ЖурналНачисленийДанныеГрафика.ВидРасчета.УчитываетОтработанныеДни
КАК ВидРасчетаУчитываетОтработанныеДни,
|
ЖурналНачисленийДанныеГрафика.ВидРасчета.СпособРасчета КАК
СпособРасчета,
|
ДанныеТабеляОбороты.КолДнейОборот КАК КолДнейПоТабелю
|ПОМЕСТИТЬ ДанныеГрафика
|ИЗ
|
|
|
РегистрРасчета.ЖурналНачислений.ДанныеГрафика(
Регистратор = &Регистратор
И ПериодРегистрации = &ПериодРегистрации) КАК
ЖурналНачисленийДанныеГрафика
|
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрНакопления.ДанныеТабеля.Обороты(&НачалоМесяца,
&КонецМесяца, , ) КАК ДанныеТабеляОбороты
|
ПО ЖурналНачисленийДанныеГрафика.Сотрудник =
ДанныеТабеляОбороты.Сотрудник";
Запрос.УстановитьПараметр("Регистратор", Регистратор);
Запрос.УстановитьПараметр("ПериодРегистрации", ПериодРегистрации);
Запрос.УстановитьПараметр("НачалоМесяца",
НачалоМесяца(ПериодРегистрации));
Запрос.УстановитьПараметр("КонецМесяца",
КонецМесяца(ПериодРегистрации));
МассивИзмерений = Новый Массив(2);
МассивИзмерений [0] = "Сотрудник";
МассивИзмерений [1] = "Подразделение";
Запрос.УстановитьПараметр("МассивИзмерений", МассивИзмерений);
Запрос.Выполнить();
Для каждого Приоритет Из СписокПриоритетов Цикл
Запрос.Текст = "ВЫБРАТЬ
|
ДанныеГрафика.НомерСтроки КАК НомерСтроки,
|
ДанныеГрафика.Сторно КАК Сторно,
|
ДанныеГрафика.Параметр КАК Параметр,
|
ДанныеГрафика.ПризнакПериодДействия КАК
ПризнакПериодДействия,
|
ДанныеГрафика.ПризнакФактическийПериодДействия КАК
ПризнакФактическийПериодДействия,
|
ДанныеГрафика.ПризнакБазовыйПериод КАК
ПризнакБазовыйПериод,
|
ДанныеГрафика.ПризнакПериодРегистрации КАК
ПризнакПериодРегистрации,
|
ДанныеГрафика.ВидРасчетаУчитываетОтработанныеДни КАК
ВидРасчетаУчитываетОтработанныеДни,
|
ДанныеГрафика.СпособРасчета КАК СпособРасчета,
|
ЕСТЬNULL(ЖурналНачисленийБазаЖурналНачислений.СуммаБаза,
0) КАК СуммаБаза,
|
ЕСТЬNULL(ЖурналНачисленийБазаЖурналНачислений.ОтработаноДнейБаза,
0) КАК ОтработаноДнейБаза,
|
ДанныеГрафика.КолДнейПоТабелю КАК КолДнейПоТабелю
|ИЗ
|
|
ДанныеГрафика КАК ДанныеГрафика
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрРасчета.ЖурналНачислений.БазаЖурналНачислений(
|
&МассивИзмерений,
|
&МассивИзмерений,
|
,
|
Регистратор = &Регистратор
|
И ПериодРегистрации =
&ПериодРегистрации
|
И ВидРасчета.Приоритет = &Приоритет)
КАК ЖурналНачисленийБазаЖурналНачислений
|
ПО ДанныеГрафика.НомерСтроки =
ЖурналНачисленийБазаЖурналНачислений.НомерСтроки";
Запрос.УстановитьПараметр("Приоритет", Приоритет);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
СтруктураПоиска = Новый Структура("НомерСтроки",);
Для каждого Запись Из НаборЗаписей Цикл
СтруктураПоиска.НомерСтроки = Запись.НомерСтроки;
Если НЕ Выборка.НайтиСледующий(СтруктураПоиска) Тогда
Запись.Сумма = 0;
Запись.ОтработаноДней = 0;
Иначе
База = Выборка.СуммаБаза;
ФактДней = Выборка.ПризнакФактическийПериодДействия;
НормаДней = Выборка.ПризнакПериодДействия;
ДнейВБазе = Выборка.ОтработаноДнейБаза;
УчитываетОтработанныеДни =
Выборка.ВидРасчетаУчитываетОтработанныеДни;
КолДнейПоТабелю = Выборка.КолДнейПоТабелю;
СпособРасчета = Выборка.СпособРасчета;
Если СпособРасчета =
Перечисления.СпособыРасчета.ФиксированнойСуммой Тогда
Запись.Сумма = Запись.Параметр;
ИначеЕсли СпособРасчета =
Перечисления.СпособыРасчета.Процентом Тогда
Запись.Сумма = База * Запись.Параметр / 100;
ИначеЕсли СпособРасчета =
Перечисления.СпособыРасчета.ЗаОтработанныеДни Тогда
Запись.Сумма = ФактДней * Запись.Параметр;
ИначеЕсли СпособРасчета =
Перечисления.СпособыРасчета.ПоТабелю Тогда
Запись.Сумма = КолДнейПоТабелю * Запись.Параметр;
ИначеЕсли СпособРасчета =
Перечисления.СпособыРасчета.ЗаМесяц Тогда
Если НормаДней = 0 Тогда
Запись.Сумма = 0;
Иначе
Запись.Сумма = ФактДней / НормаДней *
Запись.Параметр;
КонецЕсли;
ИначеЕсли СпособРасчета =
Перечисления.СпособыРасчета.ПоСреднему Тогда
Запись.Сумма = ФактДней / ДнейВБазе * База *
Запись.Параметр / 100;
КонецЕсли;
Если УчитываетОтработанныеДни Тогда
Запись.ОтработаноДней = ФактДней;
КонецЕсли;
Если Запись.Сторно Тогда
Запись.Сумма = -Запись.Сумма;
Запись.ОтработаноДней = -Запись.ОтработаноДней;
КонецЕсли;
КонецЕсли;
КонецЦикла;
НаборЗаписей.Записать(,,ЛОЖЬ);//Отключили запись
ФактическогоПериодаДействия (см.ниже объяснение и видео в 45 мин)
КонецЦикла;
РассчитатьДопНачисления(Регистратор, ПериодРегистрации);
КонецПроцедуры
Тестируем:
Результат:
Т.е. запрос выполнился 2 раза: сначала рассчитан оклад суммой (приоритет 10), потом -премия процентом (приоритет 20). Теперь можно и одним документом вводить данные в базу.
Разберем, что такое НаборЗаписей.Записать();
Важны 3-й и 4-й параметр.
ЗаписьФактическогоПараметраДействия. Если в РР записываются записи и там есть
вытеснения, система будет анализировать, нужно ли поменять таблицу
ФактическийПериодДействия или нет. Лишний раз этим систему напрягать не нужно:
НаборЗаписей.Записать(,,ЛОЖЬ); // в общем модуле не перерачитываем даты, они
определены на уровне документа
А в документе ВводПроизвольныхНачислений отключим ЗаписьПерерасчетов:
НаборЗаписей.Записать(,,,ЛОЖЬ);
Но т.к. у нас там записано Записывать, а в Записывать таких параметров нет, эту строку
закомментируем, записывтаь в РР будем принудительно, но используя параметр:
//Движения.ЖурналНачислений.Записывать = Истина;
Движения.ЖурналНачислений.Записать(,,,ЛОЖЬ);
Ведь на уровне документа значения ресурсов еще даже не расчитаны (они рассчитываются в
общем модуле) и даже нерассчитанное на что-то заменять? Не нужно, не напрягаем систему.
Но сейчас наш РР -- это “ужас какой-то” (слова автора курса): все виды расчета в одной
таблице, что не очень правильно, т.к.в РР используется механизм фактического периода
действия и система знает что с чем конкурирует (напр., прогул вытесняет оклад по дням).
Медленно. РР д.б. как минимум разделены на 2 типа: с использованием и без использования
механизма фактического периода действия. Напр., премия процентом: ни она ничего не
вытесняет, ни ее ничто не вытесняет -- ее в более простой РР. Бывает, конечно, что ничего
запись не вытесняет, но для нее важно количество дней, например, Дежурство: его ничто
вытеснить не может, оно же отмечено по факту, но оно рассчитывается по дням. Тогда оно,
конечно, останется в РР с фактическим периодом действия.
Это же можно сказать и про Базу.
Т.е. смотрим на эти 2 галочки:
Т.е. в идеале 3 РР: с галочкой Период действия, с галочкой Базовый период, без галочек.
Напр., в РР без галочек начисления по табелю.
Мы разделим на 2 (с 54 мин 52 сек)
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 33
Краткое содержание
Что было на прошлом занятии
Что не считалось на прошлом занятии (с 6.29)
Приводим в порядок с т.зрения данных, убирая из метаданных излишние виды расчета в
конфигураторе и пользователе и добавляем виды расчета в Доп начисления(с 9.20)
Резюме по необходимости разделения на несколько регистров расчета(с 15.10)
--------------------------------------------------------------------------------------------------------------------------------Задача 1 (с 16.03)
Есть несколько подразделений. Какие-то подразделения работают по 5-дневке, но с
разным количеством рабочих часов.
Вспоминаем реквизит График в РР ЖурналНачислений (с 16.58)
--------------------------------------------------------------------------------------------------------------------------------Задача 2 (с 40.13)
Нарисуем отчет по начислению премий руководителей отделов
В отделе администрация работают Иванюхин, Каменский и Чистов. Хотим начислять
премию Иванюхину как руководителю отдела в зависимости от фонда оплаты труда
всех его сотрудников за минусом его собственной зарплаты.
Т.е. вначале получаем базу не в разрезе сотрудника, а только в разрезе подразделения.
Потом из этой базы “вырезать” оплату самого сотрудника.
------------------------------------------------------------------------На этом изучение РР и расчетных механизмов по оплате труда завершен.
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 34
Краткое содержание
“Добрались” до задач.
На сайте 1С читаем общие требования к экзамену (с 5.18 видео)
Что такое каркасная конфигурация (КК) (с 8.18).
Обязательно изменяем в КК в ее свойствах Режим управления блокировкой данных с
Автоматического на Управляемый (с 13.22)
Общие картинка в КК (с 20.20) Мы будем обязаны создать подсистемы. Эти картинки для этого
здесь.
Обращаем внимание, что все люди и подразделения в справочниках -- это предопределенные
элементы (с 23.13) Это важно для бизнес-процессов.
Документы (с 23.48) Созданы 3 документа: ПН, РН и НачислениеЗарплаты. Формы не
созданы. Но Автор не рекомендует создавать формы, если этого реально в экзамене не
описано. ПН и РН абсолютно одинаковы. Смотрим, нужно ли оперативное проведение, какой
вид удаления движений установлен и какой нужен, по какому регистру движения
формируются. Если будет указан в качестве регистратора регистр, по которому не должны
формироваться движения -- это ошибка. Смотрим еще свойства документов как объектов
метаданных, здесь смотрим Запись движений как происходит (записывать выбранные (надо!)
или модифицированные). Проверить, чтобы были пустыми модуль документа и модули его
менеджеров.
О документе НачислениеЗарплаты.(с 28.16) Тип реквизита ТЧ ОсновныеНачисления
ВидРасчета -- 3 плана видов расчета. Это учесть. РР в КК нет. Оперативное проведение для
этого документа запретить. Регистратор документа -- по РР ОстаткиНоменклатуры, это
нелогично, удалить.
Обработки (с 30.01) ЗаполнениеГрафика. Как изменить модуль объекта (дополнить
доп.отборами перед Набор.Прочитать()) (с 33.48) для заполнения с разрезом по
подразделениям или по разным графикам. Не забыть ее использовать для заполнения
графика.
Обработка КонсольЗапросов (с 35.35) Так, как есть, она не работает, т.к. ее основной формой
назначена форма обычного приложения (где рисуется всё попиксельно). Надо для
возможностей ее использования: 1) Сервис -- Параметры -- Общие -- разрешить Управляемое
приложение и обычное приложение. После тестирования не забыть ее выключить. 2) (в 39.47)
в свойствах КК галочку “Использовать обычные формы в управляемом приложении” 3) (в
40.27) для тестирования запускаемся в режиме или Толстый клиент начать отладку, или
Толстый клиент (обычное приложение) начать отладку (гл.меню конфигуратора Отладка).
План счетов (с 43.46) Проверить как в режиме конфигуратора, так и в режиме пользователя.
Специально иногда удаляют. Проверить все сета и субсчета по ним, используемые в задаче.
ПВР (с 46.12) Проверить все галочки в обоих режимах каждого вида ПВР и сопоставить их в
обоих режимах между собой. Проверить, правильно ли поставлена зависимость или ее
отсутствие с периодами действия.
РС КурсыВалют (с 49.20) Проверить, вдруг периодичность установлена не в пределах дня, а в
пределах года.
РН ОстаткиНоменклатуры (с 50.11). Проверяем вид (Остатки/Обороты), измерения и их типы,
поставить галочку “Запрет незаполненных значений”, ресурсы, регистраторы.
! Методика оперативного проведения (с 51.20) РН, напр., ОстаткиНоменклатуры, вкладка
Прочее. Если здесь сняли галочку Разрешить разделение итогов, а мы это не заметим и
оставим галочку снятой, с нас снимут еще балл! разрешить разделение итогов д.б. включено!
Это нужно для механизмов оперативного проведения и оно параллелит запись данных в
регистр. И включаем в пользовательзовательском режиме режим разделения итогов (с 52.55):
Все функции -- стандартные -- Управление итогами -- справа снизу Полные возможности -должна стоять галочка Разделение итогов.
В регистрах расчета и бухгалтерии(с 53.49) смотрим связи измерений и ресурсов с признаками
учета в регистре бухгалтерии и с ДаннымиГрафика в р.расчета.
Ответы на вопросы
56.24 Почему необходимо отключить обычное приложение в конце экзамена?
Если оставим обычное + управляемое приложения и в режиме толстого клиента Операции -Документы -- Расходная накладная создадим документ, то движения будут дублироваться без
удаления при режиме “Удалять при отмене проведения”. Оставим обычное + управляемое
приложения -- придется описывать 2 сценария проведения: для случая обычной формы, то
надо удалять движения всегда перед записью. Нужна возня? Нет! оставляем только
управляемое приложение.
Задача по оперативному учету(с 55.15) Прервались на вопрос (выше). Продолжение (58.03).
Смотрим в конец задачи, на структуру отчётов. Здесь показатели (циферки) -- ресурсы и
измерения -- в шапке отчета. Это, конечно, не точная структура. Напр., Начальный ост,
Приход, Расход, Кон.остаток -- это не 4, а 1 ресурс Количество. А вот они измерения: Отдел и
Номенклатура (для отчета ДвиженияТоваров).
Отчет ВедомостьПоТоварамВТорговыхточках. Видим 2 показателя: Количество и Сумма. Но
это не Сумма, т.к. мы понимаем, что приход и расход Суммы в выручке невозможен, это с/б.
Добавляем отдел. Что 1-ым: Отдел или Номенклатуру?
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 35
Краткое содержание
Хранилище. Зачем и как.
Поставка конфигурации (выгрузка) (с 40мин видео)
Обновление конфигурации после ее поставки (с 55.58 видео)
Постановка конфигурации на поддержку (с 58.41 видео)
Настройка правил поддержки (с 59.07 видео)
Тестирование в польз.режиме (с 1.00.17 видео)
Что означает “объекты на поддержке” (эти коробочки) (с 1.01.46)
Как поставить полностью на поддержку (с 1.04.40)
Если название совпадает, а внутренний ID -- нет (с 1.06.00)
Как избежать “опустошения” конфигурации (с 1.15.3): 1) при 1-м обновлении конфигурации
проверять через сравнение и объединение, 2) всегда делать бекапы перед обновлением.
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие 36
Краткое содержание
Решаем задачу по Оперативному учёту.
Решаем с конца.
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Занятие ДЗ11
Краткое содержание
11.23 Добавляем реквизит Автор в Расходной накладной (СправочникСсылка.Сотрудники)
Заводим пользователей, предварительно добавив роль
Пользователь должен отражаться как Автор документа реализации
31.48 Заводим элемент справочника программно:
36.54 Подписка на события
42.49 Проверка, является ли этот документ новым (т.е. в БД на него еще нет ссылки, т.е. он не
разу еще не записывался):
Детальное описание
Вопросы к занятию (для самоконтроля)
Домашнее задание / Самостоятельная работа
Постановка задачи
Домашнее задание / Самостоятельная работа
Решение.
Скачать