2. code convention
1. Cấu Trúc Module
1.1 Tên Module
1.2 Cấu Trúc Thư Mục Module
├── apps/
│ ├── product/ # Domain: Sản phẩm
│ │ ├── product/
│ │ │ ├── __init__.py
│ │ │ ├── config/ # Cấu hình menu/module UI
│ │ │ │ └── desktop.py
│ │ │ ├── hooks.py # Đăng ký event, API, scheduler
│ │ │ ├── modules.txt # Liệt kê module của app
│ │ │ ├── product/ # Module: Product (bounded context)
│ │ │ │ ├── __init__.py
│ │ │ │ ├── doctype/
│ │ │ │ │ ├── product/
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── product.py # Controller
│ │ │ │ │ │ ├── product.js # Client logic
│ │ │ │ │ │ ├── product.json # Doctype schema
│ │ │ │ │ ├── product_category/
│ │ │ │ │ │ ├── product_category.py
│ │ │ │ │ │ └── product_category.json
│ │ │ │ ├── api/ # Custom API
│ │ │ │ │ └── product_api.py
│ │ │ │ ├── domain/ # Logic nghiệp vụ (DDD-style)
│ │ │ │ │ ├── entity.py
│ │ │ │ │ └── service.py
│ │ │ │ └── repository/ # Truy vấn DB riêng (nếu tách)
│ │ │ │ └── product_repository.py
│ │ │ └── public/
│ │ │ ├── js/
│ │ │ └── css/
│ │ ├── setup.py
│ │ └── MANIFEST.in
│ ├── order/ # Domain: Đơn hàng
│ │ ├── order/
│ │ │ ├── order/
│ │ │ │ ├── doctype/...
│ │ │ │ ├── api/
│ │ │ │ ├── domain/
│ │ │ │ └── repository/
│ │ │ ├── hooks.py
│ │ │ └── ...
│ │ └── setup.py
│ ├── address/ # Domain: Địa chỉ, địa bàn
│ │ ├── address/
│ │ │ ├── address/
│ │ │ │ ├── doctype/...
│ │ │ │ ├── api/
│ │ │ │ ├── domain/
│ │ │ │ └── repository/
│ │ │ ├── hooks.py
│ │ │ └── ...
│ │ └── setup.py
│ └── havico/ # ERP đặc thù của khách Havico
│ ├── havico/
│ │ ├── config/
│ │ ├── erp/
│ │ │ ├── doctype/
│ │ │ ├── api/
│ │ │ ├── domain/
│ │ │ └── integration/ # Gọi API từ các app khác
│ │ └── hooks.py
│ └── setup.py
├── sites/
│ ├── apps.txt # Danh sách apps được cài cho toàn bench
│ ├── common_site_config.json
│ ├── product.saas.vn/ # Site riêng cho domain sản phẩm
│ │ ├── site_config.json
│ │ ├── private/
│ │ └── public/
│ ├── order.saas.vn/ # Site riêng cho domain đơn hàng
│ │ └── ...
│ ├── address.saas.vn/ # Site riêng cho domain địa chỉ
│ │ └── ...
│ └── havico.saas.vn/ # ERP site của khách Havico
│ └── ...
│
├── Procfile
├── requirements.txt # Chứa frappe + apps
└── bench-config.json
2. Quy Ước DocType
2.1 Tên DocType
2.2 Thuộc Tính DocType
# Trong file .json của DocType
{
"doctype": "DocType",
"name": "_customer",
"module": "SalesManagement",
"custom": 0,
"is_submittable": 0,
"is_child_table": 0,
"track_changes": 1,
"track_seen": 1
}
3. Quy Ước Field
3.1 Naming Convention
Primary Key: {table_name}_id
- Code Field:
{table_name}_code
- Name Field:
{table_name}_name
- Foreign Key:
{related_table}_id
3.2 Ví Dụ Cụ Thể
# Bảng "_test" (tab_test)
- test_id # Primary identifier
- test_code # Unique code
- test_name # Display name
- test_description # Description
- test_status # Status field
- test_type_id # FK to "_type" table
- created_by_id # FK to User
- modified_by_id # FK to User
# Bảng "_type" (tab_type)
- type_id # Primary identifier
- type_code # Unique code
- type_name # Display name
3.3 Field Types và Properties
# String Fields
{
"fieldname": "customer_code",
"fieldtype": "Data",
"label": "Customer Code",
"reqd": 1,
"unique": 1,
"length": 20
}
# Link Fields (Foreign Key)
{
"fieldname": "customer_type_id",
"fieldtype": "Link",
"label": "Customer Type",
"options": "_customer_type",
"reqd": 1
}
# Select Fields
{
"fieldname": "customer_status",
"fieldtype": "Select",
"label": "Status",
"options": "Active\nInactive\nSuspended",
"default": "Active"
}
4. Quy Ước File và Folder
4.1 Python Files
- Controllers:
{doctype_name}.py
- Format: snake_case
- Ví dụ:
_customer.py, _sales_order.py
4.2 JavaScript Files
- Client Scripts:
{doctype_name}.js
- Format: snake_case
- Ví dụ:
_customer.js, _sales_order.js
4.3 JSON Files
- DocType Definition:
{doctype_name}.json
- Ví dụ:
_customer.json
5. Quy Ước Database
5.1 Indexes
# Trong DocType JSON
"indexes": [
{
"columns": ["customer_code"],
"unique": 1
},
{
"columns": ["customer_type_id", "customer_status"]
}
]
5.2 Constraints
- Unique Fields: Luôn đặt unique constraint cho code fields
- Required Fields: Đánh dấu rõ ràng các field bắt buộc
- Default Values: Đặt giá trị mặc định hợp lý
6. Quy Ước Code Python
6.1 Class Names
# Controller Classes
class _Customer(Document):
def validate(self):
self.validate_customer_code()
def validate_customer_code(self):
if not self.customer_code:
self.customer_code = self.generate_customer_code()
6.2 Method Names
6.3 Import Convention
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import nowdate, getdate, flt, cint
7. Quy Ước JavaScript
7.1 Client Script Structure
frappe.ui.form.on('_Customer', {
refresh: function(frm) {
// Form refresh logic
},
customer_type_id: function(frm) {
// Field change logic
},
validate: function(frm) {
// Client-side validation
}
});
8. Quy Ước Permission
8.1 Role Naming
8.2 Permission Structure
# Trong DocType permissions
{
"role": "Sales User",
"read": 1,
"write": 1,
"create": 1,
"delete": 0,
"submit": 0,
"cancel": 0,
"amend": 0
}
9. Quy Ước Report
9.1 Report Names
9.2 Report Fields
columns = [
{
"fieldname": "customer_code",
"label": _("Customer Code"),
"fieldtype": "Link",
"options": "_customer",
"width": 120
}
]
10. Quy Ước Documentation
def validate_customer_code(self):
"""
Validate customer code format and uniqueness
Returns:
None
Raises:
frappe.ValidationError: If code format is invalid
"""
pass
# Validate customer information before saving
if self.customer_type_id:
self.validate_customer_type()
# TODO: Add email validation
# FIXME: Handle duplicate code edge case
11. Quy Ước Testing
11.1 Test File Names
11.2 Test Class Structure
import unittest
import frappe
class TestCustomer(unittest.TestCase):
def setUp(self):
# Test setup code
pass
def test_customer_creation(self):
# Test customer creation
pass
def tearDown(self):
# Cleanup code
pass
12. Best Practices
- Sử dụng indexes cho các field thường xuyên query
- Tránh N+1 queries bằng cách sử dụng
frappe.get_all() với fields parameter
- Cache dữ liệu static với
frappe.cache()
12.2 Security
12.3 Maintainability
- Viết code có thể đọc được
- Sử dụng meaningful variable names
- Tách logic phức tạp thành methods nhỏ
- Documentation đầy đủ
12.4 Error Handling
try:
# Business logic
pass
except frappe.ValidationError:
frappe.throw(_("Validation failed: {0}").format(error_message))
except Exception as e:
frappe.log_error(message=str(e), title="Customer Creation Failed")
frappe.throw(_("An error occurred while creating customer"))