Administration and ERP integration

A brief and incomplete introduction to the Odoo administration

The Odoo administration was briefly seen during thewebsite supportsection. We can go back to it usingAdministrator ‣ Administratorin the menu (orSign Inif you're signed out).

The conceptual structure of the Odoo backend is simple:

  1. first are menus, a tree (menus can have sub-menus) of records. Menus without children map to…
  2. actions. Actions have various types: links, reports, code which Odoo should execute or data display. Data display actions are called window actions , and tell Odoo to display a given _model _according to a set of views…
  3. a view has a type, a broad category to which it corresponds (a list, a graph, a calendar) and an _architecture _which customises the way the model is displayed inside the view.

Editing in the Odoo administration

By default, an Odoo model is essentially invisible to a user. To make it visible it must be available through an action, which itself needs to be reachable, generally through a menu.

Let's create a menu for our model:

academy/\_manifest__.py_

    'data': [
        'security/ir.model.access.csv',
        'templates.xml',
        'views.xml',
    ],
    # only loaded in demonstration mode
    'demo': [

academy/views.xml

<odoo>
 <data>
  <record id="action_academy_teachers" model="ir.actions.act_window">
    <field name="name">Academy teachers</field>
    <field name="res_model">academy.teachers</field>
  </record>

  <menuitem sequence="0" id="menu_academy" name="Academy"/>
  <menuitem id="menu_academy_content" parent="menu_academy"
            name="Academy Content"/>
  <menuitem id="menu_academy_content_teachers"
            parent="menu_academy_content"
            action="action_academy_teachers"/>

then accessinghttp://localhost:8069/web/in the top left should be a menuAcademy, which is selected by default, as it is the first menu, and having opened a listing of teachers. From the listing it is possible toCreatenew teacher records, and to switch to the "form" by-record view.

If there is no definition of how to present records (aview) Odoo will automatically create a basic one on-the-fly. In our case it works for the "list" view for now (only displays the teacher's name) but in the "form" view the HTMLbiographyfield is displayed side-by-side with thenamefield and not given enough space. Let's define a custom form view to make viewing and editing teacher records a better experience:

academy/views.xml

    <field name="name">Academy teachers</field>
    <field name="res_model">academy.teachers</field>
  </record>

  <record id="academy_teacher_form" model="ir.ui.view">
    <field name="name">Academy teachers: form</field>
    <field name="model">academy.teachers</field>
    <field name="arch" type="xml">
      <form>
        <sheet>
          <label for="name"/> <field name="name"/>
          <label for="biography"/>
          <field name="biography"/>
        </sheet>
      </form>
    </field>
  </record>

  <menuitem sequence="0" id="menu_academy" name="Academy"/>
  <menuitem id="menu_academy_content" parent="menu_academy"

Relations between models

We have seen a pair of "basic" fields stored directly in the record. There area number of basic fields. The second broad categories of fields arerelationaland used to link records to one another (within a model or across models).

For demonstration, let's create a_courses_model. Each course should have ateacherfield, linking to a single teacher record, but each teacher can teach many courses:

academy/models.py


    name = fields.Char()
    biography = fields.Html()

class Courses(models.Model):
    _name = 'academy.courses'

    name = fields.Char()
    teacher_id = fields.Many2one('academy.teachers', string="Teacher")

academy/security/ir.model.access.csv

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0
access_academy_courses,access_academy_courses,model_academy_courses,,1,0,0,0

let's also add views so we can see and edit a course's teacher:

academy/views.xml

      </form>
    </field>
  </record>

  <record id="action_academy_courses" model="ir.actions.act_window">
    <field name="name">Academy courses</field>
    <field name="res_model">academy.courses</field>
  </record>
  <record id="academy_course_search" model="ir.ui.view">
    <field name="name">Academy courses: search</field>
    <field name="model">academy.courses</field>
    <field name="arch" type="xml">
      <search>
        <field name="name"/>
        <field name="teacher_id"/>
      </search>
    </field>
  </record>
  <record id="academy_course_list" model="ir.ui.view">
    <field name="name">Academy courses: list</field>
    <field name="model">academy.courses</field>
    <field name="arch" type="xml">
      <tree string="Courses">
        <field name="name"/>
        <field name="teacher_id"/>
      </tree>
    </field>
  </record>
  <record id="academy_course_form" model="ir.ui.view">
    <field name="name">Academy courses: form</field>
    <field name="model">academy.courses</field>
    <field name="arch" type="xml">
      <form>
        <sheet>
          <label for="name"/>
          <field name="name"/>
          <label for="teacher_id"/>
          <field name="teacher_id"/>
        </sheet>
      </form>
    </field>
  </record>

  <menuitem sequence="0" id="menu_academy" name="Academy"/>
  <menuitem id="menu_academy_content" parent="menu_academy"
            name="Academy Content"/>
  <menuitem id="menu_academy_content_courses"
            parent="menu_academy_content"
            action="action_academy_courses"/>
  <menuitem id="menu_academy_content_teachers"
            parent="menu_academy_content"

It should also be possible to create new courses directly from a teacher's page, or to see all the courses they teach, so addthe inverse relationshipto the_teachers_model:

academy/models.py


    name = fields.Char()
    biography = fields.Html()

    course_ids = fields.One2many('academy.courses', 'teacher_id', string="Courses")

class Courses(models.Model):
    _name = 'academy.courses'

academy/views.xml

    <field name="arch" type="xml">
      <form>
        <sheet>
          <label for="name"/> <field name="name"/>

          <label for="biography"/>
          <field name="biography"/>

          <field name="course_ids">
            <tree string="Courses" editable="bottom">
              <field name="name"/>
            </tree>
          </field>
        </sheet>
      </form>

Discussions and notifications

Odoo provides technical models, which don't directly fulfill business needs but which add capabilities to business objects without having to build them by hand.

One of these is the_Chatter_system, part of Odoo's email and messaging system, which can add notifications and discussion threads to any model. The model simply has to_inheritmail.thread, and add themessage_idsfield to its form view to display the discussion thread. Discussion threads are per-record.

For our academy, it makes sense to allow discussing courses to handle e.g. scheduling changes or discussions between teachers and assistants:

academy/models.py

class Courses(models.Model):
    _name = 'academy.courses'
    _inherit = 'mail.thread'

    name = fields.Char()
    teacher_id = fields.Many2one('academy.teachers', string="Teacher")

academy/views.xml

          <field name="name"/>
          <label for="teacher_id"/>
          <field name="teacher_id"/>
        </sheet>
        <div class="oe_chatter">
          <field name="message_follower_ids" widget="mail_followers"/>
          <field name="message_ids" widget="mail_thread"/>
        </div>
      </form>
    </field>

At the bottom of each course form, there is now a discussion thread and the possibility for users of the system to leave messages and follow or unfollow discussions linked to specific courses.

Selling courses

Odoo also provides business models which allow using or opting in business needs more directly. For instance thewebsite_salemodule sets up an e-commerce site based on the products in the Odoo system. We can easily make course subscriptions sellable by making our courses specific kinds of products.

Rather than the previous classical inheritance, this means replacing our_course_model by the_product_model, and extending products in-place (to add anything we need to it).

First of all we need to add a dependency onwebsite_saleso we get both products (viasale) and the ecommerce interface:

academy/\_manifest__.py_

    'version': '0.1',

    # any module necessary for this one to work correctly
    'depends': ['website_sale'],

    # always loaded
    'data': [

restart Odoo, update your module, there is now aShopsection in the website, listing a number of pre-filled (via demonstration data) products.

The second step is to replace the_courses_model byproduct.template, and add a new category of product for courses:

academy/\_manifest__.py_

        'security/ir.model.access.csv',
        'templates.xml',
        'views.xml',
        'data.xml',    
    ],
    # only loaded in demonstration mode
    'demo': [

academy/data.xml

<odoo><data>
  <record model="product.public.category" id="category_courses">
    <field name="name">Courses</field>
    <field name="parent_id" ref="website_sale.categ_others"/>
  </record>
</data></odoo>

academy/demo.xml

        <record id="vaughn" model="academy.teachers">
            <field name="name">Lester Vaughn</field>
        </record>

        <record id="course0" model="product.template">
            <field name="name">Course 0</field>
            <field name="teacher_id" ref="padilla"/>
            <field name="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/>
            <field name="website_published">True</field>
            <field name="list_price" type="float">0</field>
            <field name="type">service</field>
        </record>
        <record id="course1" model="product.template">
            <field name="name">Course 1</field>
            <field name="teacher_id" ref="padilla"/>
            <field name="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/>
            <field name="website_published">True</field>
            <field name="list_price" type="float">0</field>
            <field name="type">service</field>
        </record>
        <record id="course2" model="product.template">
            <field name="name">Course 2</field>
            <field name="teacher_id" ref="vaughn"/>
            <field name="public_categ_ids" eval="[(4, ref('academy.category_courses'), False)]"/>
            <field name="website_published">True</field>
            <field name="list_price" type="float">0</field>
            <field name="type">service</field>
        </record>
    </data>
</odoo>

academy/models.py

    name = fields.Char()
    biography = fields.Html()

    course_ids = fields.One2many('product.template', 'teacher_id', string="Courses")

class Courses(models.Model):
    _inherit = 'product.template'

    teacher_id = fields.Many2one('academy.teachers', string="Teacher")

academy/security/ir.model.access.csv

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0

academy/views.xml

      </form>
    </field>
  </record>

  <menuitem sequence="0" id="menu_academy" name="Academy"/>
  <menuitem id="menu_academy_content" parent="menu_academy"
            name="Academy Content"/>
  <menuitem id="menu_academy_content_teachers"
            parent="menu_academy_content"

With this installed, a few courses are now available in theShop, though they may have to be looked for.

Note

  • to extend a model in-place, it's inherited without giving it a new _name
  • product.templatealready uses the discussions system, so we can remove it from our extension model
  • we're creating our courses as _published _by default so they can be seen without having to log in

Altering existing views

So far, we have briefly seen:

  • the creation of new models
  • the creation of new views
  • the creation of new records
  • the alteration of existing models

We're left with the alteration of existing records and the alteration of existing views. We'll do both on theShoppages.

View alteration is done by creating_extension_views, which are applied on top of the original view and alter it. These alteration views can be added or removed without modifying the original, making it easier to try things out and roll changes back.

Since our courses are free, there is no reason to display their price on the shop page, so we're going to alter the view and hide the price if it's 0. The first task is finding out which view displays the price, this can be done viaCustomize ‣ HTML Editorwhich lets us read the various templates involved in rendering a page. Going through a few of them, "Product item" looks a likely culprit.

Altering view architectures is done in 3 steps:

  1. Create a new view
  2. Extend the view to modify by setting the new view's inherit_idto the modified view's external id
  3. In the architecture, use the xpathtag to select and alter elements from the modified view

academy/templates.xml

                <div class="oe_structure"/>
            </t>
        </template>

        <template id="product_item_hide_no_price" inherit_id="website_sale.products_item">
            <xpath expr="//div[hasclass('product_price')]/b" position="attributes">
                <attribute name="t-if">product.price &gt; 0</attribute>
            </xpath>
        </template>

        <!-- <template id="object"> -->
        <!--   <h1><t t-esc="object.display_name"/></h1> -->
        <!--   <dl> -->

The second thing we will change is making the product categories sidebar visible by default:Customize ‣ Product Categorieslets you toggle a tree of product categories (used to filter the main display) on and off.

This is done via thecustomize_showandactivefields of extension templates: an extension template (such as the one we've just created) can becustomize_show=True. This choice will display the view in theCustomizemenu with a check box, allowing administrators to activate or disable them (and easily customize their website pages).

We simply need to modify theProduct Categories_record and set its default to_active="True":

academy/templates.xml

            </xpath>
        </template>

        <record id="website_sale.products_categories" model="ir.ui.view">
            <field name="active" eval="True"/>
        </record>

        <!-- <template id="object"> -->
        <!--   <h1><t t-esc="object.display_name"/></h1> -->
        <!--   <dl> -->

With this, the _Product Categories _sidebar will automatically be enabled when the _Academy _module is installed.

results matching ""

    No results matching ""