Блог

Как добавить функцию каталога в Odoo 19

post-title

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

Добавление функции каталога товаров в Odoo 19 позволяет пользователям быстрее и нагляднее выбирать товары внутри документов. Вместо того, чтобы выбирать товары из длинных выпадающих списков, пользователи могут открыть специальный каталог, в котором товары отображаются в виде карточек с изображениями, ценами и возможностью быстрого контроля количества. Эта функция уже доступна в стандартных приложениях, таких как Sales, а Odoo 19 позволяет разработчикам повторно использовать те же возможности в пользовательских модулях.

Чтобы понять, как работает каталог, давайте сначала рассмотрим знакомый пример с экрана заказа на продажу.

В заказе на продажу при нажатии кнопки Каталог открывается таблица товаров в режиме Канбан. Здесь пользователи могут:

  1. Просматривать товары визуально
  2. Выполнять поиск по названию или внутренней ссылке
  3. Мгновенно увеличивать или уменьшать количество
  4. Добавлять или удалять товары, не закрывая каталог

Кнопка “Вернуться к предложению” позволяет пользователям вернуться к форме заказа на продажу после завершения выбора. Товары, добавленные из каталога, сразу же отображаются в строках заказа с выбранными количествами.

Такое же поведение каталога может быть реализовано в любой пользовательской модели в Odoo 19 с помощью соответствующих микшеров и методов, которые мы рассмотрим далее.

Шаг 1: Включите поддержку каталога с помощью product.catalog.mixin

Чтобы включить функциональность каталога в пользовательскую модель, модель должна наследовать product.catalog.mixin.

Этот микшер предоставляет всю внутреннюю логику, необходимую для работы с каталогом, такую как открытие каталога, обработка выбранных продуктов и синхронизация количества.

from odoo import models, fields, api
class WarrantyRequest(models.Model):
   _name = 'warranty.request'
   _description = 'Warranty Request Model'
   _inherit = ['mail.thread', 'mail.activity.mixin', 'product.catalog.mixin']

Как только это соединение будет добавлено, модель станет совместимой с интерфейсом каталога Odoo.

Шаг 2: Добавьте кнопку каталога в XML-представление

Пользователям необходимо выполнить видимое действие, чтобы открыть каталог. Это делается путем размещения кнопки каталога в разделе <control> в режиме просмотра списка one2many. Расположение кнопки здесь гарантирует, что она будет отображаться рядом со стандартными линейными действиями. При нажатии на него открывается представление каталога и идентификатор активной записи передается через контекст, позволяя Odoo правильно связать выбранные продукты с документом.

<button name="action_add_from_catalog" type="object"
       string="Catalog" class="px-4 btn-link"
       context="{'order_id': parent.id}"/>

Окончательный XML-код выглядит следующим образом,

<notebook>
  <page id="product" name="Product">
      <field name="warranty_line_ids" mode="list"
             widget="section_and_note_one2many"
             context="{'default_display_type': False}">
          <list editable='bottom'>
              <control>
                  <create name="add_product_control" string="Add a product"/>
                  <create name="add_section_control"
                          string="Add a section"
                          context="{'default_display_type': 'line_section'}"/>
                  <create name="add_note_control"
                          string="Add a note"
                          context="{'default_display_type': 'line_note'}"/>
                  <button name="action_add_from_catalog" type="object"
                          string="Catalog" class="px-4 btn-link"
                          context="{'order_id': parent.id}"/>
              </control>
              <field name="product_id" required="not display_type"/>
              <field name="name"/>
              <field name="display_type" column_invisible="1"/>
              <field name="quantity" required="not display_type"/>
              <field name="uom_id"/>
              <field name="lst_price"/>
              <field name="warranty_charge" sum="Warranty Charge"/>
          </list>
      </field>
  </page>
</notebook>

Шаг 3: Перенаправьте действие Catalog из дочерней модели

Действие catalog выполняется на родительском уровне, поэтому дочерняя модель должна правильно перенаправить запрос.В модели warranty.request.line определите метод action_add_from_catalog. Этот метод извлекает родительскую запись warranty.request, используя значение order_id из контекста, а затем запускает действие каталога из родительской model.from.

def action_add_from_catalog(self):
   """Trigger catalog action from child model."""
   warranty_request = self.env['warranty.request'].browse(
       self.env.context.get('order_id')
   )
   return warranty_request.action_add_from_catalog()

Это гарантирует, что каталог всегда взаимодействует с правильной записью, даже при доступе из строки one2many.

Шаг 4: Добавьте метод _get_product_catalog_order_data в родительскую модель

В модели warranty.request определите метод _get_product_catalog_order_data. Этот метод подготавливает информацию о продукте, которая будет отображаться в представлении каталога.

