Tài liệu addons/ (Custom modules)#
Mục đích#
Thư mục addons/ chứa custom modules (extra-addons) của dự án (module do team tự phát triển hoặc vendor/OCA đưa vào để override/mở rộng nghiệp vụ).Trong môi trường Docker, thư mục này thường được mount vào container Odoo tại:addons/ (host) → /mnt/extra-addons/ (container)
Và được khai báo trong config/odoo.conf thông qua addons_path (ví dụ: /mnt/extra-addons,/var/lib/odoo/addons).Nguyên tắc chung#
Mỗi module là một folder con trong addons/ và bắt buộc có __manifest__.py.
Không sửa trực tiếp code core trong odoo-server/ nếu có thể tránh được; ưu tiên override/extend bằng module ở addons/.
Tách bạch domain: module theo nghiệp vụ (HR, Helpdesk, Website, Integration...) thay vì gom tất cả vào một module “siêu to”.
License: module vendor/OCA có thể dùng AGPL-3/LGPL-3… giữ nguyên license và attribution trong manifest.
Cấu trúc chuẩn của một module Odoo#
my_module/
├─ __init__.py
├─ __manifest__.py
└─ models/
├─ __init__.py
└─ *.py
my_module/
├─ __init__.py
├─ __manifest__.py
├─ controllers/
│ ├─ __init__.py
│ └─ *.py
├─ data/
│ └─ *.xml
├─ demo/
│ └─ *.xml
├─ i18n/
│ ├─ vi.po
│ └─ *.po
├─ models/
│ ├─ __init__.py
│ └─ *.py
├─ security/
│ ├─ ir.model.access.csv
│ └─ *.xml
├─ static/
│ └─ src/
│ ├─ js/
│ ├─ scss/
│ ├─ xml/
│ └─ img/
├─ views/
│ └─ *.xml
└─ wizards/
├─ __init__.py
└─ *.py
Nếu là website/theme module, cấu trúc assets thường giống ví dụ website_airproof (tham khảo thêm trong README.md).__manifest__.py là “entrypoint” để Odoo nhận diện module:name/summary/description: thông tin module.
depends: danh sách module phụ thuộc (ví dụ base, mail, hr, …).
data: XML/CSV cần load khi cài/upgrade (views, security, data…).
demo: data chỉ load trong demo mode.
assets: khai báo assets cho backend/frontend (ví dụ web.assets_backend).
application: True nếu muốn module hiển thị như một app.
installable: bật/tắt khả năng cài đặt.
Khi thêm file XML mới (views, security, data…), cần bổ sung vào data trong manifest.
depends ảnh hưởng thứ tự load/upgrade; thiếu dependency dễ gây lỗi “model not found / external id not found”.
Security & Access control#
security/ir.model.access.csv: quyền CRUD cơ bản cho model.
security/*.xml: record rules, groups, ir.model.access bổ sung…
Nếu tạo model mới mà không có access CSV, Odoo thường sẽ báo lỗi truy cập khi mở menu/view.
Đặt tên group/rule rõ ràng theo module để tránh đụng namespace.
views/*.xml chứa ir.ui.view, ir.actions.act_window, menuitem, report, v.v.
Nếu module là “app”, thường sẽ có một file kiểu views/base_menu.xml để định nghĩa menu root và menu con.
Static assets (JS/SCSS/QWeb)#
Khai báo trong __manifest__.py → assets:web.assets_backend: cho backend
web.assets_frontend: cho website
"my_module/static/src/**/*" (backend) để gom toàn bộ asset trong module.
i18n/Translation#
Repo đã có hướng dẫn export/import translation trong README.md (phần i18n). Khi update text, có thể cần export lại .po rồi commit.Cách cài/upgrade module (dev)#
Cài module lần đầu#
Apps → Update Apps List → tìm module → Install
Hoặc CLI trong container:Khi nào cần -u#
Đổi XML views/menus/actions/data: cần -u <module> để nạp lại.
Đổi Python models (field/constraint): thường cần restart Odoo; nếu có thay đổi data/metadata, vẫn nên -u.
Đổi assets: tuỳ loại assets và cache; thường cần restart và refresh cache (hoặc bật dev mode assets).
Auto reload khi dev addons#
Repo đang có hướng dẫn dùng watchmedo để auto-restart khi code trong addons/ thay đổi (chạy trong container web). Tham khảo README.md / docs/backend.md để dùng đúng câu lệnh theo DB bạn đang chạy.Debug (VSCode/debugpy)#
localRoot trỏ vào ${workspaceFolder}/addons
remoteRoot trỏ vào /mnt/extra-addons/
Điều này giúp breakpoint map đúng file giữa host và container.Troubleshooting nhanh#
Odoo không thấy module trong Apps:kiểm tra addons_path có /mnt/extra-addons
kiểm tra module có __manifest__.py
Update module nhưng không thấy đổi view: đảm bảo file XML đã nằm trong data của manifest và chạy -u <module>.
Lỗi access khi mở menu: thiếu security/ir.model.access.csv hoặc record rule quá chặt.
Addons đang dùng trong repo (tóm tắt theo module)#
Phần này mô tả nhanh các module custom chính trong repo để dev/QA biết module làm gì, phụ thuộc gì, menu ở đâu, và điểm cần lưu ý khi nâng cấp.app (module tổng hợp UI/API nội bộ)#
Mục đích: module “app” tổng hợp nhiều tuỳ biến cho hệ thống (HR Attendance, Project, HR Expense/Leave…) và có assets backend.
Depends (theo __manifest__.py):HR: hr, hr_attendance, hr_expense, hr_attendance_reason, hr_daily_checkin_tracker
Menu root “Real Estate”: views/base_menu.xml (nhóm menu demo cho phần bất động sản)
Attendance: views/hr_attendance_view.xml (inherit) để hiển thị thêm report
Project: views/project_view.xml, views/project_task_view.xml
HR Expense: views/hr_expense_estimated_budget.xml
HR Leave: views/hr_leave_views.xml
Property/Owner/Tag: views/property_view.xml, views/owner_view.xml, views/tag_view.xml
hr.attendance: thêm 2 field text:report_check_in: “What are you working on today?”
report_check_out: “What did you finish today?”
project.project: thêm department_ids (Many2many) và các hàm helper phục vụ UI/API:get_project_tasks_count(domain)
get_project_members(domain) (dựa trên followers của project)
project.task: thêm priority (4 mức) + start_on + computed department_ids từ user_ids.department_id
Có khai báo web.assets_backend: "app/static/src/**/*" (JS/QWeb components…)
Override login JSON route /web/session/login để trả thêm:session_id, employee_id, và role
Extend /web/session/get_session_info để trả thêm user_employee_id
Lưu ý phụ thuộc ngầm: controller login có đọc user.role_id. Field này đến từ module user_role (nhưng app chưa khai báo depends: ['user_role']). Nếu user_role chưa cài mà app đã cài, login có thể lỗi.
hr_daily_checkin_tracker (tracking nhân viên chưa check-in trong ngày)#
Path: addons/hr_daily_checkin_tracker/
Mục đích: theo dõi tình trạng check-in/check-out hàng ngày; cung cấp menu “Nhân viên chưa checkin”.
Depends: hr_attendance, hr_holidays
Extend hr.employee với field computed/store:last_attendance_state: checked_in / checked_out
Action employee_not_checkin: list hr.employee với domain ('last_attendance_state','!=','checked_in')
Menu “Nhân viên chưa checkin” nằm dưới hr_attendance.menu_hr_attendance_root
Có filter theo trạng thái nghỉ phép (is_absent) và searchpanel theo phòng ban
hr.employee.get_attendance_daily(domain=None):Mặc định lấy attendance từ đầu ngày (check_in >= today 00:00)
Trả về { length, records } (gồm cả nhân viên chưa có attendance record)
Trong record có tách attendance_reason_check_in / attendance_reason_check_out (lọc theo action_type)
Menu “Nhân viên chưa checkin” giới hạn cho group hr_attendance.group_hr_attendance_manager.
ohrms_overtime (Open HRMS - quản lý tăng ca)#
Path: addons/ohrms_overtime/
Mục đích: quản lý đăng ký tăng ca (overtime) theo giờ/ngày, có workflow duyệt và rule phân quyền (vendor module từ Open HRMS/Cybrosys).
Depends: base, hr_attendance
Thời gian: date_from, date_to
Thời lượng: duration_type (hours/days), days_no_tmp (Hours), total_overtime_hours
Quy trình: state: draft → f_approve → approved / refused
Liên kết: employee_id, department_id, project_id, attendance_ids, overtime_type_id
Ràng buộc: không cho request overlap theo date_from/date_to (trừ state refused)
Khi approve và loại OT là “leave”: tạo hr.leave.allocation tương ứng
overtime.type: định nghĩa loại OT (cash/leave) và duration_type + rule lines
ohrms_overtime.group_hr_overtime_user (Overtime User)
ohrms_overtime.group_hr_overtime_manager (Overtime Manager)
User chỉ thấy OT của chính mình (current_user_id.id = user.id)
Manager thấy tất cả ((1,'=',1))
Module có i18n/vi.po và vendor docs README.rst, static/description/index.html.
project_extension (mở rộng Project/Task/Stage/Milestone)#
Path: addons/project_extension/
Extend project.task.type:thêm checkbox is_done_stage (trên form stage) để đánh dấu stage “hoàn thành”
override write(): nếu chuyển sang stage có is_done_stage = True thì set state = '1_done'
Extend project.milestone: security/project_task_security.xml có rule no_edit_in_done_state:nhóm áp dụng: base.group_user
domain: ('state','=','1_done')
chỉ read=1, chặn write/unlink/create
Lưu ý quan trọng: do rule domain lọc “record ở state done”, người dùng sẽ chỉ còn đọc được các task đã done (và không thấy các task chưa done) nếu rule được apply như hiện tại. Nếu mục tiêu là “task done thì không được sửa” nhưng vẫn xem/sửa task chưa done, cần chỉnh lại rule (domain OR/perm) cho phù hợp.
user_role (mở rộng res.users: role + API kiểm tra quyền)#
Mục đích: bổ sung khái niệm “role” đơn giản cho user và API trả về access rights theo model.
field role_id: user / manager / admin (default user)
override context_get() để inject context['role'] = <role_id> (để phía JS/backend có thể đọc từ context)
thêm field role_id vào tab “Access Rights” trong form user (base.view_users_form)
JSON route GET /api/user/permissions (auth=user, csrf=False)
trả về quyền read/write/create/delete cho tất cả model trong registry (bỏ abstract/transient), chỉ include model mà user có ít nhất 1 quyền
Ngày cập nhật 2026-04-09 07:50:05