[App] JSON REST API trong Odoo
Tut 19: JSON REST API trong Odoo
Odoo Tut 19: JSON REST API trong Odoo
Trước khi xem bài viết này, bạn vui lòng hoàn thành hướng dẫn ở Tut 5: Controller trong Odoo và Tut 9: API trong Odoo - XML-RPC để hiểu cách mở kết nối API từ Odoo, mà cho phép ứng dụng bên ngoài có thể gọi vào. Odoo hỗ trợ sẵn giao thức kết nối XML-RPC, nhưng giao thức XML-RPC hiện không còn phổ biến nữa.
Giao thức kết nối API giữa các hệ thống phổ biến hiện nay là JSON (JavaScript Object Notation). Để có thể mở API JSON từ Odoo, ta phải tự viết controller như Tut 5 để trả về nội dung json hoặc mua một addon/module trên Odoo App Store để thực hiện chức năng này. Hiện Minh đang đăng bán một addon để expose API JSON trong Odoo một cách dễ dàng bằng giao diện, hỗ trợ tất cả model trong Odoo và cả custom model do bạn tự hiện thực trong customized addon (tức bạn tự định nghĩa model mới thì vẫn có thể mở API Json cho nó mà không phải tự lập trình gì thêm).
Bài viết này minhng.info sẽ hướng dẫn bạn:
- Động lực để Minh phát triển addon API
- Mua addon Secure REST API Management for Odoo (secure_api) trên cửa hàng chính thức Odoo Apps.
- Cài đặt addon secure_api
- Giới thiệu tính năng chính của addon secure_api:
- Mở JSON API dễ dàng bằng giao diện
- Mở JSON API có xác thực OAuth2.0
- Các tiện ích quản lý API
- Mua addon secure_api trực tiếp từ minhng.info
Môi trường lập trình:
- Ubuntu
- Python 3
- Odoo 14 -> 18
Tham khảo bài viết sau để dựng môi trường lập trình Odoo: Tut 2: Hướng dẫn cài đặt Odoo
Động lực để Minh phát triển addon API
- Tăng tính kết nối cho Odoo, có thể biến Odoo thành backend-as-service (BaaS) khi mà công nghệ frontend đang phát triển mạnh mẽ. Như vậy, ta có thể dùng Odoo làm backend cung cấp data thông qua API để giao diện hiển thị.
- Mở API cho Odoo một cách dễ dàng (easy), nhanh chóng (fast) và thống nhất (consistency) mà không phải lập trình controller.
- Zero dependency: addon do Minh phát triển không phụ thuộc vào các addon liên quan đến nghiệp vụ (functional) cho doanh nghiệp như sale, inventory, website, … Tức chỉ việc cài đặt thì sẽ tương thích với built-in addons và custom addons đang triển khai.
- Quản lý tập trung các API tại một nơi duy nhất.
- Bảo mật API hơn so với tính năng mặc định có sẵn trong Odoo như:
- Cho phép tắt/ vô hiệu hóa XML-RPC
- Hỗ trợ OAuth2.0 cho client application
- Làm mờ khóa "id" của dữ liệu => do Odoo mặc định khóa primary key của dữ liệu (id) sẽ là số nguyên tăng dần, do đó người khác có thể truy vấn đọc dữ liệu khác mà bạn không mong muốn bằng cách tăng id lên để scan dữ liệu. Addon secure_api là một trong những giải pháp API mà Minh đã xử lý vấn đề này bằng cách làm mờ khóa "id" bằng cách encode (hashing) thành dạng chuỗi (ví dụ: 4QB2a, Z8Kw8, 8kWg8, …).
- Cung cấp thêm các tiện ích khác cho quản lý API để dễ dàng tích hợp giao diện:
- Thống kế số lượt truy cập (request)
- In log của mỗi request gần đây
Mua addon "Secure REST API Management for Odoo" (secure_api) trên cửa hàng chính thức Odoo Apps
- Bước 1: Truy cập link sau trên cửa hàng Odoo Apps: https://apps.odoo.com/apps/modules/18.0/secure_api
- Bước 2: Chọn phiên bản Odoo phù hợp hoặc theo một trong các đường dẫn sau (hỗ trợ Odoo phiên bản 14 đến 18):
- Odoo 14: https://apps.odoo.com/apps/modules/14.0/secure_api
- Odoo 15: https://apps.odoo.com/apps/modules/15.0/secure_api
- Odoo 16: https://apps.odoo.com/apps/modules/16.0/secure_api
- Odoo 17: https://apps.odoo.com/apps/modules/17.0/secure_api
- Odoo 18: https://apps.odoo.com/apps/modules/18.0/secure_api
- Lưu ý: bạn phải mua đúng phiên bản Odoo cần triển khai, và sau khi mua bạn chỉ được download về đúng phiên bản đã chọn. Đó là điều kiện mua hàng của Odoo Apps kể từ Odoo 13.0.
- Bước 3: Nhấn chọn nút " Add to Cart"
- Bước 4: Tiến hành thanh toán qua thẻ thanh toán quốc tế như VISA, MASTERCARD
- Bước 5: Sau khi mua xong addon secure_api trên Odoo Apps Store, bạn sẽ được tải về (download) addon tương thích phiên bản đã chọn để tiến hành triển khai.
Cài đặt addon secure_api
- Di chuyển module đã mua "secure_api" vào thư mục "addon" của Odoo. Nếu bạn triển khai Odoo dùng docker-compose từ minhng.info, bạn cần di chuyển vào "addons/secure_api"
- Để tiến hành cài đặt một module mới, ta phải chuyển sang developer mode.
- Trong Odoo mình thêm ?debug=True vào URL để kích hoạt chế độ debug. Sau đó, mình nhấn vào menu "Update Apps List" (chỉ xuất hiện khi ta ở chế độ debug / developer) và chọn "Update" để module secure_api mới của chúng ta xuất hiện trong danh sách. Thao tác này chính là "refresh" lại danh sách app.
- Ta tiến hành cài đặt addon bằng cách nhấn nút "Install" tại addon secure_api
Giới thiệu tính năng chính của addon secure_api
1. Mở JSON API dễ dàng bằng giao diện
Ta có thể dễ dàng mở REST API hỗ trợ đầy đủ CRUD (create, read, update, delete) chỉ cần đặc tả các thông số như hình bên dưới:
Sau khi mở API thành công, ta có thể request để truy xuất dữ liệu và nhận về dữ liệu theo format JSON đảm bảo security như:
- Che giấu id của dữ liệu
- Lọc lại dữ liệu trả về
- Giới hạn các trường dữ liệu trả về cụ thể cho CREATE, READ, UPDATE và riêng cho SEARCH (ít trường dữ liệu hơn)
Ngoài REST API phổ biến, ta có thể đặc tả kiểu RPC cho API để gọi một phương thức trong model (binding route -> method).
Format dữ liệu trả về bởi addon secure_api thống nhất dễ xử lý:
// SEARCH Response:
{
"jsonrpc": "2.0",
"id": null,
"result": {
"length": 36,
"records": [
{
"id": "NnRZn",
"email": "brandon.freeman55@example.com",
"image_128": "/api/image/2/res.partner/NnRZn/image_128",
"name": "Brandon Freeman"
},
...
]
}
}
// CREATE Response:
{
"jsonrpc": "2.0",
"id": null,
"result": {
"id": "P2y3b",
"company_id": [],
"email": "",
"image_1024": "",
"image_128": "",
"name": "Test Partner (CRUD Demo Bash)",
"phone": ""
}
}
// READ Response:
{
"jsonrpc": "2.0",
"id": null,
"result": {
"id": "8blEP",
"company_id": [],
"email": "azure.Interior24@example.com",
"image_1024": "/api/image/2/res.partner/8blEP/image_1024",
"image_128": "/api/image/2/res.partner/8blEP/image_128",
"name": "Azure Interior",
"phone": "(870)-931-0505"
}
}
// UPDATE Response:
{
"jsonrpc": "2.0",
"id": null,
"result": {
"id": "8blEP",
"company_id": [],
"email": "azure.Interior24@example.com",
"image_1024": "/api/image/2/res.partner/8blEP/image_1024",
"image_128": "/api/image/2/res.partner/8blEP/image_128",
"name": "Test Partner (CRUD Demo Bash) (Updated)",
"phone": "(870)-931-0505"
}
}
// DELETE Response:
{
"jsonrpc": "2.0",
"id": null,
"result": [
"NnRZn"
]
}
Bảng mã các loại phản hồi (response) mô tả như sau:
Kiểu phản hồi | Mã HTTP | Dữ liệu trả về |
---|---|---|
Thành công | 200 OK | Có key "result" |
Lỗi ứng dụng | 200 OK | Có key "error" |
Lỗi HTTP | Mã 4xx hoặc 5xx |
2. Mở JSON API có xác thực OAuth2.0
Cách mở API có xác thực OAuth2.0 phù hợp cho một backend khác thực hiện truy vấn API vào Odoo. Client application phải gọi vào một endpoint để lấy access token trước khi gọi API truy vấn dữ liệu.
Việc cấp phát client app cho một bên thứ ba dễ dàng bằng cách tạo form trên giao diện trong module secure_api. Qua đó, ta có thể chia sẻ dữ liệu của backend Odoo một cách an toàn và bảo mật.
3. Các tiện ích quản lý API
- Ta có thể quản lý các API bằng cách gán danh mục (category) hoặc thẻ (tag).
- Có giao diện thống kê (đếm) số lượt gọi API thành công hoặc thất bại (có tính tỉ lệ).
- Có giao diện xem các request gần đây để tiện debug các failed / error response trong quá trình tích hợp với hệ thống khác hoặc giao diện.
Mua addon secure_api trực tiếp từ minhng.info
Giá bán: 1,700,000 vnđ
Vì sao Minh có thiết lập giá này?
- Chính sách giá của Odoo Apps bắt buộc module bán trực tiếp hoặc trên các nền tảng khác phải có giá ít nhất tương đương với module đang bán trên Odoo Apps.
- Số lượng dòng code để hoàn thiện module này: >4000 LOC (lines of code)
Hướng dẫn mua hàng trực tiếp từ minhng.info (chỉ hỗ trợ thanh toán qua chuyển khoản):
- Bước 1: Điền đầy đủ thông tin mua hàng vào biểu mẫu bên dưới, nhấn nút "Gửi Yêu Cầu Mua Hàng" để lấy mã QR Code.
- Chọn 01 phiên bản Odoo.
- Địa chỉ email phải chính xác để nhận addon secure_api.
- Nếu không có mã QR Code, có thể do hệ thống đang gián đoạn => bạn có thể liên hệ mua hàng qua email sales@minhng.info.
- Bước 2: Thanh toán bằng cách quét mã QR Code để thực hiện chuyển khoản ngân hàng.
- Giữ nguyên nội dung chuyển khoản đã được điền sẵn sau khi quét mã QR. Ví dụ: "RZD7QVM MINH NGUYEN".
- Nếu nội dung chuyển khoản trống không được điền sẵn sau khi quét mã QR, có thể do hệ thống đang gián đoạn => bạn có thể liên hệ mua hàng qua email sales@minhng.info.
- Bước 3 (TÙY CHỌN): Gửi hình giao dịch thành công.
- Đính kèm ảnh thanh toán thành công và nhấn nút "Gửi hình ảnh".
- Quá trình xác thực sẽ được diễn ra nhanh hơn khi bạn gửi hình giao dịch thành công.
- Bước 4: Trong vòng 24h kể từ khi Minh nhận được chuyển khoản, bạn sẽ nhận được một email đính kèm addon secure_api theo phiên bản Odoo đã chọn.
Lưu ý:
- minhng.info không hỗ trợ xuất hóa đơn.
- Các thanh toán đã chuyển khoản sẽ không được hoàn lại.
- Email hỗ trợ nếu bạn gặp sự cố hoặc thắc mắc khi mua hàng trên minhng.info: sales@minhng.info
<meta name=referrer content=no-referrer>
<section aria-label="Notifications alt+T" tabindex=-1 aria-live=polite aria-relevant="additions text"
aria-atomic=false></section>
<div class="min-h-screen bg-gradient-background flex items-center justify-center p-6">
<div class="rounded-lg border-form text-card-foreground shadow-sm w-full shadow-form">
<div class="flex flex-col p-6 text-center space-y-2">
<h2>Giỏ Hàng</h2>
<p class="text-muted-foreground">Điền thông tin dưới đây để mua hàng trực tiếp trên minhng.info</p>
</div>
<div class="p-6 pt-0 space-y-6">
<form class=space-y-6>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class=space-y-2>
<label for=name>Họ và Tên *</label>
<input
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
id="name" placeholder="Nhập họ và tên của bạn" value>
</div>
<div class=space-y-2>
<label for=phone>Số Điện Thoại *</label>
<input
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
id="phone" placeholder="Nhập số điện thoại của bạn" value>
</div>
</div>
<div class=space-y-2>
<label for=email>Email *</label>
<input type=email
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
id="email" placeholder="Nhập địa chỉ email của bạn" value>
<p class="text-xs text-muted-foreground">Sản phẩm sẽ được gửi tới email này. Vui lòng đảm bảo
chính xác email.</p>
</div>
<div class=space-y-2><label for=street>Địa Chỉ *</label>
<textarea
class="flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 min-h-[100px]"
id="street" placeholder="Nhập địa chỉ của bạn"></textarea>
<p class="text-xs text-muted-foreground">Địa chỉ của bạn được lưu trữ an toàn chỉ để xử lý giao
dịch mua hàng và không bao giờ chia sẻ với bên thứ ba.</p>
</div>
<div class="space-y-4 p-4 bg-muted/50 rounded-lg border">
<!-- <h3 class="text-lg font-semibold text-foreground">Giỏ Hàng</h3> -->
<div class="flex items-center space-x-4"><img
src="https://firebasestorage.googleapis.com/v0/b/minh-nguyen-blog.appspot.com/o/apps_odoo%2Fsecure_api%2Ficon%20(1).png?alt=media&token=48329741-a149-4d53-9671-af5ac45fc8ea"
data-lov-id=src/components/OdooAddonForm.tsx:208:16 data-lov-name=div
data-component-path=src/components/OdooAddonForm.tsx data-component-line=208
data-component-file=OdooAddonForm.tsx data-component-name=div
data-component-content=%7B%22className%22%3A%22w-12%20h-12%20bg-primary%2F10%20rounded-lg%20flex%20items-center%20justify-center%22%7D
class="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center" />
<div class=flex-1>
<h4 class="font-medium text-foreground"><a id="addon_link_on_store"
href="https://apps.odoo.com/apps/modules/18.0/secure_api" target="_blank"
rel="noopener noreferrer">Secure REST API Management for
Odoo</a></h4>
<p class="text-sm text-muted-foreground">by minhng.info</p>
<div class="flex-wrap gap-2">
<span>Phiên Bản Odoo *</span>
<button
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input hover:text-accent-foreground h-10 px-4 py-2 bg-background hover:bg-muted"
type=button>14.0</button>
<button
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input hover:text-accent-foreground h-10 px-4 py-2 bg-background hover:bg-muted"
type=button>15.0</button>
<button
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input hover:text-accent-foreground h-10 px-4 py-2 bg-background hover:bg-muted"
type=button>16.0</button>
<button
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input hover:text-accent-foreground h-10 px-4 py-2 bg-background hover:bg-muted"
type=button>17.0</button>
<button
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-input hover:text-accent-foreground h-10 px-4 py-2 bg-background hover:bg-muted"
type=button>18.0</button>
</div>
<p class="text-xs text-muted-foreground" style="font-style: italic;">Vui lòng kiểm tra
thông tin và chọn đúng phiên bản Odoo.</p>
</div>
<div class=text-right>
<p id="addon_price" class="text-2xl font-bold text-primary"></p>
</div>
</div>
</div>
<div class=space-y-2>
<!-- <p class="text-xs text-muted-foreground">Vui lòng kiểm tra thông tin và chọn đúng phiên bản Odoo.</p> -->
<label for="is_mail_subscription" style="font-weight: bold;">
<input type="checkbox" id="is_mail_subscription" name="is_mail_subscription" checked>
Tôi đồng ý nhận email về các chương trình khuyến mãi, voucher, thông tin sản phẩm / dịch vụ
từ minhng.info
</label>
<br />
<div id="purchase_section">
<button id="purchase_now"
class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-primary-foreground hover:bg-primary/90 h-10 px-4 w-full bg-gradient-primary hover:opacity-90 transition-all duration-300 py-6 text-lg font-semibold"
type=submit>Mua Hàng</button>
</div>
</div>
</form>
<div id="payment-section">
</div>
<div id="payment-image" class="hidden">
<h3 style="color: #2c5aa0; margin-bottom: 15px;">Tải lên ảnh đã thanh toán</h3>
<div style="text-align: center;">
<div id="dropzone" class="dropzone" tabindex="0" role="button"
aria-label="Choose or drop an image">
<div class="dz-inner">
<div class="dz-title">Nhấn để chọn ảnh hoặc kéo thả vào đây</div>
<div class="dz-sub">Định dạng: PNG / JPG / JPEG • Tối đa 01 ảnh không quá 20MB</div>
</div>
<input id="fileInput" type="file" accept="image/*" class="hidden" />
</div>
<div id="preview" class="preview hidden">
<img id="previewImg" alt="preview" />
<div>
<div class="meta" id="fileMeta"></div>
<div class="actions">
<button id="send_image_now"
class="inline-flex items-center justify-center gap- whitespace-nowrap rounded-md ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-primary-foreground hover:bg-primary/90 h-10 px-4 w-full bg-gradient-primary hover:opacity-90 transition-all duration-300 py-6 text-lg font-semibold"
style="display:none;">Gửi hình ảnh</button>
<button id="reset_image"
class="inline-flex items-center justify-center gap- whitespace-nowrap rounded-md ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-primary h-10 px-4 w-full bg-muted/50 hover:opacity-90 transition-all duration-300 py-6 text-lg font-semibold"
type="button">Chọn ảnh khác</button>
</div>
</div>
</div>
<div id="message" class="msg" aria-live="polite"></div>
</div>
</div>
<div id="payment-thankyou" class="hidden">
<div
style="margin-top: 20px; padding: 15px; background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 5px; text-align: center;">
<strong>Cảm ơn bạn đã thanh toán</strong><br />
Đơn hàng sẽ được kiểm tra và xử lý trong vòng 24 giờ.<br />
Bạn hãy chú ý email đến từ <strong>sales@minhng.info</strong> nhé!<br />
</div>
</div>
</div>
</div>
</div>
</div>
Hãy tham gia group Facebook để trao đổi và học hỏi thêm về Odoo nhé các bạn => Khám phá Odoo: https://facebook.com/groups/odoo-dev
- Khóa học lập trình Odoo TP.HCM: Thông tin và đăng ký khóa học
- Khảo sát nhu cầu học nghiệp vụ Odoo Offline @ HCM & đăng ký sớm: https://forms.gle/auBXLNbfuNqhJSbj8
Cài đặt Odoo:
- Docker Compose for Odoo 10
- Docker Compose for Odoo 11
- Docker Compose for Odoo 12
- Docker Compose for Odoo 13
- Docker Compose for Odoo 14
- Docker Compose for Odoo 15
- Docker Compose for Odoo 16
- Docker Compose for Odoo 17
- Docker Compose for Odoo 18
Danh sách bài viết series Odoo:
- Tut 0: Học lập trình Odoo - tutorials
- Tut 1: Odoo là gì?
- Tut 2: Hướng dẫn cài đặt Odoo version 10, 11, 12, 13, 14, 15, 16, 17, 18
- Tut 3: Tạo model trong Odoo
- Tut 4: View trong Odoo
- Tut 5: Controller trong Odoo
- Tut 6: Thừa kế model trong Odoo
- Tut 7: Thừa kế view trong Odoo
- Tut 8: Thừa kế controller trong Odoo
- Tut 9: API trong Odoo - XML-RPC
- Tut 10: Hiện thực wizard trong Odoo
- Tut 11: Tạo widget trong Odoo
- Tut 12: Tạo settings trong Odoo cho custom addon
- Tut 13: Thêm button cho list/tree view trong Odoo
- Tut 14: Search, Filter, Group By trong Odoo
- Tut 15: Kết nối PostgreSQL database của Odoo bằng câu lệnh
- Tut 16: Kết nối pgAdmin vào PostgreSQL database của Odoo
- Tut 17: Tra cứu mã nguồn để lập trình Odoo
- Tut 18: Thêm button cho form view trong Odoo
Tham gia ngay group trên Facebook để cùng thảo luận với đồng bọn nhé:
- Fanpage Minh: https://www.facebook.com/minhng.info
- Khám phá Odoo: https://www.facebook.com/groups/odoo-dev
Khám phá Odoo