Python код
Введение
Python используется в Report.ms приложения для описания логики работы:
- действий
- событий создания/обновления/удаления
- задач по расписанию
- общей библиотеки функций
Каждый файл Python кода должен иметь расширение .py и располагаться в директории scripts/.
Для всех файлов Python кода(кроме общей библиотеки функций) необходимо наличие функции run с параметром app.
def run(app):
pass
Объект app
Объект app обеспечивает связь с приложением. Объект app передается в функцию run.
Используя объект app можно получить доступ к справочникам, перечислениям, общим функциям и другим возможностям приложения.
Работа с данными
Через объект app можно искать, создавать, изменять, удалять данные в приложении.
Для работы с конкретным справочником в объекте app есть метод dictionary(name), где name - название справочника.
Создание
Для создания новой записи справочника используйте метод create(data), где data - python dict, где ключом является название поля, а значением - значение.
def run(app):
app.dictionary('Заказы').create({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
Метод create возвращает запись справочника. Для каждой записи справочника доступно поле id. Вы можете использовать запись для значения поля.
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
- Сумма:
Тип поля: Число
- Комментарий:
- Позиции заказа:
Наименование единицы справочника: Позиция заказа
Поля:
- Заказ:
Тип поля: Элемент справочника
Справочник: Заказы
- Артикул:
- Сумма:
Тип поля: Число
def run(app):
order = app.dictionary('Заказы').create({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
app.dictionary('Позиции заказа').create({
'Заказ': order,
'Артикул': '123',
'Сумма': 6000,
})
app.dictionary('Позиции заказа').create({
'Заказ': order,
'Артикул': '456',
'Сумма': 12000,
})
При вызовае метода create будут выполнены события при создании определённые для этого справочника. Если вы хотите чтобы события не выполнялись, то используйте метод create_without_events(data).
def run(app):
app.dictionary('Заказы').create_without_events({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
Получить значение полей
Для получения значения полей у объекта записи есть метод get(field_name), где field_name - название поля справочники.
def run(app):
order = app.dictionary('Заказы').create({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
# Будет выведено "003"
print(order.get('Номер'))
Также значения полей можно получать через оператор [].
def run(app):
order = app.dictionary('Заказы').create({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
# Будет выведено "003"
print(order['Номер'])
Обновление
Для изменения значений полей у объекта записи есть метод set.
Возможны 2 варианта вызова:
set(field_name, field_value), гдеfield_name- название поля справочники,field_value- значение. Удобно использовать если вам нужно изменить значение только одного поля.set(data), гдеdata- python dict, где ключом является название поля, а значением - значение.
def run(app):
order = app.dictionary('Заказы').create_without_events({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
# Обновление одного поля
order.set('Номер', '004')
# Обновление нескольких полей
order.set({
'Сумма': 13000,
'Комментарий': 'Без оплаты',
})
Также значения полей можно устанавливать через оператор [].
def run(app):
order = app.dictionary('Заказы').create_without_events({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
order['Номер'] = '004'
order['Сумма'] = 13000
order['Комментарий'] = 'Без оплаты'
Если вы устанавливаете несколько полей подряд, то предпочтительно использовать set(data).
При вызовае метода set будут выполнены события при обновалении определённые для этого справочника. Если вы хотите чтобы события не выполнялись, то используйте метод set_without_events.
def run(app):
order = app.dictionary('Заказы').create_without_events({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
order.set_without_events('Номер', '004')
Работа с перечислениями
Для доступа к перечислениям используется метод app.enum(name, value), где name - название перечисления, value - значение перечисления.
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
- Сумма:
Тип поля: Число
- Статус:
Тип поля: Значение перечисления
Перечисление: Статус заказа
Перечисления:
- Статус заказа:
Значения:
- Новый
- В работе
- Завершён
def run(app):
order = app.dictionary('Заказы').create_without_events({
'Номер': '003',
'Сумма': 12000,
'Статус': app.enum('Статус заказа', 'Новый'),
})
if order.get('Статус') == app.enum('Статус заказа', 'Новый'):
print('Новый заказ')
Поиск
Фильтр
У объекта справочника есть метод find(filter), где filter - python dict, где ключом является название поля, а значением - значение или объект уточнения поиска.
Объект уточнения поиска - объект с полями from и to, где from - минимальное значение поля, to - максимальное значение поля.
Метод возвращает массив объектов записей справочника, которые удовлетворяют условию поиска.
def run(app):
# поиск по значению поля
exist_orders = app.dictionary('Заказы').find({
'Номер': '003',
})
if len(exist_orders) == 0:
order = app.dictionary('Заказы').find({
'Номер': '003',
})
else:
order = exist_orders
print(order[0].get('Сумма'))
# поиск по объекту уточнения
orders = app.dictionary('Заказы').find({
'Сумма': {
'from': 10000,
'to': 15000,
},
})
print('Количество заказов: ' + len(orders))
Поиск по Id
Для поиска по id используйте метод get_by_id(id).
def run(app):
order = app.dictionary('Заказы').create({
'Номер': '003',
'Сумма': 12000,
'Комментарий': 'Предоплата',
})
this_order = app.dictionary('Заказы').get_by_id(order.id)
Поиск первого объекта
Если вам нужен первый объект справочника, то используйте метод first().
def run(app):
# поиск по значению поля
exist_order = app.dictionary('Заказы').first({
'Номер': '003',
})
if exist_order is None:
print('Заказ не найден!')
else:
print('Заказ найден!')
Получить все объекты
Если вам нужены все объекты справочника, то используйте метод all().
def run(app):
orders = app.dictionary('Заказы').all()
print('Количество заказов: ' + len(orders))
for order in orders:
print(order.get('Сумма'))
Получить количество объектов
Если вам нужено узнать только количество всех объекты справочника или количсетво обхектов удовлетворяющих фильтру, то используйте метод count().
def run(app):
orders_count = app.dictionary('Заказы').count()
print('Количество заказов: ' + orders_count)
orders_count_more_10000 = app.dictionary('Заказы').count({
'Сумма': {
'from': 10000,
},
})
print('Количество заказов больше 10000: ' + orders_count_more_10000)
Использование ролей
Вы можете использовать роли для поиска и для установки значений. Для доступа к ролям используете свойство app.role(role_name).
Роли:
- Администратор:
- Менеджер:
- Супер-менеджер:
Справочники:
- Полььзователи:
Наименование единицы справочника: Заказ
Поля:
- Роль:
Тип поля: Выбор роли
def run(app):
if app.context_user.get('Роль') == app.role('Менеджер'):
app.context_user.set('Роль', app.role('Супер-менеджер'))
Удаление
Для удаления объекта используете метод delete().
def run(app):
app.context_object.delete()
Установка пароля
Для установки пароля пользователю используете метод app.set_password(user, password).
def run(app):
app.set_password(app.context_object, 'password')
Контекст
События, действия и методы вызываются в некотором контексте и к этому контексту можно получить доступ через объект app. В контектс входят:
- текущий пользователь
- параметры
- контектсный объект
Пользователь
Чтобы получить текущего пользователя используйте свойство app.context_user.
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
- Сумма:
Тип поля: Число
- Ответственный менеджер:
Тип поля: Элемент справочника
Справочник: Пользователи
def run(app):
order = app.dictionary('Заказы').create({
'Номер': '003',
'Сумма': 12000,
'Ответственный менеджер': app.context_user,
})
Текущий пользователь доступен для действий и событий. В случае плановых задач контекстным пользователем будет пользователь admin.
Объект
Чтобы получить текущий объект используйте свойство app.context_object.
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
- Сумма:
Тип поля: Число
- Ответственный менеджер:
Тип поля: Элемент справочника
Справочник: Пользователи
Действия:
- Поставить меня менеджером:
Python код: scripts/set_current_manager.py
def run(app):
app.context_object.set('Ответственный менеджер', app.context_user)
Параметры
Чтобы получить параметры используйте свойство app.context_parameters.
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
- Сумма:
Тип поля: Число
- Ответственный менеджер:
Тип поля: Элемент справочника
Справочник: Пользователи
- Действует до:
Тип поля: Дата
Действия:
- Поставить меня менеджером:
Параметры:
- Срок действия:
Тип поля: Дата
Python код: scripts/set_current_manager.py
def run(app):
app.context_object.set({
'Ответственный менеджер': app.context_user,
'Действует до': app.context_parameters.get('Срок действия'),
})
Системные параметры
Системные параметры определены в секции Параметры секции Проект в файле app.rms.
Чтение
Для установки системных параметров используете метод get_parameter(parameter_name).
def run(app):
app.dictionary('Заказы').create({
'Номер': app.get_parameter('Префикс номера заказа') + '003',
})
Установка
Для установки системных параметров используете метод set_parameter(parameter_name, value).
Проект:
Параметры:
- Номер последнего заказа:
Тип поля: Число
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
def run(app):
last_order_number = app.get_parameter('Номер последнего заказа')
last_order_number = last_order_number + 1
order = app.dictionary('Заказы').create({
'Номер': str(last_order_number),
})
app.set_parameter('Номер последнего заказа', last_order_number)
Файлы
Временная папка
Чтобы получить абсолютный путь к временной папке используете метод app.temp().
Используйте временную папку для хранения временных файлов и манипуляций с ними. Вы можете создавать подпапки во временной папке.
Временная папка будет очищена при выходе из приложения.
Загрузка
Для загрузки файлов используете метод app.upload_file(file_path).
Метод возвращает объект файла, его можно сохранить в поле справочника с типом Файл.
Скачивание
Чтобы работать с содержимым файла параметра или свойства объекта используете метод app.get_file(file_field_value).
Метод возвращает url-ссылку на файл.
import urllib.request
def run(app):
file_url = app.get_file(app.context_object.get('Файл отчета'))
urllib.request.urlretrieve(file_url, app.temp() + '/report.pdf')
return 'Файл отчета сохранён в временную папку'
import pandas as pd
def run(app):
orders_df = pd.read_excel(app.get_file(app.context_parameters.get('Файл с заказами')))
return 'Вы загрузили ' + len(orders_df) + ' заказов'
Вернуть файл
Чтобы вернуть файл как результат действия используйте метод app.add_result_file(file)
def run(app):
app.add_result_file(app.context_object.get('Файл отчета'))
return 'Файл отчета скачивается'
Оповещения
Вы можете оповещать пользователей о событиях с помощью нескольких инструментов:
- Почта
- Уведомления
- Чат
Почта
Для отправки уведомления используете метод send_email(to, subject, body, files) объекта app.
| Параметр | Тип | Описание |
|---|---|---|
| to | Строка/Пользователь | Почта или объект пользователя. |
| subject | Строка | Тема письма. |
| body | Строка | Тело письма в формате HTML. |
| files | Массив файлов | Массив объектов файлов. |
При отправке почты письма будут сохраняться в папке .data/emails/.
Для полноценной отправки писем с вашего почтового сервера заполните секцию Отправка почты в секции Проект в app.rms.
def run(app):
number = '003'
app.context_object.set({
'Номер': number,
'Ответственный менеджер': app.context_user,
})
app.send_email(app.context_user, 'Вы назначены менеджером', f'Вы назначены менеджером по заказу {number}.')
app.send_email('superviser@mail.com', 'Заказ распределён', 'Тело письма в формате HTML.', [])
Уведомления
Уведомления будут показаны пользователю в виде toast сообщений.
Для отправки уведомлений используете метод notify(user, message) объекта app.
| Параметр | Тип | Описание |
|---|---|---|
| user | Строка/Пользователь | Id пользователя или объект пользователя. |
| message | Строка | Текст уведомления. |
def run(app):
number = '003'
app.context_object.set({
'Номер': number,
'Ответственный менеджер': app.context_user,
})
app.notify(app.context_user, f'Вы назначены менеджером по заказу {number}.')
Отправка уведомления пользователю приводит к обновлению страницы открытого справочника.
Чат
Для отправки сообщений в чате используете метод chat(sender, receiver, message) объекта app.
| Параметр | Тип | Описание |
|---|---|---|
| sender | Строка/Пользователь | Id пользователя или объект пользователя. |
| receiver | Строка/Пользователь | Id пользователя или объект пользователя. |
| message | Строка | Текст сообщения. |
Проект:
Параметры:
- Менеджер:
Тип поля: Элемент справочника
Справочник: Пользователи
Справочники:
- Заказы:
Наименование единицы справочника: Заказ
Поля:
- Номер:
- Ответственный:
Тип поля: Элемент справочника
Справочник: Пользователи
def run(app):
number = '003'
app.context_object.set({
'Номер': number,
'Ответственный': app.context_user,
})
app.chat(app.context_user, app.get_parameter('Менеджер'), f'Я взял на себя заказ {number}!')
Исключения
В вашем python коде вы можете вызвать исключения, например raise Exception('Сообщение пользователю о ошибке'). Есчли вы вызываете исключение в коде действия события, то сообщение исключения будет передано пользователю в виде уведомления.
Если исключение было вызвано в коде действия события валидации, то пользователь получит уведомление о ошибке. Вы можете передать вторым аргументом название поля или массив названий полей, они будут помечены как ошибочные.
def run(app):
if len(app.dictionary('Заказы').find({'Номер': app.context_parameters.get('Номер')})) > 0:
raise Exception('Заказ с таким номером уже существует', 'Номер')