Tiny REST API — Tài liệu sử dụng
Hướng dẫn kết nối và sử dụng REST API của module tiny_rest_api — xác thực, CRUD theo model, rate limit, và ví dụ chi tiết với cosan.order, cosan.product.
Tiny REST API
Base URL: https://{your-odoo-domain}
Module: tiny_rest_api
Phiên bản tài liệu: 1.0
Tài liệu này cung cấp thông tin cần thiết để làm việc với REST API của hệ thống (module tiny_rest_api). Mỗi endpoint gồm ví dụ request (cURL / JavaScript), ví dụ response, và mô tả chi tiết Headers / Params.
1. Giới thiệu
API cho phép đọc (GET), tạo mới (POST), cập nhật (PUT) và xoá (DELETE) dữ liệu trên bất kỳ model Odoo nào đã được cấu hình cho phép truy cập (qua rest.api.connect), có kiểm soát field được đọc/ghi (allowlist / blocklist) và có rate limit theo từng API key.
Ngoài các API REST theo model, hệ thống còn có API POST /api/v1/auth/login để đăng nhập bằng login/password và lấy API key (token) dùng cho các request tiếp theo, thay cho việc tạo token thủ công.
2. Xác thực (Authenticating requests)
Tất cả các endpoint dưới /api/v1/... yêu cầu xác thực bằng API key, gửi qua header:
X-API-KEY: {token}| Đặc điểm | Mô tả |
|---|---|
| API key không hợp lệ | Không tồn tại, sai, hoặc đã hết hạn → trả về lỗi 403 với status: error |
| Ngôn ngữ trả về | Có thể chỉ định qua header tuỳ chọn X-LANG (áp dụng cho field dịch, ví dụ state selection) |
| Ngôn ngữ mặc định | Nếu không truyền X-LANG, hệ thống dùng ngôn ngữ mặc định của user gắn với token (hoặc vi_VN) |
X-LANG: vi_VNAPI key sẽ được Zotech cấp — khách hàng không tự tạo/đăng nhập lấy token trên hệ thống. Vui lòng liên hệ đội ngũ Zotech để được cấp API key cho môi trường test/production.
2.1. Rate limit
Nếu token được cấu hình rate_limit > 0, mỗi response GET/POST/PUT/DELETE hợp lệ sẽ kèm theo header:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 3421Khi vượt quá giới hạn, request sẽ bị từ chối với lỗi 403 và message Rate limit exceeded. Try again in {n} seconds.
3. Ví dụ: Đơn hàng Cosan (cosan.order)
Model cosan.order (Đơn hàng Cosan) lưu đơn hàng/vận đơn đồng bộ từ đối tác vận chuyển Cosan (địa chỉ giao, tiền COD, trạng thái giao hàng...) và liên kết với sale.order nội bộ qua odoo_order_id — xem Bảng trường cosan.order bên dưới để tra field khi truyền fields=/include=.
Danh sách đơn hàng Cosan
/api/v1/cosan.order
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.order" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"fields\": \"id,order_code,cpn_code,partner_name,phone,address,total_amount,amount_cod,delivery_state,sync_state,create_time\",
\"domain\": [[\"sync_state\", \"=\", \"pending\"]],
\"page\": 1,
\"page_size\": 50
}"const body = {
fields: "id,order_code,cpn_code,partner_name,phone,address,total_amount,amount_cod,delivery_state,sync_state,create_time",
domain: [["sync_state", "=", "pending"]],
page: 1,
page_size: 50,
};
fetch("https://your-domain.odoo.com/api/v1/cosan.order", {
method: "GET",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"model": "cosan.order",
"page": 1,
"page_size": 50,
"total_records": 34,
"total_pages": 1,
"has_next": false,
"has_previous": false,
"records": [
{
"id": 6021,
"order_code": "CSN2607880123",
"cpn_code": "CPN881029341",
"partner_name": "Nguyễn Văn A",
"phone": "0909123456",
"address": "12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM",
"total_amount": 500000,
"amount_cod": 500000,
"delivery_state": "delivering",
"sync_state": { "value": "pending", "label": "Chờ đồng bộ" },
"create_time": "2026-07-28 09:10:00"
}
]
}| Tham số | Kiểu | Gợi ý hay dùng |
|---|---|---|
fields | string | id,order_code,cpn_code,partner_name,phone,address,total_amount,amount_cod,delivery_state,order_state,sync_state,create_time,date_delivered |
include | string | line_ids — mở rộng chi tiết sản phẩm trong đơn (model cosan.order.line) thành object đầy đủ |
domain | list | Theo mã đơn: [["order_code","=","CSN2607880123"]] · theo mã vận đơn: [["cpn_code","=","CPN881029341"]] · theo trạng thái đồng bộ: [["sync_state","=","pending"]] · theo ngày tạo: [["create_time",">=","2026-07-01 00:00:00"],["create_time","<=","2026-07-31 23:59:59"]] |
Chi tiết 1 đơn hàng Cosan
/api/v1/cosan.order/{record_id}
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.order/6021" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"include\": \"line_ids\"
}"fetch("https://your-domain.odoo.com/api/v1/cosan.order/6021", {
method: "GET",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify({ include: "line_ids" }),
}).then(r => r.json());{
"model": "cosan.order",
"record": {
"id": 6021,
"order_code": "CSN2607880123",
"cpn_code": "CPN881029341",
"odoo_order_id": [201215, "D_SO00201091"],
"partner_name": "Nguyễn Văn A",
"phone": "0909123456",
"address": "12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM",
"total_amount": 500000,
"amount_cod": 500000,
"delivery_state": "delivering",
"order_state": "confirmed",
"sync_state": { "value": "pending", "label": "Chờ đồng bộ" },
"create_time": "2026-07-28 09:10:00",
"date_delivered": false,
"line_ids": [
{
"id": 15092,
"product_id": [3391, "Áo thun nam form rộng"],
"default_code": "26AG06DENM",
"name": "Áo thun nam form rộng",
"invoice_name": "Áo thun nam",
"barcode": "8938501234567",
"quantity": 2,
"tax": 8,
"uom": "Cái",
"currency_id": [1, "VND"],
"price": 500000,
"total_discount": 0
}
]
}
}Response mẫu (404):
{
"status": "error",
"message": "Record with ID 6021 not found"
}Tạo mới đơn hàng Cosan
/api/v1/cosan.order
Yêu cầu xác thực
curl --request POST \
"https://your-domain.odoo.com/api/v1/cosan.order" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"order_code\": \"CSN2607880126\",
\"cpn_code\": \"861730159299\",
\"partner_name\": \"Nguyễn Văn A\",
\"phone\": \"0909123456\",
\"address\": \"12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM\",
\"amount_cod\": 500000,
\"total_amount\": 500000,
\"order_state\": \"Đang giao\",
\"delivery_state\": \"Đang giao\",
\"create_time\": \"2026-08-19\",
\"date_delivered\": \"2026-08-19\",
\"line_ids\": [
[0, 0, {
\"default_code\": \"26AG06DENM\",
\"name\": \"Áo thun nam form rộng\",
\"invoice_name\": \"Áo thun nam\",
\"barcode\": \"8938501234567\",
\"quantity\": 2,
\"tax\": 8,
\"uom\": \"Cái\",
\"price\": 500000,
\"total_discount\": 0
}]
]
}"const body = {
order_code: "CSN2607880126",
cpn_code: "861730159299",
partner_name: "Nguyễn Văn A",
phone: "0909123456",
address: "12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM",
amount_cod: 500000,
total_amount: 500000,
order_state: "Đang giao",
delivery_state: "Đang giao",
create_time: "2026-08-19",
date_delivered: "2026-08-19",
line_ids: [
[0, 0, {
default_code: "26AG06DENM",
name: "Áo thun nam form rộng",
invoice_name: "Áo thun nam",
barcode: "8938501234567",
quantity: 2,
tax: 8,
uom: "Cái",
price: 500000,
total_discount: 0,
}],
],
};
fetch("https://your-domain.odoo.com/api/v1/cosan.order", {
method: "POST",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"status": "success",
"id": 6099,
"display_name": "CSN2607880126"
}{
"status": "error",
"message": "Missing required field: order_code"
}Field line_ids dùng cú pháp lệnh one2many chuẩn của Odoo — [0, 0, {...}] để thêm dòng mới; mỗi dòng thuộc model cosan.order.line với các field product_id, default_code, name, invoice_name, barcode, quantity, tax, uom, currency_id, price, total_discount. Field order_id trên cosan.order.line (many2one trỏ về cosan.order) được hệ thống tự gán, không cần truyền.
Cập nhật địa chỉ / SĐT giao hàng
/api/v1/cosan.order/{record_id}
Yêu cầu xác thực
curl --request PUT \
"https://your-domain.odoo.com/api/v1/cosan.order/6021" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"address\": \"25 Lê Lợi, P. Bến Thành, Q.1, TP.HCM\",
\"phone\": \"0909999999\"
}"fetch("https://your-domain.odoo.com/api/v1/cosan.order/6021", {
method: "PUT",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify({
address: "25 Lê Lợi, P. Bến Thành, Q.1, TP.HCM",
phone: "0909999999",
}),
}).then(r => r.json());{
"status": "success",
"id": 6021,
"display_name": "CSN2607880123"
}{
"status": "error",
"message": "Fields not allowed via API: total_amount, amount_cod, sync_state"
}Các field số liệu/trạng thái (total_amount, amount_cod, delivery_state, order_state, sync_state...) do đối tác vận chuyển Cosan đồng bộ về và không nên ghi tay. Sau khi tạo đơn qua POST, API chỉ mở PUT cho các field liên hệ giao hàng (address, phone) để kịp chỉnh sửa trước khi đơn được lấy hàng.
Bảng trường cosan.order (Đơn hàng Cosan)
Tổng hợp toàn bộ field khả dụng của model cosan.order, dùng để tham chiếu khi truyền fields= / include= cho các API GET, hoặc khi build body cho PUT.
| Tên trường | Nhãn trường | Loại trường | Đối tượng liên quan | Đã lưu | Lập chỉ mục | Chỉ đọc |
|---|---|---|---|---|---|---|
address | Địa chỉ | char | Có | Không | Không | |
amount_cod | COD | tiền tệ | Có | Không | Không | |
cpn_code | Mã CPN | char | Có | Không | Không | |
create_time | Ngày tạo | ngày giờ | Có | Không | Không | |
date_delivered | Ngày giao hàng thành công | ngày | Có | Không | Không | |
delivery_state | Trạng thái vận chuyển | char | Có | Không | Không | |
line_ids | Chi tiết đơn hàng | one2many | cosan.order.line | Có | Không | Không |
odoo_order_id | Đơn hàng nội bộ | many2one | sale.order | Có | Không | Không |
order_code | Mã đơn hàng (Cosan) | char | Có | Có | Không | |
order_state | Trạng thái đơn hàng | char | Có | Không | Không | |
partner_name | Tên khách hàng | char | Có | Không | Không | |
phone | Số điện thoại | char | Có | Không | Không | |
sync_state | Trạng thái đồng bộ | lựa chọn | Có | Không | Không | |
total_amount | Tổng tiền | tiền tệ | Có | Không | Không |
4. Ví dụ: Sản phẩm Cosan (cosan.product)
Model cosan.product (Sản phẩm Cosan) lưu thông tin sản phẩm đồng bộ với đối tác vận chuyển Cosan — kích thước, cân nặng dùng để tính phí vận chuyển — và liên kết với product.product nội bộ qua odoo_product_id — xem Bảng trường cosan.product bên dưới để tra field khi truyền fields=/include=.
Danh sách sản phẩm Cosan
/api/v1/cosan.product
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.product" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"fields\": \"id,product_id,default_code,name,barcode,weight,height,length,width,total_price,odoo_product_id\",
\"domain\": [[\"default_code\", \"=\", \"26AG06DENM\"]],
\"page\": 1,
\"page_size\": 50
}"const body = {
fields: "id,product_id,default_code,name,barcode,weight,height,length,width,total_price,odoo_product_id",
domain: [["default_code", "=", "26AG06DENM"]],
page: 1,
page_size: 50,
};
fetch("https://your-domain.odoo.com/api/v1/cosan.product", {
method: "GET",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"model": "cosan.product",
"page": 1,
"page_size": 50,
"total_records": 1,
"total_pages": 1,
"has_next": false,
"has_previous": false,
"records": [
{
"id": 9042,
"product_id": "CSN-P-30281",
"default_code": "26AG06DENM",
"name": "Áo thun nam form rộng",
"barcode": "8938501234567",
"weight": 0.2,
"height": 3,
"length": 30,
"width": 25,
"total_price": 250000,
"odoo_product_id": [3391, "Áo thun nam form rộng"]
}
]
}| Tham số | Kiểu | Gợi ý hay dùng |
|---|---|---|
fields | string | id,product_id,default_code,name,barcode,description,weight,height,length,width,total_price,invoice_name,odoo_product_id,sync_message |
domain | list | Theo SKU nội bộ: [["default_code","=","26AG06DENM"]] · theo ID sản phẩm bên Cosan: [["product_id","=","CSN-P-30281"]] · theo barcode: [["barcode","=","8938501234567"]] |
Chi tiết 1 sản phẩm Cosan
/api/v1/cosan.product/{record_id}
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.product/9042" \
--header "X-API-KEY: {token}" \
--header "Accept: application/json"fetch("https://your-domain.odoo.com/api/v1/cosan.product/9042", {
headers: { "X-API-KEY": "{token}" },
}).then(r => r.json());{
"model": "cosan.product",
"record": {
"id": 9042,
"product_id": "CSN-P-30281",
"default_code": "26AG06DENM",
"name": "Áo thun nam form rộng",
"description": "Áo thun cotton, form rộng, unisex",
"barcode": "8938501234567",
"invoice_name": "Áo thun nam",
"weight": 0.2,
"height": 3,
"length": 30,
"width": 25,
"total_price": 250000,
"odoo_product_id": [3391, "Áo thun nam form rộng"],
"sync_message": false
}
}Response mẫu (404):
{
"status": "error",
"message": "Record with ID 9042 not found"
}Tạo mới sản phẩm Cosan
/api/v1/cosan.product
Yêu cầu xác thực
curl --request POST \
"https://your-domain.odoo.com/api/v1/cosan.product" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"name\": \"Sản phẩm 3\",
\"default_code\": \"003\",
\"product_id\": 2,
\"barcode\": \"0052566\",
\"invoice_name\": \"Sản phẩm 3\",
\"description\": \"Sản phẩm 3\",
\"total_price\": 500000
}"const body = {
name: "Sản phẩm 3",
default_code: "003",
product_id: 2,
barcode: "0052566",
invoice_name: "Sản phẩm 3",
description: "Sản phẩm 3",
total_price: 500000,
};
fetch("https://your-domain.odoo.com/api/v1/cosan.product", {
method: "POST",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"status": "success",
"id": 9051,
"display_name": "Sản phẩm 3"
}{
"status": "error",
"message": "Missing required field: default_code"
}Cập nhật kích thước / cân nặng
/api/v1/cosan.product/{record_id}
Yêu cầu xác thực
curl --request PUT \
"https://your-domain.odoo.com/api/v1/cosan.product/9042" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"weight\": 0.25,
\"height\": 4,
\"length\": 32,
\"width\": 26
}"fetch("https://your-domain.odoo.com/api/v1/cosan.product/9042", {
method: "PUT",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify({ weight: 0.25, height: 4, length: 32, width: 26 }),
}).then(r => r.json());{
"status": "success",
"id": 9042,
"display_name": "Áo thun nam form rộng"
}Kích thước/cân nặng khai báo ở đây được Cosan dùng để tính phí vận chuyển — nên cập nhật đúng số đo thực tế đóng gói (bao gồm hộp/bao bì), không phải kích thước sản phẩm thô. Field odoo_product_id dùng để đối chiếu 1-1 với product.product nội bộ; sửa liên kết này qua PUT nếu hệ thống map nhầm sản phẩm.
Bảng trường cosan.product (Sản phẩm Cosan)
Tổng hợp toàn bộ field khả dụng của model cosan.product, dùng để tham chiếu khi truyền fields= / include= cho các API GET, hoặc khi build body cho PUT.
| Tên trường | Nhãn trường | Loại trường | Đối tượng liên quan | Đã lưu | Lập chỉ mục | Chỉ đọc |
|---|---|---|---|---|---|---|
barcode | Barcode | char | Có | Không | Không | |
default_code | Mã sản phẩm | char | Có | Có | Không | |
description | Mô tả | char | Có | Không | Không | |
height | Chiều cao | dự trữ | Có | Không | Không | |
id | ID | số nguyên | Có | Không | Có | |
invoice_name | Tên xuất hoá đơn | char | Có | Không | Không | |
length | Chiều dài | dự trữ | Có | Không | Không | |
name | Tên sản phẩm | char | Có | Có | Không | |
odoo_product_id | Sản phẩm | many2one | product.product | Có | Không | Không |
product_id | ID sản phẩm (Cosan) | char | Có | Không | Không | |
sync_message | Ghi chú đồng bộ | văn bản | Có | Không | Không | |
total_price | Giá | dự trữ | Có | Không | Không | |
weight | Cân nặng | dự trữ | Có | Không | Không | |
width | Chiều rộng | dự trữ | Có | Không | Không |
5. Cấu hình Webhook Cosan
Trước khi hàm action_push_webhook_cosan hoạt động, cần khai báo URL webhook tại màn hình cấu hình module Cosan.
Cosan → Cấu hìnhMở bản ghi cấu hình (mặc định tên Default).
Cấu hình webhookURL endpoint nhận payload trạng thái hoá đơn (nơi hệ thống ngoài lắng nghe).
Giá trị này được đọc và truyền vào tham số webhook_url khi gọi action_push_webhook_cosan.
Nếu ô này trống, hàm sẽ gọi requests.post(None, ...) và raise lỗi MissingSchema. Nên validate trước khi gọi webhook.
Khi hàm chạy, hệ thống gửi POST tới webhook_url đã cấu hình ở trên, kèm payload JSON tổng hợp từ hoá đơn và sale order liên kết:
Payload đẩy sang Cosan
{webhook_url} — cấu hình tại mục 5
{
"order_code": "CSN2607880123",
"invoice_no": "INV/2026/00845",
"state_push_invoice": "done",
"state_push_invoice_name": "Thành công",
"state_publish": "4",
"state_publish_name": "Thành công",
"sync_invoice": {
"success": true,
"data": "[{\"RefID\":\"c8772cdb-b225-4b63-a4b8-064e49c51b7b\",\"InvSeries\":\"1C26TUT\",\"InvDate\":\"2026-08-24T00:00:00+07:00\",\"EInvoiceStatus\":1}]",
"error": null,
"error_description": null,
"errorCode": []
}
}| Trường | Giá trị | Ý nghĩa |
|---|---|---|
order_code | — | Mã đơn hàng sàn TMĐT hoặc mã đơn do Cosan bắn sang |
invoice_no | — | Số hoá đơn điện tử do MISA trả về |
state_push_invoicefield state_push_invoice | new | Mới — chưa đẩy hoá đơn điện tử |
processing | Đang thực hiện | |
done | Thành công | |
error | Lỗi | |
state_publishfield publish_status | 0 | Chưa phát hành |
3 | Đã phát hành |
state_push_invoice_name và state_publish_name là nhãn (label) tiếng Việt tương ứng với 2 field trên, đã được resolve sẵn trong payload — bên nhận không cần tự map lại.
Tiny REST API — Tài liệu sử dụng
Hướng dẫn kết nối và sử dụng REST API của module tiny_rest_api — xác thực, CRUD theo model, rate limit, và ví dụ chi tiết với cosan.order, cosan.product.
Tiny REST API
Base URL: https://{your-odoo-domain}
Module: tiny_rest_api
Phiên bản tài liệu: 1.0
Tài liệu này cung cấp thông tin cần thiết để làm việc với REST API của hệ thống (module tiny_rest_api). Mỗi endpoint gồm ví dụ request (cURL / JavaScript), ví dụ response, và mô tả chi tiết Headers / Params.
1. Giới thiệu
API cho phép đọc (GET), tạo mới (POST), cập nhật (PUT) và xoá (DELETE) dữ liệu trên bất kỳ model Odoo nào đã được cấu hình cho phép truy cập (qua rest.api.connect), có kiểm soát field được đọc/ghi (allowlist / blocklist) và có rate limit theo từng API key.
Ngoài các API REST theo model, hệ thống còn có API POST /api/v1/auth/login để đăng nhập bằng login/password và lấy API key (token) dùng cho các request tiếp theo, thay cho việc tạo token thủ công.
2. Xác thực (Authenticating requests)
Tất cả các endpoint dưới /api/v1/... yêu cầu xác thực bằng API key, gửi qua header:
X-API-KEY: {token}| Đặc điểm | Mô tả |
|---|---|
| API key không hợp lệ | Không tồn tại, sai, hoặc đã hết hạn → trả về lỗi 403 với status: error |
| Ngôn ngữ trả về | Có thể chỉ định qua header tuỳ chọn X-LANG (áp dụng cho field dịch, ví dụ state selection) |
| Ngôn ngữ mặc định | Nếu không truyền X-LANG, hệ thống dùng ngôn ngữ mặc định của user gắn với token (hoặc vi_VN) |
X-LANG: vi_VNAPI key sẽ được Zotech cấp — khách hàng không tự tạo/đăng nhập lấy token trên hệ thống. Vui lòng liên hệ đội ngũ Zotech để được cấp API key cho môi trường test/production.
2.1. Rate limit
Nếu token được cấu hình rate_limit > 0, mỗi response GET/POST/PUT/DELETE hợp lệ sẽ kèm theo header:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 3421Khi vượt quá giới hạn, request sẽ bị từ chối với lỗi 403 và message Rate limit exceeded. Try again in {n} seconds.
3. Ví dụ: Đơn hàng Cosan (cosan.order)
Model cosan.order (Đơn hàng Cosan) lưu đơn hàng/vận đơn đồng bộ từ đối tác vận chuyển Cosan (địa chỉ giao, tiền COD, trạng thái giao hàng...) và liên kết với sale.order nội bộ qua odoo_order_id — xem Bảng trường cosan.order bên dưới để tra field khi truyền fields=/include=.
Danh sách đơn hàng Cosan
/api/v1/cosan.order
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.order" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"fields\": \"id,order_code,cpn_code,partner_name,phone,address,total_amount,amount_cod,delivery_state,sync_state,create_time\",
\"domain\": [[\"sync_state\", \"=\", \"pending\"]],
\"page\": 1,
\"page_size\": 50
}"const body = {
fields: "id,order_code,cpn_code,partner_name,phone,address,total_amount,amount_cod,delivery_state,sync_state,create_time",
domain: [["sync_state", "=", "pending"]],
page: 1,
page_size: 50,
};
fetch("https://your-domain.odoo.com/api/v1/cosan.order", {
method: "GET",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"model": "cosan.order",
"page": 1,
"page_size": 50,
"total_records": 34,
"total_pages": 1,
"has_next": false,
"has_previous": false,
"records": [
{
"id": 6021,
"order_code": "CSN2607880123",
"cpn_code": "CPN881029341",
"partner_name": "Nguyễn Văn A",
"phone": "0909123456",
"address": "12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM",
"total_amount": 500000,
"amount_cod": 500000,
"delivery_state": "delivering",
"sync_state": { "value": "pending", "label": "Chờ đồng bộ" },
"create_time": "2026-07-28 09:10:00"
}
]
}| Tham số | Kiểu | Gợi ý hay dùng |
|---|---|---|
fields | string | id,order_code,cpn_code,partner_name,phone,address,total_amount,amount_cod,delivery_state,order_state,sync_state,create_time,date_delivered |
include | string | line_ids — mở rộng chi tiết sản phẩm trong đơn (model cosan.order.line) thành object đầy đủ |
domain | list | Theo mã đơn: [["order_code","=","CSN2607880123"]] · theo mã vận đơn: [["cpn_code","=","CPN881029341"]] · theo trạng thái đồng bộ: [["sync_state","=","pending"]] · theo ngày tạo: [["create_time",">=","2026-07-01 00:00:00"],["create_time","<=","2026-07-31 23:59:59"]] |
Chi tiết 1 đơn hàng Cosan
/api/v1/cosan.order/{record_id}
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.order/6021" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"include\": \"line_ids\"
}"fetch("https://your-domain.odoo.com/api/v1/cosan.order/6021", {
method: "GET",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify({ include: "line_ids" }),
}).then(r => r.json());{
"model": "cosan.order",
"record": {
"id": 6021,
"order_code": "CSN2607880123",
"cpn_code": "CPN881029341",
"odoo_order_id": [201215, "D_SO00201091"],
"partner_name": "Nguyễn Văn A",
"phone": "0909123456",
"address": "12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM",
"total_amount": 500000,
"amount_cod": 500000,
"delivery_state": "delivering",
"order_state": "confirmed",
"sync_state": { "value": "pending", "label": "Chờ đồng bộ" },
"create_time": "2026-07-28 09:10:00",
"date_delivered": false,
"line_ids": [
{
"id": 15092,
"product_id": [3391, "Áo thun nam form rộng"],
"default_code": "26AG06DENM",
"name": "Áo thun nam form rộng",
"invoice_name": "Áo thun nam",
"barcode": "8938501234567",
"quantity": 2,
"tax": 8,
"uom": "Cái",
"currency_id": [1, "VND"],
"price": 500000,
"total_discount": 0
}
]
}
}Response mẫu (404):
{
"status": "error",
"message": "Record with ID 6021 not found"
}Tạo mới đơn hàng Cosan
/api/v1/cosan.order
Yêu cầu xác thực
curl --request POST \
"https://your-domain.odoo.com/api/v1/cosan.order" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"order_code\": \"CSN2607880126\",
\"cpn_code\": \"861730159299\",
\"partner_name\": \"Nguyễn Văn A\",
\"phone\": \"0909123456\",
\"address\": \"12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM\",
\"amount_cod\": 500000,
\"total_amount\": 500000,
\"order_state\": \"Đang giao\",
\"delivery_state\": \"Đang giao\",
\"create_time\": \"2026-08-19\",
\"date_delivered\": \"2026-08-19\",
\"line_ids\": [
[0, 0, {
\"default_code\": \"26AG06DENM\",
\"name\": \"Áo thun nam form rộng\",
\"invoice_name\": \"Áo thun nam\",
\"barcode\": \"8938501234567\",
\"quantity\": 2,
\"tax\": 8,
\"uom\": \"Cái\",
\"price\": 500000,
\"total_discount\": 0
}]
]
}"const body = {
order_code: "CSN2607880126",
cpn_code: "861730159299",
partner_name: "Nguyễn Văn A",
phone: "0909123456",
address: "12 Nguyễn Huệ, P. Bến Nghé, Q.1, TP.HCM",
amount_cod: 500000,
total_amount: 500000,
order_state: "Đang giao",
delivery_state: "Đang giao",
create_time: "2026-08-19",
date_delivered: "2026-08-19",
line_ids: [
[0, 0, {
default_code: "26AG06DENM",
name: "Áo thun nam form rộng",
invoice_name: "Áo thun nam",
barcode: "8938501234567",
quantity: 2,
tax: 8,
uom: "Cái",
price: 500000,
total_discount: 0,
}],
],
};
fetch("https://your-domain.odoo.com/api/v1/cosan.order", {
method: "POST",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"status": "success",
"id": 6099,
"display_name": "CSN2607880126"
}{
"status": "error",
"message": "Missing required field: order_code"
}Field line_ids dùng cú pháp lệnh one2many chuẩn của Odoo — [0, 0, {...}] để thêm dòng mới; mỗi dòng thuộc model cosan.order.line với các field product_id, default_code, name, invoice_name, barcode, quantity, tax, uom, currency_id, price, total_discount. Field order_id trên cosan.order.line (many2one trỏ về cosan.order) được hệ thống tự gán, không cần truyền.
Cập nhật địa chỉ / SĐT giao hàng
/api/v1/cosan.order/{record_id}
Yêu cầu xác thực
curl --request PUT \
"https://your-domain.odoo.com/api/v1/cosan.order/6021" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"address\": \"25 Lê Lợi, P. Bến Thành, Q.1, TP.HCM\",
\"phone\": \"0909999999\"
}"fetch("https://your-domain.odoo.com/api/v1/cosan.order/6021", {
method: "PUT",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify({
address: "25 Lê Lợi, P. Bến Thành, Q.1, TP.HCM",
phone: "0909999999",
}),
}).then(r => r.json());{
"status": "success",
"id": 6021,
"display_name": "CSN2607880123"
}{
"status": "error",
"message": "Fields not allowed via API: total_amount, amount_cod, sync_state"
}Các field số liệu/trạng thái (total_amount, amount_cod, delivery_state, order_state, sync_state...) do đối tác vận chuyển Cosan đồng bộ về và không nên ghi tay. Sau khi tạo đơn qua POST, API chỉ mở PUT cho các field liên hệ giao hàng (address, phone) để kịp chỉnh sửa trước khi đơn được lấy hàng.
Bảng trường cosan.order (Đơn hàng Cosan)
Tổng hợp toàn bộ field khả dụng của model cosan.order, dùng để tham chiếu khi truyền fields= / include= cho các API GET, hoặc khi build body cho PUT.
| Tên trường | Nhãn trường | Loại trường | Đối tượng liên quan | Đã lưu | Lập chỉ mục | Chỉ đọc |
|---|---|---|---|---|---|---|
address | Địa chỉ | char | Có | Không | Không | |
amount_cod | COD | tiền tệ | Có | Không | Không | |
cpn_code | Mã CPN | char | Có | Không | Không | |
create_time | Ngày tạo | ngày giờ | Có | Không | Không | |
date_delivered | Ngày giao hàng thành công | ngày | Có | Không | Không | |
delivery_state | Trạng thái vận chuyển | char | Có | Không | Không | |
line_ids | Chi tiết đơn hàng | one2many | cosan.order.line | Có | Không | Không |
odoo_order_id | Đơn hàng nội bộ | many2one | sale.order | Có | Không | Không |
order_code | Mã đơn hàng (Cosan) | char | Có | Có | Không | |
order_state | Trạng thái đơn hàng | char | Có | Không | Không | |
partner_name | Tên khách hàng | char | Có | Không | Không | |
phone | Số điện thoại | char | Có | Không | Không | |
sync_state | Trạng thái đồng bộ | lựa chọn | Có | Không | Không | |
total_amount | Tổng tiền | tiền tệ | Có | Không | Không |
4. Ví dụ: Sản phẩm Cosan (cosan.product)
Model cosan.product (Sản phẩm Cosan) lưu thông tin sản phẩm đồng bộ với đối tác vận chuyển Cosan — kích thước, cân nặng dùng để tính phí vận chuyển — và liên kết với product.product nội bộ qua odoo_product_id — xem Bảng trường cosan.product bên dưới để tra field khi truyền fields=/include=.
Danh sách sản phẩm Cosan
/api/v1/cosan.product
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.product" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"fields\": \"id,product_id,default_code,name,barcode,weight,height,length,width,total_price,odoo_product_id\",
\"domain\": [[\"default_code\", \"=\", \"26AG06DENM\"]],
\"page\": 1,
\"page_size\": 50
}"const body = {
fields: "id,product_id,default_code,name,barcode,weight,height,length,width,total_price,odoo_product_id",
domain: [["default_code", "=", "26AG06DENM"]],
page: 1,
page_size: 50,
};
fetch("https://your-domain.odoo.com/api/v1/cosan.product", {
method: "GET",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"model": "cosan.product",
"page": 1,
"page_size": 50,
"total_records": 1,
"total_pages": 1,
"has_next": false,
"has_previous": false,
"records": [
{
"id": 9042,
"product_id": "CSN-P-30281",
"default_code": "26AG06DENM",
"name": "Áo thun nam form rộng",
"barcode": "8938501234567",
"weight": 0.2,
"height": 3,
"length": 30,
"width": 25,
"total_price": 250000,
"odoo_product_id": [3391, "Áo thun nam form rộng"]
}
]
}| Tham số | Kiểu | Gợi ý hay dùng |
|---|---|---|
fields | string | id,product_id,default_code,name,barcode,description,weight,height,length,width,total_price,invoice_name,odoo_product_id,sync_message |
domain | list | Theo SKU nội bộ: [["default_code","=","26AG06DENM"]] · theo ID sản phẩm bên Cosan: [["product_id","=","CSN-P-30281"]] · theo barcode: [["barcode","=","8938501234567"]] |
Chi tiết 1 sản phẩm Cosan
/api/v1/cosan.product/{record_id}
Yêu cầu xác thực
curl --request GET \
"https://your-domain.odoo.com/api/v1/cosan.product/9042" \
--header "X-API-KEY: {token}" \
--header "Accept: application/json"fetch("https://your-domain.odoo.com/api/v1/cosan.product/9042", {
headers: { "X-API-KEY": "{token}" },
}).then(r => r.json());{
"model": "cosan.product",
"record": {
"id": 9042,
"product_id": "CSN-P-30281",
"default_code": "26AG06DENM",
"name": "Áo thun nam form rộng",
"description": "Áo thun cotton, form rộng, unisex",
"barcode": "8938501234567",
"invoice_name": "Áo thun nam",
"weight": 0.2,
"height": 3,
"length": 30,
"width": 25,
"total_price": 250000,
"odoo_product_id": [3391, "Áo thun nam form rộng"],
"sync_message": false
}
}Response mẫu (404):
{
"status": "error",
"message": "Record with ID 9042 not found"
}Tạo mới sản phẩm Cosan
/api/v1/cosan.product
Yêu cầu xác thực
curl --request POST \
"https://your-domain.odoo.com/api/v1/cosan.product" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"name\": \"Sản phẩm 3\",
\"default_code\": \"003\",
\"product_id\": 2,
\"barcode\": \"0052566\",
\"invoice_name\": \"Sản phẩm 3\",
\"description\": \"Sản phẩm 3\",
\"total_price\": 500000
}"const body = {
name: "Sản phẩm 3",
default_code: "003",
product_id: 2,
barcode: "0052566",
invoice_name: "Sản phẩm 3",
description: "Sản phẩm 3",
total_price: 500000,
};
fetch("https://your-domain.odoo.com/api/v1/cosan.product", {
method: "POST",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify(body),
}).then(r => r.json());{
"status": "success",
"id": 9051,
"display_name": "Sản phẩm 3"
}{
"status": "error",
"message": "Missing required field: default_code"
}Cập nhật kích thước / cân nặng
/api/v1/cosan.product/{record_id}
Yêu cầu xác thực
curl --request PUT \
"https://your-domain.odoo.com/api/v1/cosan.product/9042" \
--header "X-API-KEY: {token}" \
--header "Content-Type: application/json" \
--data "{
\"weight\": 0.25,
\"height\": 4,
\"length\": 32,
\"width\": 26
}"fetch("https://your-domain.odoo.com/api/v1/cosan.product/9042", {
method: "PUT",
headers: { "X-API-KEY": "{token}", "Content-Type": "application/json" },
body: JSON.stringify({ weight: 0.25, height: 4, length: 32, width: 26 }),
}).then(r => r.json());{
"status": "success",
"id": 9042,
"display_name": "Áo thun nam form rộng"
}Kích thước/cân nặng khai báo ở đây được Cosan dùng để tính phí vận chuyển — nên cập nhật đúng số đo thực tế đóng gói (bao gồm hộp/bao bì), không phải kích thước sản phẩm thô. Field odoo_product_id dùng để đối chiếu 1-1 với product.product nội bộ; sửa liên kết này qua PUT nếu hệ thống map nhầm sản phẩm.
Bảng trường cosan.product (Sản phẩm Cosan)
Tổng hợp toàn bộ field khả dụng của model cosan.product, dùng để tham chiếu khi truyền fields= / include= cho các API GET, hoặc khi build body cho PUT.
| Tên trường | Nhãn trường | Loại trường | Đối tượng liên quan | Đã lưu | Lập chỉ mục | Chỉ đọc |
|---|---|---|---|---|---|---|
barcode | Barcode | char | Có | Không | Không | |
default_code | Mã sản phẩm | char | Có | Có | Không | |
description | Mô tả | char | Có | Không | Không | |
height | Chiều cao | dự trữ | Có | Không | Không | |
id | ID | số nguyên | Có | Không | Có | |
invoice_name | Tên xuất hoá đơn | char | Có | Không | Không | |
length | Chiều dài | dự trữ | Có | Không | Không | |
name | Tên sản phẩm | char | Có | Có | Không | |
odoo_product_id | Sản phẩm | many2one | product.product | Có | Không | Không |
product_id | ID sản phẩm (Cosan) | char | Có | Không | Không | |
sync_message | Ghi chú đồng bộ | văn bản | Có | Không | Không | |
total_price | Giá | dự trữ | Có | Không | Không | |
weight | Cân nặng | dự trữ | Có | Không | Không | |
width | Chiều rộng | dự trữ | Có | Không | Không |
5. Cấu hình Webhook Cosan
Trước khi hàm action_push_webhook_cosan hoạt động, cần khai báo URL webhook tại màn hình cấu hình module Cosan.
Cosan → Cấu hìnhMở bản ghi cấu hình (mặc định tên Default).
Cấu hình webhookURL endpoint nhận payload trạng thái hoá đơn (nơi hệ thống ngoài lắng nghe).
Giá trị này được đọc và truyền vào tham số webhook_url khi gọi action_push_webhook_cosan.
Nếu ô này trống, hàm sẽ gọi requests.post(None, ...) và raise lỗi MissingSchema. Nên validate trước khi gọi webhook.
Khi hàm chạy, hệ thống gửi POST tới webhook_url đã cấu hình ở trên, kèm payload JSON tổng hợp từ hoá đơn và sale order liên kết:
Payload đẩy sang Cosan
{webhook_url} — cấu hình tại mục 5
{
"order_code": "CSN2607880123",
"invoice_no": "INV/2026/00845",
"state_push_invoice": "done",
"state_push_invoice_name": "Thành công",
"state_publish": "4",
"state_publish_name": "Thành công",
"sync_invoice": {
"success": true,
"data": "[{\"RefID\":\"c8772cdb-b225-4b63-a4b8-064e49c51b7b\",\"InvSeries\":\"1C26TUT\",\"InvDate\":\"2026-08-24T00:00:00+07:00\",\"EInvoiceStatus\":1}]",
"error": null,
"error_description": null,
"errorCode": []
}
}| Trường | Giá trị | Ý nghĩa |
|---|---|---|
order_code | — | Mã đơn hàng sàn TMĐT hoặc mã đơn do Cosan bắn sang |
invoice_no | — | Số hoá đơn điện tử do MISA trả về |
state_push_invoicefield state_push_invoice | new | Mới — chưa đẩy hoá đơn điện tử |
processing | Đang thực hiện | |
done | Thành công | |
error | Lỗi | |
state_publishfield publish_status | 0 | Chưa phát hành |
3 | Đã phát hành |
state_push_invoice_name và state_publish_name là nhãn (label) tiếng Việt tương ứng với 2 field trên, đã được resolve sẵn trong payload — bên nhận không cần tự map lại.