Skip to main content

2. code convention

Frappe Code Convention - Quy Ước Lập Trình

1. Cấu Trúc Module

1.1 Tên Module

  • Format: CamelCase
  • Ví dụHumanResourceSalesManagementInventoryControl
  • Lưu ý: Sử dụng tên mô tả rõ ràng chức năng của module

1.2 Cấu Trúc Thư Mục Module

module_name/
├── __init__.py
├── hooks.py
├── modules.txt
├── patches.txt
├── doctype/
├── report/
├── dashboard_chart/
├── workspace/
├── public/
│   ├── css/
│   └── js/
└── templates/

2. Quy Ước DocType

2.1 Tên DocType

  • Format_ten_bang (snake_case với underscore đầu)
  • Lý do: Frappe tự động thêm prefix "tab", underscore đầu giúp dễ tìm kiếm
  • Ví dụ:
    • _customer → tab_customer
    • _sales_order → tab_sales_order
    • _product_category → tab_product_category

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

  • Format: snake_case
  • Prefix:
    • validate_ cho validation methods
    • get_ cho getter methods
    • set_ cho setter methods
    • generate_ cho generation methods

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

  • Format{Module} {Permission Level}
  • Ví dụ:
    • Sales User
    • Sales Manager
    • HR User
    • HR Manager

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

  • Format{Module} - {Report Purpose}
  • Ví dụ:
    • Sales - Customer Analysis
    • Inventory - Stock Summary

9.2 Report Fields

columns = [
    {
        "fieldname": "customer_code",
        "label": _("Customer Code"),
        "fieldtype": "Link",
        "options": "_customer",
        "width": 120
    }
]

10. Quy Ước Documentation

10.1 Docstring Format

def validate_customer_code(self):
    """
    Validate customer code format and uniqueness
    
    Returns:
        None
        
    Raises:
        frappe.ValidationError: If code format is invalid
    """
    pass

10.2 Comment Style

# 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

  • Formattest_{doctype_name}.py
  • Ví dụtest__customer.py

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

12.1 Performance

  • 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

  • Luôn validate input data
  • Sử dụng permission framework của Frappe
  • Escape user input khi hiển thị

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