ERP
    • ERP Frontend
    • ERP Backend
    • ERP Custom modules

    ERP Custom modules

    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#

    Tối thiểu:
    my_module/
    ├─ __init__.py
    ├─ __manifest__.py
    └─ models/
       ├─ __init__.py
       └─ *.py
    Thường gặp trong dự án:
    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 (metadata & dependency)#

    __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.
    Lưu ý:
    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#

    Các file thường cần có:
    security/ir.model.access.csv: quyền CRUD cơ bản cho model.
    security/*.xml: record rules, groups, ir.model.access bổ sung…
    Rule-of-thumb:
    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, Menus, Actions#

    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)#

    Nếu module có assets:
    Đặt dưới static/src/…
    Khai báo trong __manifest__.py → assets:
    web.assets_backend: cho backend
    web.assets_frontend: cho website
    Ví dụ pattern hay dùng:
    "my_module/static/src/**/*" (backend) để gom toàn bộ asset trong module.

    i18n/Translation#

    Chuẩn đặt file dịch:
    your_module/i18n/vi.po
    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#

    Trong UI:
    Apps → Update Apps List → tìm module → Install
    Hoặc CLI trong container:
    Update 1 module:
    Update tất cả:

    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)#

    Nếu attach debugger:
    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
    Apps → Update Apps List
    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ộ)#

    Path: addons/app/
    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):
    base, web, mail
    HR: hr, hr_attendance, hr_expense, hr_attendance_reason, hr_daily_checkin_tracker
    Data/Views chính:
    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
    Model/Field nổi bật:
    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
    Assets:
    Có khai báo web.assets_backend: "app/static/src/**/*" (JS/QWeb components…)
    Controllers/API/Session:
    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
    Tính năng chính:
    Extend hr.employee với field computed/store:
    last_attendance_state: checked_in / checked_out
    Action + Menu:
    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
    API/Helper cho frontend:
    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)
    Phân quyền:
    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
    Model chính:
    hr.overtime:
    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
    Security/Groups:
    Group:
    ohrms_overtime.group_hr_overtime_user (Overtime User)
    ohrms_overtime.group_hr_overtime_manager (Overtime Manager)
    Record rule:
    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))
    Ghi chú:
    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/
    Depends: project
    Tính năng chính:
    Extend project.task.type:
    thêm checkbox is_done_stage (trên form stage) để đánh dấu stage “hoàn thành”
    Extend project.task:
    override write(): nếu chuyển sang stage có is_done_stage = True thì set state = '1_done'
    Extend project.milestone:
    thêm start_date
    Security/Record rule:
    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)#

    Path: addons/user_role/
    Depends: base
    Mục đích: bổ sung khái niệm “role” đơn giản cho user và API trả về access rights theo model.
    Tính năng:
    Extend res.users:
    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)
    View:
    thêm field role_id vào tab “Access Rights” trong form user (base.view_users_form)
    API:
    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
    Trước
    ERP Backend
    Built with