Odoo 19: Lập trình chuyên sâu

Sê-ri ~200 bài lập trình Odoo 19 chuyên sâu: ORM, view/QWeb, OWL/JS, controller/website, tích hợp, test, deploy — có code và thao tác thật.

200/200 phần đã đăng Odoo
1 Trước khi viết dòng code Odoo đầu tiên: dựng môi trường dev đúng để về sau khỏi khổ Cài Odoo từ source, tách venv, viết odoo.conf và bật chế độ dev — nền tảng của mọi lập trình viên Odoo. Có cấu hình thật và kiểm chứng trên máy chủ Odoo 19. 17/09/2026 · 4 phút đọc 2 Vì sao Odoo của bạn chậm, ngốn RAM, hay treo? Câu trả lời nằm ở mấy dòng trong odoo.conf Giải mã các tham số quan trọng nhất của odoo.conf: workers, giới hạn RAM/thời gian, dbfilter, proxy_mode, log. Kèm file cấu hình thật đọc từ máy chủ Odoo 19. 17/09/2026 · 4 phút đọc 3 Đặt module vào đúng chỗ mà Odoo vẫn 'không thấy'? Hiểu cách Odoo quét addons_path để hết bối rối Odoo tìm module bằng cách quét thư mục addons lõi cộng addons_path, nhận diện qua __manifest__.py. Hiểu cơ chế này để cài module tự viết trơn tru. Có dữ liệu thật từ Odoo 19. 17/09/2026 · 4 phút đọc 4 Sửa một dòng Python rồi phải Ctrl+C khởi động lại Odoo mỗi lần? Cờ --dev=all xoá bỏ nghi thức đó Chế độ --dev của Odoo 19 gồm 6 tính năng: reload tự khởi động lại khi sửa .py, xml đọc view thẳng từ file, qweb/access/werkzeug để debug. Mổ xẻ từng cái kèm bằng chứng thật từ mã nguồn. 17/09/2026 · 5 phút đọc 5 Muốn thử một câu ORM mà không phải viết cả module rồi khởi động lại? odoo shell cho bạn một dấu nhắc Python vào thẳng cơ sở dữ liệu odoo shell là REPL Python có sẵn env, self, và toàn bộ ORM Odoo 19. Cách mở, search/read/create, và vì sao env.cr.commit() là bắt buộc khi ghi. Có output thật trên CSDL blog19. 17/09/2026 · 4 phút đọc 6 Log Odoo trôi hàng nghìn dòng mà bạn chỉ cần thấy câu SQL đang chậm? Bí quyết nằm ở --log-handler theo từng module Mổ xẻ một dòng log Odoo 19 gồm 6 phần, năm cấp log, và cách bật DEBUG chỉ cho đúng module cần soi bằng --log-handler MODULE:LEVEL. Có log SQL thật kèm thời gian truy vấn. 17/09/2026 · 4 phút đọc 7 Muốn học Odoo nhanh nhất? Đừng đọc tài liệu — mở thẳng 693 module code core mà Odoo đã viết sẵn ngay trong máy bạn Toàn bộ mã nguồn Odoo 19 nằm sẵn trong container. Cách điều hướng nó bằng grep để tìm model, người kế thừa, và học từ chính code core thay vì đoán mò. Có ví dụ thật từ sale_order.py. 17/09/2026 · 4 phút đọc 8 Lỡ commit cả thư mục __pycache__ và file odoo.conf chứa mật khẩu lên Git? Một file .gitignore đúng chặn được cả hai Quy trình Git cho một module Odoo: cấu trúc repo, .gitignore chuẩn (bỏ pyc và bí mật), quy ước version manifest, và làm việc trên nhánh tính năng. Có phiên git thật với module quan_ca_phe. 17/09/2026 · 4 phút đọc 9 Đừng ngồi gõ tay 10 file rỗng cho module mới — odoo scaffold dựng sẵn cả khung chuẩn trong đúng một dòng lệnh Lệnh odoo scaffold tạo sẵn toàn bộ khung thư mục một module Odoo: manifest, models, controllers, views, security, demo. Xem cấu trúc và nội dung thật nó sinh ra, và nên sửa gì trước tiên. 17/09/2026 · 3 phút đọc 10 Còn dùng print() để debug và để version manifest sai định dạng? pylint-odoo bắt hết những lỗi 'rất Odoo' mà pylint thường bỏ qua pre-commit chạy kiểm tra tự động trước mỗi commit; pylint-odoo thêm hàng loạt checker riêng cho Odoo (sai version, thiếu tác giả, print thay logger). Có output lint thật trên một module lỗi cố ý. 17/09/2026 · 3 phút đọc 11 Rải print() khắp code rồi đọc log để tìm bug? Đặt một breakpoint() và bạn dừng ngay tại chỗ, soi được mọi biến ORM đang sống pdb là trình gỡ lỗi tương tác có sẵn của Python. Cách đặt breakpoint trong code Odoo, chạy với workers 0, và các lệnh p/l/w/n/s/c. Có một phiên pdb thật soi dữ liệu ORM trên db blog19. 17/09/2026 · 3 phút đọc 12 Lỗi JavaScript báo ở dòng 1 của một file .min.js dài 1MB — làm sao debug? Bật assets dev là thấy đúng file gốc Odoo gộp hàng trăm file JS/CSS thành bundle nén 1MB kèm hash. Chế độ --dev=assets tách chúng ra để debug; source map trỏ lỗi về mã gốc. Có URL bundle thật và ảnh giao diện Odoo 19 thật. 17/09/2026 · 4 phút đọc 13 Một máy chủ Odoo lỡ hiện danh sách 'blog19, shop19' cho mọi khách vào — vá bằng đúng một dòng --db-filter Một tiến trình Odoo phục vụ được nhiều database PostgreSQL. --db-filter là regex chọn đúng database theo tên miền; list_db=False giấu danh sách. Có 2 database Odoo thật và ảnh trang chọn db. 17/09/2026 · 4 phút đọc 14 Cần chạy Odoo 17 của khách cũ và Odoo 19 của dự án mới trên cùng một máy? Bí quyết là mỗi bản một bộ cổng và data_dir riêng Nhiều instance Odoo chạy song song trên một máy chỉ cần khác http_port, gevent_port, data_dir và database. Có bằng chứng thật hai tiến trình Odoo cùng chạy trên cổng 8069 và 8070. 17/09/2026 · 4 phút đọc 15 Mỗi lần sửa code lại docker cp rồi restart container? Một dòng bind-mount trong compose xoá sạch nghi thức đó Docker Compose cho dev Odoo: bind-mount thư mục addons từ máy host để sửa code thấy ngay, kết hợp --dev=all. Có bằng chứng thật sửa file trên host và container đọc thấy tức thì. 17/09/2026 · 4 phút đọc 16 Trong Odoo, biến giữ 'một khách hàng' và biến giữ '500 khách hàng' là CÙNG một kiểu — hiểu recordset là hiểu nửa cái ORM Recordset là khái niệm nền tảng nhất của ORM Odoo: mọi thứ là tập bản ghi, kể cả tập rỗng và tập một phần tử. Cách nó hành xử như tập hợp Python, qua output shell thật trên db blog19. 17/09/2026 · 4 phút đọc 17 'ValueError: Expected singleton' — lỗi Odoo ám ảnh nhất, và tại sao ensure_one() lại là bạn chứ không phải thù Cách lấy đúng một bản ghi từ recordset: browse với id, duyệt bằng vòng for, và ensure_one để chốt tập một phần tử. Có lỗi Expected singleton thật chụp từ odoo shell trên db blog19. 17/09/2026 · 4 phút đọc 18 Tạo 200 bản ghi bằng vòng for create() mất gấp 3 lần một lệnh create() nhận cả danh sách — con số đo thật trong Odoo 19 create() tạo một bản ghi khi nhận dict, tạo hàng loạt khi nhận list dict. Vì sao create theo lô nhanh hơn hẳn create trong vòng lặp, với số đo thật trên db blog19. 17/09/2026 · 4 phút đọc 19 recs.write({...}) trên 100 bản ghi chỉ chạy MỘT câu UPDATE — và nhanh gấp 8 lần vòng lặp, đo thật trong Odoo 19 write() cập nhật cả recordset bằng một câu SQL. Gán trực tiếp thuộc tính là write ngầm; trường quan hệ dùng lệnh đặc biệt. Có số đo thật write cả tập vs vòng lặp trên db blog19. 17/09/2026 · 4 phút đọc 20 Viết len(search([])) để đếm bản ghi là nạp cả nghìn object chỉ để ra một con số — search_count có sẵn cho đúng việc đó Ba cách truy vấn trong Odoo: search trả recordset, search_count trả số, search_read trả list dict. Khác nhau về hiệu năng và mục đích, đo thật trên 3030 bản ghi db blog19. 17/09/2026 · 3 phút đọc 21 Lọc 'thành phố chứa minh' mà like không ra kết quả còn ilike ra 3 — hiểu domain là hiểu cách Odoo hỏi cơ sở dữ liệu Domain là ngôn ngữ lọc của Odoo: danh sách bộ ba (field, toán tử, value). Các toán tử =, in, like/ilike, =False, xuyên quan hệ bằng dấu chấm. Đếm thật trên db blog19. 17/09/2026 · 4 phút đọc 22 Vì sao dấu '|' trong domain Odoo lại đặt TRƯỚC hai điều kiện chứ không phải ở giữa? Hiểu ký pháp tiền tố là hết rối Domain nâng cao trong Odoo dùng toán tử tiền tố &, |, ! (ký pháp Ba Lan). Cách đọc, cách lồng OR/NOT, và cách khớp phép tập hợp. Có đếm thật kiểm chứng logic trên db blog19. 17/09/2026 · 4 phút đọc 23 Muốn tổng doanh thu theo trạng thái đơn hàng mà nạp cả nghìn đơn về Python để tự cộng? read_group cộng ngay trong cơ sở dữ liệu read_group là GROUP BY của Odoo: gom nhóm và tính sum/avg/count/min/max ngay ở tầng SQL, không nạp từng bản ghi. Nền tảng của mọi báo cáo và biểu đồ. Có output thật trên db blog19. 17/09/2026 · 4 phút đọc 24 Xóa một khách hàng mà Odoo báo 'cannot be deleted' — không phải lỗi, mà là khóa ngoại đang bảo vệ dữ liệu của bạn unlink() xóa bản ghi, nhưng bị chặn khi bản ghi đang được tham chiếu — hai kiểu chặn: UserError của Odoo và ForeignKeyViolation của CSDL. Vì sao nên archive thay vì xóa. Có lỗi thật trên db blog19. 17/09/2026 · 4 phút đọc 25 Nhân bản một đơn hàng mà số đơn vẫn là S00015 thì loạn ngay — copy=False chính là thứ giữ mỗi bản một số riêng copy() nhân bản một bản ghi trong Odoo: tự thêm (copy) vào tên, nhận default để ghi đè, và tôn trọng copy=False trên từng trường. Có output thật nhân bản partner và sale.order trên db blog19. 17/09/2026 · 4 phút đọc 26 Đổi số lượng một dòng hàng, tổng tiền đơn tự nhảy theo mà bạn không gọi hàm nào — phép màu đó tên là @api.depends Trường computed tự tính giá trị từ trường khác; @api.depends báo Odoo biết khi nào tính lại. Ba quy tắc bắt buộc khi viết, và demo thật tổng đơn hàng tự cập nhật trên db blog19. 17/09/2026 · 4 phút đọc 27 Trường tính toán tìm kiếm và sắp xếp được, trường kia thì báo 'Cannot convert to SQL' — khác biệt chỉ nằm ở store=True Computed store=True lưu xuống CSDL (tìm/sắp/báo cáo được), không store thì tính mỗi lần đọc. Đánh đổi hiệu năng đọc–ghi và khi nào chọn cái nào. Có bằng chứng cột SQL và lỗi thật trên db blog19. 17/09/2026 · 4 phút đọc 28 Gõ order_id.partner_id.name khắp nơi mỏi tay? Một dòng related='order_id.partner_id' biến nó thành một trường phẳng dùng thẳng Trường related rút gọn một chuỗi quan hệ thành một trường phẳng, không cần viết hàm compute. Bản chất nó là computed rút gọn, store được để tìm/sắp. Có ví dụ thật từ sale.order.line trên db blog19. 17/09/2026 · 3 phút đọc 29 Muốn chặn điểm tích luỹ âm dù người dùng nhập kiểu gì? @api.constrains viết luật một lần, Odoo canh mọi lần lưu @api.constrains cho phép viết luật kiểm tra hợp lệ bằng Python, chạy mỗi khi trường liên quan đổi. Sai thì raise ValidationError và rollback. Có constraint tự viết chạy thật trên module quan_ca_phe. 17/09/2026 · 4 phút đọc 30 Odoo 19 vừa khai tử _sql_constraints: ràng buộc CSDL giờ viết bằng models.Constraint — và tôi có bằng chứng nó tạo UNIQUE/CHECK thật Ràng buộc ở tầng PostgreSQL (unique, check) nhanh và chắc hơn kiểm bằng Python. Odoo 19 thay _sql_constraints bằng models.Constraint. Có cảnh báo deprecate thật, ràng buộc sinh trong pg_constraint, và lỗi kích thật. 17/09/2026 · 4 phút đọc 31 Chọn khách hàng, ô mã thẻ tự điền và hiện cảnh báo 'khách chưa mua' — đó là @api.onchange, và nó KHÔNG phải hàng rào chặn dữ liệu @api.onchange phản ứng ngay khi người dùng nhập trên form: tự điền ô khác, hiện cảnh báo. Nhưng nó không lưu, không chặn, không chạy từ API. Có onchange tự viết chạy thật trên module quan_ca_phe. 17/09/2026 · 4 phút đọc 32 Mở form tạo thẻ mới, ngày cấp đã điền sẵn hôm nay và hạng thẻ là 'Bạc' — ba cách đặt giá trị mặc định trong Odoo 19 default tĩnh cho hằng số, default callable cho giá trị động (ngày hôm nay, người dùng), default_get cho logic phức tạp. Thứ tự ưu tiên và cách chọn. Có output thật trên module quan_ca_phe. 17/09/2026 · 4 phút đọc 33 Odoo 19 lại khai tử name_get: muốn ô Many2one hiện 'TV-777 (Vàng) - Cà Phê Việt' thì giờ override _compute_display_name display_name quyết định tên hiển thị của bản ghi trong ô Many2one, breadcrumb, danh sách. Odoo 19 bỏ name_get, chuyển hẳn sang _compute_display_name. Có bằng chứng name_get biến mất và display_name tuỳ biến chạy thật. 17/09/2026 · 3 phút đọc 34 Nhân viên gõ tên khách vào ô 'Thẻ' mà không ra thẻ nào? Một dòng _rec_names_search cho tìm khớp cả mã thẻ lẫn tên khách _rec_name chọn trường định danh; _rec_names_search mở rộng các trường mà ô Many2one tìm khớp khi gõ. Odoo 19 override _search_display_name cho logic phức tạp. Có name_search chạy thật trên db blog19. 17/09/2026 · 4 phút đọc 35 Danh sách đơn hàng luôn hiện đơn mới nhất trên đầu mà không ai bấm sắp xếp — đó là _order, một dòng định đoạt thứ tự mặc định _order đặt thứ tự sắp xếp mặc định cho mọi truy vấn của model, thành ORDER BY của SQL. Cú pháp, override tại chỗ, và bẫy hiệu năng khi sắp theo trường không index. Có thứ tự thật trên db blog19. 17/09/2026 · 4 phút đọc 36 Gán order.line = [1,2,3] để thêm dòng hàng thì Odoo báo lỗi khó hiểu — trường quan hệ nhiều bản ghi cần bộ lệnh Command (0..6) One2many/Many2many không gán trực tiếp mà dùng Command: create, update, delete, unlink, link, clear, set. Bảy lệnh và khi nào dùng. Có tất cả Command chạy thật trên module quan_ca_phe. 17/09/2026 · 3 phút đọc 37 CSDL lưu 'dang_dung' nhưng người dùng thấy 'Đang dùng' — đó là trường Selection, và bạn còn sinh được lựa chọn động bằng một hàm Trường Selection là danh sách lựa chọn: mã lưu CSDL, nhãn hiển thị. Khai tĩnh bằng list cặp, hoặc động bằng tên method sinh lúc chạy. Có cả hai kiểu chạy thật trên module quan_ca_phe. 17/09/2026 · 3 phút đọc 38 Lưu số tiền bằng Float thì 123456.789 VND vẫn còn phần lẻ vô nghĩa — trường Monetary tự làm tròn về đồng theo đúng tiền tệ Trường Monetary xử lý số tiền kèm tiền tệ: bắt buộc có currency_field, tự làm tròn theo số chữ số thập phân của tiền, hiển thị đúng ký hiệu. Có làm tròn VND vs USD chạy thật trên db blog19. 17/09/2026 · 4 phút đọc 39 Nhập '<script>alert(1)</script>' vào ô ghi chú Odoo mà không hack được ai — trường Html tự làm sạch mọi mã độc trước khi lưu Ba trường nội dung phong phú của Odoo: Html (văn bản định dạng, tự làm sạch XSS), Binary (tệp nhị phân), Image (ảnh tự resize). Cách dùng và bẫy. Có sanitize gỡ script chạy thật trên module quan_ca_phe. 17/09/2026 · 3 phút đọc 40 Đơn tạo lúc 5 giờ chiều nhưng báo cáo ghi 10 giờ sáng? Odoo lưu mọi Datetime bằng UTC — hiểu điều này là hết lệch giờ Odoo lưu Datetime theo UTC, hiển thị theo múi giờ người dùng; Date thì không có giờ, không tz. Cách dùng đúng fields.Datetime.now, context_timestamp, và các bẫy lệch 7 giờ. Có múi giờ chạy thật trên db blog19. 17/09/2026 · 3 phút đọc 41 Một trường trỏ được lúc thì tới khách hàng, lúc thì tới đơn hàng, lúc thì tới người dùng — đó là Reference, 'Many2one động' của Odoo Trường Reference trỏ tới bản ghi của model bất kỳ trong danh sách, lưu chuỗi model,id. Many2oneReference tách id và tên model để nhanh hơn. Có 1 field trỏ 3 model chạy thật trên module quan_ca_phe. 17/09/2026 · 3 phút đọc 42 Cho người dùng tự thêm trường vào bản ghi mà không cần lập trình viên và không ALTER TABLE — phép màu đó tên là Properties Trường Properties cho mỗi bản ghi một schema linh hoạt do bản ghi cha định nghĩa, lưu trong một cột JSONB. Cách khai PropertiesDefinition + Properties, và output thật trên project.task db blog19. 17/09/2026 · 4 phút đọc 43 env.cr, env.uid, env.context — ba thứ mọi recordset Odoo mang theo, và hiểu chúng là hiểu 'ai đang làm gì với dữ liệu nào' env là môi trường ORM: cr (con trỏ CSDL), uid/user (người dùng), context (ngôn ngữ, múi giờ), company (công ty). Mọi recordset mang theo env. Có giá trị env thật chụp từ odoo shell db blog19. 17/09/2026 · 4 phút đọc 44 Cùng một câu search cho 30 hay 33 bản ghi tùy một cờ trong context — with_context là cách đổi 'bối cảnh' mà không đụng dữ liệu with_context tạo recordset mới với context thay đổi: đổi ngôn ngữ, thấy bản ghi đã lưu trữ (active_test), truyền cờ tùy biến. Context cộng dồn, không sửa env gốc. Có output thật đổi lang và active_test trên db blog19. 17/09/2026 · 3 phút đọc 45 sudo() trong Odoo không hoá bạn thành admin — nó chỉ tắt cái công tắc kiểm quyền Nhiều người tưởng sudo() đổi user hiện tại thành quản trị viên. Không phải. Bài này dùng odoo shell thật chỉ ra sudo() giữ nguyên user, chỉ bật cờ su để bỏ qua access rights và record rules — và cách dùng nó cho an toàn. 17/09/2026 · 6 phút đọc 46 with_user và with_company: khi bạn cần chạy code 'dưới danh nghĩa người khác' — chứ không phải tắt kiểm quyền như sudo sudo() bỏ qua quyền. with_user đổi hẳn sang một user và áp đúng quyền của họ. with_company đổi công ty đang hoạt động. Ba thứ hay bị nhầm, phân biệt bằng odoo shell thật trên Odoo 19. 17/09/2026 · 5 phút đọc 47 env.ref('base.group_user'): cách Odoo tìm đúng một bản ghi qua cái tên bất biến, thay vì id số dễ đổi External id (XML id) là tên định danh ổn định của một bản ghi. env.ref biến cái tên đó thành recordset. Hiểu bảng ir.model.data đứng sau nó, tham số raise_if_not_found, và chiều ngược get_external_id — qua odoo shell thật. 17/09/2026 · 5 phút đọc 48 Vì sao tv.diem vẫn ra 120 dù bạn vừa UPDATE nó thành 999 trong database — cache ẩn của ORM Odoo ORM Odoo giữ giá trị field trong một bộ đệm để đỡ chạm database. Rất nhanh, nhưng nếu bạn ghi thẳng bằng SQL thô, cache lệch và bạn đọc ra số sai. Hiểu cache và ba cách invalidate qua odoo shell thật. 17/09/2026 · 5 phút đọc 49 Gọi write() xong mà database vẫn chưa đổi: cơ chế 'ghi lười' của ORM Odoo và vai trò của flush write() trong Odoo không UPDATE ngay xuống database — nó gom thay đổi vào cache rồi mới đẩy một lượt. flush là lúc thật sự chạm ổ đĩa. Hiểu khi nào ORM tự flush và khi nào bạn phải gọi tay, qua odoo shell thật. 17/09/2026 · 5 phút đọc 50 Lặp qua 20 bản ghi, đọc field mỗi vòng, mà chỉ tốn 1 câu SELECT: prefetch của ORM Odoo Vòng lặp for trên recordset đọc field tưởng chừng gây N+1 query, nhưng đo thật chỉ ra ORM Odoo nạp field cho cả lô một lượt nhờ prefetch. Hiểu cơ chế này, cái gì phá nó, và cách chủ động điều khiển — qua phép đo query thật. 17/09/2026 · 4 phút đọc 51 Một dòng nối chuỗi vào cr.execute đủ để lộ cả bảng: gọi SQL thô an toàn trong Odoo 19 Đôi khi ORM không đủ và bạn phải viết SQL tay. cr.execute rất mạnh nhưng nối chuỗi input vào là mở toang cửa cho SQL injection. Hiểu tham số hoá %s, đối tượng SQL của Odoo 19, và cặp flush/invalidate — qua ví dụ injection thật. 17/09/2026 · 5 phút đọc 52 Cái hộp thoại 'cộng điểm hàng loạt' trong Odoo thật ra là một model — chỉ khác là dữ liệu tự bốc hơi sau 1 giờ Mọi wizard trong Odoo đứng trên một TransientModel: model có bảng thật nhưng bản ghi tự dọn định kỳ. Tự tay dựng một wizard cộng điểm cho module cà phê và chạy thử, để hiểu vòng đời và cách nạp dữ liệu từ active_ids. 17/09/2026 · 4 phút đọc 53 Viết một lần, dùng cho mọi model: AbstractModel và mixin — model không có bảng nhưng cho đi field và method AbstractModel là model không sinh bảng, dùng để gói field và logic dùng lại rồi trộn vào nhiều model bằng _inherit. Tự dựng một mixin ghi chú và gắn vào model thẻ thành viên, chạy thử trên hệ thống thật. 17/09/2026 · 4 phút đọc 54 Sao res.users.name lại nằm ở bảng res_partner? Bí mật của _inherits — kế thừa uỷ quyền trong Odoo _inherits (có chữ s) khác hẳn _inherit: nó nối model của bạn vào một model khác qua Many2one, cho phép mượn toàn bộ field và tự tạo bản ghi liên kết. Mổ xẻ quan hệ res.users và res.partner qua dữ liệu thật. 17/09/2026 · 4 phút đọc 55 Quên @api.model_create_multi hay quên super() là dữ liệu sai âm thầm: ghi đè create/write/unlink đúng cách trong Odoo 19 Ghi đè create/write/unlink là cách chèn logic vào vòng đời bản ghi, nhưng đầy bẫy: create nhận danh sách vals, super() phải gọi đúng chỗ, hậu xử lý phải chạy trên bản ghi đã tạo. Tự dựng một model có ba override và chạy thử. 17/09/2026 · 4 phút đọc 56 sheet, group lồng group, notebook: ba khối XML dựng nên mọi form đẹp trong Odoo 19 Một form view gọn gàng của Odoo không phải nhờ CSS mà nhờ bố cục XML chuẩn: sheet làm khung, group lồng group chia hai cột, notebook gom tab. Dựng form thật cho thẻ thành viên và xem nó hiện ra trên giao diện. 17/09/2026 · 4 phút đọc 57 Danh sách thẻ tự sắp theo điểm, hàng VIP tô xanh, chân bảng cộng tổng: list view Odoo 19 làm được hết bằng thuộc tính XML List view của Odoo không chỉ là bảng phẳng. Vài thuộc tính XML cho bạn sắp xếp mặc định, tô màu dòng theo điều kiện, cộng tổng cột số, ẩn cột và cột tuỳ chọn. Dựng list thật cho thẻ thành viên và xem kết quả. 17/09/2026 · 4 phút đọc 58 Mỗi thẻ thành viên một tấm card, badge tự đổi màu theo điểm: kanban view và QWeb trong Odoo 19 Kanban biến mỗi bản ghi thành một tấm thẻ HTML do bạn tự thiết kế bằng template QWeb. Hiểu t-name card, t-esc chèn giá trị, t-attf-class đổi màu theo điều kiện — qua một kanban thật cho thẻ thành viên. 17/09/2026 · 4 phút đọc 59 Mở màn hình là đã tự nhóm theo hạng thẻ: search view và bí mật của search_default trong Odoo 19 Search view khai báo các field tìm được, các bộ lọc bấm sẵn và cách nhóm dữ liệu. Còn search_default_ trong context của action là thứ bật sẵn một filter hay group ngay khi mở màn hình. Dựng search view thật và xem nó chạy. 17/09/2026 · 4 phút đọc 60 Khai ba dòng XML, người dùng tự xoay số liệu và vẽ biểu đồ: pivot và graph view trong Odoo 19 Pivot biến dữ liệu thành bảng chéo cộng tổng theo hàng và cột; graph vẽ biểu đồ cột, đường, tròn. Cả hai chỉ cần khai field nào là hàng, cột, thước đo — không viết code báo cáo. Dựng thật cho thẻ thành viên. 17/09/2026 · 4 phút đọc 61 Xem thẻ trên lịch theo ngày cấp, gắn việc 'gọi mời gia hạn' vào từng thẻ: calendar và activity view Odoo 19 Calendar view hiện bản ghi trên lịch theo một field ngày, tô màu theo phân loại. Activity view gom việc cần làm trên từng bản ghi — nhưng model phải kế thừa mail.activity.mixin. Dựng cả hai và lên lịch một việc thật. 17/09/2026 · 4 phút đọc 62 Thẻ Vàng thì hiện thêm ô ghi chú, thẻ từ 100 điểm thì bắt buộc chọn khách: form tự đổi theo dữ liệu với invisible/readonly/required Odoo 19 bỏ cú pháp attrs cũ: giờ đặt biểu thức Python thẳng vào invisible, readonly, required trên field. Form tự ẩn/hiện, khoá/mở, bắt buộc theo giá trị người dùng nhập. Dựng thật và xem hai trạng thái. 17/09/2026 · 4 phút đọc 63 Thẻ khoá thì đỏ, thẻ hết hạn thì cam, thẻ VIP thì xanh đậm: tô màu từng dòng list bằng decoration decoration-* cho phép tô màu và đổi kiểu chữ từng dòng list theo một biểu thức điều kiện. Bảy giá trị màu, ghép được nhiều loại, và một cạm bẫy: field trong biểu thức phải có trong list. Dựng thật với đủ màu. 17/09/2026 · 4 phút đọc 64 Cùng một trường điểm, hiện thành con số hay thanh tiến độ là do một chữ widget: chọn đúng widget trong Odoo 19 widget đổi cách một field hiển thị và cho nhập — badge, progressbar, image, monetary, many2many_tags, priority, boolean_toggle... Chọn đúng widget làm form vừa đẹp vừa đúng chức năng. Xem nhiều widget chạy thật trên một form. 17/09/2026 · 4 phút đọc 65 Nút 'Gia hạn thẻ' bấm một cái là +20 điểm: button type object gọi method Python, type action mở màn hình khác Nút trên form Odoo có hai loại chính: type object gọi thẳng một method Python trên bản ghi, type action mở một action (wizard, màn hình khác). Hiểu chúng khác nhau ra sao, method trả về gì, và một cạm bẫy thứ tự nạp. Dựng nút thật. 17/09/2026 · 4 phút đọc 66 Dải 'Mới cấp → Đang dùng → Hết hạn' ở đầu form: biến một field Selection thành quy trình bấm được với statusbar statusbar biến một field trạng thái thành dải các bước quy trình ngay đầu form. Hiểu statusbar_visible để ẩn bước ngoại lệ, options clickable để bấm chuyển, và cách kết hợp với nút quy trình. Dựng thật. 17/09/2026 · 4 phút đọc 67 Khung trao đổi bên phải form tự ghi lại '120 → 150 điểm' mà không cần code: chatter và mail.thread trong Odoo 19 Chatter là khung bên phải form: lịch sử thay đổi, ghi chú, tin nhắn, hoạt động — tất cả gắn vào bản ghi. Chỉ cần model kế thừa mail.thread, thêm tracking=True, và một thẻ chatter. Dựng thật với log thay đổi và ghi chú. 17/09/2026 · 4 phút đọc 68 Cùng một form, admin thấy 'Số dư' còn nhân viên thì không: ẩn field và nút theo nhóm với thuộc tính groups Thuộc tính groups trên field, nút, tab hay group ẩn phần tử đó với ai không thuộc nhóm. Hiểu các nhóm hay dùng, và khác biệt quan trọng giữa groups ở view (ẩn giao diện) và ở model (bảo mật thật). Xem hai user thấy hai form khác nhau. 17/09/2026 · 4 phút đọc 69 Một template, bốn thẻ, tự tô màu VIP và cộng tổng 385 điểm: QWeb backend và các directive t-* trong Odoo 19 QWeb là ngôn ngữ template của Odoo, dùng cho kanban, báo cáo, email và website. Hiểu các directive cốt lõi t-foreach, t-if, t-esc, t-set, t-att, t-call — và render một template thật bằng ir.qweb để thấy chúng chạy. 17/09/2026 · 4 phút đọc 70 Thêm một tab 'Thẻ thành viên' vào form Liên hệ của Odoo mà không đụng một dòng code lõi: view inherit với xpath và position View inheritance cho phép chèn, sửa, xoá phần tử trong view của module khác mà không chép lại. Hiểu inherit_id, xpath tìm phần tử, và các position (inside/after/before/replace/attributes). Xem một tab thật được thêm vào form Liên hệ. 17/09/2026 · 4 phút đọc 71 Cùng một view gốc, thêm mode='primary' là ra một form biến thể mới mà không đụng bản gốc: primary vs extension trong Odoo Kế thừa view có hai chế độ: extension sửa view gốc tại chỗ (mọi nơi dùng nó đều đổi), primary tạo một view mới dựa trên gốc mà gốc không đổi. Hiểu khi nào dùng cái nào, qua một form biến thể thật. 17/09/2026 · 5 phút đọc 72 Chuyển field 'Hạng thẻ' từ cột trái sang cột phải mà không nhân đôi nó: position move và replace trong Odoo Muốn dời một field sang chỗ khác trong form, hay thay nó bằng phiên bản có widget khác? position move di chuyển field có sẵn, replace thay hoặc xoá. Hiểu khác biệt và cạm bẫy field trùng tên, qua một field được dời thật. 17/09/2026 · 4 phút đọc 73 Gắn nút 'Thẻ TV: 1' lên form Liên hệ của Odoo để bấm là xem thẻ của khách: thêm nút vào form app khác Kết hợp inherit model (thêm method + field) với inherit view (chèn nút qua xpath) để thêm một nút thống kê vào form của app khác. Nút bấm mở danh sách liên quan. Dựng nút Thẻ thành viên thật trên form Liên hệ. 17/09/2026 · 4 phút đọc 74 Một dải banner tự đếm '4 thẻ thành viên' chèn vào form lúc chạy: view động qua get_view trong Odoo 19 Khi XML tĩnh không đủ, bạn có thể ghi đè get_view (trước là fields_view_get) để sửa cấu trúc view bằng code lúc chạy — thêm field, cột, hay banner theo dữ liệu thật. Hiểu cách và khi nào nên dùng, qua một banner sinh động. 17/09/2026 · 4 phút đọc 75 Bấm 'Cấp thẻ Vàng' là form mở ra đã điền sẵn hạng Vàng, 100 điểm, đang dùng: context truyền từ action vào view context của action là kênh nhẹ để đặt giá trị mặc định, bật filter, hay đổi hành vi form mà không đụng cấu trúc view. Hiểu default_, search_default_, và cách đọc cờ tuỳ biến — qua một form tự điền sẵn. 17/09/2026 · 4 phút đọc 76 Gom thẻ theo hạng rồi theo trạng thái, mỗi nhóm tự cộng tổng điểm: sắp xếp và nhóm nhiều cấp trên list Odoo Ba tầng điều khiển thứ tự và nhóm: _order của model, default_order của list, và group_by của search view. Nhóm nhiều cấp tự tính subtotal. Hiểu cách phối hợp và vì sao field không store không sort được. 17/09/2026 · 4 phút đọc 77 One2many mà phải bấm mở form con cho từng dòng thì ai chịu nổi — editable list cho gõ thẳng trên bảng editable list biến danh sách con trong form thành nơi nhập liệu trực tiếp: bấm vào ô là sửa được ngay, không mở form con. Mổ xẻ editable top/bottom, required/readonly/column_invisible theo cột, và onchange chạy khi gõ — trên form thật của Odoo 19. 17/09/2026 · 5 phút đọc 78 Form Odoo dài lê thê phải cuộn mỏi tay? Bọc nửa dưới vào một cặp thẻ và nó gập lại thành các tab gọn gàng notebook và page là cách Odoo chia một form dài thành các tab. Mỗi <page> là một tab; đặt string, invisible, autofocus, groups để điều khiển. Thêm một tab thật vào form của module demo rồi xem kết quả trên Odoo 19. 17/09/2026 · 5 phút đọc 79 Cái nút hình hộp có con số to đùng ở đầu mọi form Odoo: đó là smart button, và tự làm một cái chỉ tốn 20 dòng Smart button là nút thống kê ở đầu form: hiện một con số tổng hợp và bấm vào là mở danh sách bản ghi liên quan. Ghép một field compute, widget statinfo và một method trả act_window — dựng từ đầu trên module demo Odoo 19. 17/09/2026 · 4 phút đọc 80 Cùng một trường quan hệ nhiều-nhiều, đổi mỗi chữ 'widget' là giao diện nhảy từ bảng con sang badge màu hay ô tick Many2many mặc định hiển thị thành bảng con, nhưng many2many_tags cho badge gọn có màu còn many2many_checkboxes cho ô tick. Chọn đúng widget theo bản chất dữ liệu — minh hoạ bằng cùng một field trên form Odoo 19 thật. 17/09/2026 · 4 phút đọc 81 Muốn cho người dùng kéo–thả sắp lại thứ tự các dòng? Odoo làm được chỉ với một field ẩn tên sequence và bốn chữ widget='handle' Widget handle biến một cột số thứ tự thành cái tay nắm để kéo–thả sắp lại thứ tự các dòng. Cần đúng hai thứ: field sequence và _order theo nó. Xem nó hoạt động trên One2many thật của Odoo 19, kèm ảnh trước và sau khi kéo. 17/09/2026 · 4 phút đọc 82 Ngôi sao bấm một cái là xong, cái công tắc xanh, nhãn màu 'Vàng/Bạc': ba widget nhỏ khiến bảng dữ liệu Odoo bớt khô khan hẳn badge biến Selection thành nhãn màu, priority thành dãy sao bấm nhanh, boolean_toggle thành công tắc. Ba widget hiển thị nhỏ mà đổi hẳn trải nghiệm đọc và sửa nhanh trên bảng — xem cả ba cùng lúc trên list thật của Odoo 19. 17/09/2026 · 4 phút đọc 83 Ô 'chọn khách hàng' đang cho chọn cả 6 công ty lẫn 25 cá nhân — thêm một dòng domain là nó chỉ còn hiện đúng người bạn muốn domain trên field giới hạn những bản ghi được phép chọn trong Many2one/Many2many. Có domain tĩnh, domain động theo field khác, và domain đặt ngay trong định nghĩa field. Xem nó lọc thật trên Odoo 19 qua dropdown và odoo shell. 17/09/2026 · 4 phút đọc 84 Bấm 'Thêm một dòng' mà ngày, điểm, ghi chú đã điền sẵn giùm: bí mật là một khoá tên default_ trong context của field context trên field truyền giá trị mặc định (default_<field>) xuống bản ghi con khi tạo nhanh. Khác domain: domain lọc chọn được, context điền sẵn khi tạo. Xem 'Thêm một dòng' tự điền ngày/điểm/ghi chú trên Odoo 19 thật. 17/09/2026 · 4 phút đọc 85 Người dùng cứ hỏi 'ô này điền gì?' — thay vì trả lời từng lần, gắn cho field một cái nhãn rõ hơn và một dòng tooltip là xong string đổi nhãn hiển thị của field, help thêm tooltip khi rê chuột vào dấu hỏi. Đặt ở view cho một màn hình, hoặc ở định nghĩa field cho mọi nơi. Xem nhãn Chủ thẻ và tooltip thật trên form Odoo 19. 17/09/2026 · 4 phút đọc 86 Dòng chữ xám mờ trong ô trống không phải dữ liệu — và cái JSON bí ẩn sau chữ 'options' mới là nơi bạn tinh chỉnh widget placeholder là gợi ý mờ hiện khi ô trống, biến mất khi gõ. options là JSON đổi cách một widget hoạt động: màu tag, cấm tạo mới, số chữ số thập phân... Xem placeholder thật và các options đang chạy trên Odoo 19. 17/09/2026 · 4 phút đọc 87 Vì sao form Odoo mặc định mỗi field một hàng, còn muốn xếp hai field cạnh nhau thì phải hiểu con số 'col' đằng sau mỗi group group chia form thành cột; col đặt số cột logic, colspan cho field chiếm rộng, và group lồng group dựng bố cục nhiều cột. Xem bố cục 2 cột kinh điển và một nhóm col=4 chạy thật trên Odoo 19. 17/09/2026 · 4 phút đọc 88 Không cần lồng thêm group để chia đoạn cho form — một thẻ separator kẻ vạch kèm tiêu đề là gom được các field liên quan lại separator kẻ một vạch ngang kèm tiêu đề để chia một group thành các đoạn có nhãn, gọn hơn lồng group con. Xem ba đoạn được separator gom lại trên form Odoo 19 thật. 17/09/2026 · 3 phút đọc 89 Dải nút với thanh trạng thái hình mũi tên trên đầu form, và cái tên bản ghi to đùng ngay dưới — đó là header và oe_title header là dải trên cùng của form chứa nút hành động và statusbar; oe_title là khối tiêu đề lớn của bản ghi; oe_avatar đẩy ảnh về góc phải. Ba mảnh dựng nên phần đầu quen thuộc của mọi form Odoo 19. 17/09/2026 · 4 phút đọc 90 Bảng kanban chia cột theo trạng thái, mỗi cột có một thanh màu tóm tắt — và cái bẫy khiến nó nổ OwlError chỉ vì một dấu nháy default_group_by chia kanban thành cột theo field; progressbar vẽ thanh phân bố màu trên đầu mỗi cột. Kèm một bẫy thật của Odoo 19: colors phải là JSON nháy kép, không phải dict Python. Xem kanban nhóm chạy thật. 17/09/2026 · 4 phút đọc 91 Cái bảng lọc dọc bên trái danh sách Odoo, bấm một cái là gọn còn đúng nhóm mình cần — dựng nó chỉ tốn một thẻ searchpanel searchpanel là bảng lọc dọc bên trái list và kanban, chia dữ liệu theo danh mục để bấm một cái là thu hẹp. Phân biệt select one và multi, enable_counters, icon. Xem panel lọc theo hạng và trạng thái chạy thật trên Odoo 19. 17/09/2026 · 4 phút đọc 92 Cùng một dữ liệu, lúc là bảng thẻ kéo-thả, lúc là biểu đồ cột — chỉ nhờ một chuỗi phân tách bằng phẩy tên là view_mode view_mode trong ir.actions.act_window liệt kê mọi kiểu view một action cho phép; thứ tự quyết định view mở đầu và thứ tự nút chuyển. Xem cùng một action hiện thành kanban và graph trên Odoo 19 thật. 17/09/2026 · 4 phút đọc 93 Người dùng mở app lần đầu thấy một cái bảng trắng trơn và không biết bấm gì — chữa bằng field help với cái mặt cười của Odoo no-content help là đoạn HTML hiện giữa màn hình khi danh sách rỗng, hướng người dùng tạo bản ghi đầu tiên. Khai bằng field help trên action, class o_view_nocontent_smiling_face thêm mặt cười. Xem nó hiện thật trên Odoo 19. 17/09/2026 · 4 phút đọc 94 Muốn cái tên thẻ in màu nâu cà phê với vạch nhấn bên trái, mà không đụng một dòng code lõi Odoo? Một file SCSS của riêng module là đủ Tuỳ biến giao diện backend Odoo bằng SCSS: thêm file .scss vào module, khai trong assets, nhắm class của Odoo hoặc class riêng để đổi màu, khoảng cách, nền. Xem form thẻ được nhuộm màu cà phê thật trên Odoo 19. 17/09/2026 · 4 phút đọc 95 File SCSS bạn viết cho module biến đi đâu? Odoo nuốt nó vào một 'bó' rồi nhả ra đúng một tệp .min.css khổng lồ — và tôi grep ra được nó nằm đó assets_backend là cơ chế Odoo gom file CSS/JS/XML của module thành một bó, minify, phục vụ một URL. Hiểu các bó (backend/frontend/POS), glob, và bộ chỉ dẫn chèn tương đối. Kèm bằng chứng grep từ bó thật trên Odoo 19. 17/09/2026 · 4 phút đọc 96 Cùng một action mở model, nhưng đổi mỗi chữ 'new' ở field target là nó bật lên thành popup thay vì chuyển cả trang ir.actions.act_window là loại action phổ biến nhất; các field target, res_id, domain, context, view_id, limit quyết định action mở ra cái gì và mở như thế nào. Xem wizard bật lên dạng dialog nhờ target=new trên Odoo 19. 17/09/2026 · 4 phút đọc 97 Không phải action nào cũng mở màn hình — có loại chỉ lặng lẽ chạy code, và tôi vấp ngay 'forbidden opcode' khi viết the.diem += 5 Server action (ir.actions.server) chạy logic mà không mở view; field state chọn loại việc: code, object_create, object_write, multi, mail_post... Gắn qua nút, menu Tác vụ, hoặc tự động hoá. Kèm một bẫy sandbox thật của Odoo 19. 17/09/2026 · 3 phút đọc 98 Khi list, kanban, form đều không vẽ nổi cái màn hình bạn muốn — viết một Component OWL rồi cắm vào Odoo bằng client action Client action (ir.actions.client) là màn hình do JavaScript OWL tự vẽ ở trình duyệt, dùng khi các view có sẵn không đủ. Đăng ký Component vào registry actions, tag khớp action record. Dựng một dashboard OWL lấy dữ liệu thật trên Odoo 19. 17/09/2026 · 4 phút đọc 99 Loại action đơn giản nhất Odoo: không view, không JS, chỉ một dict ba khoá là mở được trang ngoài hay tải một file PDF URL action (ir.actions.act_url) chỉ điều hướng trình duyệt tới một url; target new mở tab mới, self mở cùng tab. Khai bằng record XML hoặc method Python trả dict. Xem method trả act_url thật và nút mở website trên Odoo 19. 17/09/2026 · 3 phút đọc 100 Từ một bản ghi thẻ thành viên tới tờ PDF có logo, địa chỉ công ty và chân trang — tất cả nhờ report action trỏ vào một template QWeb Report action (ir.actions.report) biến bản ghi thành tài liệu in được: trỏ tới một template QWeb, Odoo render HTML rồi xuất PDF. Hiểu docs, external_layout, t-field, report_name. Xem một thẻ thành viên in ra thật trên Odoo 19. 17/09/2026 · 4 phút đọc 101 Hộp thoại 'cộng điểm hàng loạt' cần một model để chứa dữ liệu người dùng gõ — nhưng lưu vĩnh viễn thì phí, nên Odoo có TransientModel Wizard trong Odoo là một TransientModel: bản ghi tạm cho hộp thoại nhập liệu, tự dọn định kỳ, không để rác trong CSDL. Hiểu default_get lấy bản ghi được chọn và method đóng dialog. Xem một wizard cộng điểm chạy thật trên Odoo 19. 17/09/2026 · 4 phút đọc 102 Wizard 'chọn rồi xác nhận' hai màn hình mà vẫn là một hộp thoại: bí quyết là một field state và cái mẹo cho method mở lại chính nó Wizard nhiều bước trong Odoo dùng một field state để ẩn/hiện từng nhóm và đổi nút; method đổi state rồi trả về action mở lại chính wizard (cùng res_id) nên dữ liệu vẫn còn. Xem hai bước Chọn và Xác nhận chạy thật trên Odoo 19. 17/09/2026 · 4 phút đọc 103 Wizard chạy xong đừng chỉ đóng lại bỏ mặc người dùng — cho method trả về một action để đưa họ thẳng tới danh sách kết quả Method của wizard trả được bất kỳ action nào, không chỉ act_window_close. Trả act_window để mở danh sách bản ghi vừa xử lý, một form, hay act_url/report. Xem wizard cộng điểm dẫn thẳng tới danh sách thẻ vừa cập nhật trên Odoo 19. 17/09/2026 · 4 phút đọc 104 Năm cái directive bắt đầu bằng t- là đủ để biến một bản ghi thành trang in có bảng lịch sử và dòng tổng cộng tự tính Viết template QWeb cho PDF: t-field tự format, t-esc in biểu thức, t-if/t-else rẽ nhánh, t-foreach lặp dòng con, t-set cộng dồn tổng. Dựng một bảng lịch sử điểm có tổng trên report Odoo 19 thật. 17/09/2026 · 3 phút đọc 105 Report của bạn đang tự động in trên A4 dọc — muốn nó ra khổ A5 nằm ngang với lề riêng thì thêm một report.paperformat rồi gán vào paperformat khai khổ giấy, hướng dọc/ngang, lề và DPI cho bản in; gán vào report qua paperformat_id. Lưu ý paperformat chỉ áp cho PDF, không đổi bản HTML. Đo khổ trang PDF thật đổi từ A4 sang A5 ngang trên Odoo 19. 17/09/2026 · 3 phút đọc 106 Sửa một mẫu in của Odoo mà không đụng một dòng nào của bản gốc: inherit_id + xpath Report trong Odoo cũng là QWeb template, nên kế thừa được y như view. Khai inherit_id trỏ tới mẫu gốc rồi dùng xpath position=after/before/attributes để chèn và sửa — bản gốc giữ nguyên. Xem trước/sau trên PDF thật của Odoo 19. 18/09/2026 · 6 phút đọc 107 Vì sao đổi số điện thoại công ty mà chân trang PDF vẫn không đổi? Header/footer report đọc từ đâu Mọi report của Odoo dùng chung một header/footer qua web.external_layout, nội dung lấy từ res.company. Nhưng dòng chân trang KHÔNG đọc trường phone — nó đọc report_footer. Xem nguồn thật và một phép thử trước/sau trên PDF Odoo 19. 18/09/2026 · 5 phút đọc 108 Nhét mã QR quét được vào thẻ PDF của Odoo — và vì sao cách 'đúng sách' lại ra ô trống Odoo sinh QR, Code128, EAN13... qua ir.actions.report.barcode(). Nhưng nhúng bằng URL /report/barcode trong PDF có thể ra ô rỗng khi workers=0. Cách chắc ăn: sinh ảnh thành Binary rồi image_data_uri. Đo thật trên Odoo 19. 18/09/2026 · 5 phút đọc 109 Hỏi người dùng trước khi in: dựng một wizard chọn tham số rồi mới xuất PDF trong Odoo Một wizard TransientModel thu tham số rồi trả về report_action(records, data). Nhưng data KHÔNG vào QWeb dưới tên data — các khoá của nó trải thành biến rời. Dựng wizard In thẻ theo hạng thật trên Odoo 19 và xem data điều khiển nội dung PDF. 18/09/2026 · 5 phút đọc 110 In 4 thẻ một lần lại ra 5 trang: cách QWeb gộp nhiều bản ghi vào một file PDF Chọn nhiều bản ghi rồi in là ra một file PDF gộp — nhờ t-foreach docs và class page trong QWeb. Nhưng số trang không bằng số bản ghi: bản ghi dài tự tràn trang. Đo bằng PyPDF2 trên Odoo 19. 18/09/2026 · 4 phút đọc 111 Bảng sao kê có cộng dồn theo tháng và một dòng TỔNG CỘNG: kỹ thuật t-set tích luỹ trong QWeb QWeb không có hàm SUM. Muốn cộng dồn, gộp nhóm, ra subtotal và tổng cuối, bạn dùng một biến t-set tích luỹ qua vòng lặp. Dựng bảng sao kê điểm nhóm theo tháng, đo số khớp trên PDF thật Odoo 19. 18/09/2026 · 4 phút đọc 112 Một mẫu in, ba khuôn mặt: dùng t-if/t-elif/t-else để report tự đổi theo trạng thái bản ghi QWeb chọn nhánh hiển thị bằng t-if, t-elif, t-else — mỗi trạng thái thẻ ra một banner khác. Và t-if đơn giúp bỏ hẳn phần rỗng thay vì in khoảng trắng. Xem ba thẻ thật in ra ba kiểu trên Odoo 19. 18/09/2026 · 4 phút đọc 113 Đưa SCSS vào bản in Odoo đúng chuẩn — rồi phát hiện style không ăn: chuyện bundle report và wkhtmltopdf Odoo có bundle web.report_assets_common để nạp CSS/SCSS cho mọi report. Nhưng nó vào PDF qua link ngoài mà wkhtmltopdf phải fetch — với workers=0 thì style rơi mất. Cách chắc ăn và giới hạn WebKit, đo trên Odoo 19. 18/09/2026 · 4 phút đọc 114 Menu mở ra đã lọc sẵn, bấm Mới là điền sẵn: bí mật nằm ở domain và context của action ir.actions.act_window có hai trường quyền lực: domain lọc trước danh sách, context với default_X điền sẵn field và search_default_X bật sẵn bộ lọc. Dựng một action lọc-và-điền thật rồi soi kết quả trên Odoo 19. 18/09/2026 · 4 phút đọc 115 Tick 4 thẻ, bấm một lần, cả 4 cùng cộng điểm: action chạy trên nhiều bản ghi qua active_ids Chọn nhiều dòng rồi vào menu Tác vụ là chạy một action trên cả lô. Bí mật là active_ids biến lựa chọn thành một recordset, và code chỉ việc lặp records. Đo thật một server action cộng điểm hàng loạt trên Odoo 19. 18/09/2026 · 4 phút đọc 116 Vì sao lệnh của bạn hiện trong menu Tác vụ còn report thì nằm ở nút In: ba trường binding binding_model_id, binding_type, binding_view_types quyết định action của bạn xuất hiện ở đâu trong menu ngữ cảnh. Server action vào Tác vụ, report vào In — cùng một cơ chế. Soi registry get_bindings thật trên Odoo 19. 18/09/2026 · 4 phút đọc 117 Để Odoo tự chuyển thẻ quá hạn mỗi đêm mà không ai bấm nút: khai một ir.cron ir.cron là việc chạy nền theo lịch. Khai một record trỏ tới method của model, đặt chu kỳ, và scheduler tự gọi. Cách test không cần chờ, và những field Odoo 19 đã bỏ. Dựng cron rà soát hạn thẻ thật. 18/09/2026 · 4 phút đọc 118 Viết thẳng Python vào ô Mã của cron: những biến Odoo đưa sẵn cho bạn, và thứ sandbox chặn state='code' cho phép cron chạy đoạn Python nhiều dòng. Odoo cấp sẵn env, model, log, datetime, Command, UserError... nhưng không có 'fields' và chặn import. Danh sách biến đo thật và một ví dụ chạy được trên Odoo 19. 18/09/2026 · 4 phút đọc 119 Cron chờ đồng hồ, Automated Action chờ sự kiện: hai cơ chế tự động của Odoo khác nhau chỗ nào Cron chạy theo lịch định kỳ; Automated Action chạy ngay khi một bản ghi được tạo/sửa/xoá. Dựng một quy tắc on_create thật và đo nó bắn tức thì, so với cron phải chờ nextcall. Trên Odoo 19. 18/09/2026 · 4 phút đọc 120 Một mẫu email điền sẵn tên khách và số điểm: khai mail.template bằng XML, dùng {{ }} và t-out mail.template là email có chỗ trống điền động. Subject và địa chỉ dùng {{ }}, còn body_html dùng QWeb t-out. Khai bằng XML trong module rồi render thật cho một thẻ trên Odoo 19 — chưa gửi, chỉ xem. 18/09/2026 · 4 phút đọc 121 send_mail xong rồi mà khách chưa nhận thư: email vào hàng đợi chứ chưa gửi ngay Gọi send_mail không gửi email tức thì — nó tạo một mail.mail state outgoing chờ cron. force_send=True mới gửi ngay nhưng phụ thuộc SMTP. Đo thật trạng thái thư trên Odoo 19, cùng message_post và mail.mail.send. 18/09/2026 · 4 phút đọc 122 Giao việc 'gọi khách gia hạn' cho nhân viên ngay từ code: một dòng activity_schedule Activity là việc-cần-làm gắn vào bản ghi, có hạn và người phụ trách. Model kế thừa mail.activity.mixin gọi activity_schedule là xong. Xem nó hiện trong chatter và cách đánh dấu hoàn tất, đo thật trên Odoo 19. 18/09/2026 · 4 phút đọc 123 Ghi một dòng vào khung trao đổi từ code: message_post, và vì sao 'ghi chú' khác 'bình luận' message_post ghi vào chatter của bản ghi. Nhưng subtype quyết định ai được báo: mt_note chỉ lưu vết nội bộ, mt_comment gửi thông báo cho follower. Đo thật hai loại trên Odoo 19. 18/09/2026 · 4 phút đọc 124 Vì sao mã thẻ nhảy từ 0002 sang 0004: ir.sequence và cái bẫy khoảng trống ir.sequence tự cấp mã tăng dần (TV-2026-0001...). Nhưng chế độ standard tiêu số ngay cả khi giao dịch rollback — nên có khoảng trống. Khi nào chấp nhận, khi nào phải dùng no_gap. Đo thật trên Odoo 19. 18/09/2026 · 4 phút đọc 125 Đính kèm hợp đồng vào một bản ghi từ code — và tệp đó thực ra nằm ở đâu ir.attachment gắn file vào bất kỳ bản ghi nào qua res_model và res_id. Nhưng bytes không nằm trong CSDL — chúng ở filestore trên đĩa, DB chỉ giữ metadata và checksum. Đo thật trên Odoo 19. 18/09/2026 · 4 phút đọc 126 Một màn hình Odoo tự vẽ bằng JavaScript: giải phẫu component OWL đầu tiên OWL là framework giao diện của Odoo 19. Một component gồm ba mảnh: lớp JS với setup và state, template QWeb-OWL, và đăng ký vào registry. Dựng một dashboard lấy số liệu thật qua ORM, chạy trong trình duyệt. 18/09/2026 · 3 phút đọc 127 t-out trong OWL không render HTML như bạn tưởng: chuyện escape, markup và XSS Trong template OWL, t-esc luôn escape. Nhưng t-out với một chuỗi thường CŨNG escape — khác hẳn report QWeb. Muốn render HTML phải bọc markup(). Dựng demo thật trong dashboard OWL trên Odoo 19. 18/09/2026 · 4 phút đọc 128 Đổ một mảng thẻ ra bảng trong OWL: t-foreach, và vì sao t-key không phải trang trí t-foreach lặp qua mảng để dựng danh sách trong OWL. Nhưng t-key là bắt buộc — và dùng id thay vì chỉ số quyết định OWL tái dùng DOM đúng khi danh sách đổi. Dựng bảng thẻ thật trên Odoo 19. 18/09/2026 · 3 phút đọc 129 Bấm nút 3 lần mà số không nhúc nhích: useState và bí mật của trạng thái phản ứng trong OWL Trong OWL, gán this.state.x làm giao diện tự vẽ lại; gán this.x thì không. useState trả về một proxy theo dõi thay đổi. Đo thật bằng hai bộ đếm cạnh nhau trên Odoo 19. 18/09/2026 · 4 phút đọc 130 Tách một thẻ thành component riêng và truyền dữ liệu xuống: props trong OWL Ứng dụng thật gồm nhiều component lồng nhau. Cha truyền dữ liệu xuống con qua props — một chiều, có kiểm kiểu. Con chỉ đọc, không sửa. Dựng component con TheCard nhận props thật trên Odoo 19. 18/09/2026 · 4 phút đọc 131 Vì sao chạm vào DOM trong setup luôn null: hooks vòng đời onWillStart và onMounted Component OWL có vòng đời: setup, onWillStart, render, onMounted. Nạp dữ liệu ở onWillStart; chạm DOM ở onMounted vì trước đó node chưa tồn tại. Đo thật thứ tự hook qua Console trên Odoo 19. 18/09/2026 · 4 phút đọc 132 Bấm một thẻ để chọn nó: bắt sự kiện với t-on trong OWL, và cái bẫy khi lặp danh sách t-on-click gắn handler cho sự kiện. Gọi thẳng method thì nhận event; muốn truyền tham số (item trong t-foreach) phải dùng arrow function. OWL không có modifier .stop/.prevent như Vue. Đo thật trên Odoo 19. 18/09/2026 · 3 phút đọc 133 Component OWL của bạn cần gọi server, hiện toast, mở một view — nó không tự làm, nó mượn 'service' useService là cách một component OWL lấy về những mảnh chức năng dùng chung của web client: orm để nói chuyện với server, notification để hiện toast, action để mở view. Mổ xẻ cơ chế và thử thật trên Odoo 19. 18/09/2026 · 6 phút đọc 134 Gọi ORM của Odoo ngay từ JavaScript: searchRead, searchCount, và cái bẫy readGroup đã bị đổi tên ở Odoo 19 orm service là cầu nối để component OWL đọc/ghi dữ liệu qua đúng ORM của Odoo mà không tự dựng request. Xem searchRead/searchCount/call chạy thật, và vì sao readGroup của bạn ném lỗi trên Odoo 19. 18/09/2026 · 5 phút đọc 135 Khi orm không đủ: gọi thẳng một controller Python tự viết từ JavaScript — và hai cái tên Odoo 19 đã lặng lẽ đổi rpc là cách component OWL gọi một route HTTP bất kỳ và nhận JSON, dùng khi orm không phủ được. Nhưng ở Odoo 19 rpc không còn là service, và type='json' đã thành 'jsonrpc'. Xem cả hai lỗi thật và cách sửa. 18/09/2026 · 5 phút đọc 136 Cái 'registry' mà mọi module OWL đều gọi tới: cách bạn nhét thêm một mảnh giao diện vào Odoo mà không sửa lõi registry là hệ thống sổ đăng ký của web client Odoo — nơi client action, systray, field widget, view đăng ký tên để được nhận diện. Đăng ký thật một mục systray hiện điểm và xem nó xuất hiện trên thanh trên cùng. 18/09/2026 · 5 phút đọc 137 Không thích cách Odoo hiển thị một field? Viết widget của riêng bạn: biến ô 'điểm' thành huy hiệu đổi màu Field widget quyết định một field hiển thị và cho nhập thế nào trong view. Tự viết một widget OWL: đọc giá trị qua props.record, đăng ký vào category fields, gắn vào form bằng widget=. Làm thật trên Odoo 19. 18/09/2026 · 5 phút đọc 138 1234567 hiện thành '1.234.567', và người dùng gõ 'abc' thì sao? Hai hàm formatter và parser đứng giữa dữ liệu và ô nhập Formatter biến giá trị thô thành chuỗi đẹp theo locale; parser làm ngược lại và ném lỗi khi người dùng gõ sai. Đây là cặp hàm mọi field widget cho nhập đều cần. Gọi thật formatInteger, parseFloat trên Odoo 19. 18/09/2026 · 4 phút đọc 139 list, form, kanban đều chỉ là 'object' trong một sổ đăng ký — nên bạn viết được loại view thứ tư của riêng mình Một view trong Odoo là một object JS với Controller, Renderer, Model, ArchParser. Kế thừa một view có sẵn và thay đúng mảnh bạn cần, rồi gắn qua js_class. Làm thật một view kanban có dải tổng kết trên Odoo 19. 18/09/2026 · 4 phút đọc 140 Thêm một icon có dropdown lên thanh trên cùng Odoo: ba số liệu và một nút mở màn hình, gói trong systray Systray là dải icon bên phải thanh điều hướng Odoo. Viết một mục systray bài bản: icon FontAwesome, badge đếm, dropdown hiện số liệu và một mục mở view. Chú ý sequence quyết định vị trí. Làm thật trên Odoo 19. 18/09/2026 · 4 phút đọc 141 Khi form và list không đủ: tự vẽ nguyên một màn hình trong Odoo bằng client action OWL Client action là một màn hình do component OWL toàn quyền vẽ, mở từ menu như một app. Không bị ràng buộc bởi view chuẩn. Dựng thật một bảng xếp hạng thẻ theo điểm và mở nó từ menu trên Odoo 19. 18/09/2026 · 4 phút đọc 142 Cần đổi hành vi một component lõi của Odoo mà không được chép cả file: patch() là câu trả lời patch chèn thêm hoặc thay method của một class/component có sẵn ngay tại chỗ, super gọi lại bản gốc, và áp cho mọi instance. Vá thật component KanbanRenderer lõi và bắt bằng chứng ở console Odoo 19. 18/09/2026 · 4 phút đọc 143 Toast trong Odoo không chỉ có 'thành công' màu xanh: type, sticky, và cả nút hành động ngay trong thông báo notification service bật toast góc phải: bốn loại màu, tùy chọn dính (sticky) để không tự tắt, và nút bấm ngay trong toast. Xem cả ba loại hiện thật cạnh nhau trên Odoo 19 và khi nào dùng thay cho dialog. 18/09/2026 · 3 phút đọc 144 Khi cần một câu trả lời trước khi đi tiếp: dialog service mở hộp thoại chặn ngay giữa màn hình Odoo dialog service mở một hộp thoại chặn màn hình cho tới khi người dùng chọn xong. ConfirmationDialog là mẫu có sẵn với title, body và callback confirm/cancel. Dựng thật một hộp xác nhận xoá trên Odoo 19. 18/09/2026 · 4 phút đọc 145 Code JS của bạn im lặng không chạy, không một dòng lỗi? Rất có thể bạn khai nhầm asset bundle Odoo gom JS/CSS/template thành các bundle, mỗi trang chỉ nạp bundle của nó. web.assets_backend cho web client, web.assets_frontend cho website. Đặt nhầm bundle là code chết lặng lẽ — chứng minh thật trên Odoo 19. 18/09/2026 · 4 phút đọc 146 Đổi một dòng SCSS, cả form đổi màu theo: biến Sass và cách ghi đè biến theme của Odoo Odoo viết style bằng SCSS. Biến $ khai một lần dùng khắp nơi, nesting gom selector, và ghi đè biến theme để đổi màu toàn hệ thống. Xem một biến đổi màu cả form qua ảnh thật trước và sau trên Odoo 19. 18/09/2026 · 4 phút đọc 147 Ba loại QWeb dễ nhầm làm một: dựng một trang web công khai bằng template QWeb và controller website QWeb website render HTML phía server cho trang công khai, khác QWeb của OWL (trình duyệt) và của report (PDF). Dựng thật một trang bảng điểm công khai với controller website=True và t-foreach trên Odoo 19. 18/09/2026 · 4 phút đọc 148 Viết một 'thẻ' một lần rồi dùng lại khắp nơi: t-call và cái khe bí ẩn t-out='0' trong QWeb t-call chèn một template QWeb vào chỗ khác, template con thấy chung context. Nội dung con của lời gọi rơi vào khe số 0. Dùng để tách phần dùng chung. Làm thật một trang gọi lại template card qua ảnh trên Odoo 19. 18/09/2026 · 4 phút đọc 149 Cùng một field, hai kết quả: t-esc in '1234.5', t-field in '$ 1.234,50' — vì t-field hiểu kiểu dữ liệu t-field đọc kiểu của field và tự định dạng: Monetary ra ký hiệu tiền, Selection ra nhãn, Date ra định dạng ngày. Còn cho sửa tại chỗ trên website. So sánh thật t-field với t-esc trên cùng một bản ghi Odoo 19. 18/09/2026 · 4 phút đọc 150 Một component 'khung thẻ' dùng cho ba nội dung khác nhau: slots của OWL cho phép nơi gọi quyết định ruột bên trong Slots để component cha nhét nội dung tuỳ ý vào những chỗ định sẵn của component con — giống khe 0 của QWeb nhưng cho component OWL. Có slot mặc định và slot đặt tên. Làm thật ba thẻ dùng chung một khung trên Odoo 19. 18/09/2026 · 4 phút đọc 151 Một component 300 dòng làm mọi thứ là ác mộng bảo trì: cắt nó thành sub-component, dữ liệu chảy xuống props, sự kiện dội lên callback Chia một component OWL lớn thành các con nhỏ: cha truyền dữ liệu xuống bằng props, con báo ngược lên bằng callback prop. Mỗi con tự lo phần của mình. Làm thật trên dashboard Odoo 19 với ảnh click và toast. 18/09/2026 · 4 phút đọc 152 Khi bạn thật sự cần chạm vào DOM: useRef để tự focus một ô, đo kích thước thật, vẽ lên canvas OWL vẽ DOM cho bạn, nhưng đôi khi cần chạm thẳng vào node thật: focus, đo px, gọi thư viện ngoài. useRef + t-ref là cách an toàn — nhưng .el chỉ có sau khi mount. Làm thật một ô tự focus và đo bề rộng trên Odoo 19. 18/09/2026 · 4 phút đọc 153 Vue có 'computed', React có useMemo — OWL thì chỉ cần một getter JavaScript bình thường Giá trị dẫn xuất từ state không nên lưu riêng (dễ lệch). OWL không có cơ chế computed riêng: chỉ dùng getter JS, đọc state phản ứng, tự cập nhật mỗi lần render. Xem tổng/trung bình/cao nhất tự đổi theo state trên Odoo 19. 18/09/2026 · 4 phút đọc 154 Một widget lỗi kéo sập trắng cả màn hình? onError bọc nó lại, phần còn lại vẫn sống Một lỗi khi render một component OWL có thể làm hỏng cả cây giao diện. onError (error boundary) bắt lỗi từ component con, hiện một thông báo thay thế, và giữ phần còn lại của UI hoạt động. Làm thật trên Odoo 19. 18/09/2026 · 4 phút đọc 155 Kiểm thử JavaScript trong Odoo 19: QUnit đã lùi, Hoot lên thay — viết một test và xem nó xanh ở /web/tests Odoo 19 chuyển bộ khung test frontend từ QUnit sang Hoot: cú pháp describe/test/expect gọn như Jest. Viết một test cho hàm thuần, khai vào bundle unit test, và chạy nó thật trên trình chạy /web/tests. 18/09/2026 · 4 phút đọc 156 Kiểm thử cả một luồng người dùng, không chỉ một hàm: tour của Odoo bấm nút và điền form giúp bạn Tour là kịch bản mô phỏng người dùng đi qua nhiều bước trên UI thật — bấm, điền, chuyển màn hình. Dùng để kiểm thử end-to-end (và làm hướng dẫn onboarding). Viết một tour và chạy nó thật trên Odoo 19, thấy SUCCEEDED. 18/09/2026 · 4 phút đọc 157 Cái khối kéo–thả trong trình dựng website Odoo thật ra chỉ là một đoạn HTML: viết snippet đầu tiên của bạn Snippet là khối nội dung người dùng kéo–thả vào trang bằng trình dựng website. Nó chỉ là một template <section> HTML, cộng một dòng đăng ký vào bảng chọn. Tạo một snippet thật và render nó trên Odoo 19. 18/09/2026 · 3 phút đọc 158 Thêm bảng tùy chỉnh cho snippet của bạn: Odoo 19 viết lại toàn bộ API options thành html_builder Plugin Snippet options là bảng nút cho người dùng chỉnh khối trong trình dựng website. Odoo 19 bỏ we-select/SnippetOptionWidget cũ, chuyển sang html_builder Plugin với BuilderRow/BuilderSelect và classAction. Làm thật một option đổi tông màu. 18/09/2026 · 4 phút đọc 159 Thêm lệnh của riêng bạn vào trình biên tập website Odoo: gõ '/' là ra, một Plugin html_editor là đủ Không chỉ tùy chỉnh một snippet — bạn mở rộng được chính trình biên tập: thêm lệnh vào powerbox (menu gõ /), nút vào thanh công cụ. Odoo 19 làm qua Plugin với user_commands và powerbox_items. Code thật, nạp sạch. 18/09/2026 · 4 phút đọc 160 Mỗi thẻ Kanban tự vẽ một thanh tiến độ: field widget OWL không chỉ dùng cho form, mà cho cả card Field widget bạn viết cho form dùng lại được ngay trong thẻ Kanban. Gắn widget='ten' lên một field trong t-name='card' là mỗi thẻ tự render một mảnh JS. Làm thật một thanh tiến độ điểm trên Odoo 19. 18/09/2026 · 4 phút đọc 161 Cái avatar tròn cạnh tên người bạn thấy khắp Odoo: many2one_avatar — một chữ widget, không dòng JS nào many2one_avatar hiển thị một field Many2one kèm ảnh đại diện của bản ghi liên quan. Có biến thể riêng cho user, nhân viên. Không ảnh thì tự vẽ avatar-chữ-cái. Thấy thật trên kanban Odoo 19. 18/09/2026 · 3 phút đọc 162 Vì sao một ô 'Ghi chú' trong Odoo lại có nút đậm, nghiêng, gạch đầu dòng — còn ô kia thì không? Bí mật nằm ở fields.Html field Html không lưu chuỗi thô như Text hay Char — nó lưu HTML có định dạng và tự nhúng trình soạn thảo html_editor vào form. Mổ xẻ sanitize, widget='html', các options tùy biến qua field ghi_chu_html thật trên Odoo 19. 18/09/2026 · 6 phút đọc 163 _t('Save') ra 'Lưu', còn tên quán của bạn thì giữ nguyên: cách Odoo 19 dịch chuỗi ngay trong JavaScript Hàm _t bọc mọi chuỗi hiển thị trong front-end Odoo để nó tự đổi theo ngôn ngữ người dùng. Mổ xẻ nguồn dữ liệu, cơ chế 'lười' mới của Odoo 19 (bỏ hẳn _lt), và các kiểu tham số — qua _t gõ thẳng trên hệ thống thật. 18/09/2026 · 7 phút đọc 164 Trình soạn web của Odoo nặng vài megabyte — nhưng nó chỉ tải về đúng lúc bạn bấm 'Sửa': kỹ thuật lazy load component Nạp trễ (lazy load) hoãn tải một khối giao diện nặng đến đúng lúc cần, thay vì gánh hết ngay từ đầu. Mổ xẻ loadBundle, LazyComponent và dynamic import trong Odoo 19 — qua lõi thật và network fetch bundle chụp trực tiếp. 18/09/2026 · 6 phút đọc 165 Gõ một dòng vào console là thấy cả cây giao diện Odoo: WebClient, NavBar và những 'lưới an toàn' ErrorHandler quanh mỗi menu OWL để lộ toàn bộ cây component đang chạy qua window.__OWL_DEVTOOLS__ và this.__owl__. Cách xem state/props của bất kỳ component nào, đọc cây từ gốc WebClient, và vì sao mỗi menu systray được bọc trong một error boundary — soi trực tiếp trên Odoo 19. 18/09/2026 · 5 phút đọc 166 Không cần view, không cần OWL: 12 dòng Python biến /quan_ca_phe/thong_ke thành một URL thật trả JSON http.Controller và @route là cách Odoo biến một hàm Python thành một địa chỉ web. Mổ xẻ type http vs jsonrpc, auth, csrf và đối tượng request — qua controller thật dựng trên quan_ca_phe rồi curl lấy kết quả sống. 18/09/2026 · 5 phút đọc 167 Cùng một dòng code request.env.user, ba route trả ba câu trả lời khác nhau: bí mật nằm ở auth auth trong @route quyết định ai được gọi và code chạy dưới danh nghĩa ai. Phân biệt user, public, none (và bearer mới của Odoo 19) qua ba route thật cùng thân hàm, curl lấy kết quả sống khi chưa và đã đăng nhập. 18/09/2026 · 5 phút đọc 168 Cùng trả về {'so_the': 4}, ba route cho ba dạng JSON khác nhau: khi nào bọc phong bì, khi nào trả thô Controller trả JSON cho front-end OWL và cho API ngoài có nhiều đường: jsonrpc bọc phong bì result, http trả thô tự set header, và json2 mới của Odoo 19 trả REST-style. So sánh qua ba route thật, curl lấy kết quả sống. 18/09/2026 · 5 phút đọc 169 request.render một dòng, Odoo tự lắp header và footer quanh bảng của bạn thành trang web hoàn chỉnh Controller không chỉ trả JSON — nó dựng được cả trang website. Mổ xẻ request.render, template QWeb kế thừa website.layout, và website=True qua một trang bảng vàng thành viên dựng thật rồi chụp trực tiếp trên Odoo 19. 18/09/2026 · 4 phút đọc 170 Một trang web thật với URL riêng, không một dòng Python: khai website.page bằng XML và để Odoo tự tạo route Trang tĩnh không cần controller. Khai một template QWeb và một bản ghi website.page trỏ url + view_id, Odoo tự tạo route và cho quản lý trong Website > Pages. Dựng thật một trang thể lệ, chụp trực tiếp trên Odoo 19. 18/09/2026 · 4 phút đọc 171 Trang /my của Odoo: mỗi khách đăng nhập vào chỉ thấy đúng đơn, hoá đơn, thẻ của chính mình — cơ chế đằng sau là portal Portal là khu 'Tài khoản của tôi' cho khách hàng đăng nhập. Mổ xẻ CustomerPortal, override _prepare_home_portal_values để thêm ô đếm, và portal.mixin (access_url/access_token) — qua một ô Thẻ thành viên gắn thật vào /my của Odoo 19. 18/09/2026 · 5 phút đọc 172 Trang /my/the với bảng thẻ, nút sắp xếp và phân trang 1-2-3: dựng bằng portal_pager và t-call portal.portal_table Sau ô đếm ở /my, đây là trang danh sách đầy đủ của portal: liệt kê bản ghi của chính khách, phân trang bằng portal_pager, sắp xếp bằng searchbar_sortings. Dựng thật route /my/the và chụp bảng + pager trên Odoo 19. 18/09/2026 · 4 phút đọc 173 Quên một dòng csrf_token, cả biểu mẫu đăng ký trả về 400: cách nhận dữ liệu từ khách qua website form đúng cách Dựng một form công khai để khách gửi dữ liệu vào Odoo: route GET render form, route POST tạo bản ghi. Mổ xẻ csrf_token bắt buộc, sudo cho public user, validate và honeypot chống bot — qua form đăng ký dựng thật rồi submit trên Odoo 19. 18/09/2026 · 5 phút đọc 174 File khách gửi lên không nằm trong request.params — nó ở httprequest.files, và đây là cách đọc rồi lưu an toàn Nhận file upload trong controller Odoo: đọc từ request.httprequest.files, kiểm loại và kích thước, lưu thành ir.attachment. Demo thật một form tải ảnh, upload một PNG và xác nhận attachment trên Odoo 19. 18/09/2026 · 5 phút đọc 175 Trả một file cho khách tải về mà tên 'danh-sách-thẻ.csv' không bị vỡ dấu: bí mật ở content_disposition Controller trả file để tải xuống: make_response với Content-Type và Content-Disposition đúng, content_disposition cho tên tiếng Việt có dấu, và Stream cho file lớn. Demo thật một route xuất CSV rồi curl xem header trên Odoo 19. 18/09/2026 · 5 phút đọc 176 Trang tùy biến của bạn Google không bao giờ thấy — cho tới khi bạn thêm đúng một chữ: sitemap=True Route tự viết không tự vào sitemap.xml, không tự có thẻ meta. Cách khai sitemap=True (và callback cho URL động), đặt title/meta description/OG qua website.layout — kiểm chứng bằng sitemap.xml và thẻ head thật trên Odoo 19. 18/09/2026 · 4 phút đọc 177 Thêm một dòng XML, mục 'Ưu đãi' hiện ngay trên thanh menu website — và Odoo tự sao nó sang từng site website.menu là cây điều hướng của website; website.page là trang. Cách khai một mục menu trỏ tới trang bằng XML, hiểu parent_id/sequence/page_id, và cơ chế Odoo sao menu sang từng website — qua một mục menu thật hiện trên header Odoo 19. 18/09/2026 · 5 phút đọc 178 Cùng một route POST, thêm token thì 200, thiếu token thì 400: mổ xẻ tấm khiên CSRF của Odoo CSRF bảo vệ form POST khỏi giả mạo request. Cách Odoo sinh và kiểm token (HMAC + database.secret, consteq), khi nào csrf=False là đúng, và vì sao jsonrpc không cần — kiểm chứng bằng ba lần POST thật trên Odoo 19. 18/09/2026 · 4 phút đọc 179 so_lan_vao: 1, 2, 3 — controller không có bộ nhớ, nhưng session thì có: mổ xẻ request.env và request.session request.env là cửa vào ORM (dựng theo auth), request.session giữ trạng thái giữa các request. Cách chúng hoạt động, khác nhau ra sao, và cách dùng đúng — kiểm chứng bằng một bộ đếm session thật tăng qua từng lần gọi trên Odoo 19. 18/09/2026 · 4 phút đọc 180 Đổi một con số trong CSDL, tải lại trang là bảng điểm tự sắp lại: snippet đọc dữ liệu sống từ model Snippet tĩnh gõ cứng nội dung; snippet động đọc thẳng model, tự cập nhật mỗi lần tải. Cách dựng một khối QWeb truy vấn model bằng request.env + t-foreach, và website.snippet.filter của builder — chứng minh 'sống' bằng before/after thật trên Odoo 19. 18/09/2026 · 4 phút đọc 181 Cùng một sản phẩm, giá trên trang từ 185.000 thành 148.000 mà list_price không đổi: móc vào _get_combination_info Muốn áp giá riêng trên trang sản phẩm eCommerce? Odoo tính giá qua nhiều tầng — bảng giá, _get_combination_info, dòng đơn. Cách override đúng chỗ để can thiệp giá, minh hoạ bằng giá thành viên -20% thật trên Odoo 19. 18/09/2026 · 4 phút đọc 182 Khách gõ một câu hỏi trên trang tài khoản, nhân viên thấy ngay trong chatter: cùng một mail.message, hai nơi hiển thị Chatter cho phép khách và nhân viên trao đổi ngay trên bản ghi. Cách kế thừa mail.thread, message_post tạo mail.message, và cơ chế portal chatter (PortalChatter + token) cho khách gửi tin an toàn — qua một tin thật đăng lên Odoo 19. 18/09/2026 · 4 phút đọc 183 Vì sao nút 'Add to cart' và footer của cả website đổi màu cùng lúc chỉ với một dòng SCSS: cấu trúc một theme Odoo Theme không phải bộ CSS rời — nó là một module đặc biệt khai màu/font qua hệ biến SCSS và color-combination. Mổ xẻ manifest theme, $o-color-palettes, o-cc, và cách theme áp lên toàn site — soi lõi thật trên Odoo 19. 18/09/2026 · 4 phút đọc 184 redirect('https://evil.com/x') mà trình duyệt lại đi tới /x cùng site: local=True âm thầm chặn open-redirect Chuyển hướng, trả 404, đọc request thô — tất cả đi qua Werkzeug bên dưới controller Odoo. Cách dùng request.redirect (và bẫy local=True), raise NotFound, đọc request.httprequest — kiểm chứng bằng curl thật trên Odoo 19. 18/09/2026 · 4 phút đọc 185 ?page=99 trên một danh sách 2 trang: một dòng clamp cứu bạn khỏi trang trắng và lỗi offset Phân trang danh sách trong controller tự viết: đếm tổng, tính offset/limit, clamp số trang, giữ tham số sắp xếp qua các trang. Bốn bước và cạm bẫy hay gặp — kiểm chứng bằng curl thật trên Odoo 19. 18/09/2026 · 4 phút đọc 186 20 dòng Python bên ngoài Odoo, đăng nhập rồi đọc/ghi dữ liệu như một module nội bộ: XML-RPC Một script bất kỳ ngôn ngữ nào cũng gọi được Odoo từ xa qua XML-RPC: authenticate lấy uid, rồi execute_kw để search/create/write/unlink. Luồng chuẩn, bảo mật, và một vòng CRUD thật trên Odoo 19. 18/09/2026 · 4 phút đọc 187 Cùng đọc 4 thẻ thành viên, nhưng lần này bằng fetch() từ JavaScript: gọi Odoo qua JSON-RPC JSON-RPC cho cùng sức mạnh XML-RPC nhưng payload JSON nhẹ, gọi được ngay từ fetch/JavaScript. Cấu trúc /jsonrpc, cách gọi từ JS, và session-based cho front-end — với kết quả curl thật trên Odoo 19. 18/09/2026 · 4 phút đọc 188 Chiều ngược lại: để Odoo tự gọi ra một dịch vụ bên ngoài — và vì sao thiếu một chữ timeout=10 có thể treo cả worker Hai bài trước bên ngoài gọi vào Odoo. Giờ Odoo là client gọi ra dịch vụ thứ ba bằng requests. timeout bắt buộc, bắt đúng exception, đừng chặn luồng — với kết quả requests chạy thật từ trong Odoo shell. 18/09/2026 · 5 phút đọc 189 Thay vì để Odoo hỏi 'xong chưa?' mỗi 30 giây, hãy để bên kia gõ cửa: viết một webhook nhận sự kiện, ký bằng HMAC Webhook lật chiều tích hợp: hệ thống khác chủ động POST vào Odoo khi có sự kiện. Controller csrf=False, xác thực bằng chữ ký HMAC-SHA256 thay vì session — với một webhook chạy thật trên module quản cà phê. 18/09/2026 · 5 phút đọc 190 Viết 4 test cho module cà phê, chạy thật, rồi kiểm CSDL: không một dòng TEST-0001 nào sót lại — phép màu của TransactionCase TransactionCase là lớp nền để viết test tự động trong Odoo. Mỗi test chạy trong một giao dịch riêng rồi tự cuộn ngược, nên không bao giờ bẩn CSDL. Cấu trúc setUp, self.env, cách chạy --test-enable — với 4 test chạy thật. 18/09/2026 · 4 phút đọc 191 Test của bạn chạy khi mới cài xong 94/175 module, hay đợi đủ 175? Một dòng @tagged quyết định điều đó — và cả việc nó xanh hay đỏ at_install chạy ngay giữa lúc cài, post_install đợi mọi module xong. Chọn sai là test đỏ ngẫu nhiên. Cú pháp @tagged, dấu trừ để tắt tag, lọc bằng --test-tags — chứng minh bằng log thật cho thấy đúng thứ tự chạy. 18/09/2026 · 4 phút đọc 192 Một test không có dòng assert nào vẫn 'xanh' — và đó là loại test tệ nhất: viết dữ liệu và khẳng định cho đúng Dựng dữ liệu nền gọn bằng setUpClass, so nhiều field nhiều bản ghi bằng assertRecordValues, lặp case bằng subTest, và những bẫy xanh giả khiến test vô dụng. Kèm log thật cho thấy assertEqual báo lỗi rõ ra sao. 18/09/2026 · 4 phút đọc 193 Một lệnh biến 183 chuỗi trong module thành file .pot để dịch — không sửa một dòng code: cơ chế i18n của Odoo Odoo tự trích chuỗi từ field, view, nút ra file .pot mẫu; bạn dịch sang .po từng ngôn ngữ. Chuỗi Python thì bọc _(). Xem file .pot thật xuất từ module quản cà phê, với đủ msgid và chú thích vị trí. 18/09/2026 · 4 phút đọc 194 Cùng ra một con số 1750, nhưng một cách bắn 50 câu SQL còn cách kia chỉ 1 — bẫy N+1 đo bằng số query thật N+1 là bẫy hiệu năng phổ biến nhất: lặp qua N bản ghi, mỗi vòng một truy vấn. Đo query thật cho thấy 50 câu SQL sụt còn 1 khi thay vòng lặp search bằng read_group. Cách nhận diện và né N+1 trong ORM Odoo. 18/09/2026 · 4 phút đọc 195 EXPLAIN ANALYZE nói thật: cùng một câu WHERE, thêm một dòng index=True biến Seq Scan cost 85 thành Index Scan cost 8 Index là B-tree giúp PostgreSQL nhảy thẳng tới dòng cần thay vì quét cả bảng. Odoo tự index gì, khi nào bạn phải thêm index=True, và cái giá của index — với plan EXPLAIN ANALYZE thật trên 3000 bản ghi. 18/09/2026 · 4 phút đọc 196 Bật một dòng workers=2, gõ ps, và một tiến trình Odoo hoá thành năm: vì sao production phải chạy đa tiến trình Odoo dev chạy 1 tiến trình đa luồng, nhưng GIL của Python khiến nó không dùng nổi nhiều CPU. Chế độ multi-process (prefork) mới tận dụng được. Xem master sinh ra worker HTTP, cron, longpolling thật bằng ps, với đủ limit cấu hình. 18/09/2026 · 4 phút đọc 197 Cùng một trang, hai lần curl: Odoo trần trả 303 không nén, qua nginx thành 200 gzip có TLS — vai trò của reverse proxy nginx đứng trước Odoo lo TLS, nén gzip, và định tuyến websocket/longpolling sang cổng gevent. Bốn header proxy bắt buộc để Odoo nhìn đúng khách. So header thật của Odoo trần với một trang qua nginx. 18/09/2026 · 4 phút đọc 198 Sao lưu CSDL 10.5MB xong tưởng an toàn — nhưng filestore 65MB nằm ngoài đó, và quên nó là ảnh vỡ hết khi phục hồi Backup Odoo phải gồm CẢ HAI: CSDL (pg_dump) và filestore chứa attachment. Xem kích thước thật của cả hai trên blog19, cách restore đúng, và khung migration script migrate(cr, version) để đổi schema an toàn. 18/09/2026 · 4 phút đọc 199 Tăng version module lên một số, gõ -u, và log hiện đúng thứ tự: PRE chạy khi còn 4 thẻ, POST chạy sau khi schema đã đổi pre-migrate cứu dữ liệu trước khi cột đổi, post-migrate điền dữ liệu sau khi schema mới sẵn sàng, end-migrate chạy cuối cùng. Xem thứ tự thật qua log của một lần nâng cấp module quản cà phê. 18/09/2026 · 5 phút đọc 200 Phần 200: biến một module 'chạy được' thành module chuyên nghiệp — chuẩn OCA, và lời tạm biệt sau 200 phần Odoo 19 OCA chuẩn hoá cấu trúc thư mục, manifest, và chất lượng code của module Odoo. Xem cấu trúc thật của module quản cà phê, 13 file py biên dịch sạch, và checklist OCA. Bài cuối cùng khép lại sê-ri 200 phần. 18/09/2026 · 5 phút đọc