2020年2月27日 星期四

Odoo教育訓練學習筆記 (2)

  由於轉換職場跑道的緣故,許久沒有撰寫學習筆記或書摘心得等文章,原本自學內容以Python為核心,著重資料科學的技術與應用,但由於新工作的需求,必須轉而學習開放原始碼ERP系統-Odoo的開發,對於僅有類似系統分析經驗的我而言,這是非常難得的機會,可以讓我真正跨入系統工程師的領域。

  由於自己並非資訊相關科系畢業,基礎知識與技能相當貧乏,透過此次學習Odoo系統基礎建置與開發,我才初次接觸了如何在Windows系統透過VM來使用Linux系統,以及終端機指令的操作,來一步一步建置Odoo系統,包括所需的Python套件、PostgreSQL資料庫系統、pgAdmin資料庫管理工具、Odoo系統資料夾結構、Odoo系統設定文件、PyCharm整合開發環境、系統更新與維護方式等等,需要了解與學習的技術非常非常的多,自己也僅學習使用了一小部分的工具與技巧而已。

  系統開發由於牽涉到許多資料夾結構與不同文件如csvpyxml等,因此未來使用Git做為示範檔案存放空間的需求或許也將越來越迫切,現階段仍純粹在網誌整理、發布文章;本系列一共六篇學習筆記,為花費大量時間綜合整理Peter Wu老師課堂教學、《Odoo快速入門與實戰》簡體書,以及零散的網路教學文章而得,前五篇的學習筆記完全操作使用Odoo社群版,第六篇的學習筆記則加入「康虎雲報表」的安裝與教學,可至「康虎軟件工作室」官方網站下載相關軟體,配合學習筆記的說明來安裝使用。

  自學Odoo系統的建置開發相當不容易:在台灣,Odoo系統的知名度與普及性遠不如SAP、鼎新、Oracle等大廠,資訊廠商與開發人員的數量也相對稀少,就連參考書籍也完全沒有繁體中文的版本,僅有少數英文書與簡體書可以購買(而且這些書都寫得很糟),加上這幾年Odoo系統的快速發展,不同的版本之間有不小的變動,學會的技術可能過幾年就完全不能用了…。綜合上述許多原因,即便本系列學習筆記已經整理很清楚了,對於完全零經驗的開發者而言,相信仍舊難以閱讀、理解。

  目前Odoo系統在歐美與中國大陸的資源、社群與討論,都比台灣頻繁、密集得多,想要入門Odoo系統的學習者建議還是需要老師來帶領,才有辦法掌握操作、管理、開發的各項眉眉角角。

Odoo客製化開發模組的結構
l   custom資料夾存放客製化開發模組的結構如下:
/openacademy
    /data
    /i18n
    /models
        /__init__.py
    /security
    /static
        /description
            /icon.png
    /views
    /wizards
    __init__.py
    __manifest__.py
l   data資料夾:
用以在資料庫自動放置資料、郵件模板。
l   i18n資料夾:
放置翻譯檔,為po檔案。
l   models資料夾:
放置所設計欄位的py檔案。
l   security資料夾:
放置所規劃權限的csv檔案與xml檔案。
l   static/description資料夾:
放置模組logo圖像等附件。
l   views資料夾:
放置所設計頁面的xml檔案。
l   wizards資料夾:
放置所設計彈出式視窗的py檔案與xml檔案。
l   __init__.py
# -*- coding: utf-8 -*-
# Author: Peter Wu

#
載入models資料夾的Python程式碼
from . import models
l   __manifest__.py
# -*- coding: utf-8 -*-
# Author: Peter Wu

#
安裝模組時顯示的資料
{
    'name': 'Openacademy',
    'version': '12',
    # sequence
代表該模組於安裝畫面顯示的順序
    'sequence': 200,
    'category': 'Student Academy Score system',
    'website': 'https://www.alldo.com.tw',
    'summary': 'Student Academy Score system',
    'description': """
學生成績系統
""",
    #
安裝使用該模組需要哪些模組的支援
    'depends': [
    ],
    #
模組所使用的csv檔案與xml檔案,需於此加入路徑名稱
    'data': [
    ],
    #
可安裝與否、可應用與否
    'installable': True,
    'application': True,
}

