Backend Views

Hệ thống quản lí nội dung

Trong phần này, bạn sẽ thấy phần giao diện người dùng của Odoo. Bạn sẽ học cách tạo các loại khung views khác nhau. Ngoài các khung views, phần này cũng bao gồm các thành phần khác, chẳng hạn như các action buttons, menu và widget, sẽ giúp bạn làm cho ứng dụng của mình thân thiện hơn với người dùng. Sau khi hoàn thành phần này, bạn sẽ có thể thiết kế giao diện người dùng của phần trình Odoo backend.

Note

Lưu ý rằng phần này không bao gồm phần trang web của Odoo.

Thêm một mục menu và window actions

Cách rõ ràng nhất để cung cấp một tính năng mới cho người dùng là thêm một mục menu. Khi bạn nhấp vào một mục menu, điều gì đó sẽ xảy ra. Trong đề mục chúng ta sẽ tạo một top-level menu và sub-menu của nó, menu này sẽ mở ra danh sách tất cả học sinh. Điều này cũng có thể được thực hiện bằng cách sử dụng web user interface, thông qua menu thiết lập, nhưng trong mục này chúng ta sẽ sử dụng XML data files cho các module.

Chuẩn bị

Trong đề mục này, chúng ta cần chuẩn tạo module education trong đó thêm một model education.student. Trong model chúng ta tạo ra một field student_score.

Các bước thực hiện

Trong XML data file của module, hãy thực hiện các bước sau:

  1. Khai báo một action sẽ được thực hiện:

    <act_window
       id="education_student_action"
       name="All students"
       res_model="education.student"
       view_mode="tree,form"
       domain="[('student_score', '>', 1)]"
       context="{'default_student_score': 1}"
       limit="20"/>
    
  2. Tạo top-level menu, sẽ như sau:

    <menuitem id="education_student_menu_top_level"
       name="My App menu" />
    
  3. Tạo sub-menu, sẽ như sau:

    <menuitem id="education_student_menu"
       parent="education_student_menu_top_level"
       action="education_student_action"
       sequence="10"/>
    

bây giờ khi nâng cấp module, chúng ta sẽ thấy một top-level menu với label My App menu, mở sub-menu gọi tất cả học sinh. Nhấp vào mục menu đó sẽ mở ra một danh sách của tất cả học sinh.

Cơ chế hoạt động

một số thuộc tính quan trọng:

  • name: Được sử dụng làm tiêu đề cho cho views được mở bởi action.

  • view_mode: Phần này liệt kê các views cần cung cấp. Nó là một tệp giá trị được phân tách bằng dấu phẩy của loại views .Những giá trị mặc định là tree, form. Nếu bạn chỉ muốn hiển thị calendarform views thì giá trị của view_mode phải là calendar, form. Một số view khác là kanban , graph , pivot , calendar , cohort , và dashboard.

  • domain: Là một tùy chọn và cho phép bạn thiết lập một bộ lọc trên các bản ghi để hiển thị lên các views. Trong trường hợp trên, domain lọc ra những học sinh có điểm số lớn hơn 1. Điều này sẽ được đề cập sâu hơn trong phần Xác định bộ lọc trên danh sách bản ghi - domain của phần này.

  • context: Có thể đặt các giá trị được cung cấp cho các views đã mở, ảnh hưởng đến hành vi của chúng. Trong ví dụ trên, trên các bản ghi mới, giá trị mặc định điểm của học sinh là 1. Điều này sẽ được đề cập sâu hơn trong phần Truyền tham số cho forms và action - context của phần này.

  • limit: Đặt giới hạn số lượng bản ghi có thể được nhìn thấy trên các dạng views danh sách. Trong ví dụ trên, giới hạn là 20, nhưng nếu bạn không đưa ra giá trị giới hạn, Odoo sẽ sử dụng giá trị mặc định là 80.