def _get_product_catalog_order_data(self, products, **kwargs):
   """Retrieve product details for the catalog."""
   res = super()._get_product_catalog_order_data(products, **kwargs)
   for product in products:
       res[product.id] |= {
           'price': product.standard_price,
       }
   return res

Расширяя базовую реализацию и вызывая функцию super(), вы сохраняете поведение каталога по умолчанию, позволяя при этом включать в отображение каталога дополнительные сведения о продукте, такие как цены.

Шаг 5: Отобразите уже добавленные продукты в представлении каталога

Чтобы отразить продукты, которые уже добавлены в документ, методы должны быть реализованы как в родительской, так и в дочерней моделях.

а) Сгруппируйте существующие строки в родительской модели

В модели warranty.request определите метод _get_product_catalog_record_lines. Этот метод группирует существующие строки запроса гарантии по продуктам, гарантируя, что продукты, уже присутствующие в поле one2many, будут распознаны каталогом. Он возвращает словарь, группирующий записи по идентификатору продукта.

def _get_product_catalog_record_lines(self, product_ids, child_field=False, **kwargs):
   """Group warranty request lines by product for catalog view."""
   grouped_lines = defaultdict(lambda: self.env['warranty.request.line'])
   for line in self.warranty_line_ids:
       if line.product_id.id not in product_ids:
           continue
       grouped_lines[line.product_id] |= line
   return grouped_lines

b) Укажите сведения о строке в дочерней модели

В запросе на гарантию.в модели строки укажите метод _get_product_catalog_lines_data. Этот метод предоставляет информацию о количестве и ценах для каждой линейки продуктов. Если продукт еще не был добавлен, количество по умолчанию равно нулю.

def _get_product_catalog_lines_data(self, **kwargs):
   """Provide details for warranty request lines in catalog view."""
   self.ensure_one()
   return {
       'quantity': self.quantity or 0,
       'price': self.product_id.standard_price,
       'uomDisplayName': self.uom_id.name if self.uom_id else '',
       'readOnly': False,
   }

В представлении каталога теперь будут отображаться товары, уже добавленные в строку запроса на гарантийное обслуживание. Пользователи могут удалять или добавлять товары непосредственно из представления каталога.

Шаг 6: Обновите гарантийные строки в представлении каталога

В модели warranty.request определите метод _update_order_line_info. Этот метод обрабатывает обновления, поступающие из представления каталога, всякий раз, когда пользователь добавляет продукт, изменяет его количество или удаляет его, установив количество равным нулю.

def _update_order_line_info(self, product_id, quantity, **kwargs):
   """
   Update warranty request line information for a given product.
   :param int product_id: The product ID (`product.product`).
   :param float quantity: The quantity to update.
   :return: The updated warranty request line or None if removed.
   """
   self.ensure_one()
   warranty_line = self.warranty_line_ids.filtered(
       lambda line: line.product_id.id == product_id
   )
   if warranty_line:
       if quantity != 0:
           warranty_line.quantity = quantity
       else:
           warranty_line.unlink() 
   elif quantity > 0:
       product = self.env['product.product'].browse(product_id)
       warranty_line = self.env['warranty.request.line'].create({
           'warranty_id': self.id,
           'product_id': product_id,
           'name': product.display_name,
           'quantity': quantity,
       })
   return warranty_line

Этот метод гарантирует, что строки one2many обновляются, создаются или удаляются соответствующим образом, сохраняя полную синхронизацию каталога и представления формы.

Как вы можете видеть из приведенного выше примера, товары, выбранные из каталога, отображаются в гарантийных строках с указанием их количества; все синхронизируется правильно, и если пользователь установит количество равным 0, товар будет удален из гарантийных строк.

Шаг 7: Отфильтруйте товары, отображаемые в каталоге.

В модели warranty.request определите метод _get_product_catalog_domain. Этот метод ограничивает количество продуктов, отображаемых в каталоге, в зависимости от конкретных условий, таких как продукты sale_ok или компания.

def _get_product_catalog_domain(self):
   """Define the product filter for the catalog."""
   return [
       ('company_id', 'in', [self.company_id.id, False]),
       ('sale_ok', '=', True),  # Show products that can be sold
   ]

Как вы можете видеть в примере ниже, все перечисленные здесь товары являются товарами с включенной функцией sale_ok.

Функция каталога продуктов в Odoo 19 предоставляет простой и эффективный способ управления выбором продуктов в пользовательских модулях. Унаследовав соответствующий набор компонентов и внедрив несколько вспомогательных методов в соответствующие модели, разработчики могут воссоздать тот же опыт работы с каталогом, который используется в стандартных заказах на продажу.

Такой подход повышает удобство использования, уменьшает количество ошибок, допущенных вручную, и обеспечивает более плавный рабочий процесс для конечных пользователей. При надлежащей настройке функция каталога может быть адаптирована к широкому спектру бизнес-процессов в Odoo 19.