Openacademy模組範例一-學習基礎模組、Many2one字段、基礎權限
l   Step 1models資料夾內創建openacademy_student.py
# -*- coding: utf-8 -*-
# Author: Peter Wu

from odoo import models, fields, api
from odoo.exceptions import UserError

#
整個Odoo系統的class名稱絕對不可以重複
# models.Model
用在一般欄位
class openacademystudent(models.Model):
    #
模型屬性說明:
    # _name
-與.py檔名盡量一致,是class的唯一標示字段,其它class可以通過此字段引用
    # _description
-類似於標籤,提高友善的查詢
    # _rec_name
-是record name的縮寫,Odoo預設使用物件名稱name做為一條紀錄的描述,我們可以藉由_rec_name指定其它的物件名稱
    # _order
-是在模型紀錄集展示時,預設的排序
    # _table
-是在模型裡面制定後台資料表名稱,預設資料表名稱是更改模型的名稱,把點(.)換成底線(_)
    _name = 'openacademy.student'
    _rec_name = 'student_name'
   
    #
字段類型說明:
    #
字串相關類型-fields.Char()fields.Text()fields.Selection()fields.Html()
    #
數值相關類型-fields.Integer()fields.Float()
    #
日期類型-fields.Date()fields.Datetime()
    #
布林類型-fields.Boolean()
    #
二進制類型-fields.Binary()
    #
字段屬性說明:
    # string
-是在前端介面看到的名稱
    # help
-是在前端介面看到的提示訊息
    # default
-設定預設值
    # readonly
-預設是False,如果設定為True,則在前端介面不可編輯
    # required
-預設是False,如果設定為True,則在創建紀錄時為必填,此項設置對模型層也是有作用的
    # states
-用字典來設定readonlyrequiredinvisible三個屬性,例如states = {'done': ['readonly', True]}
    # size
-僅可使用在Char,限制最大字元數
    # translate
-僅可使用在CharTextHtml,使文字可以被翻譯
    # sequence
-允許我們手動拖曳紀錄來定義順序,如要使用,要在模型屬性_order中引入
    # index
-預設是False,如果設定為True,則會在資料庫上創建索引
    # groups
-用於限定檢視該字段的security group
    # deprecated
-如果設定為True,則一旦使用該字段,就會在日誌中記錄警告訊息
    # copy
-預設是True,如果設定為False,則在複製紀錄時,該字段不被複製
    # active
-如果設定為False,則在前端查詢時,該紀錄會被排除在外
    # oldname
