Skip to main content

2. code convention

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

├── 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

  • 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

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