iT邦幫忙

2025 iThome 鐵人賽

DAY 3
1
Odoo

Odoo × 生成式 AI:從零到一的企業自動化實戰系列 第 3

Day 3:開發者視角:Odoo 模組開發與 API 串接

  • 分享至 

  • xImage
  •  

你將學到

  • Odoo 模組開發的基礎架構與組成要素(模型、視圖、控制器、清單檔等)
  • 透過 JSON-RPC、Web 控制器及 Python requests 呼叫等方法串接外部 API 的最佳實踐
  • 開發人員如何將 Odoo 與第三方 AI 平台(如 OpenAI)整合的實例與技巧

關鍵字

ORM、清單檔(Manifest)、JSON-RPC、控制器(Controller)、OpenAI API


前言:從需求出發的開發情境導入

想像你是一位 Odoo 開發人員,接到一個新挑戰:你的公司希望在 Odoo ERP 系統中引入 AI 能力,例如自動摘要客服工單或分類客戶留言,提升工作效率。面對這個需求,你該如何下手?

本篇文章將從開發者視角出發,逐步拆解 Odoo 模組開發的結構,並分享如何將 Odoo 串接外部 API(例如 OpenAI 的大型語言模型服務)以實現這些智慧功能。在這篇文章中,我們會導入真實情境、解析開發觀點,並輔以簡要程式碼片段,帶領讀者深入了解 Odoo 模組開發 以及 AI 服務整合 的實戰技巧。


Odoo 模組開發基礎與架構

在 Odoo 中,一切都是模組。模組是功能的基本單位,可以獨立開發、安裝或移除。每個 Odoo 模組其實就是一個特定結構的目錄,包含各種子元件。當我們啟動 Odoo 伺服器時,系統會掃描所有已安裝模組並載入其中定義的內容,包括初始化 Python 模型類別、建立/更新資料表、讀取 XML/CSV 資料檔載入視圖與權限設定,最後將所有元件註冊至 Odoo 的模型註冊表(Registry)。可以說,一個完整的 Odoo 系統就是由許多模組像積木一樣組合而成的。接下來,我們從開發觀點剖析 Odoo 模組的核心構成與實作邏輯。

模組的典型結構

Odoo模組架構