-可以在複製紀錄時,使舊版本的數據自動複製到對應的新字段
    student_no = fields.Char(string = '
學生學號', required = True)
    student_name = fields.Char(string = '
學生姓名', required = True)
    student_contact = fields.Char(string = '
聯絡人')
    student_class = fields.Selection([('1', '
一年級'), ('2', '二年級'), ('3', '三年級')], string = '年級')
    student_fm = fields.Selection([('M', '
'), ('F', '')], string = '性別', default = 'M')
   
    #
我們可以自由創建student_no等物件,但以下保留字段不可使用:
    # _last_update
-並不會實際存儲值,僅有觸發檢查的作用
    # id
-是紀錄的唯一標示
    # create_date
-記錄創建的日期
    # create_uid
-創建Many2one類型紀錄的用戶
    # write_date
-記錄的最後修改日期
    # write_uid
-最後修改Many2one類型紀錄的用戶
l   Step 2models資料夾內修改__init__.py
# -*- coding: utf-8 -*-
# Author : Peter Wu

from . import openacademy_student
l   Step 3models資料夾內創建openacademy_score.py
# -*- coding: utf-8 -*-
# Author: Peter Wu

from odoo import models, fields, api
from odoo.exceptions import UserError

class openacademyscore(models.Model):
    _name = 'openacademy.score'
   
    score_year = fields.Char(string = '
學年', required = True)
    # Many2one
字段屬性說明:
    # comodel
-設置與本模型關聯的另一個模型的名稱
    # string
-是在前端介面看到的名稱
    # ondelete
-刪除關聯紀錄時本字段的動作-
       
預設是set null-關聯紀錄被刪除時本字段將被賦值為null
        restricted
-關聯紀錄被刪除時會報錯,阻止關聯字段被刪除;
        cascade
-關聯字段被刪除則本紀錄也將被刪除
    # context
-關聯資料時附帶訊息,例如設置預設值
    # domain
-設定查詢條件,用於限定查詢的數據
    # auto_join
-將會繞過查看的安全規則,用戶可以查看權限以外的數據
    score_student = fields.Many2one('openacademy.student', string = '
學生', required = True)
    score_chinese = fields.Float(string = '
國文分數', default = 0)
    score_math = fields.Float(string = '
數學分數', default = 0)
    score_english = fields.Float(string = '
英文分數', default = 0)
l   Step 4models資料夾內修改__init__.py
#
原程式碼增加以下程式碼
from . import openacademy_score
l   Step 5views資料夾內創建openacademy_student.xml
因為已將setting.jar匯入Live templates,可使用編輯技巧-
輸入odoo_data再按Tab鍵、
輸入odoo_tree再按Tab鍵、
輸入odoo_form再按Tab鍵、
輸入odoo_view_action再按Tab鍵:

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <!--
顯示外層橫式的表格畫面-->
        <!--model
屬性標示本紀錄要使用的模型-->
        <!--id
屬性在整個Odoo系統中不可以重複,而且id的命名規則最多只能有一個點(.)-->
        <record id="view_openacademy_student_tree" model="ir.ui.view">
            <field name="name">view.openacademy.student.tree</field>
            <field name="model">openacademy.student</field>
            <field name="arch" type="xml">
                <tree string="">
                    <field name="student_no"/>
                    <field name="student_name"/>
                    <field name="student_contact"/>
                    <field name="student_class"/>
                    <field name="student_fm"/>
                </tree>
            </field>
        </record>
        <!--
顯示內層直式的表格畫面-->
        <record id="view_openacademy_student_form" model="ir.ui.view">
            <field name="name">view.openacademy.student.form</field>
            <field name="model">openacademy.student</field>
            <field name="arch" type="xml">
                <form string="">
                    <sheet>
                        <group>
                            <field name="student_no"/>
                            <field name="student_name"/>
                            <field name="student_contact"/>
                            <field name="student_class"/>
                            <field name="student_fm"/>
                        </group>
                    </sheet>
                </form>
            </field>
        </record>
        <record id="action_openacademy_student_view" model="ir.actions.act_window">
            <field name="name">
學生基本資料</field>
            <field name="type">ir.actions.act_window</field>
            <field name="res_model">openacademy.student</field>
            <field name="view_mode">tree,form</field>
            <field name="help" type="html">
                <p class="oe_view_nocontent_create"></p>
                <p></p>
            </field>
        </record>
    </data>
</odoo>
l   Step 6views資料夾內創建openacademy_score.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <record id="view_openacademy_score_tree" model="ir.ui.view">
            <field name="name">view.openacademy.score.tree</field>
            <field name="model">openacademy.score</field>
            <field name="arch" type="xml">
                <tree string="">
                    <field name="score_year"/>
                    <field name="score_student"/>
                    <field name="score_chinese"/>
                    <field name="score_math"/>
                    <field name="score_english"/>
                </tree>
            </field>
        </record>
        <record id="view_openacademy_score_form" model="ir.ui.view">
            <field name="name">view.openacademy.score.form</field>
            <field name="model">openacademy.score</field>
            <field name="arch" type="xml">
                <form string="">
                    <sheet>
                        <group>
                            <field name="score_year"/>
                            <field name="score_student"/>
                            <field name="score_chinese"/>
                            <field name="score_math"/>
                            <field name="score_english"/>
                        </group>
                    </sheet>
                </form>
            </field>
        </record>
        <record id="action_openacademy_score_view" model="ir.actions.act_window">
            <field name="name">
學期成績紀錄</field>
            <field name="type">ir.actions.act_window</field>
            <field name="res_model">openacademy.score</field>
            <field name="view_mode">tree,form</field>
            <field name="help" type="html">
                <p class="oe_view_nocontent_create"></p>
                <p></p>
            </field>
        </record>
    </data>
</odoo>
l   Step 7views資料夾內創建openacademy_menu.xml
因為已將setting.jar匯入Live templates,可使用編輯技巧-
輸入odoo_data再按Tab鍵、
輸入odoo_menuitem_root再按Tab鍵、
輸入odoo_menuitem_categ再按Tab鍵、
輸入odoo_menuitem_action再按Tab鍵:

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <menuitem id="menu_openacademy_root"
            name="
學生成績系統"
            sequence="200"/>
        <menuitem id="menu_openacademy_categ"
            name="
基礎資料"
            parent="menu_openacademy_root"
          
 sequence="10"/>
        <menuitem id="menu_openacademy_student_view"
            name="" parent="menu_openacademy_categ"
            action="action_openacademy_student_view"
            sequence="10"/>
        <menuitem id="menu_openacademy_score_categ"
            name="
成績紀錄"
            parent="menu_openacademy_root"
            sequence="20"/>
        <menuitem id="menu_openacademy_score_view"
            name="" parent="menu_openacademy_score_categ"
            action="action_openacademy_score_view"
            sequence="10"/>
    </data>
</odoo>
l   Step 8security資料夾內創建ir.model.access.csv,分別建立openacademy_studentopenacademy_score兩個模型的權限:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
"access_openacademy_score","access.openacademy.score","openacademy.model_openacademy_score","base.group_user","True","True","True","True"
"access_openacademy_student","access.openacademy.student","openacademy.model_openacademy_student","base.group_user","True","True","True","True"
欄位說明如下:
id
-紀錄的唯一標示。
name
-紀錄的唯一名稱,命名方法是用點(.)連接模組名稱。
model_id
-是設置訪問權限的模組_name_name如果是bm.bugmodel_id就是model_bm_bug
group_id
-是我們要授予權限的security group id,例如base.group_user
perm
-對應讀、寫、新建、刪除操作的設置。
未來可由Settings > Activate the developer mode > Technical > Access Right匯出csv檔快速建立檔案,如下圖。


l   Step 9修改__manifest__.py
#
模組所使用的csv檔案與xml檔案,需於此加入路徑名稱
'data': [
    #
因程式執行有順序性,權限檔案建議放在最前面
    'security/ir.model.access.csv',
    'views/openacademy_student.xml',
    'views/openacademy_score.xml',
    #
因程式執行有順序性,menu一定要放在最後面
    'views/openacademy_menu.xml',
],
l   Step 10登入Odoo系統檢視客製化開發結果:
Settings > Activate the developer mode
Apps > Update Apps List > INSTALL Openacademy
l   補充一:
未來若只修改models資料夾的Python程式碼,不需要UPGRADE模組即可檢視客製化開發結果,但若不只修改Python程式碼,例如修改xml檔案,則需要UPGRADE模組。
l   補充二:
Activate the developer mode
-在Odoo中為了加快速度,頁面端對JavaScriptCSS資源進行了壓縮。
Activate the developer mode (with assets)
-會加載頁面需要的JavaScriptCSS資源,但這會導致頁面的瀏覽速度有所下降。

更新Odoo 12、更新PostgreSQL資料庫
l   在終端機輸入指令更新Odoo 12
cd /opt/odoo/odoo

sudo git pull origin 12.0

l   開啟Pycharm,點選Run > Edit Configurations...,修改Parameters設定,資料庫名稱以odoo12為例,設定完成後點選OK,再點選Run > Run 'odoo12',這個動作會將更新寫入PostgreSQL資料庫,執行完畢記得將Parameters改回來:
-c /etc/odoo.conf -u all -d odoo12


忘記Odoo系統管理員的密碼
l   在終端機輸入指令登入PostgreSQL資料庫,資料庫名稱以odoo12為例,帳號以admin為例:
psql -d odoo12

select login from res_users;

update res_users set password='123456' where login='admin';

\q

沒有留言:

張貼留言