Tiếp theo, chúng ta sẽ taọ menu item phân cấp từ top-level menu. Các thuộc tính quan trọng nhất cho menuitem như sau:

  • name: Được sử dụng làm văn bản mà các menu item hiển thị. Nếu menu item của bạn liên kết tới một action, bạn có thể bỏ qua trường này , vì tên của action sẽ được sử dụng cho menu item đó.

  • parent (parent_id nếu sử dụng phần tử record): Đây là XML ID tham chiếu tới parent menu item. Các mục không có parentstop-level menus.

  • action: Đây là XML ID tham chiếu tới hành động được gọi.

  • sequence: Được sử dụng để sắp xếp độ ưu tiên của menu items.

  • groups (groups_id với record` tag): Là danh sách tùy chọn gồm các nhóm người dùng có thể truy cập vào menu item. Nếu để trống, tất cả nguời dùng có thể truy cập.

Nếu bạn không muốn sử dụng các XML tags, thì bạn có thể tạo một bản ghi của ir.actions.act_windowir.ui.menu thông qua <record> tags. Ví dụ, nếu bạn muốn load act_window với <record>, bạn có thể làm như sau:

<record id='education_student_action' model='ir.actions.act_window'>
   <field name="name">All students</field>
   <field name="res_model">education.student</field>
   <field name="view_mode">tree,form</field>
   <field name="domain">[('student_score','>', 1)]</field>
   <field name="context">{'default_student_score': 1}</field>
   <field name="limit">20</field>
</record>

bạn cũng có thể tạo ra menuitem thông qua <record>.

Warning

Các tên được sử dụng với menu-item có thể không liên kết với các field names được sử dụng khi sử dụng một phần tử record, parent phải là parent_idgroups phải là groups_id.

Có một hành động sẽ mở ra một view cụ thể

Window actions tự động xác định chế độ view sẽ được sử dụng nếu không chế độ nào được đưa ra. Nhưng đôi khi chúng ta muốn một hành động để mở một view đặc biệt, chúng ta sẽ tạo ra một form đơn giản cho model education.student, và sau đó chúng ta sẽ tạo ra một action window mới để mở form view.

Các bước thực hiện

  1. Khai báo một treeform nhỏ cho education.student:

    <record id="education_student_view_tree" model="ir.ui.view">
       <field name="name">All students</field>
       <field name="model">education.student</field>
       <field name="arch" type="xml">
          <tree>
             <field name="name" />
          </tree>
       </field>
    </record>
    <record id="education_student_view_form" model="ir.ui.view">
       <field name="name">All students</field>
       <field name="model">education.student</field>
       <field name="arch" type="xml">
          <form>
             <group>
                <field name="name" />
             </group>
          </form>
       </field>
    </record>
    
  2. Cập nhật action từ mục Thêm một mục menu và window actions để sử dụng form view mới:

    <record id="education_student_action_tree" model="ir.actions.act_window.view">
       <field name="act_window_id" ref="education_student_action" />
       <field name="view_id" ref="education_student_view_tree" />
       <field name="view_mode">tree</field>
       <field name="sequence" eval="2"/>
    </record>
    <record id="education_student_action_form" model="ir.actions.act_window.view">
       <field name="act_window_id" ref="education_student_action" />
       <field name="view_id" ref="education_student_view_form" />
       <field name="view_mode">form</field>
       <field name="sequence" eval="2"/>
    </record>
    

Bây giờ, nếu bạn mở menu và click vào một student trong danh sách, bạn sẽ thấy form và tree nhỏ mà chúng ta vừa khai báo.

Cơ chế hoạt động

Lần này, chúng ta sử dụng XML code cho bất kì loại record nào,nghĩa là recordid và thuộc tính model bắt buộc. Id trên record là một chuỗi string tùy ý và phải là duy nhất. Thuộc tính model đề cập đến tên của model mà bạn muốn tạo.

ir.actions.act_window.view

Record thứ hai mà chúng ta khai báo hoạt động song song với act_window chúng ta đã khai báo từ trước trong Thêm một mục menu và window actions.

Warning

Sau khi bạn khai báo những bản ghi ir.actions.act_window.view, chúng được ưu tiên hơn những view_mode trong actions. Vì vậy, với các bản ghi trước đó bạn sẽ không thấy list mà chỉ thấy form. Bạn nên thêm một bản ghi ir.actions.act_window.view khác trỏ tới list view cho model education.student.

Thêm nội dung và tiện ích vào form view

Ở những mục trước đó giúp ta tạo ra một view cụ thể cho một action. Bây giờ chúng ta sẽ thực hiện làm cho form view trở lên tiện ích hơn. Ở bài này, chúng ta sẽ sử dụng form view mà đã được khai báo trước đó trong Thêm một mục menu và window actions. Trong form view, chúng ta sẽ thêm widgetscontent.

Các bước thực hiện

  1. Khai báo một form view đơn giản:

    <record id="education_student_view_form" model="ir.ui.view">
       <field name="name">All students</field>
       <field name="model">education.student</field>
       <field name="arch" type="xml">
          <form>
             <!--form content goes here -->
          </form>
       </field>
    </record>
    
  2. Thêm một header, nó thường được sử dụng cho action buttongiại đoạn, thêm bên trong form:

    <header>
       <button type="object" name="open_commercial_entity"
          string="Open commercial partner" class="btn-primary" />
    </header>
    
  3. Thêm một field tới form, sử dụng thẻ group để sắp xếp chúng một cách trực quan:

    <group string="Content" name="my_content">
       <field name="name" />
       <field name="category_id" widget="many2many_tags" />
    </group>
    

Bây giờ, Form sẽ hiển thị một thanh trên cùng với một nút và hai trường được căn chỉnh theo chiều dọc như được hiển thị trong ảnh chụp màn hình sau:

Adding content and widgets to a form view

Cơ chế hoạt động

Chúng ta sẽ xem xét trường arch đầu tiên của ir.ui.view model. Trước tiên, hãy lưu ý rằng views được định nghĩa trong XML, vì vậy bạn cần chuyển thuộc tính type="XML" cho trường arch, nếu không trình phân tích cú pháp sẽ bị nhầm lẫn. Bây giờ chúng ta sẽ đi qua các thẻ mà chúng ta đã sử dụng:

form

Khi bạn khai báo một Form view, bắt buộc phần tử đầu tiên trong trường arch phải là một phần tử Form. Ngoài các phần tử của odoo bạn cũng có thể sử dụng các thẻ HTML nhưng hãy cẩn thận khi sử dụng nó.

button

Button được sử dụng để người dùng có thể thực hiện một hành động. Tham khảo Thêm buttons vào forms.

group

Phần tử <group> là phần tử của Odoo và được sử dụng để tổ chức nội dung.Các trường được đặt trong phần tử <group> được hiển thị với tiêu đề của chúng và tất cả các trường trong phần tử cùng một nhóm được căn chỉnh để cũng có một chỉ báo trực quan rằng chúng thuộc về nhau.Bạn cũng có thể lồng các phần tử <group>, điều này khiến Odoo hiển thị các trường chứa trong các cột liền kề. Nói chung, bạn nên sử dụng cơ chế <group> để hiển thị tất cả các trường của mình trong dạng form view và chỉ quay về các phần tử khác, chẳng hạn như <notebook>, <label>, <newline>, v.v., khi cần thiết.

field

Để thực sự hiển thị và thao tác dữ liệu, dạng form view của bạn phải chứa một số phần tử field. Đây là một ví dụ:

<field name="category_id"
   attrs="{'readpnly': [('state', '='. 'done')]}"
   groups="base.group_no_one" />

notebook và page

Nếu model của bạn có quá nhiều fields, thì bạn có thể sử dụng thẻ <notebook> và <page> để tạo tab.Mỗi <page> trong thẻ <notebook> sẽ tạo một tab mới và nội dung bên trong trang sẽ là nội dung tab. Ví dụ sau sẽ tạo hai tab với ba trường trong mỗi tab:

<notebook>
   <page string="Tab 1">
      <field name="field1" />
      <field name="field2" />
      <field name="field3" />
   </page>
   <page string="Tab 2">
      <field name="field4" />
      <field name="field5" />
      <field name="field6" />
   </page>
</notebook>

Thuộc tính string trong thẻ <page> sẽ là tên của tab. Bạn chỉ có thể sử dụng thẻ <page> trong thẻ <notebook>, nhưng trong thẻ <page>, bạn có thể sử dụng bất kỳ phần tử nào khác.

General attributes

Trên hầu hết các phần tử (điều này bao gồm group, field và button), bạn có thể đặt thuộc tính attrs và thuộc tính groups . Đây là một ví dụ nhỏ:

<field name="category_id"
   attrs="{'readpnly': [('state', '='. 'done')]}"
   groups="base.group_no_one" />

Warning

Không được giải quyết các nút XML bằng thuộc tính chuỗi của chúng ( hoặc bất kì thuộc tính đã dịch nào khác, cho vấn đề đó), vì views ghi đè của bạn sẽ làm hỏng các views ở ngôn ngữ khác vì views được dịch trước khi áp dụng kế thừa.

Thêm buttons vào forms

Buttons được sử dụng trong form view để xử lý hành động của người dùng. Chúng ta đã thêm một button vào đoạn code ở những bài trước, nhưng có khá nhiều loại button để chúng ta có thể sử dụng, ở bài này, chúng ta sẽ thêm một button khác để người dùng có thể mở một view khác.

Các bước thực hiện

Thêm một button đề cập tới một action:

<button type="action"
   name="%(base.action_partner_category_form)d"
   string="Open partner categories" />

Cơ chế hoạt động

Thuộc tính type của button xác định ngữ nghĩa của các trường khác,vì vậy trước tiên chúng ta sẽ xem xét các giá trị có thể có:

  • action: làm cho button gọi một hành động được khai báo trong không gian của ir.actions.*. Thuộc tính name cần phải chứa ID cơ sở dữ liệu của hành động, bạn có thể thuận tiện để Odoo tra cứu bằng chuỗi định dạng Python có chứa ID XML của hành động được đề cập.

  • object: dùng để gọi một phương thức trong model hiện tại. Thuộc tính name chứa tên của function.

  • string: thuộc tính này dùng để gán văn bản mà người dùng nhìn thấy.

Note

Lưu ý rằng việc nhấp vào một button luôn khiến máy khách thực hiện lệnh write hoặc create trước khi chạy phương thức.

Truyền tham số cho forms và action - context

Mọi phương thức của odoo đều có quyền truy cập vào một dicnationary, được gọi là context, được truyền từ mọi hành động đến các phương thức liên quan trong việc thực hiện hành động đó. Giao diện người dùng cũng có quyền truy cập vào nó và nó có thể được sửa đổi theo nhiều cách khác nhau bằng cách đặt các giá trị trong context.

Chuẩn bị

Mặc dù không quá cần thiết nhưng công thức này sẽ thú vị hơn nếu bạn cài đặt tiếng Pháp, nếu bạn đã cài đặt tiếng Pháp, hãy thay đổi sang ngôn ngữ khác. Ngoài ra, hãy nhấn vào nút kích hoạt (thay đổi thành lưu trữ) cho một trong những khách hàng của bạn để lưu trữ nó và xác minh rằng đối tác này không hiển thị nữa trong danh sách.

Các bước thực hiện

  1. Tạo một action mới, giống với một action từ Thêm một mục menu và window actions:

    <act_window id="education_student_action_fr"
       name="Tous les clients" res_model="education.student"
       domain="[('student_score', '<', 1)]"
       context="{'lang': 'fr_FR', 'default_lang': 'fr_FR',
                      'active_test': False, 'default_student_score':1}" />
    
  2. Thêm menu gọi action này. Đây là một bài tập cho người đọc.

    Khi bạn mở menu này, các views sẽ hiển thị bằng tiếng Pháp và nếu bạn tạo đối tác mới, họ sẽ có tiếng Pháp làm ngôn ngữ được chọn trước. Một sự khác biệt ít rõ ràng hơn là bạn cũng sẽ thấy các bản ghi đối tác đã hủy kích hoạt (đã lưu trữ).

Cơ chế hoạt động

Từ điển ngữ cảnh được điền từ một số nguồn. Đầu tiên, một số giá trị từ bản ghi của người dùng hiện tại (lang và tz, cho ngôn ngữ của người dùng và múi giờ của người dùng) là đọc. Sau đó, chúng ta có một số tiện ích bổ sung thêm khóa cho các mục đích riêng của chúng. Hơn nữa, giao diện người dùng thêm các khóa về model nào và bản ghi nào mà chúng ta đang bận rộn vào lúc này (active_id, active_ids, active_model). Ngoài ra, như đã thấy trong phần Có một hành động sẽ mở ra một view cụ thể, chúng ta có thể thêm các khóa của riêng mình trong actions. Chúng được hợp nhất với nhau và được chuyển đến các chức năng máy chủ cơ bản và cả giao diện người dùng phía máy khách.

Vì vậy, bằng cách đặt khóa ngữ cảnh lang, chúng ta buộc ngôn ngữ hiển thị phải là tiếng Pháp. Bạn sẽ lưu ý rằng điều này không thay đổi toàn bộ ngôn ngữ giao diện người dùng, vì chỉ giao diện danh sách mà chúng tôi mở nằm trong phạm vi của bối cảnh này. Phần còn lại của giao diện người dùng đã được tải với một ngữ cảnh khác có chứa ngôn ngữ gốc của người dùng. Tuy nhiên, nếu bạn mở một bản ghi trong giao diện danh sách này, nó cũng sẽ được trình bày bằng tiếng Pháp và nếu bạn mở một bản ghi được liên kết trên biểu mẫu hoặc nhấn một nút để thực hiện một hành động, ngôn ngữ cũng sẽ được truyền bá.

Bằng cách đặt default_lang, chúng tôi đặt giá trị mặc định cho mọi bản ghi được tạo trong phạm vi của context này. Mẫu chung là default_ $ fieldname: my_default_value, cho phép bạn đặt các giá trị mặc định cho các học sinh mới được tạo, trong trường hợp này. Vì menu của chúng ta về học sinh, vì vậy chúng ta đã thêm default_student_score: 1 làm giá trị cho trường student_score theo mặc định.

Note

Lưu ý rằng các giá trị mặc định được đặt trong ngữ cảnh sẽ ghi đè các giá trị mặc định được đặt trong định nghĩa model, vì vậy bạn có thể có các giá trị mặc định khác nhau trong các trường hợp khác nhau.

Khóa cuối cùng là active_test, có ngữ nghĩa rất đặc biệt. Đối với mọi model có trường được gọi active, Odoo sẽ tự động lọc ra các bản ghi trong đó active = false. Đây là lý do tại sao học sinh mà bạn bỏ chọn trường này biến mất khỏi danh sách. Đặt khóa này, chúng ta có thể ngăn chặn hành vi này.

Warning

Điều này hữu ích cho giao diện người dùng theo đúng nghĩa của nó nhưng thậm chí còn hữu ích hơn trong code Python của bạn khi bạn cần đảm bảo rằng một hoạt động được áp dụng cho tất cả các bản ghi, không chỉ các bản ghi đang hoạt động.

Tip

Hầu hết các domains được đánh giá ở phía máy khách. Đánh giá server-side domain bị hạn chế vì lý do bảo mật. Khi đánh giá phía khách hàng được giới thiệu, lựa chọn tốt nhất để không phá vỡ toàn bộ hệ thống là triển khai một phần của Python trong JavaScript. Có một trình thông dịch Python JavaScript nhỏ được tích hợp trong Odoo hoạt động tốt cho các biểu thức đơn giản và thường là đủ. Cẩn thận với việc sử dụng giá trị context trong <act_window />. Chúng được đánh giá tại thời điểm cài đặt, gần như không bao giờ là những gì bạn muốn. Nếu bạn cần các biến trong context của mình, hãy sử dụng cú pháp <record />.

Xác định bộ lọc trên danh sách bản ghi - domain

Chúng ta đã thấy một ví dụ về domain trong đoạn code đầu tiên của phần này đó là [('student_score', '>', 0)]. Thông thường, bạn cần hiển thị một tập hợp con của tất cả các bản ghi có sẵn từ một hành động hoặc chỉ cho phép một tập hợp con các bản ghi có thể trở thành mục tiêu của một mối quan hệ many2one. Để tạo ra các filter trong Odoo, chúng ta sẽ sử dụng domain. Đoạn code dưới đây sẽ minh họa cách sử dụng domain để lọc học sinh.

Các bước thực hiện

Để hiển thị một tập hợp con các học sinh từ hành động của bạn, bạn cần thực hiện các bước sau:

  1. Thêm hành động cho học sinh không nói tiếng Pháp:

    <record id="education_student_action_my_students"
       model="ir.actions.act_window">
       <field name="name">All my customers who don't speak French</field>
       <field name="res_model">education.student</field>
       <field name="domain">
          [('type', '=', 'contact'), ('user_id', '=', uid),
          ('lang', '!=', 'fr_FR')]
       </field>
    </record>
    
  2. Thêm hành động cho học sinh không có số điện thoại hoặc không có email:

    <record id="education_student_action_no_email_or_phone"
       model="ir.actions.act_window">
       <field name="name">students with no email or phone</field>
       <field name="res_model">education.student</field>
       <field name="domain">['|', ('phone', '=', False), ('email', '=',False)]</field>
    </record>
    
  3. Thêm một menu gọi tới action đó. Đây là một bài tập cho người đọc.

Cơ chế hoạt động

Domain đơn giản nhất là một list tuple chứa tên trường của model được đề cập dưới dạng string trong phần tử đầu tiên, một toán tử dưới dạng string trong phần tử thứ 2. và giá trị mà trường sẽ được kiểm tra với tư cách là phần tử thứ ba. Đây là những gì chúng ta đã làm trước đây và điều này được hiểu là “Tất cả những điều kiện phải áp dụng cho các hồ sơ mà chúng ta quan tâm.” Đây thực sự là một phím tắt, vì các domains có hai tiền tố toán tử - & và | - ở đâu & là mặc định. Vì vậy, ở dạng chuẩn hóa, domain đầu tiên sẽ được viết như sau:

['&', '&', ('type', '=', 'contact'), ('user_id', '=', uid), ('lang', '!=', 'fr_FR')]

Mặc dù chúng có thể hơi khó đọc đối với các biểu thức lớn hơn, nhưng lợi thế của các toán tử tiền tố là phạm vi của chúng được xác định chặt chẽ, điều này giúp bạn không phải lo lắng về mức độ ưu tiên của toán tử và dấu ngoặc. Nó luôn có hai biểu thức: người đầu tiên & áp dụng thành ‘&’, (‘type’, ‘=’, ‘contact’), với (‘user_id’, ‘=’, uid) là đầu tiên toán hạng và (‘lang’, ‘! =’, ‘fr_FR’) là toán hạng thứ hai.Sau đó, thứ hai & áp dụng cho (‘customer’, ‘=’, True) làm toán hạng đầu tiên và (‘user_id’, ‘=’, uid) như thú hai.

Trong bước thứ hai, chúng ta phải viết ra biểu mẫu đầy đủ vì chúng ta cần dấu | operator.

Ví dụ: giả sử chúng ta có một miền phức tạp như sau: ['|', ('user_id', '=', uid), '&', ('lang', '! =', 'fr_FR'), '|', ('phone', '=', False), ('email', '=', False)]. Xem hình sau để tìm hiểu về cách domain này được đánh giá:

Defining filters on record lists – domain

Cũng có một ! toán tử để phủ định, nhưng các toán tử so sánh tương đương đã cho và các toán tử so sánh bị phủ định như != và not in, nó không thực sự cần thiết.

Note

Lưu ý rằng đây là toán tử tiền tố một ngôi, vì vậy nó chỉ áp dụng cho biểu thức sau trong domain chứ không áp dụng cho mọi thứ theo sau.

Còn nữa…

Operators

Defining filters on record lists – domain

Warning

Mọi người thường quên rằng họ đang viết các tệp XML khi nói đến tên domains.Bạn cần phải tránh toán tử nhỏ hơn. Tìm kiếm các bản ghi đã được tạo trước ngày hiện tại sẽ phải được viết là [('create_date', '&lt;', current_date)] trong XML

Xác định list views

Trong bài này, chúng ta sẽ tiếp cận cách để khai báo ra một list views. Trong code thì chúng ta sẽ gọi nó là tree views, còn bên ngoài views người dùng thì sẽ là list.

Các bước thực hiện

  1. Tạo một list view:

    <record id="education_student_view_tree_all_contacts"
       model="ir.ui.view">
       <field name="model">education.student</field>
       <field name="arch" type="xml">
          <tree>
             <field name="name" />
             <field name="state_id" optional="hide" />
             <field name="country_id" optional="show" />
             <field name="student_score" invisible="1" />
          </tree>
       </field>
    </record>
    
  2. Thêm một tree view trong action mà chúng ta đã tạo trong Thêm một mục menu và window actions của phần này:

    <record id='education_student_action'
       model='ir.actions.act_window'>
       <field name="name">All Students</field>
       <field name="res_model">education.student</field>
       <field name="view_mode">tree,form</field>
       <field name="context">
          {'tree_view_ref': 'viin_education.education_student_view_tree'}
       </field>
       <field name="limit">20</field>
    </record>
    
  3. Thêm menu gọi các hành động này. Đây là một bài tập cho người đọc.

    Cài đặt / Nâng cấp mô-đun. Sau đó, bạn sẽ thấy giao diện tree của chúng ta cho học sinh. Và nếu bạn kiểm tra nó, nó sẽ hiển thị các kiểu khác nhau dựa trên các điều kiện mà ta vừa code.

Cơ chế hoạt động

Như bạn thấy trong quá trình thực hiện, lần này, chúng ta khai báo một view thuộc kiểu tree và đính kèm nó vào hành động của chúng ta bằng một phần tử ir.actions.act_window.view. Đối với một list, bạn không có nhiều sự lựa chọn thiết kế cho nó, vì vậy phần tử con hợp lệ duy nhất của nó là field và button.

Bằng cách sử dụng attribute bạn có thể hiển thị các trường theo tùy ý của bạn. Thêm attribute vào một field sẽ cho phép người dùng ẩn và hiện cột bất kì trên giao diện người dùng. Trong ví dụ của chúng ta, chúng ta đã cho ẩn trường state_id,`student_score` và hiện trường country_id.

Warning

Ordering thường là một nguồn gây thất vọng cho các developers mới. Vì Odoo cho phép PostgreSQL thực hiện công việc ở đây, bạn chỉ có thể sắp xếp theo các trường mà PostgreSQL biết và chỉ các trường nằm trong cùng một bảng cơ sở dữ liệu. Vì vậy nếu bạn muốn sắp xếp bằng function hoặc relate field, hãy chắc chắn store của field = true. Nếu bạn cần sắp xếp một trường kế thừa từ model khác, hãy khai báo một trường relate store.

Xác định search views

Khi mở chế độ list view, Bạn sẽ thấy thanh tìm kiếm ở phía bên phải. Nếu bạn nhập nội dung nào đó vào đó, bạn sẽ nhận được những gợi ý về thứ bạn muốn tiềm kiếm và cũng có một tập hợp bộ lọc được xác định trước để lựa chọn. Bài này sẽ giúp bạn tạo ra những đề xuất và tùy chọn cho search.

Các bước thực hiện

  1. Khai báo search view:

    <record id="education_student_view_search" model="ir.ui.view">
       <field name="model">education.student</field>
       <field name="arch" type="xml">
          <search>
             <field name="name" />
             <field name="class_id" widget="many2one" />
             <filter name="ftr_student_score" string="score"
                domain="[('student_score', '>', 0)]" />
             <group expand="0" string="Group By">
                <filter string="Country" name="ftr_country"
                   context="{'group_by':'country_id'}" />
             </group>
          </search>
       </field>
    </record>
    
  2. Gọi action để sử dụng nó:

    <record id="education_student_action" model="ir.actions.act_window">
       <field name="name">All students</field>
       <field name="res_model">education.student</field>
       <field name="view_mode">tree,form</field>
       <field name="search_view_id" ref="s" />
    </record>
    

Bây giờ, khi nhập nội dung gì đó vào thanh tìm kiếm, bạn sẽ được cung cấp khả năng tìm kiếm cho thuật ngữ này trong các trường name, student_socre, class_id, country.

Cơ chế hoạt động

trong trường hợp name, chúng ta chỉ liệt kê trường là trường được cung cấp cho người dùng để tìm kiếm đến trường many2many, name_search, sẽ là một substring search trong tên class trong trường hợp này. thuộc tính filter_domain có thể chứa một domain tùy ý, vì vậy bạn không được phép tìm kiếm trường giống với trường mà bạn đã đặt tên hoặc chỉ sử dụng một thuật ngữ. Biến self là những gì người dùng đã điền vào và cũng là biến duy nhất mà bạn có thể sử dụng ở đây.

Dưới đây là một ví dụ phức tạp hơn từ giao diện tìm kiếm mặc định dành cho học sinh:

<field name="name" filter_domain="['|', '|', ('display_name', 'ilike', self), ('ref', '=', self), ('email', 'ilike', self)]"/>

Điều này có nghĩa là người dùng không phải suy nghĩ về những gì cần tìm kiếm. Tất cả những gì họ cần làm là nhập một số chữ cái, nhấn Enter, và với một chút may mắn, một trong những trường được đề cập có chứa chuỗi mà chúng ta đang tìm kiếm.

Tip

Lưu ý rằng mọi trường có bộ widget many2one sẽ kích hoạt tìm kiếm trên model của nó cho mỗi lần nhấn phím của người dùng; không sử dụng chúng quá nhiều.

Bạn cũng nên đặt các trường được sử dụng nhiều nhất ở trên cùng, vì trường đầu tiên là trường được tìm kiếm nếu người dùng chỉ cần nhập nội dung nào đó và nhấn Enter. Thanh tìm kiếm cũng có thể được sử dụng với bàn phím; chọn một gợi ý bằng cách nhấn vào mũi tên xuống và mở gợi ý hoàn thành của many2one bằng cách nhấn vào mũi tên phải. điều này sẽ hiệu quả hơn nhiều so với việc nhập nội dung nào đó trước, lấy chuột và chọn một lựa chọn.

Còn nữa…

Bạn có thể nhóm các bộ lọc bằng group tag, điều này khiến chúng được hiển thị gần nhau hơn một chút so với các bộ lọc khác, nhưng điều này cũng có ý nghĩa. Nếu bạn đặt nhiều bộ lọc trong cùng một nhóm và kích hoạt nhiều bộ lọc trong số chúng, tên miền của chúng sẽ được kết hợp với nha thông qua toán tử | , trong khi các bộ lọc và trường không cùng group được kết hợp với nhau bằng toán tử &.

Tip

Lưu ý rằng nếu người dùng điền nhiều truy vấn cho cùng một trường, chúng sẽ được kết hợp, vì vậy bạn không cần phải lo lắng về điều đó.

Thêm bảng điều khiển bên bộ lọc tìm kiếm

Odoo cung cấp thêm một cách để hiển thị bộ lọc tìm kiếm, đó là search filter side panel tiếng việt là bảng điều khiển bên bộ lọc tìm kiếm. Bảng điều khiển này hiển thị danh sách các bộ lọc ở bên cạnh view Bảng điều khiển tìm kiếm rất hữu ích khi bộ lọc tìm kiếm được người dùng sử dụng thường xuyên.

Chuẩn bị

search panel là một phần của search view. Vì vậy, đối với bài này, chúng ta sẽ tiếp tục sử dụng module viin_education từ bài trước. Chúng ta sẽ thêm search panel vào search view được thiết kế trước đó.

Các bước thực hiện

Thêm <searchpanel> trong search view:

<record id="education_student_view_search" model="ir.ui.view">
   <field name="model">education.student</field>
   <field name="arch" type="xml">
      <search>
         <field name="name" />
         <field name="category_id"
            filter_domain="[('category_id', 'child_of', self)]" />
         <filter name="student_score" string="student_score"
            domain="[('student_score', '>', 0)]" />
         <group expand="0" string="Group By">
            <filter string="Country" name="ftr_country"
               context="{'group_by':'country_id'}" />
         </group>
         <!-- Search Panel code -->
         <searchpanel>
            <field name="user_id" icon="fa fa-users" />
            <field name="category_id" icon="fa fa-list" select="multi" />
         </searchpanel>
      </search>
   </field>
</record>

Sau khi cập nhật lại module, bạn sẽ thấy search panel ở bên trái của view.

Cơ chế hoạt động

Để thêm search panel, bạn sẽ cần sử dụng thẻ <searchpanel> trong search view. Để thêm bộ lọc của bạn, bạn sẽ cần thêm một trường trong search panel. Trong ví dụ của chúng ta, đầu tiên chúng ta thêm trường user_id. Bạn cũng cần thêm thuộc icon vào trường. icon này sẽ được hiển thị trước tiêu đề của bộ lọc. Khi bạn thêm trường vào bảng điều khiển tìm kiếm, trường đó sẽ hiển thị tiêu đề có icon và bên dưới là danh sách tất cả người dùng. Khi nhấp vào một người dùng, các bản ghi trong list view sẽ được lọc và bạn sẽ chỉ thấy các liên hệ của người dùng đã chọn.Trong bộ lọc này, chỉ một mục có thể hoạt động, nghĩa là khi bạn nhấp vào bộ lọc của người dùng khác, bộ lọc của người dùng trước đó sẽ bị xóa. Nếu bạn muốn kích hoạt bộ lọc nhiều người dùng, bạn có thể sử dụng thuộc tính select = "multi". Nếu bạn sử dụng thuộc tính đó, bạn sẽ tìm thấy checkbox cho mỗi tùy chọn bộ lọc và bạn sẽ có thể kích hoạt nhiều bộ lọc cùng một lúc. Ở đoạn code trên, chúng ta đã sử dụng thuộc tính select = "multi" trên bộ lọc category_id. Điều này sẽ cho phép chọn và lọc theo nhiều danh mục cùng một lúc.

Note

Hãy cẩn thận khi bạn sử dụng bộ lọc bảng điều khiển trên many2one hoặc many2many. Nếu mô hình quan hệ có quá nhiều bản ghi, chỉ 200 bản ghi hàng đầu sẽ được hiển thị, để tránh các vấn đề về hiệu suất.

Còn nữa…

Nếu bạn muốn hiển thị các mục trong bảng tìm kiếm theo nhóm, bạn có thể sử dụng thuộc tính groupby trên một trường. Ví dụ: nếu bạn muốn nhóm một category dựa trên phân cấp chính của nó, bạn có thể thêm thuộc tính groupby với trường parent_id, như sau:

<field name="category_id" icon="fa fa-list" select="multi" groupby="parent_id"/>

Đoạn code này sẽ hiển thị các bộ lọc category được nhóm theo danh mục chính của bản ghi.

Thay đổi views hiện có - views kế thừa

Bạn sẽ muốn sửa đổi một chút các chế độ views hiện có, Bài này sẽ hướng dẫn bạn làm thế nào để thực hiện điều đó.

Các bước thực hiện

  1. Chèn trường vào form view mặc định:

    <record id="education_student_view_form" model="ir.ui.view">
       <field name="model">education.student</field>
       <field name="inherit_id"
          ref="viin_education.education_student_view_form" />
       <field name="arch" type="xml">
          <field name="website" position="after">
             <field name="write_date" />
          </field>
       </field>
    </record>
    
  2. Thêm trường vào search view mặc định:

    <record id="education_student_view_filter" model="ir.ui.view">
       <field name="model">education.student</field>
       <field name="inherit_id"
          ref="viin_education.education_student_view_filter" />
       <field name="arch" type="xml">
          <xpath expr="." position="inside">
             <field name="phone_number" />
          </xpath>
       </field>
    </record>
    
  3. Thêm trường vào list view mặc định:

    <record id="education_student_view_tree" model="ir.ui.view">
       <field name="model">education.student</field>
       <field name="inherit_id"
          ref="viin_education.education_student_view_tree" />
       <field name="arch" type="xml">
          <field name="email" position="after">
             <field name="phone_number" position="move" />
          </field>
       </field>
    </record>
    

Sau khi cập nhật mô-đun của mình, khi bạn nhập nội dung nào đó vào search box, điều đó sẽ gợi ý bạn nên tìm kiếm học sinh trên trường số điện thoại và trong list view học sinh. Bạn sẽ thấy số điện thoại và email đã thay đổi.

Cơ chế hoạt động

bước 1, chúng ta đã thêm một cấu trúc cơ bản cho kế thừa form. Trường quan trọng ở đây là inherit_id. Bạn cần chuyền cho nó một XML ID của view mà bạn muốn kế thừa. Trường arch chứa các hướng dẫn về cách sửa đổi các nút XML hiện có trong dạng xem mà bạn đang thừa kế từ view cha.

Bước 2 thể hiện một cách tiếp cận khác. Phần tử xpath chọn phần tử đầu tiên phù hợp với biểu thức XPath có tên trong thuộc tính expr. Ở đây, thuộc tính position cho bộ xử lý biết nơi đặt nội dung của phần tử xpath.

Note

Nếu bạn muốn tạo một biểu thức XPath dựa trên một lớp CSS, Odoo cung cấp một hàm đặc biệt gọi là hasclass. Ví dụ: nếu bạn muốn chọn một phần tử <div> với lớp CSS test_class, thì biểu thức sẽ là expr = "// div [hasclass ('test_class')]".

Bước 3 cho biết cách bạn có thể thay đổi vị trí của một phần tử.Tùy chọn này đã được giới thiệu trong phiên bản 12 và nó hiếm khi được sử dụng. Trong ví dụ của chúng ta, ta đã chuyển trường phone_number đến sau trường email với tùy chọn position = move. Trong odoo, xpath là thuộc tính quan trọng trong việc kế thừa một view, nó giúp ta tìm tới nút xml cần chỉnh sửa.

Tip

Lưu ý rằng bạn có thể có nhiều phần tử hướng dẫn trong trường arch tùy theo ý bạn. Chúng ta chỉ sử dụng một cho mỗi view được kế thừa vì hiện tại không muốn thay đổi gì khác.

Còn nữa….

Thuộc tính position có 2 giá trị khả dụng khác nhau đó là replace và attributes. Việc sử dụng replace khiến phần tử đã chọn được thay thế bằng nội dung của phần tử khác. Do đó, nếu bạn không có bất kỳ nội dung nào, bạn có thể đơn giản xóa phần tử đã chọn. Ví dụ minh họa dưới đây sẽ làm cho trường email bị loại bỏ khỏi form hoặc list view:

<field name="email" position="replace" />

Warning

Việc xóa các trường có thể khiến các views kế thừa khác bị phá vỡ và một số tác dụng phụ không mong muốn khác, vì vậy hãy tránh điều đó nếu có thể.

Nếu bạn vấn muốn làm mất trường đó đi mà không sợ bị làm ảnh hưởng tới views mà mình kế thừa thì chúng ta có thuộc tính invisible. Ví dụ dưới đây sẽ giúp ta ẩn đi trường email mà không ảnh hưởng tới những thứ khác:

<field name="email" position="attributes">
   <attribute name="invisible">1</attribute>
</field>

Một nút attributes có thể có các thuộc tính thêm và bớt, các thuộc tính này phải chứa giá trị được xóa hoặc thêm vào danh sách được phân tách bằng dấu cách. Điều này rất hữu ích cho thuộc tính class, nơi bạn sẽ thêm một class (thay vì ghi đè toàn bộ thuộc tính) bằng cách sử dụng như sau:

<field name="email" position="attributes">
   <attribute name="class" add="oe_inline" separator=" "/>
</field>

đoạn code này thêm class oe_inline vào trường email. Nếu trường đã có thuộc tính class, Odoo sẽ nối giá trị với giá trị của thuộc tính bằng dấu phân cách (separator).

Thứ tự đánh giá trong views kế thừa

Vì hiện tại chúng ta chỉ có một view gốc và một view kế thừa, nên chúng ta không gặp phải bất kỳ sự cố nào với việc ghi đè view xung đột. Khi bạn đã cài đặt một vài mô-đun, bạn sẽ tìm thấy nhiều views ghi đè lên form của student. Điều này là tốt miễn là thay đổi những thuộc tính khác nhau trong view, nhưng có những trường hợp chúng ta sẽ gặp xung đột khi ghi đè. Vì vậy chúng ta lên để ý thứ tự ưu tiên (priority) của nó khi ghi đè để tránh việc xung đột.

Note

Loại kế thừa này luôn hoạt động trên XML tree hoàn chỉnh từ view ban đầu, với các sửa đổi từ các views kế thừa trước đó được áp dụng.

Xác định forms kiểu tài liệu

Trong bài này, chúng ta sẽ xem xét một số nguyên tắc thiết kế để mang lại trải nghiệm người dùng đồng nhất.

Các bước thực hiện

Bắt đầu biểu mẫu của bạn với một phần tử tiêu đề:

  1. Bắt đầu form của bạn với một phần tử header :

    <header>
       <button type="object" name="open_commercial_entity"
          string="Open commercial partner" class="btn-primary" />
    </header>
    
  2. thêm một sheet cho content:

    <sheet>
    
  3. Đặt vào nút thống kê, nút này sẽ được sử dụng để hiển thị tổng số điểm của học sinh và chuyển hướng đến các học sinh:

    <div class="oe_button_box" name="button_box">
       <button type="object" class="oe_stat_button"
          icon="fa-pencil-square-o" name="action_view_student_score">
          <div class="o_form_field o_stat_info">
             <span class="o_stat_value">
                <field name="total_score" />
             </span>
             <span class="o_stat_text">Score</span>
          </div>
       </button>
    </div>
    
  4. Thêm một số trường nổi bật:

    <div class="oe_left oe_title">
       <label for="name" />
       <h1>
          <field name="name" />
       </h1>
    </div>
    
  5. Thêm nội dung của bạn, bạn có thể sử dụng notebook nếu có nhiều trường:

    <group>
       <field name="category_id" widget="many2many_tags" />
       <field name="email" />
       <field name="phone" />
    </group>
    
  6. sau sheet, thêm chatter widget (nếu có):

    </sheet>
    <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>
    

Hãy cùng xem đoạn code này hoạt động như thế nào nhé.

Cơ chế hoạt động

header phải chứa các button thực thi các action trên object mà người dùng hiện đang nhìn thấy. Sử dụng class btn-primary để làm cho các button nổi bật về mặt trực quan (có màu tím tại thời điểm viết bài), đây là một cách tốt để hướng dẫn người dùng về hành động hợp lý nhất để cải thiện trải nghiệm người dùng. Nếu model có state, hãy hiển thị nó trong header bằng tiện ích thanh trạng thái (statusbar widget). Nó sẽ được hiển thị như được căn phải trong header.

Phần tử sheet được hiển thị dưới dạng sheet cách điệu và các trường quan trọng nhất phải là thứ đầu tiên người dùng nhìn thấy khi nhìn vào nó. Sử dụng các class oe_titleoe_left để hiển thị chúng ở vị trí nổi bật (nổi bên trái với phông chữ được điều chỉnh một chút kích thước).

Nếu có các bản ghi khác liên quan tới bản ghi hiện tại mà người dùng đang thấy (chẳng hạn như điểm số của học sinh trên form học sinh), hãy đặt chúng vào một phần tử với các class oe_right và oe_button_box; điều này căn chỉnh các nút trong đó sang bên phải. Trên chính các buttons, sử dụng class oe_stat_button để thực thi hiển thị đồng nhất các buttons. Cũng có thể chỉ định một icon class từ Font Awesome cho thuộc tính icon. Bạn có thể tìm hiểu thêm về Font Awesome tại https://fontawesome.com/v4.7.0/icons/. .

Note

Ngay cả khi bạn không thích layout này, hãy giữ nguyên tên phần tử và class được mô tả ở đây, đồng thời điều chỉnh những gì bạn cần bằng CSS và có thể là JavaScript. Điều này sẽ làm cho giao diện người dùng tương thích hơn và tăng giá trị trải nghiệm người dùng.

Các phần tử forms động sử dụng tập tin

Cho đến nay, chúng ta mới chỉ thay đổ các form tùy thuộc vào các nhóm của người dùng (thuộc tính nhóm trên các phần tử và trường groups_id trên các dạng xem kế thừa) và không có gì hơn. Bài này sẽ chỉ cho bạn cách sửa đổi form view dựa trên giá trị của các trường trong đó.

Các bước thực hiện

  1. Xác định một thuộc tính được gọi là attrs trên form:

    <field name="parent_id"
       attrs="{
             'invisible': [('student_score', '>', 5)],
             'required': [('student_score', '<', 5)]
             }" />
    
  2. Đảm bảo rằng tất cả các trường bạn tham chiếu đều có sẵn trong form của bạn:

    <field name="student_score"/>
    

Điều này sẽ làm cho trường parent_id ẩn nếu học sinh có điểm trên 5 và required nếu học sinh có điểm dưới 5.

Cơ chế hoạt động

Thuộc tính attrs có gồm một số từ khóa invisible , required , and readonly (tất cả đều là tùy chọn). Các giá trị là các domains có thể tham chiếu đến các trường tồn tại trên form.

Xác định views được nhúng

Khi bạn hiển thị trường one2many hoặc many2many trên form, bạn không có nhiều quyền kiểm soát cách hiển thị nó nếu bạn chưa sử dụng một trong các widgets chuyên dụng. Ngoài ra, trong trường hợp của các trường many2one, đôi khi mong muốn có thể ảnh hưởng đến cách mở bản ghi được liên kết. Trong bài này, chúng ta sẽ xem xét cách khai báo private views cho các trường đó.

Các bước thực hiện

  1. khai báo trường của bạn như bình thường, nhưng không đóng thẻ:

    <field name="child_ids">
    
  2. Viết các kiểu view vào tag:

    <tree>
       <field name="name" />
       <field name="email" />
       <field name="phone" />
    </tree>
    <form>
       <group>
          <field name="name" />
          <field name="function" />
       </group>
    </form>
    
  3. đóng tag:

    </field>
    

Cơ chế hoạt động

Khi Odoo tải một form view, trước tiên nó sẽ kiểm tra xem các trường kiểu quan hệ có các dạng xem được nhúng trong trường hay không.

Còn nữa…

Mặc dù views được nhúng có vẻ như là một tính năng tuyệt vời, nhưng chúng lại làm phức tạp thêm rất nhiều việc kế thừa views.

Hiển thị tệp đính kèm ở bên cạnh form views

Trong một số ứng dụng, chẳng hạn như lập hóa đơn, bạn cần điền vào dữ liệu dựa trên một tài liệu. Để dễ dàng quá trình điền dữ liệu, một tính năng mới đã được thêm vào Odoo phiên bản 12 để hiển thị tài liệu ở một bên của form view.

Trong bài này, chúng ta sẽ học cách hiển thị dạng form view và tài liệu cạnh nhau:

Displaying attachments on the side of the form view

Note

Tính năng này chỉ dành cho màn hình lớn (> 1534px), vì vậy nếu bạn có giao diện nhỏ, tính năng này sẽ bị ẩn.

Các bước thực hiện

Chúng ta sẽ kích hoạt tính năng này để sửa đổi form view cho model education.student:

<record id="eudcation_student_view_form" model="ir.ui.view">
   <field name="name">All students</field>
   <field name="model">education.student</field>
   <field name="arch" type="xml">
      <form>
         <sheet>
            <group>
               <field name="name" />
               <field name="email" />
            </group>
         </sheet>
         <div class="o_attachment_preview"
            options="{types: ['image', 'pdf'], 'order': 'desc'}" />
         <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>

Cập nhật module để áp dụng các thay đổi. Bạn cần tải lên tệp PDF hoặc hình ảnh qua chatter. Khi bạn tải nó lên, Odoo sẽ hiển thị tệp đính kèm ở bên cạnh.

Cơ chế hoạt động

Tính năng này chỉ hoạt động nếu mô hình của bạn đã kế thừa model mail.thread. Để hiển thị tài liệu ở bên cạnh bất kỳ form view nào, bạn sẽ cần thêm một <div> trống với class o_attachment_preview trước các phần tử chatter. Vậy là xong - các tài liệu đính kèm trong cuộc trò chuyện sẽ được hiển thị ở một bên của form view.

Mặc định, tài liệu pdf và hình ảnh sẽ được hiển thị theo thứ tự tăng dần theo ngày. Bạn có thể thay đổi hành vi này bằng cách cung cấp các tùy chọn bổ sung, bao gồm các tùy chọn sau:

  • type: Bạn cần chuyển danh sách các loại tài liệu mà bạn muốn cho phép. Chỉ có thể có hai giá trị: pdf và image. Ví dụ: nếu bạn chỉ muốn hiển thị hình ảnh loại pdf, bạn có thể chuyển {'type': ['pdf']}.

  • order: Các giá trị có thể là asc và desc. Những điều này cho phép bạn hiển thị tài liệu theo thứ tự tăng dần hoặc giảm dần của ngày tạo tài liệu.

Xác định kanban views

Trong bài này chúng ta sẽ tiếp tục tìm hiểu về một kiểu view mới đó là Kanban view, một view có cách hiển thị hay và bớt nhàm chán hơn các dạng view dữ liệu khác.

Các bước thực hiện

  1. Khai báo một view của kiểu kanban:

    <record id="education_student_view_kanban" model="ir.ui.view">
       <field name="model">education.student</field>
       <field name="arch" type="xml">
          <kanban>
    
  2. Liệt kê các trường bạn sẽ sử dụng trong giao diện của mình:

    <field name="name" />
    <field name="supplier_rank" />
    <field name="customer_rank" />
    
  3. Thực hiện một thiết kế:

    <templates>
       <t t-name="kanban-box">
          <div class="oe_kanban_card">
             <a type="open">
                <field name="name" />
             </a>
             <t
                t-if="record.supplier_rank.raw_value or record.customer_rank.raw_value">
                is
                <t t-if="record.customer_rank.raw_value">
                   a customer
                </t>
                <t
                   t-if="record.customer_rank.raw_value and record.supplier_rank.raw_value">
                   and
                </t>
                <t t-if="record.supplier_rank.raw_value">
                   a supplier
                </t>
             </t>
          </div>
       </t>
    </templates>
    
  4. Đóng toàn bộ tags:

          </kanban>
       </field>
    </record>
    
  5. Thêm view này vào action của bạn. Đây sẽ là bài tập thực hành dành cho người đọc. Bạn có thể tìm thấy toàn bộ ví dụ ở link Github này : https://github.com/Viindoo/education/tree/14.0/viin_education.

Cơ chế hoạt động

Chúng ta cần cung cấp danh sách các trường cần tải ở bước 2 để có thể truy cập chúng sau này. Nội dung của phần tử templates phải là một phần tử t duy nhất với thuộc tính t-name được đặt thành kanban-box.

Có một số sửa đổi dành riêng cho kanban views. Bạn có quyền truy cập vào các biến read_only_mode, recordwidget trong quá trình đánh giá. Fields có thể được truy cập bằng cách sử dụng record.fieldname.

Note

Trường many2many tạo một ngoại lệ ở đây. Bạn sẽ chỉ nhận được danh sách ID thông qua biến bản ghi. Đối với người dùng có thể đọc được, bạn phải sử dụng phần tử field.

Còn nữa…

Có một vài chức năng trợ giúp đáng nói. Nếu bạn cần tạo màu ngẫu nhiên cho một phần tử, hãy sử dụng hàm kanban_color (some_variable), hàm này sẽ trả về một class CSS đặt thuộc tính background và color. Điều này thường được sử dụng trong các phần tử lớp t-att.

Hiển thị thẻ kanban trong các cột theo trạng thái của chúng

Ở bài này hướng dẫn bạn cách thiết lập dạng xem kanban nơi người dùng có thể kéo và thả bản ghi từ cột này sang cột khác, từ đó đẩy bản ghi sang trạng thái khác.

Chuẩn bị

Từ giờ trở đi, chúng ta sẽ sử dụng module project ở đây, vì module này xác định các model phù hợp hơn với các giao diện dựa trên name và state so với các module được xác định trong module cơ sở. Vì vậy, trước khi tiếp tục, hãy thêm project vào danh sách phụ thuộc (depend) của module của bạn.

Các bước thực hiện

  1. Khai báo một kanban view cho project.task:

    <record id="project_task_view_kanban" model="ir.ui.view">
       <field name="name">project.task.kanban</field>
       <field name="model">project.task</field>
       <field name="sequence">20</field>
       <field name="arch" type="xml">
          <kanban default_group_by="stage_id">
             <field name="stage_id" />
             <field name="name" />
             <templates>
                <t t-name="kanban-box">
                   <div class="oe_kanban_card oe_kanban_global_click">
                      <field name="name" />
                   </div>
                </t>
             </templates>
          </kanban>
       </field>
    </record>
    
  2. Thêm một menu và một action sử dụng view này. Đây là một bài tập cho người đọc.

Cơ chế hoạt động

Kanban view hỗ trợ nhóm các bản ghi lại có trường group chung trong cùng một cột. Điều này thường được sử dụng cho trường state hoặc stage_id. vì nó cho phép người dùng thay đổi giá trị của trường này cho một bản ghi bằng cách chỉ cần kéo nó vào một cột khác. Đặt thuộc tính default_group_by trên phần tử kanban và gán tên của trường bạn muốn nhóm theo để sử dụng chức năng này.

Để kiểm soát hành vi của nhóm kanban, có một số tùy chọn có sẵn trong Odoo:

  • group_create: Được sử dụng để ẩn hoặc hiển thị chức năng Add a new column trong kanban được nhóm lại. Giá trị mặc định là true.

  • group_delete: Bật hoặc tắt chức năng Column delete trong menu ngữ cảnh nhóm kanban. Giá trị mặc định là true.

  • group_edit: Bật hoặc tắt chức năng Column edit trong menu ngữ cảnh nhóm kanban. Giá trị mặc định là true.

  • archivable: Bật hoặc tắt tùy chọn lưu trữ và khôi phục các bản ghi từ menu ngữ cảnh nhóm kanban. Điều này chỉ hoạt động nếu trường active Boolean có trong mô hình của bạn.

  • quick_create: Với lệnh này, bạn có thể tạo bản ghi trực tiếp từ giao diện kanban.

  • quick_create_view: Mặc định, tùy chọn quick_create chỉ hiển thị trường name trong kanban. Nhưng với quick_create_view, bạn có thể cung cấp tham chiếu của form view tối thiểu để hiển thị nó trong kanban.

  • on_create: Nếu bạn không muốn sử dụng quick_create khi tạo bản ghi mới và bạn cũng không muốn chuyển hướng người dùng đến form view, bạn có thể cung cấp tham chiếu tới wizard.

Xác định calendar views

Bài này sẽ hướng dẫn bạn hiển thị và chỉnh sửa thông tin về ngày tháng và thời lượng trong hồ sơ của bạn một cách trực quan.

Các bước thực hiện

Làm theo các bước sau để thêm calendar view cho mô hình project.task:

  1. Khai báo một calendar view:

    <record id="project_task_view_calendar" model="ir.ui.view">
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <calendar date_start="date_assign" date_stop="date_end"
             color="project_id">
             <field name="name" />
             <field name="user_id" />
          </calendar>
       </field>
    </record>
    
  2. Thêm menu và action sử dụng view. Đây sẽ là một bài tập dành cho người đọc.

Cơ chế hoạt động

Calendar views cần được chuyển tên trường trong các thuộc tính date_start và date_stop để cho biết trường nào cần xem khi xây dựng biểu diễn trực quan. Chỉ sử dụng các trường có kiểu Datetime hoặc Date, các loại trường khác sẽ không hoạt động và thay vào đó sẽ tạo ra lỗi. Mặc dù date_start là bắt buộc, nhưng bạn có thể bỏ qua date_stop và đặt thuộc tính date_delay thay vào đó, thuộc tính này được mong đợi là trường Float đại diện cho thời lượng tính bằng giờ.

Calendar views cho phép bạn cung cấp cho các bản ghi có cùng giá trị trong một trường có cùng màu (được gán tùy ý). Để sử dụng chức năng này, hãy đặt thuộc tính color thành tên của trường bạn cần. Trong ví dụ trên, chúng ta có thể xem nhanh nhiệm vụ nào thuộc cùng một dự án, vì chúng ta đã gán project_id làm trường để xác định các nhóm màu.

Xác định graph và and pivot view

Trong bài này, chúng ta sẽ tìm hiểu về các view nghiệp vụ thông minh của odoo. Đây là những giao diện chỉ có thể xem, chuyên dùng để trình bày dữ liệu.

Chuẩn bị

Chúng ta vẫn sẽ sử dụng module project ở đây. Chúng ta sẽ tạo một graph view và pivot view để xem người dùng của các tác vụ trên mỗi người dùng. Nhân tiện, người dùng cuối có thể tạo thống kê theo lựa chọn của họ bằng cách sửa đổi các tùy chọn giao diện.

Các bước thực hiện

  1. Khai báo một graph view sử dụng bars:

    <record id="project_task_view_graph" model="ir.ui.view">
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <graph type="bar">
             <field name="user_id"/>
             <field name="stage_id"/>
          </graph>
       </field>
    </record>
    
  2. Khai báo một pivot view:

    <record id="project_task_view_pivot" model="ir.ui.view">
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <pivot>
             <field name="user_id" type="row"/>
             <field name="project_id" type="col"/>
             <field name="stage_id" type="col"/>
          </pivot>
       </field>
    </record>
    
  3. Thêm menu và actions bằng cách sử dụng giao diện này. Đây là một bài tập cho người đọc.

Nếu mọi thứ diễn ra tốt đẹp, bạn sẽ thấy graphs hiển thị số lượng tác vụ được giao cho người dùng nào và trạng thái của các tác vụ đó.

Cơ chế hoạt động

Graph view được khai báo với phần tử root, graph. Thuộc tính type trên phần tử graph xác định chế độ ban đầu của graph view. Các giá trị có thể là bar, line và chart, nhưng bar là giá trị mặc định.

Graph view có tính tương tác cao, vì vậy người dùng có thể chuyển đổi giữa các chế độ khác nhau và cũng có thể thêm và xóa các trường. Nếu bạn sử dụng type = "bar", bạn cũng có thể sử dụng stacked = "1" để hiển thị biểu đồ bar xếp chồng trong phân nhóm.

Pivot views có phần tử root của riêng nó, pivot. Pivot hỗ trợ một lượng các trường nhóm và đo lường tùy ý.

Còn nữa…

Đối với tất cả các loại biểu đồ, trường DateTime rất khó để nhóm với nhau, bởi vì bạn sẽ hiếm khi gặp cùng một giá trị trường ở đây. Vì vậy, nếu bạn có trường DateTime thuộc loại hàng, cũng chỉ định thuộc tính khoảng thời gian với một trong các giá trị sau: day , week , month , quarter , year. Nó sẽ cho ta nhóm được các khoảng thời gian nhất định.

Note

Grouping, giống như sorting, phụ thuộc rất nhiều vào PostgreQuery. Vì vậy, ở đây cũng vậy, quy tắc áp dụng rằng một trường phải hoạt động trong cơ sở dữ liệu và trong bảng hiện tại để có thể sử dụng được.

Tùy thuộc vào sự phức tạp view của bạn và grouping, build một graph có thể khá tốn kém. Xem xét việc đặt thuộc tính auto_search thành false, để người dùng đầu tiên có thể điều chỉnh tất cả các tham số và sau đó kích hoạt tìm kiếm.

Bảng Pivot cũng hỗ trợ grouping trong các cột. Sử dụng col cho các trường bạn muốn có ở đó.

Xác định cohort view

cohort view đã được thêm từ Odoo 12. Cohort views được sử dụng để tìm ra vòng đời của một bản ghi trong một khoảng thời gian cụ thể. Với Cohort views, bạn có thể thấy tỷ lệ sóng và duy trì của bất kỳ đối tượng nào trong một thời gian cụ thể.

Chuẩn bị

Cohort views là một phần của Odoo Enterprise Edition, vì vậy bạn không thể sử dụng nó chỉ với phiên bản Community. Nếu bạn đang sử dụng phiên bản Enterprise, bạn cần thêm Web_CoHort trong tệp kê khai của module. Ở ví dụ sau đây, chúng ta sẽ tạo một view để xem phân tích cohort cho các nhiệm vụ.

Các bước thực hiện

Thực hiện theo các bước sau để thêm Cohort view cho project.task Model:

  1. Khai báo một cohort view:

    <record id="project_task_view_graph" model="ir.ui.view">
       <field name="name">project task cohort</field>
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <cohort date_start="date_start" date_stop="date_deadline" interval="month" string="Task Cohort" />
       </field>
    </record>
    
  2. Thêm menu và hành động sử dụng view này. Điều này còn lại như một bài tập cho người đọc.

Cơ chế hoạt động

Để tạo giao diện Cohort, bạn cần cung cấp Date_start và Date_Stop. Chúng sẽ được sử dụng trong view để xác định khoảng thời gian của bất kỳ bản ghi. Ví dụ: nếu bạn đang quản lý đăng ký dịch vụ, ngày bắt đầu đăng ký sẽ là date_start và ngày đăng ký sẽ hết hạn sẽ là date_stop.

Mặc định, Cohort views sẽ được hiển thị ở chế độ ghi nhớ theo các khoảng thời gian của một tháng. Bạn có thể sử dụng các tùy chọn đã cho để có được các hành vi khác nhau trong Cohort view:

  • mode: Bạn có thể sử dụng Cohort với hai chế độ, retention (mặc định) hoặc churn. Chế độ retention bắt đầu với 100% và giảm theo thời gian, trong khi chế độ churn bắt đầu ở mức 0% và tăng theo thời gian.

  • timeline: Tùy chọn này chấp nhận hai giá trị: forward (mặc định) hoặc backward. Trong hầu hết các trường hợp, bạn cần sử dụng forward nhưng nếu date_start đang ở trong tương lai, bạn sẽ cần sử dụng backward

  • interval: Mặc định, Cohort được nhóm theo tháng, nhưng bạn có thể thay đổi điều này trong các tùy chọn khoảng thời gian. Ngoài những tháng, Cohort cũng hỗ trợ các khoảng thời gian ngày, tuần và quanh năm.

  • measure: Giống như graph đồ và pivot, measure được sử dụng để hiển thị giá trị tổng hợp của một trường nhất định. Nếu không có tùy chọn nào được đưa ra, Cohort sẽ hiển thị số lượng bản ghi.

Xác định dashboard view

dashboard view xuất hiện từ phiên bản 12.0 nó được sử dụng để hiển thị nhiều giao diện và KPI doanh nghiệp khác nhau trong một màn hình.

Chuẩn bị

Dashboard view là một phần của phiên bản Odoo Enterprise, vì vậy bạn không thể sử dụng nó với phiên bản Community. Nếu bạn đang sử dụng phiên bản Enterprise, bạn cần thêm sự phụ thuộc của Web_Dashboard trong tệp kê khai của module. Trong ví dụ này, chúng ta sẽ hiển thị một vài KPI và một vài views hiện có, chúng ta sẽ tận dụng code ở những bài trước.

Các bước thực hiện

  1. khai báo một dashboard view:

    <record id="project_task_view_dashboard" model="ir.ui.view">
       <field name="name">project task dashbaord</field>
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <dashboard>
             <view ref="my_project.project_task_view_graph" type="graph" />
             <group>
                <aggregate name="all_task" string="Total Tasks"
                   group_operator="count"
                   field="id" measure="__count__" />
                <aggregate name="progress_task" string="In Progress Tasks"
                   domain="[('stage_id.name', 'ilike', 'In Progress')]"
                   group_operator="count"
                   field="id" measure="__count__" />
                <aggregate name="done_task"
                   string="Completed Tasks"
                   domain="[('stage_id.name', 'ilike', 'Done')]"
                   group_operator="count" field="id"
                   measure="__count__" />
                <formula name="price_average"
                   string="Overall Progress"
                   value="record.done_task / record.all_task"
                   widget="percentage" />
             </group>
             <view ref="my_project.project_task_view_pivot" type="pivot"/>
          </dashboard>
       </field>
    </record>
    
  2. Thêm menu và action bằng cách sử dụng giao diện này. phần còn lại như một bài tập cho người đọc.

Cơ chế hoạt động

Với dashboard view, bạn có thể hiển thị KPI với aggregateformula.

Bạn có thể hiển thị nhiều giao diện trên cùng một màn hình. Như bạn có thể thấy ở phần code thì ta đã thêm hai views : graphpivot. Để hiển thị các views, bạn chỉ cần sử dụng thẻ <view> với tham chiếu XML và type của view.

Xác định gantt view

Odoo phiên bản 13 đã thêm một view mới đó là Gantt với các tùy chọn mới. Gantt View rất hữu ích để thấy sự tiến bộ chung và lập kế hoạch quy trình kinh doanh. Trong bài này, chúng tôi sẽ tạo một cái nhìn Gantt mới và nhìn vào các tùy chọn của nó.

Chuẩn bị

Gantt View là một phần của Odoo Enterprise Edition, vì vậy bạn không thể sử dụng nó với phiên bản Community. Nếu bạn đang sử dụng phiên bản Enterprise, bạn cần thêm sự phụ thuộc của Web_Dashboard trong tệp kê khai của module.

Trong ví dụ này, chúng ta sẽ tiếp tục sử dụng module my_project từ bài trước đó. Chúng ta sẽ tạo một Gantt view mới cho các nhiệm vụ của Project.

Các bước thực hiện

  1. Khai báo gantt view cho model project.task:

    <record id="project_task_view_gantt" model="ir.ui.view">
       <field name="name">project task gantt</field>
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <gantt date_start="date_assign" date_stop="date_end"
                string="Tasks" default_group_by="project_id"
                color="project_id" progress="sequence">
             <field name="name"/>
             <field name="stage_id"/>
          </gantt>
       </field>
    </record>
    
  2. Thêm menu và action bằng cách sử dụng view này. Phần còn lại như một bài tập cho người đọc.

Cơ chế hoạt động

Với gantt view, bạn có thể hiển thị một lịch trình tổng thể trên một màn hình. Trong ví dụ, chúng ta đã tạo một gantt view cho các nhiệm vụ được nhóm theo dự án. Thông thường, bạn cần hai thuộc tính để tạo gantt view đó là start_datestop_date.

Ngoài ra có thêm một số thuộc tính mở rộng tính năng cho gantt view, hãy xem tất cả tùy chọn:

  • start_date: Xác định thời gian bắt đầu của mục Gantt. Nó phải là date hoặc datetime.

  • stop_date: Xác định thời gian dừng của mục Gantt. Nó phải là date hoặc datetime.

  • default_group_by: Sử dụng thuộc tính này nếu bạn muốn nhóm các mục Gantt dựa trên trường.

  • color: Thuộc tính này được sử dụng để quyết định màu của một mục gantt.

  • progress: Thuộc tính này được sử dụng để biểu thị tiến trình của một mục Gantt.

  • decoration-*: Thuộc tính decoration được sử dụng để quyết định màu sắc của một mặt hàng Gantt dựa trên các điều kiện. Nó có thể được sử dụng như thế này: decoration-danger="state == 'lost'". Các giá trị khác của nó là decoration-success, decoration-info, decoration-warningdecoration-secondary.

  • Mặc định, các mục gantt views có thể thay đổi kích thước và có thể kéo được, nhưng nếu bạn muốn tắt nó, bạn có thể sử dụng thuộc tính edit = "0".

Còn nữa…

Khi bạn di chuột qua một mục gantt View, bạn sẽ thấy tên và ngày của mục đó. Nếu bạn muốn tùy chỉnh cửa sổ bật lên đó, bạn có thể khai báo một Qweb template trong gantt View như sau:

<gantt date_start="date_assign" date_stop="date_end" string="Tasks">
   <field name="name"/>
   <field name="stage_id"/>
   <templates>
      <div t-name="gantt-popover">
         <ul class="pl-1 mb-0 list-unstyled">
            <li>
               <strong>Name: </strong> <t t-esc="name"/>
            </li>
            <li>
               <strong>Stage: </strong> <t t-esc="stage_id[1]"/>
            </li>
         </ul>
      </div>
   </templates>
</gantt>

Lưu ý bạn sẽ cần thêm các trường mà bạn muốn sử dụng trong mẫu thông qua thẻ <field>.

Xác định activity view

Activities là một phần quan trọng của các ứng dụng Odoo. Chúng được sử dụng để tạo ra các hành động để phục vụ cho các nghiệp vụ khác nhau. Activity view giúp bạn xem các trạng thái và lịch trình của tất cả các hoạt động trên model.

Chuẩn bị

Trong ví dụ này, chúng ta sẽ tiếp tục sử dụng module my_project từ bài trước đó. Chúng ta sẽ tạo một activity view mới cho các nhiệm vụ của dự án.

Các bước thực hiện

  1. Khai báo một activity view cho model project.task:

    <record id="project_task_view_activity" model="ir.ui.view">
       <field name="name">project task activity</field>
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <activity string="Tasks">
             <templates>
                <div t-name="activity-box">
                   <div>
                      <field name="name" display="full"/>
                      <field name="project_id" muted="1" display="full"/>
                   </div>
                </div>
             </templates>
          </activity>
       </field>
    </record>
    
  2. Thêm các menu và activity bằng views này. Còn lại như một bài tập cho người đọc.

Cơ chế hoạt động

Activity views đơn giản, hầu hết các thứ được quản lý tự động. Bạn chỉ cần tùy chọn để tùy chỉnh cột đầu tiên. Để hiển thị dữ liệu của bạn trong cột đầu tiên, bạn cần tạo mẫu Qweb bằng activity-box. Odoo sẽ quản lý phần còn lại.

Activity views sẽ hiển thị mẫu của bạn trong cột đầu tiên và các cột khác sẽ hiển thị các hoạt động theo lịch trình được nhóm theo loại hoạt động.

Xác định map view

Odoo phiên bản 13 thêm một giao diện mới gọi là map view. Như tên của nó cho thấy, nó được sử dụng để hiển thị bản đồ với một điểm đánh dấu. Chúng rất hữu ích cho các dịch vụ tại chỗ.

Chuẩn bị

Trong ví dụ này, chúng ta sẽ tiếp tục sử dụng module my_project từ bài trước. Chúng ta sẽ tạo giao diện map view cho khách hàng của nhiệm vụ. Map views là một phần của Odoo Enterprise Edition, vì vậy bạn không thể sử dụng nó với phiên bản Community. Nếu bạn đang sử dụng phiên bản Enterprise, bạn cần thêm phụ thuộc Web_Map vào tệp kê khai của module.

Các bước thực hiện

  1. Khai báo một map view cho model project.task:

    <record id="project_task_view_map" model="ir.ui.view">
       <field name="name">project task map</field>
       <field name="model">project.task</field>
       <field name="arch" type="xml">
          <map res_partner="student_id">
             <marker-popup>
                <field name="name" string="Title " />
                <field name="student_id" string="Student" />
             </marker-popup>
          </map>
       </field>
    </record>
    
  2. Thêm các menu và activity bằng views này. Còn lại như một bài tập cho người đọc.

Cơ chế hoạt động

Tạo giao diện bản đồ khá đơn giản: bạn chỉ cần một trường many2one liên kết đến model education.student. Model education.student có các trường địa chỉ, được sử dụng Bằng chế độ map view để hiển thị điểm đánh dấu cho địa chỉ. Bạn sẽ cần sử dụng thuộc tính res_partner để ánh xạ địa chỉ tới map view. Trong trường hợp này, chúng ta đã sử dụng trường student_id vì bản ghi học sinh được đặt trong trường student_id.

Ngoài ra, để hiển thị dữ liệu trong cửa sổ bật lên đánh dấu, bạn sẽ cần sử dụng thẻ <marker-popup> và đặt các trường bên trong nó.