Міксини та корисні класи¶
Odoo реалізує деякі корисні класи та міксини, які спрощують додавання часто використовуваної поведінки до ваших об’єктів. У цьому посібнику докладно описано більшість із них із прикладами та випадками використання.
Функції обміну повідомленнями¶
Інтеграція обміну повідомленнями¶
Основна система обміну повідомленнями¶
Інтегрувати функції обміну повідомленнями у вашу модель надзвичайно просто. Просто успадкувавши модель mail.thread
і додавши поля обміну повідомленнями (і відповідні віджети) до перегляду вашої форми, ви швидко почнете працювати.
Example
Створимо спрощену модель відрядження. Оскільки організація такого роду поїздок зазвичай включає багато людей і багато обговорень, давайте додамо підтримку обміну повідомленнями в моделі.
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char()
partner_id = fields.Many2one('res.partner', 'Responsible')
guest_ids = fields.Many2many('res.partner', 'Participants')
У представленні форми:
<record id="business_trip_form" model="ir.ui.view">
<field name="name">business.trip.form</field>
<field name="model">business.trip</field>
<field name="arch" type="xml">
<form string="Business Trip">
<!-- Your usual form view goes here
...
Then comes chatter integration -->
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
Після того, як ви додали підтримку чату у свою модель, користувачі зможуть легко додавати повідомлення або внутрішні нотатки до будь-якого запису вашої моделі; кожен із них надішле сповіщення (усім підписникам для повідомлень, співробітникам (base.group_user) користувачам для внутрішніх приміток). Якщо ваш шлюз ел. пошти та адресу ел. пошти налаштовано правильно, ці сповіщення надсилатимуться ел. поштою, і на них можна буде відповісти безпосередньо з вашого поштового клієнта; система автоматичного маршрутизації скерує відповідь до правильного потоку.
На стороні сервера є деякі допоміжні функції, які допоможуть вам легко надсилати повідомлення та керувати підписками у вашому записі:
Розміщення повідомлень
- message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, **kwargs)¶
Опублікуйте нове повідомлення в існуючому ланцюжку, повернувши новий ID mail.message.
- Параметри
body (str | Markup) – body of the message. Will be escaped if
str
. Use aMarkup
object for HTML content.message_type (str) – див. поле mail_message.message_type
parent_id (int) – обробити відповідь на попереднє повідомлення, додавши до повідомлення батьківських партнерів у разі приватного обговорення
attachments (list(tuple(str,str))) – список кортежів вкладень у формі
(назва,вміст)
, де вміст НЕ закодований base64body_is_html (bool) – indicates whether
body
should be treated as HTML, even ifstr
.**kwargs – додаткові аргументи ключових слів використовуватимуться як значення стовпця за замовчуванням для нового запису mail.message
- Повертає
ID щойно створеного mail.message
- Тип повернення
- message_post_with_view(views_or_xmlid, **kwargs):
Допоміжний метод для надсилання ел. пошти/опублікування повідомлення за допомогою view_id для візуалізації за допомогою механізму ir.qweb. Цей метод є самостійним, оскільки в шаблоні та composer немає нічого, що дозволяло б обробляти представлення в пакеті. Цей метод, ймовірно, зникне, коли шаблони оброблятимуть представлення ir ui.
- Параметри
record (str or ir.ui.view) – зовнішній id або запис представлення, який потрібно надіслати
- message_post_with_template(template_id, **kwargs)¶
Допоміжний метод для надсилання листів із шаблоном
- Параметри
template_id – ідентифікатор шаблону для створення основної частини повідомлення
**kwargs – параметр для створення майстра mail.compose.message (який успадковується від mail.message)
Отримання повідомлень
Ці методи викликаються, коли нове повідомлення ел. пошти обробляється поштовим шлюзом. Ці ел. листи можуть бути або новою темою (якщо вони надходять через аліас), або просто відповідями з існуючої теми. Перевизначаючи їх, ви можете встановлювати значення в записі потоку залежно від деяких значень із самого ел. листа (тобто оновити дату чи адресу ел. пошти, додати адреси CC як підписників тощо).
- message_new(msg_dict, custom_values=None)¶
Викликається
message_process
, коли нове повідомлення отримано для даної моделі потоку, якщо повідомлення не належало до існуючого потоку.Поведінка за замовчуванням полягає у створенні нового запису відповідної моделі (на основі деякої базової інформації, отриманої з повідомлення). Додаткову поведінку можна реалізувати шляхом перевизначення цього методу.
- Параметри
msg_dict (dict) – карта, що містить деталі ел. пошти та вкладення. Перегляньте
message_process
іmail.message.parse
для деталейcustom_values (dict) – необов’язковий словник додаткових значень полів для передачі в create() під час створення нового запису потоку; будьте обережні, ці значення можуть перевизначати будь-які інші значення, що надходять із повідомлення
- Тип повернення
- Повертає
ідентифікатор щойно створеного об’єкта потоку
- message_update(msg_dict, update_vals=None)¶
Викликається
message_process
, коли отримано нове повідомлення для існуючого потоку. За замовчуванням запис оновлюється за допомогоюupdate_vals
, взятого з вхідного ел. листа.Додаткову поведінку можна реалізувати шляхом перевизначення цього методу.
- Параметри
- Повертає
True
Управління підписниками
- message_subscribe(partner_ids=None, channel_ids=None, subtype_ids=None, force=True)¶
Додайте партнерів до підписників записів.
- Параметри
partner_ids (list(int)) – Ідентифікатори партнерів, які будуть підписані на запис
channel_ids (list(int)) – Ідентифікатори партнерів, які будуть підписані на запис
subtype_ids (list(int)) – Ідентифікатори підтипів, на які будуть підписані канали/партнери (за умовчанням використовуються підтипи за замовчуванням, якщо
None
)force – якщо True, видаліть наявних підписників перед створенням нового, використовуючи підтипи, указані в параметрах
- Повертає
Успіх/Невдача
- Тип повернення
- message_unsubscribe(partner_ids=None, channel_ids=None)¶
Видалити партнерів із підписників запису.
Реєстрація змін¶
Модуль mail
додає потужну систему відстеження полів, що дозволяє реєструвати зміни в певних полях у чаті запису. Щоб додати відстеження до поля, просто встановіть для атрибута відстеження значення True.
Example
Слідкуємо за змінами назви та відповідальних наших відряджень:
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char(tracking=True)
partner_id = fields.Many2one('res.partner', 'Responsible',
tracking=True)
guest_ids = fields.Many2many('res.partner', 'Participants')
Відтепер кожна зміна назви поїздки або відповідального реєструватиметься в записі. У сповіщенні також відображатиметься поле name
, щоб надати більше інформації про сповіщення (навіть якщо назва не змінилося).
Підтипи¶
Підтипи дають вам більш детальний контроль над повідомленнями. Підтипи діють як система класифікації сповіщень, дозволяючи підписникам на документ налаштовувати підтип сповіщень, які вони бажають отримувати.
Підтипи створюються як дані у вашому модулі; модель має такі поля:
name
(обов’язковий) -Char
назву підтипу, відображатиметься у спливаючому вікні налаштування сповіщень
description
-Char
опис, який буде додано в повідомленні, розміщеному для цього підтипу. Якщо він недійсний, замість нього буде додана назва
internal
-Boolean
повідомлення з внутрішніми підтипами будуть видимі лише для співробітників, або членів групи
base.group_user
parent_id
-Many2one
підтипи посилань для автоматичної підписки; наприклад, підтипи проекту пов’язані з підтипами завдань за допомогою цього посилання. Коли хтось підписаний на проект, він буде підписаний на всі завдання цього проекту з підтипами, знайденими за допомогою батьківського підтипу
relation_field
-Char
наприклад, при зв’язуванні підтипів проекту та завдань поле зв’язку є полем project_id завдань
res_model
-Char
модель відноситься до підтипу. Якщо False, цей підтип застосовується до всіх моделей
default
-Boolean
чи активовано підтип за замовчуванням під час підписки
sequence
-Integer
використовується для впорядкування підтипів у спливаючому вікні налаштування сповіщень
hidden
-Boolean
чи приховано підтип у спливаючому вікні налаштування сповіщень
Інтерфейс підтипів із відстеженням полів дозволяє підписуватися на різні види повідомлень залежно від того, що може зацікавити користувачів. Для цього ви можете змінити функцію _track_subtype()
:
- _track_subtype(init_values)¶
Укажіть підтип, ініційований змінами в записі, відповідно до оновлених значень.
- Параметри
init_values (dict) – вихідні значення запису; лише змінені поля присутні в dict
- Повертає
повний зовнішній id підтипу або False, якщо підтип не запущено
Example
Давайте додамо поле state
до нашого прикладу класу та запускаємо сповіщення з певним підтипом, коли це поле змінює значення.
Спочатку визначимо наш підтип:
<record id="mt_state_change" model="mail.message.subtype">
<field name="name">Trip confirmed</field>
<field name="res_model">business.trip</field>
<field name="default" eval="True"/>
<field name="description">Business Trip confirmed!</field>
</record>
Потім нам потрібно перевизначити функцію track_subtype()
. Ця функція викликається системою відстеження, щоб дізнатися, який підтип слід використовувати залежно від поточної зміни. У нашому випадку ми хочемо використовувати наш блискучий новий підтип, коли поле state
змінюється з draft на confirmed:
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread']
_description = 'Business Trip'
name = fields.Char(tracking=True)
partner_id = fields.Many2one('res.partner', 'Responsible',
tracking=True)
guest_ids = fields.Many2many('res.partner', 'Participants')
state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
tracking=True)
def _track_subtype(self, init_values):
# init_values contains the modified fields' values before the changes
#
# the applied values can be accessed on the record as they are already
# in cache
self.ensure_one()
if 'state' in init_values and self.state == 'confirmed':
return self.env.ref('my_module.mt_state_change')
return super(BusinessTrip, self)._track_subtype(init_values)
Налаштування сповіщень¶
Під час надсилання сповіщень підписникам може бути дуже корисним додати кнопки в шаблон, щоб дозволити швидкі дії безпосередньо з ел. пошти. Навіть проста кнопка для прямого посилання на представлення форми запису може бути корисною; однак у більшості випадків ви не бажаєте відображати ці кнопки користувачам порталу.
Система сповіщень дозволяє налаштовувати шаблони сповіщень такими способами:
Відображати Кнопки доступу: ці кнопки видно у верхній частині повідомлення ел. пошти та дозволяють одержувачу отримати прямий доступ до представлення форми запису
Відображати Кнопки слідкувати: ці кнопки дозволяють одержувачу безпосередньо швидко підписатися на запис
Відображати Кнопки слідкувати: ці кнопки дозволяють одержувачу безпосередньо швидко підписатися на запис
Відображати Кнопки користувацьких дій: ці кнопки є викликами певних маршрутів і дозволяють зробити деякі корисні дії безпосередньо доступними з ел. пошти (наприклад, перетворення лід на нагоду, перевірка відомості про витрати для менеджера витрат тощо)
Налаштування цих кнопок можна застосовувати до різних груп, які ви можете визначити самостійно, замінивши функцію _notify_get_groups
.
- _notify_get_groups(message, groups)¶
Укажіть підтип, ініційований змінами в записі, відповідно до оновлених значень.
- Параметри
message (record) – запис
mail.message
наразі надсилаєтьсяgroups (list(tuple)) – список кортежів у формі (назва_групи, функція_групи,дані_групи), де: назва_групи - це ідентифікатор, який використовується лише для того, щоб перевизначати групи та керувати ними. Групи за замовчуванням:
користувач
(одержувачі, пов’язані з користувачем-працівником),портал
(одержувачі, пов’язані з користувачем порталу) іклієнт
(одержувачі, не пов’язані з жодним користувачем). Прикладом використання перевизначення може бути додавання групи, пов’язаної з res.groups, як-от відділ кадрів, щоб установити для них певні кнопки дій. group_func - це покажчик на функцію, який приймає запис партнера як параметр. Цей метод буде застосовано до одержувачів, щоб дізнатися, чи належать вони до певної групи чи ні. Зберігається лише перша відповідна група. Порядок оцінки – це порядок списку. group_data - це dict, що містить параметри ел. листа зі сповіщеннями з такими можливими ключами та значеннями: has_button_access, чи відображати Доступ <Document> в ел. пошті. За замовчуванням True для нових груп, False для порталу/клієнта. - button_access dict з URL-адресою та заголовком кнопки - has_button_follow, чи відображати Підписатися в ел. листі (якщо одержувач наразі не стежить за ланцюжком). За замовчуванням True для нових груп, False для порталу/клієнта. - button_follow dict з URL-адресою та заголовком кнопки - has_button_unfollow, чи відображати функцію «Скасувати підписку» в електронному листі (якщо одержувач зараз стежить за ланцюжком). За замовчуванням True для нових груп, False для порталу/клієнта. - button_unfollow dict з URL-адресою та назвою кнопки - список дій кнопок дій для відображення в ел. сповіщенні. Кожна дія - це dict, що містить URL-адресу та назву кнопки.
- Повертає
повний зовнішній id підтипу або False, якщо підтип не запущено
URL-адреси в списку дій можна створити автоматично, викликавши функцію _notify_get_action_link()
:
- _notify_get_action_link(self, link_type, **kwargs)¶
Згенерувати посилання для вказаного типу на поточному записі (або на певному записі, якщо встановлено
model
іres_id
kwargs).- Параметри
link_type (str) – тип посилання, яке буде згенеровано; може мати будь-яке з цих значень:
view
посилання на форму прпедставлення записуassign
призначити зареєстрованому користувачеві полеuser_id
у записі (якщо воно існує)follow
self -пояснювальнийunfollow
самопояснювальнийmethod
виклик методу в записі; ім’я методу має бути надано як kwargmethod
new
відкрити порожню форму для нового запису; ви можете вказати конкретну дію, вказавши її ідентифікатор (ідентифікатор бази даних або повністю визначений зовнішній id) у kwargaction_id
- Повертає
посилання типу, вибраного для запису
- Тип повернення
Example
Давайте додамо спеціальну кнопку до сповіщень про зміну стану відрядження; ця кнопка скине стан до чернетки та буде видимою лише для учасника (уявної) групи Travel Manager (business.group_trip_manager
)
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.alias.mixin']
_description = 'Business Trip'
# Pevious code goes here
def action_cancel(self):
self.write({'state': 'draft'})
def _notify_get_groups(self, message, groups):
""" Handle Trip Manager recipients that can cancel the trip at the last
minute and kill all the fun. """
groups = super(BusinessTrip, self)._notify_get_groups(message, groups)
self.ensure_one()
if self.state == 'confirmed':
app_action = self._notify_get_action_link('method',
method='action_cancel')
trip_actions = [{'url': app_action, 'title': _('Cancel')}]
new_group = (
'group_trip_manager',
lambda partner: bool(partner.user_ids) and
any(user.has_group('business.group_trip_manager')
for user in partner.user_ids),
{
'actions': trip_actions,
})
return [new_group] + groups
Зауважте, що я міг би визначити свою функцію оцінки поза цим методом і визначити глобальну функцію для виконання цього замість лямбда-визначення, але щоб бути більш коротким і менш багатослівним у цих файлах документації, які іноді можуть бути нудними, я вибираю перше замість останнього.
Заміна значень за замовчуванням¶
Є кілька способів налаштувати поведінку моделей mail.thread
, включаючи (але не обмежуючись ними):
_mail_post_access
- атрибутModel
необхідні права доступу для можливості розміщення повідомлення на моделі; за умовчанням потрібен доступ
запис
, також можна встановитичитання
- Ключі контексту:
Ці контекстні ключі можна використовувати для певної міри керування такими функціями
mail.thread
, як автоматична підписка або відстеження полів під час викликівcreate()
абоwrite()
(або будь-якого іншого методу, де це може бути корисним).mail_create_nosubscribe
: у create або message_post не підписуйте поточного користувача на потік записівmail_create_nolog
: під час create не реєструвати автоматичне повідомлення „<Документ> створено“mail_notrack
: під час create та write не виконувати відстеження значення під час створення повідомленьtracking_disable
: під час create та write не використовувати функції MailThread (автоматична підписка, відстеження, публікація, …)mail_auto_delete
: автоматичне видалення повідомлень про пошту; True за замовчуваннямmail_notify_force_send
: якщо менше 50 сповіщень ел. поштою для надсилання, надсилайте їх безпосередньо замість використання черги; True за замовчуваннямmail_notify_user_signature
: додати поточний підпис користувача в сповіщення ел. поштою; True за замовчуванням
Поштові аліаси¶
Аліаси - це настроювані адреси ел. пошти, пов’язані з певним записом (який зазвичай успадковує модель mail.alias.mixin
), які створюватимуть нові записи під час зв’язку ел. поштою. Вони є простим способом зробити вашу систему доступною ззовні, дозволяючи користувачам або клієнтам швидко створювати записи у вашій базі даних без необхідності безпосереднього підключення до Odoo.
Аліаси проти шлюзу вхідної пошти¶
Деякі люди використовують шлюз вхідної пошти для цієї ж мети. Вам усе ще потрібен правильно налаштований поштовий шлюз для використання аліаси, однак одного домену catchall буде достатньо, оскільки вся маршрутизація здійснюватиметься всередині Odoo. Аліаси мають кілька переваг перед поштовими шлюзами:
- Легше налаштувати
Один вхідний шлюз може використовуватися багатьма аліасами; це дозволяє уникнути необхідності налаштовувати кілька ел. листів у вашому доменному імені (все налаштування виконується в Odoo)
Для налаштування аліасів не потрібні права доступу до системи
- Більш зв’язний
Можна налаштувати на пов’язаному записі, а не в підменю Параметри
- Простіше перевизначати на стороні сервера
Модель Mixin створена для розширення з самого початку, дозволяючи витягувати корисні дані з вхідних ел. листів легше, ніж за допомогою поштового шлюзу.
Аліас підтримує інтеграцію¶
Аліаси зазвичай налаштовуються на батьківській моделі, яка потім створить певний запис, коли зв’яжеться ел. поштою. Наприклад, у Project є аліаси для створення завдань або проблем, у Sales Team є аліаси для створення лідів.
Примітка
Модель, яка буде створена за допомогою аліаса, має успадкувати модель mail_thread
.
Підтримка аліасів додається шляхом успадкування mail.alias.mixin
; цей міксин генеруватиме новий запис mail.alias
для кожного запису батьківського класу, який створюється (наприклад, кожен запис project.project
має свій запис mail.alias
, ініціалізований під час створення ).
Примітка
Аліаси також можна створити вручну та підтримувати простим полем Many2one
. Цей посібник припускає, що ви бажаєте повнішої інтеграції з автоматичним створенням аліасу, значеннями за замовчуванням для певного запису тощо.
На відміну від успадкування mail.thread
, mail.alias.mixin
вимагає певних перевизначень для правильної роботи. Ці перевизначення вказуватимуть значення створеного аліасу, наприклад тип запису, який він має створити, і, можливо, деякі значення за замовчуванням, які ці записи можуть мати залежно від батьківського об’єкта:
- _get_alias_model_name(vals)¶
Поверніть назву моделі для аліасу. Вхідні ел. листи, які не є відповідями на існуючі записи, призведуть до створення нового запису цієї моделі аліасу. Значення може залежати від
vals
, назви значень, що передаються вcreate
під час створення запису цієї моделі.- Параметри
dict (vals) – значення новоствореного запису, який буде містити аліас
- Повертає
назва моделі
- Тип повернення
- _get_alias_values()¶
Повертайте значення для створення аліасу або запису на аліасі після його створення. Хоча це і не є обов’язковим, зазвичай потрібно переконатися, що новостворені записи будуть пов’язані з батьківським аліасом (тобто завдання, створювані в правильному проекті), встановивши словник значень за замовчуванням у аліасі
alias_defaults
поле.- Повертає
словник значень, які будуть записані в новий аліас
- Тип повернення
Перевизначення _get_alias_values()
особливо цікаве, оскільки дозволяє легко змінювати поведінку ваших аліасів. Серед полів, які можна встановити на адіасі, особливо цікаві наступні:
alias_name
-Char
назва аліаса ел. пошти, напр. „jobs“, якщо ви хочете отримувати ел. листи для <jobs@example.odoo.com>
alias_user_id
-Many2one
(res.users
)власник записів, створених після отримання ел. листів на цей аліас; якщо це поле не встановлено, система намагатиметься знайти правильного власника на основі адреси відправника (від) або використовуватиме обліковий запис адміністратора, якщо для цієї адреси не знайдено системного користувача
alias_defaults
-Text
Словник Python, який буде оцінено для надання значень за замовчуванням під час створення нових записів для цього аліасу
alias_force_thread_id
-Integer
необов’язковий ID потоку (запису), до якого будуть прикріплені всі вхідні повідомлення, навіть якщо вони не відповіли на нього; якщо встановлено, створення нових записів буде повністю вимкнено
alias_contact
-Selection
Політика розміщення повідомлення в документі за допомогою шлюзу пошти
кожен: кожен може публікувати
партнери: лише автентифіковані партнери
підписники: лише підписники пов’язаного документа або учасники наступних каналів
Зауважте, що аліаси використовують успадкування делегування, що означає, що поки аліас зберігається в іншій таблиці, ви маєте доступ до всіх цих полів безпосередньо з вашого батьківського об’єкта. Це дає змогу легко налаштовувати ваш аліас із перегляду форми запису.
Example
Давайте додамо аліаси до нашого класу бізнес-подорожі, щоб створювати витрати на льоту ел. поштою.
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.alias.mixin']
_description = 'Business Trip'
name = fields.Char(tracking=True)
partner_id = fields.Many2one('res.partner', 'Responsible',
tracking=True)
guest_ids = fields.Many2many('res.partner', 'Participants')
state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
tracking=True)
expense_ids = fields.One2many('business.expense', 'trip_id', 'Expenses')
alias_id = fields.Many2one('mail.alias', string='Alias', ondelete="restrict",
required=True)
def _get_alias_model_name(self, vals):
""" Specify the model that will get created when the alias receives a message """
return 'business.expense'
def _get_alias_values(self):
""" Specify some default values that will be set in the alias at its creation """
values = super(BusinessTrip, self)._get_alias_values()
# alias_defaults holds a dictionary that will be written
# to all records created by this alias
#
# in this case, we want all expense records sent to a trip alias
# to be linked to the corresponding business trip
values['alias_defaults'] = {'trip_id': self.id}
# we only want followers of the trip to be able to post expenses
# by default
values['alias_contact'] = 'followers'
return values
class BusinessExpense(models.Model):
_name = 'business.expense'
_inherit = ['mail.thread']
_description = 'Business Expense'
name = fields.Char()
amount = fields.Float('Amount')
trip_id = fields.Many2one('business.trip', 'Business Trip')
partner_id = fields.Many2one('res.partner', 'Created by')
Ми хотіли б, щоб наш аліас можна було легко настроїти у формі наших відряджень, тому давайте додамо наступне до нашої форми:
<page string="Emails">
<group name="group_alias">
<label for="alias_name" string="Email Alias"/>
<div name="alias_def">
<!-- display a link while in view mode and a configurable field
while in edit mode -->
<field name="alias_id" class="oe_read_only oe_inline"
string="Email Alias" required="0"/>
<div class="oe_edit_only oe_inline" name="edit_alias"
style="display: inline;" >
<field name="alias_name" class="oe_inline"/>
@
<field name="alias_domain" class="oe_inline" readonly="1"/>
</div>
</div>
<field name="alias_contact" class="oe_inline"
string="Accept Emails From"/>
</group>
</page>
Тепер ми можемо змінити адресу аліасу безпосередньо з перегляду форми та змінити, хто може надсилати ел. листи на аліас.
Потім ми можемо перевизначити message_new()
у нашій моделі витрат, щоб отримати значення з нашої ел. пошти, коли буде створено витрати:
class BusinessExpense(models.Model):
# Previous code goes here
# ...
def message_new(self, msg, custom_values=None):
""" Override to set values according to the email.
In this simple example, we simply use the email title as the name
of the expense, try to find a partner with this email address and
do a regex match to find the amount of the expense."""
name = msg_dict.get('subject', 'New Expense')
# Match the last occurrence of a float in the string
# Example: '50.3 bar 34.5' becomes '34.5'. This is potentially the price
# to encode on the expense. If not, take 1.0 instead
amount_pattern = '(\d+(\.\d*)?|\.\d+)'
expense_price = re.findall(amount_pattern, name)
price = expense_price and float(expense_price[-1][0]) or 1.0
# find the partner by looking for it's email
partner = self.env['res.partner'].search([('email', 'ilike', email_address)],
limit=1)
defaults = {
'name': name,
'amount': price,
'partner_id': partner.id
}
defaults.update(custom_values or {})
res = super(BusinessExpense, self).message_new(msg, custom_values=defaults)
return res
Відстеження активності¶
Активності – це дії, які користувачі мають виконати з документом, як-от телефонний дзвінок або організація зустрічі. Дії постачаються з модулем пошти, оскільки вони інтегровані в Chatter, але не входять до складу mail.thread. Активності - це записи класу mail.activity
, які мають тип (mail.activity.type
), назву, опис, запланований час (серед іншого). Незавершені дії відображаються над історією повідомлень у віджеті чату.
Ви можете інтегрувати дії за допомогою класу mail.activity.mixin
для свого об’єкта та певних віджетів для їх відображення (через поле activity_ids
) у поданні форми та поданні Канбан ваших записів (mail_activity
і kanban_activity
віджети відповідно).
Example
Організація ділової поїздки – це виснажливий процес, тому відстеження необхідних активностей, як-от замовлення квитків на літак або замовлення таксі в аеропорт, може бути корисним. Для цього ми додамо добірку заходів у нашу модель і відобразимо наступні заплановані активності в історії повідомлень про нашу подорож.
class BusinessTrip(models.Model):
_name = 'business.trip'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Business Trip'
name = fields.Char()
# [...]
Ми змінюємо вигляд форми наших подорожей, щоб відображати наступні активності:
<record id="business_trip_form" model="ir.ui.view">
<field name="name">business.trip.form</field>
<field name="model">business.trip</field>
<field name="arch" type="xml">
<form string="Business Trip">
<!-- Your usual form view goes here -->
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
Ви можете знайти конкретні приклади інтеграції в наступних моделях:
crm.lead
у додатку CRM (crm)sale.order
у додатку Продажі (sale)project.task
у додатку Проект (project)
Особливості сайту¶
Відстеження відвідувачів¶
Клас utm.mixin
можна використовувати для відстеження онлайн-маркетингових/комунікаційних кампаній за допомогою аргументів у посиланнях на вказані ресурси. Міксин додає до вашої моделі 3 поля:
Поле
campaign_id
:Many2one
до об’єктаutm.campaign
(наприклад, Christmas_Special, Fall_Collection тощо)Поле
source_id
:Many2one
в об’єктutm.source
(тобто пошукова система, список розсилки тощо)Поле
medium_id
:Many2one
до об’єктаutm.medium
(тобто Snail Mail, ел. пошта, оновлення соціальної мережі тощо)
Ці моделі мають єдине поле name
(тобто вони існують лише для розрізнення кампаній, але не мають жодної конкретної поведінки).
Коли клієнт відвідує ваш веб-сайт із цими параметрами, встановленими в URL-адресі (тобто https://simbiozems.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website), на веб-сайті відвідувача встановлюються три файли cookie для цих параметрів . Коли на веб-сайті створюється об’єкт, який успадковує utm.mixin (наприклад, форма ліда, заява на роботу тощо), запускається код utm.mixin і отримує значення з файлів cookie, щоб встановити їх у новому записі. Коли це буде зроблено, ви зможете використовувати поля кампанії/джерела/медіани як будь-які інші поля під час визначення звітів і представлень (групування за тощо).
Щоб розширити цю поведінку, просто додайте реляційне поле до простої моделі (модель має підтримувати швидке створення (тобто виклик create()
з одним значенням name
) і розширте функцію ` tracking_fields()`
:
class UtmMyTrack(models.Model):
_name = 'my_module.my_track'
_description = 'My Tracking Object'
name = fields.Char(string='Name', required=True)
class MyModel(models.Models):
_name = 'my_module.my_model'
_inherit = ['utm.mixin']
_description = 'My Tracked Object'
my_field = fields.Many2one('my_module.my_track', 'My Field')
@api.model
def tracking_fields(self):
result = super(MyModel, self).tracking_fields()
result.append([
# ("URL_PARAMETER", "FIELD_NAME_MIXIN", "NAME_IN_COOKIES")
('my_field', 'my_field', 'odoo_utm_my_field')
])
return result
Це вкаже системі створити файл cookie з іменем odoo_utm_my_field зі значенням, знайденим у параметрі URL-адреси my_field
; як тільки новий запис цієї моделі буде створено за допомогою виклику з форми веб-сайту, загальне перевизначення методу create()
utm.mixin
отримає значення за замовчуванням для цього поля з файлу cookie ( а запис my_module.my_track
буде створено на льоту, якщо він ще не існує).
Ви можете знайти конкретні приклади інтеграції в наступних моделях:
crm.lead
у додатку CRM (crm)hr.applicant
у додатку Процес найму (hr_recruitment)helpdesk.ticket
у додатку тех. підтримки (helpdesk - лише Odoo Enterprise)
Видимість сайту¶
Ви можете досить легко додати перемикач видимості веб-сайту до будь-якого свого запису. Хоча цей міксин досить легко реалізувати вручну, він найчастіше використовується після успадкування mail.thread
; свідчення його корисності. Типовим варіантом використання цього міксину є будь-який об’єкт, який має зовнішню сторінку; можливість контролювати видимість сторінки дозволяє вам не поспішати під час редагування сторінки та публікувати її лише тоді, коли ви задоволені.
Щоб включити цю функцію, вам потрібно лише успадкувати website.published.mixin
:
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.published.mixin']
Цей міксин додає 2 поля до вашої моделі:
website_published
:Boolean
поле, яке представляє статус публікаціїwebsite_url
:Char
поле, яке представляє URL-адресу, через яку здійснюється доступ до об’єкта
Зауважте, що це останнє поле є обчислюваним і має бути реалізоване для вашого класу:
def _compute_website_url(self):
for blog_post in self:
blog_post.website_url = "/blog/%s" % (log_post.blog_id)
Після того, як механізм налагоджено, вам просто потрібно адаптувати ваш інтерфейс і бекенд, щоб зробити його доступним. У бекенді додавання кнопки у вікні кнопок – це зазвичай шлях:
<button class="oe_stat_button" name="website_publish_button"
type="object" icon="fa-globe">
<field name="website_published" widget="website_button"/>
</button>
Щоб уникнути показу кнопок „Редагувати“ відвідувачам веб-сайту, у інтерфейсі потрібно виконати деякі перевірки безпеки:
<div id="website_published_button" class="float-right"
groups="base.group_website_publisher"> <!-- or any other meaningful group -->
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>
<t t-set="action" t-value="'blog.blog_post_action'"/>
</t>
</div>
Зауважте, що ви повинні передати свій об’єкт як змінну object
до шаблону; у цьому прикладі запис blog.post
було передано як змінну blog_post
механізму відтворення qweb
, необхідно вказати це в шаблоні керування публікацією. Змінна publish_edit
дозволяє кнопці інтерфейсу зв’язуватися з сервером (дозволяючи легко перемикатися з інтерфейсу на сервер і навпаки); якщо встановлено, ви повинні вказати повний зовнішній ідентифікатор дії, яку ви хочете викликати у серверній частині, у змінній action
(зауважте, що для моделі має існувати представлення форми).
Дія website_publish_button
визначена в mixin і адаптує свою поведінку до вашого об’єкта: якщо клас має дійсну функцію обчислення website_url
, користувач перенаправляється до інтерфейсу, коли він натискає кнопку; потім користувач може опублікувати сторінку безпосередньо з інтерфейсу. Це гарантує, що жодна онлайн-публікація не станеться випадково. Якщо функція обчислення відсутня, просто запускається логічне значення website_published
.
Метадані веб-сайту¶
Цей простий міксин просто дозволяє вам легко вставляти метадані на ваші зовнішні сторінки.
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['website.seo.metadata', 'website.published.mixin']
Цей міксин додає 2 поля до вашої моделі:
website_meta_title
:Char
поле, яке дозволяє встановити додатковий заголовок для вашої сторінкиwebsite_meta_description
:Char
поле, яке містить короткий опис сторінки (іноді використовується в результатах пошукових систем)website_meta_keywords
:Char
поле, яке містить деякі ключові слова, які допоможуть пошуковим системам точніше класифікувати вашу сторінку; Інструмент «Просувати» допоможе вам легко підібрати лексичні ключові слова
Ці поля можна редагувати у інтерфейсі за допомогою інструмента «Просування» на панелі інструментів редактора. Налаштування цих полів може допомогти пошуковим системам краще індексувати ваші сторінки. Зверніть увагу, що пошукові системи не базують свої результати лише на цих метаданих; найкращою практикою SEO все ще є посилання на надійні джерела.
Інші¶
Рейтинг клієнта¶
Рейтинговий міксин дає змогу надсилати ел. листи із запитом про оцінку клієнта, автоматичний перехід у процеси канбану та збирати статистичні дані про ваші рейтинги.
Додавання оцінки вашої моделі¶
Щоб додати підтримку рейтингу, просто успадкуйте модель rating.mixin
:
class MyModel(models.Models):
_name = 'my_module.my_model'
_inherit = ['rating.mixin', 'mail.thread']
user_id = fields.Many2one('res.users', 'Responsible')
partner_id = fields.Many2one('res.partner', 'Customer')
Поведінка mixin адаптується до вашої моделі:
Запис
rating.rating
буде пов’язано з полемpartner_id
вашої моделі (якщо поле є).цю поведінку можна змінити за допомогою функції rating_get_partner_id() якщо ви використовуєте інше поле ніж partner_id
Запис
rating.rating
буде пов’язано з полем партнеруuser_id
вашої моделі (якщо поле є) (тобто партнер, який оцінюється)цю поведінку можна змінити за допомогою функції rating_get_rated_partner_id() якщо ви використовуєте поле, відмінне від user_id (зауважте, що функція має повертати res.partner, для user_id система автоматично отримує партнера користувача)
Історія чату відображатиме подію оцінки (якщо ваша модель успадкована від
mail.thread
)
Надсилайте запити на оцінку ел. поштою¶
Якщо ви хочете надіслати ел. листи з запитом на рейтинг, просто створіть ел. лист із посиланнями на об’єкт рейтингу. Дуже простий шаблон ел. пошти може виглядати так:
<record id="rating_my_model_email_template" model="mail.template">
<field name="name">My Model: Rating Request</field>
<field name="email_from">${object.rating_get_rated_partner_id().email or '' | safe}</field>
<field name="subject">Service Rating Request</field>
<field name="model_id" ref="my_module.model_my_model"/>
<field name="partner_to" >${object.rating_get_partner_id().id}</field>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
% set access_token = object.rating_get_access_token()
<p>Hi,</p>
<p>How satsified are you?</p>
<ul>
<li><a href="/rate/${access_token}/5">Satisfied</a></li>
<li><a href="/rate/${access_token}/3">Okay</a></li>
<li><a href="/rate/${access_token}/1">Dissatisfied</a></li>
</ul>
]]></field>
</record>
Потім ваш клієнт отримає електронний лист із посиланнями на просту веб-сторінку, що дозволить йому надати відгук про свою взаємодію з вашими користувачами (включно з довільним текстовим повідомленням).
Потім ви можете досить легко інтегрувати ваші оцінки з переглядом форми, визначивши дію для оцінок:
<record id="rating_rating_action_my_model" model="ir.actions.act_window">
<field name="name">Customer Ratings</field>
<field name="res_model">rating.rating</field>
<field name="view_mode">kanban,pivot,graph</field>
<field name="domain">[('res_model', '=', 'my_module.my_model'), ('res_id', '=', active_id), ('consumed', '=', True)]</field>
</record>
<record id="my_module_my_model_view_form_inherit_rating" model="ir.ui.view">
<field name="name">my_module.my_model.view.form.inherit.rating</field>
<field name="model">my_module.my_model</field>
<field name="inherit_id" ref="my_module.my_model_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="%(rating_rating_action_my_model)d" type="action"
class="oe_stat_button" icon="fa-smile-o">
<field name="rating_count" string="Rating" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
Зауважте, що для рейтингів є режими перегляду за замовчуванням (канбан, зведена таблиця, графік), які дають змогу швидко переглядати рейтинги клієнтів з висоти пташиного польоту.
Ви можете знайти конкретні приклади інтеграції в наступних моделях:
project.task
у додатку Project (rating_project)helpdesk.ticket
у додатку тех. підтримки (helpdesk - лише Odoo Enterprise)