一個典型的 Odoo 模組目錄包含以下主要部分:

  • 清單檔(__manifest__.py:模組的說明檔案,定義名稱、版本、摘要、作者、相依模組、要載入的資料檔等。清單檔宣告這個資料夾是一個 Odoo 模組。例如:

    {
        'name': 'My AI Integration',
        'version': '1.0.0',
        'summary': 'Integrate Odoo with OpenAI services',
        'author': 'Your Name',
        'depends': ['base', 'helpdesk'],  # 相依於 base 和 helpdesk 模組
        'data': [
            'security/ir.model.access.csv',
            'views/helpdesk_ticket_views.xml',
        ],
        'installable': True,
        'application': False,
    }
    
    

    上述範例中,我們宣告了一個名為 “My AI Integration” 的模組,相依於 Odoo 基礎模組和客服模組(helpdesk),並指定了需要載入的檔案(如安全權限及視圖定義)。當前端使用者在應用列表中看到此模組時,清單檔中的資訊也會用於呈現。

  • 模型(models/:存放資料模型定義的 Python 檔案。模型是 Odoo ORM 的核心,對應資料庫中的表格,負責定義資料結構以及商業邏輯(如欄位屬性、預設值、onchange 和 constraints 等)。模型類別繼承自 odoo.models.Model 並以 _name 屬性指定模型識別名。例如稍後我們將展示的客服工單模型擴充,就屬於這一層。

  • 視圖(views/:儲存 XML 格式的視圖定義,包括表單(form)、清單(tree)、看板(kanban)、統計圖表(graph)等各種 UI 畫面。視圖通常綁定到特定模型,用來描述資料該如何呈現給使用者。例如,我們可以為客服工單增加一個按鈕,讓使用者可以點擊後呼叫 AI 摘要功能,這個按鈕和摘要欄位的界面就是透過 XML 視圖定義的。

  • 控制器(controllers/:包含處理 HTTP 請求的 Python 程式碼,通常使用 @http.route 裝飾器定義路由。控制器允許我們建立自定義的 Web API 端點或網頁(例如提供給第三方系統呼叫 Odoo 資料的 REST API)**。**不過如果只是 Odoo 自己呼叫外部服務,未必需要寫控制器;控制器比較常用於當我們希望暴露一個 Odoo 接口給外部存取時使用,我們會在後面詳述。

  • 資料與安全(data/security/ 等):包含初始化資料檔(如預設設定、參數值)以及安全相關設定。例如,security/ir.model.access.csv 定義模型的存取權限,決定哪些使用者組別可以讀取、建立、修改或刪除模型記錄。如果開發自定義模型,別忘了新增對應的存取控制,否則即使安裝模組,非管理員的使用者可能無法操作該模型紀錄。

上述元素構成了 Odoo 模組的基本架構。舉例來說,一個簡單的自定義模組目錄結構可能如下(以我們的 AI 串接模組為例):

my_ai_integration/            # 模組目錄
├── __manifest__.py           # 模組清單檔
├── __init__.py              # 初始化檔,匯入模型等
├── models/
│   ├── __init__.py
│   └── helpdesk_ai.py       # 自定義的模型或模型擴充檔案
├── views/
│   └── helpdesk_ticket_views.xml   # 定義模型對應的UI畫面
├── security/
│   └── ir.model.access.csv  # 存取權限設定
└── data/
    └── initial_data.xml (或 demo 資料等)

當我們將模組目錄放入 Odoo 的 addons 路徑並安裝模組時,Odoo 就會依序載入上述元件:先讀 manifest 注册模組資訊和相依關係,再執行 models/ 下的 Python 檔案定義模型,接著載入 data/ 初始資料和 views/ 視圖等 UI 設定,最後應用 security/ 權限規則,讓模組完整生效。

💡 Gary’s Pro Tip|用 scaffold 快速創建模組範本
Odoo 提供了名為 scaffold 的指令,可快速建立模組範本結構。只需在終端機執行 odoo-bin scaffold <模組名稱> <放置路徑>,即可自動生成包含基本檔案(如 manifest.py、init.py、樣板模型與視圖檔案)的模組目錄。另外,開發自己的模組時別忘了將模組所在路徑新增到 Odoo 設定檔的 addons_path 中,否則 Odoo 伺服器將無法找到你的模組喔!

odoo-bin scaffold

./odoo-bin scaffold my_ai_module ./odoo/addons

模型與商業邏輯:資料表與 Python ORM

  • 模型(Model)是 Odoo 應用層的核心。在開發上,我們會透過定義 Python 類別來描述一個模型,即資料庫中的一個表,以及其上的商業邏輯。Odoo 採用ORM(物件關聯對應)來讓開發者操作資料更為便利。例如,我們可以定義一個模型來擴充 Odoo 的客服工單,使其多一個文字欄位存放 AI 摘要內容:
# models/helpdesk_ai.py
from odoo import models, fields, api

class HelpdeskTicket(models.Model):
    _inherit = 'helpdesk.ticket'  # 繼承既有的客服工單模型
    _description = 'Helpdesk Ticket (with AI Summary)'

    ai_summary = fields.Text(string="AI 摘要", help="由 AI 產生的工單摘要", readonly=True)

    def action_generate_summary(self):
        """呼叫 OpenAI API 產生摘要並寫入 ai_summary 欄位"""
        for record in self:
            if not record.description:
                continue
            summary = self._call_openai(record.description)
            record.ai_summary = summary

    def _call_openai(self, text):
        # 這是私有輔助方法,實際呼叫 OpenAI API 並返回摘要內容
        import requests, json
        api_key = self.env['ir.config_parameter'].sudo().get_param('openai.api_key')
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
        payload = {
            'model': 'gpt-5',
            'messages': [{'role': 'user', 'content': f"請總結以下內容:{text}"}],
            'temperature': 0.1
        }
        resp = requests.post("https://api.openai.com/v1/chat/completions",
                             headers=headers, json=payload, timeout=10)
        if resp.status_code == 200:
            result = resp.json()
            # 提取並回傳 AI 產生的摘要文字
            return result.get('choices')[0].get('message').get('content').strip()
        else:
            # 簡單的錯誤處理
            raise Exception(f"OpenAI API 調用失敗: {resp.status_code} - {resp.text}")

上述程式碼展示了模型開發的一些關鍵點:

  • _inherit = 'helpdesk.ticket':這表示我們並非從零開發一個新模型,而是繼承 Odoo 內建的 helpdesk.ticket 模型,藉此擴充它的欄位和功能(繼承與覆寫是 Odoo 模組擴充的強大機制之一)。如果是全新模型,則會使用 _name 來指定模型名稱。
  • 定義了一個新的欄位 ai_summary,類型為 Text,用來存放 AI 生成的摘要結果。我們將其設為唯讀,避免使用者手動編輯,並提供說明文字。
  • 定義了一個動作方法 action_generate_summary,供稍後視圖中的按鈕調用。這個方法會對記錄逐一處理:略過沒有描述內容的工單,對其餘的呼叫內部的 _call_openai 來取得摘要,然後寫回 ai_summary 欄位。
  • _call_openai 方法則負責與 OpenAI 的聊天模型 API 通訊:使用 Python 的 requests 函式庫發出 HTTP POST 請求。我們構造了適當的請求標頭(帶上先前在系統參數中設定的 OpenAI API 金鑰)、請求內容 JSON(指定模型、對話訊息、溫度等參數),並對 OpenAI API 進行呼叫。取得回應後,我們解析 JSON 並提取生成的摘要文字。如果 API 回傳錯誤狀態,則拋出異常供上層處理。

值得一提的是,上述程式碼中我們將 OpenAI API 金鑰存放在 Odoo 的 系統參數 (ir.config_parameter) 中,並透過 sudo() 權限讀取。這樣做的好處是避免將敏感金鑰寫在程式中,方便日後在介面上更改,且提高安全性。稍後在本文我們會更深入討論這類整合的最佳實踐

視圖與使用者介面:UI 如何調用後端邏輯

定義好模型和方法後,我們需要在 Odoo 介面上給使用者一個入口來使用 AI 摘要功能。這就涉及 視圖(View) 開發。我們可以通過 XML 視圖,將前述模型的 ai_summary 欄位加入到客服工單的表單畫面,並增加一個按鈕來觸發 action_generate_summary 方法。例如以下是一段視圖 XML:

<!-- views/helpdesk_ticket_views.xml -->
<odoo>
  <record id="view_helpdesk_ticket_form_inherit_ai" model="ir.ui.view">
    <field name="name">helpdesk.ticket.form.inherit.ai</field>
    <field name="model">helpdesk.ticket</field>
    <field name="inherit_id" ref="helpdesk.view_helpdesk_ticket_form"/>
    <field name="arch" type="xml">
      <!-- 在客服工單表單的筆記欄位後插入一個摘要欄位 -->
      <xpath expr="//field[@name='description']" position="after">
        <field name="ai_summary" readonly="1" colspan="2"/>
      </xpath>
      <!-- 在表單的按鈕區域新增一個「產生摘要」按鈕 -->
      <xpath expr="//header" position="inside">
        <button name="action_generate_summary" type="object" string="產生AI摘要" class="btn-primary" icon="fa-magic"/>
      </xpath>
    </field>
  </record>
</odoo>

這段 XML 利用了 Odoo 的視圖繼承機制,我們以 inherit_id 指定繼承原始的客服工單表單視圖,然後通過 xpath 定位在描述欄位之後插入我們的新欄位,以及在表單頂部的工具列(header)內加入一個按鈕。按鈕的 name 屬性對應我們模型中定義的方法名 action_generate_summarytype="object" 表示點擊時將呼叫該伺服器動作。如此一來,使用者在前端即可看到由 AI 產生的摘要欄位,以及一鍵產生摘要的功能。

完成上述模型與視圖的開發,更新模組後,我們就能在 Odoo 客服工單頁面上體驗到 AI 串接的成果:點擊按鈕,稍等片刻,AI 產生的重點摘要便自動出現在欄位中,協助客服人員更快了解工單內容。

💡 Gary’s Pro Tip|開發者模式下調整視圖
開發 Odoo 視圖時,善用 開發者模式(Activating Developer Mode)提供的「視圖調試」功能,可以直接在前端檢視任何頁面的 XML 定義並進行繼承定位。此外,為避免視圖衝突,xpath 的選擇器應盡量精準;若不確定選擇位置,也可以使用 <field name="... position="attributes"> 等方式修改現有欄位屬性,而不直接插入新的節點。


外部 API 串接的方法與最佳實踐

有了模組開發的基礎,我們接下來探討如何將 Odoo 與外部服務串接。常見的情境包括:

  • 由外部系統存取 Odoo 資料(外部 → Odoo)
  • 或是 由 Odoo 呼叫外部服務(Odoo → 外部)

針對這兩種方向,Odoo 提供了不同的工具與建議做法。以下我們從三個面向說明:Odoo 內建的 RPC 介面、建立自定義控制器,以及在 Odoo 中呼叫外部 API。

利用 Odoo RPC 介面

如果第三方應用需要讀寫 Odoo 裡的資料,最直接的辦法是使用 Odoo 官方提供的 外部 API。Odoo 自很早的版本開始就支援以 RPC (Remote Procedure Call) 的方式對外提供服務,包括傳統的 XML-RPC 以及較輕量的 JSON-RPC 兩種介面。透過 RPC,外部程式可以調用 Odoo 的任意模型方法,例如使用 execute_kw 來呼叫 searchcreatewrite 等操作。

使用 RPC 串接 Odoo 的步驟大致如下:

  1. 驗證:先呼叫 Odoo 的 /xmlrpc/2/common/jsonrpc 端點進行使用者驗證,取得 uid(使用者 ID)。驗證時需要提供資料庫名稱、使用者帳號與密碼(或 API 金鑰)。
  2. 呼叫數據操作:驗證成功後,可透過 /xmlrpc/2/object 端點(XML-RPC)或對 /jsonrpc 發送帶 modelmethod 的 JSON 請求,讓 Odoo 執行相應的模型方法。例如,我們可以遠端呼叫 helpdesk.ticket 模型的 search_read 來獲取工單列表,或呼叫我們前面實作的 action_generate_summary 來讓 Odoo 替某張工單產生摘要。值得注意的是,預設情況下 Odoo 的許多模型方法都有存取控制,使用 RPC 呼叫時,一樣需要該使用者具有相應權限才能成功執行。
  3. 處理回應:Odoo 會將結果(或錯誤訊息)透過 RPC 回傳給呼叫方,我們的外部應用即可據此進行後續處理。

透過 RPC 介面整合的優點是快速且通用:不用自己實作 API,直接利用 Odoo 全露出的資料與方法。不過缺點是開發彈性較低效能需注意——RPC 調用每次都需要經過驗證和資料序列化,若外部應用需要大量高頻存取,可能需要考慮快取或批次處理。另外,在某些情況下,你可能不想給外部系統過多權限使用所有模型,這時 RPC 通用介面就不敷使用,需要更細粒度的控制方式。

建立自定義 Web 控制器

另一種串接方法是為 Odoo 建立自定義的 API 端點。透過撰寫 控制器(Controller) 類別並使用 @odoo.http.route 來定義路由,我們可以對外提供 RESTful 風格的接口。例如,我們想提供一個僅回傳特定資訊的簡易 API,讓外部系統能 GET 該 URL 拿資料,或 POST 資料進 Odoo,而不透過一般 RPC 機制。

一個簡單的控制器範例:

# controllers/my_api_controller.py
from odoo import http

class MyAPIController(http.Controller):

    @http.route('/myapi/ticket_summary/<int:ticket_id>', auth='api_key', methods=['GET'], type='json')
    def get_ticket_summary(self, ticket_id):
        ticket = http.request.env['helpdesk.ticket'].browse(ticket_id)
        if not ticket.exists():
            return {"error": "Ticket not found"}
        return {"id": ticket.id, "summary": ticket.ai_summary or ""}

這段程式定義了一個 URL 路徑 /myapi/ticket_summary/<ticket_id>,當外部對此發送 GET 請求且帶有有效的 API 金鑰(因為我們使用 auth='api_key' 驗證)時,Odoo 將回傳指定工單的 AI 摘要內容(JSON 格式)。這種方式的優點是彈性高:我們可以精確控制提供哪些資料、資料格式為何,也可以在控制器方法中編寫任意的邏輯(例如額外驗證、日誌記錄等)。特別在需要公開特定功能給外部、但不願開放整個 Odoo 資料庫的情況下,自建控制器是很好的選擇。

不過,實作控制器也要留意幾點最佳實踐

  • 認證與授權:如上例,我們使用了 auth='api_key'來要求每次請求必須帶 API 金鑰,也可以用 auth='user' 讓其繼承使用者權限或 auth='public' 開放給未登入訪客(後者需自行實作驗證機制)。確保你的 API 不會因為過度開放而造成安全漏洞。
  • 請求頻率與效能:若預期外部會高頻調用控制器,可考慮在 Odoo 前端加上 Nginx 等做快取,或在程式中使用 Odoo 的 @http.route 限流或 queue 機制。同時,也要注意資料庫查詢效能,避免一次取出過多記錄導致延遲。
  • 輸出格式:上例我們用了 type='json' 直接返回字典物件,Odoo 會自動將其序列化為 JSON。但若需要返回非 JSON(如文件、圖片),可以使用 return http.send_file(...)return werkzeug.wrappers.Response(...) 手工構造回應。根據介接對象的需要來設計即可。

在 Odoo 內呼叫外部 API

最後一種情境,也是我們這篇文章聚焦的:由 Odoo 主動呼叫第三方服務(例如 AI 模型 API)。事實上,在 Odoo 中呼叫外部 API 跟一般的 Python 腳本中呼叫差異不大——因為 Odoo 本質上就是 Python 應用,我們可以直接使用 Python 標準庫或第三方庫。最常用的就是前面示範過的 requests 函式庫,用於發送 HTTP 請求並接收結果。

然而,將外部 API 整合進 Odoo 環境,仍有一些開發考量與最佳實踐需要注意:

  • 環境與相依套件:確認你的 Odoo 執行環境能連上目標服務(例如出站防火牆未阻擋 OpenAI)。如果需要使用特定的 Python 套件(如官方的 openai SDK),可以在模組的 manifest 中透過 external_dependencies 宣告,需要先安裝該套件或確保環境已備妥。以我們的案例,其實使用 Python 標準庫即可完成 REST 呼叫,因此無須額外安裝套件。
  • 錯誤處理與重試機制:網路請求可能失敗(連線超時、服務錯誤等),撰寫整合程式時務必加入錯誤處理。例如上面的 _call_openai 方法中,我們對非 200 狀態碼直接 raise Exception。在實務中,你可能希望更優雅地處理:例如記錄錯誤日誌、重試幾次、或至少不要讓整個 interaction 崩潰。必要時可結合 Python 的重試套件或撰寫重試邏輯。
  • 非同步處理:呼叫外部 API 時,等待回應可能耗時數秒甚至更久。若在使用者按下按鈕後同步等待,會導致前端界面鎖住。Odoo 標準會等待伺服器響應才解除按鈕。對於長耗時任務,建議使用 Odoo 的 排程器 (Cron)Queue 工作 將任務背景化。例如,可將摘要請求放入佇列,立即讓使用者介面可操作,待結果回來後再更新紀錄欄位。
  • 安全與成本:切勿將 API 密鑰硬編碼在 Git 原始碼中,一旦外洩後果嚴重。建議將金鑰放在 Odoo 系統參數或伺服器的環境變數中,並配合 Odoo 的加密選項(如果有 Enterprise 版的密鑰儲存功能)。另外,像 OpenAI 這類服務通常按用量計費,因此在設計功能時也要避免不必要的呼叫(例如限制每次僅針對使用者選定的資料呼叫,而不是無意義地對大量記錄反覆請求)。

💡 Gary’s Pro Tip|測試 Odoo 外部 API 串接
在 Odoo 環境中呼叫外部 API 進行測試時,可以使用 Odoo Shell (odoo-bin shell) 啟動互動式環境,在載入 Odoo 環境下手動執行你的方法,這比透過介面點擊更方便調試。此外,充分利用如 Postman 之類的工具來模擬外部服務或檢查 API 回傳,有助於你在開發 Odoo 模組前先確認外部服務的行為,撰寫出更健全的整合代碼。


Odoo × OpenAI 實戰:打造智慧摘要功能

理解了以上基礎與實踐要點,讓我們回到一開始的情境:將 AI 能力融入 Odoo。以下我們以客服工單摘要為例,說明如何運用自定義模組與 OpenAI API,讓 ERP 系統具備一定的「理解文字」能力。整體流程可用下圖表示:

客服工單摘要流程

如上所示,使用者在 Odoo 前端點擊按鈕後,後端會將工單的描述文字發送給 OpenAI 的模型服務,拿到回應摘要,再回寫到 Odoo 資料庫,最後透過視圖更新將結果呈現給使用者。這種串接屬於由 Odoo 呼出的形式,因此我們充分利用了 Odoo 模組開發和 Python 語言的威力。

我們建立了一個自定義模組來擴充現有模型,新增 AI 摘要欄位和生成摘要的伺服器動作,並且修改對應視圖讓使用者可以操作。程式碼編寫上,其實與一般 Python 應用並無二致,但結合 Odoo 的 MVC 架構之後,立即昇華為一個可供 ERP 端使用的功能點。

更棒的是,由於 Odoo 模組化的設計,我們的開發對原有系統是低侵入性的(不需要改核心碼,只是額外載入模組),未來升級 Odoo 或移除此功能也相對容易。這充分體現了 Odoo 作為整合平台的彈性與強大。

當前例僅是眾多可能性之一。透過類似的方式,你可以將各種 AI 能力融入不同業務場景,例如:自動分類客訴訊息、為電子郵件產生回覆草稿、根據產品描述自動標註標籤等等。開發者需要做的,就是運用 Odoo 模組開發技巧,適當地調用外部 AI 接口,並將結果接軌到系統流程中去。


選擇你的 API 串接策略

「直接呼叫 API」聽起來很簡單,但真的是最佳解嗎?面對不同的情境,我們需要不同的策略。就像選武器一樣,殺雞焉用牛刀,反之亦然。讓我們比較幾種常見的整合方式:

  • 直接呼叫 (Direct Call): 簡單粗暴,適合快速驗證功能,但使用者可能需要等待回應,且錯誤處理較麻煩。
  • 非同步佇列 (Async Queue): 使用 Odoo 的佇列工作,將 API 呼叫丟到背景執行。使用者體驗好,系統穩定性高,但架構較複雜。
  • 服務層封裝 (Service Layer): 建立一個專門處理 AI 溝通的 Python 類別。程式碼乾淨、易於維護和擴充,是大型專案的首選。

https://ithelp.ithome.com.tw/upload/images/20250917/20120208rKZbwEVoGt.png


今日結語

站在開發者的角度,Odoo 的模組化架構為我們打下了扎實的基礎:透過清晰分工的模型、視圖、控制器等部分,我們能有條不紊地擴充系統功能。

而透過 RPC、控制器與外部請求等機制,我們又能將 Odoo 作為數位中樞,與各種現代服務(尤其是 AI 平台)連結。本篇範例展示了將 OpenAI 串接進 Odoo 的一種實現,你或許已經開始想像更多可能性。

總而言之,掌握 Odoo 模組開發與 API 串接的方法,將讓我們有能力打造出智慧 ERP的雛形——把生成式 AI 的威力引入企業日常工作流。這正是為後續更深入的 AI 應用所做的技術準備與探路。下一篇文章,我們將更進一步,從開發流程本身出發,探討如何運用 Coding Agent 這類 AI 工具輔助 Odoo 開發(例如自動產生程式碼雛形等),讓開發工作更加高效輕鬆!


上一篇
Day 2:Odoo 架構解析:數位整合工具的基石
下一篇
Day 4:Coding Agent 助力 Odoo 開發:從需求到程式碼
系列文
Odoo × 生成式 AI:從零到一的企業自動化實戰4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言