Repo SharedConcepts
Appearance
Repo_SharedConcepts — Patterns Across Projects
This page documents patterns and concepts that appear across multiple repositories in the ComFac ecosystem.
Frappe Framework Patterns
DocType: The Core Abstraction
A DocType is Frappe's fundamental building block. It combines:
- Database table — Schema definition
- Form UI — Layout and fields
- Permissions — Access control
- Workflow — States and transitions
- Controller — Python class for business logic
Key insight: Editing a DocType JSON changes the database schema. Editing the Python controller changes behavior.
DocType File Locations
app_name/
└── app_name/
└── doctype/
└── doctype_name/
├── doctype_name.json # Schema (fields, permissions)
├── doctype_name.py # Controller (validations, methods)
├── doctype_name_list.js # List view customization
├── doctype_name_calendar.js # Calendar view
└── test_doctype_name.py # Unit tests
hooks.py: The Wiring Diagram
Every Frappe app has a hooks.py that registers:
| Hook Type | Purpose | Example |
|---|---|---|
doc_events |
React to document changes | Send email when Order submitted |
scheduler_events |
Background jobs | Daily report generation |
override_doctype_class |
Replace core behavior | Custom Employee validation |
doctype_js |
Client-side scripts | Form field visibility |
website_generators |
Public pages | Product pages |
Request Flow in Frappe
HTTP Request
↓
Frappe Router
↓
Permission Check
↓
Controller Method (whitelisted)
↓
Document Load/Save (if DocType)
↓
Database (MariaDB)
↓
Response (JSON/HTML)
Common Database Operations
| Operation | Code | Use When |
|---|---|---|
| Get single doc | frappe.get_doc("Doctype", "name") |
You need the full object with methods |
| Query multiple | frappe.get_all("Doctype", filters={}) |
Read-only list, respects permissions |
| Raw SQL | frappe.db.sql("SELECT ...") |
Complex queries (avoid if possible) |
| Single value | frappe.db.get_value("Doctype", "name", "field") |
Just need one field |
Extension Patterns
The Override Pattern
Instead of modifying core code, create an override:
# In your app: my_app/overrides/employee.py
from erpnext.setup.doctype.employee.employee import Employee
class CustomEmployee(Employee):
def validate(self):
super().validate() # Call original
# Add your custom validation
if self.date_of_birth and self.date_of_birth > today():
frappe.throw("Date of birth cannot be in the future")
Register in hooks.py:
override_doctype_class = {
"Employee": "my_app.overrides.employee.CustomEmployee"
}
The Event Hook Pattern
Add behavior without changing the original class:
# hooks.py
doc_events = {
"Sales Order": {
"on_submit": "my_app.hooks.send_notification_email"
}
}
# my_app/hooks.py
def send_notification_email(doc, method=None):
frappe.sendmail(
recipients=[doc.contact_email],
subject=f"Order {doc.name} confirmed!",
message="..."
)
Custom Fields Pattern
Add fields without forking the DocType:
| Method | Best For | Persistence |
|---|---|---|
| Desk UI (Customize Form) | Quick additions | Database only |
| Fixtures | Version controlled | Exportable JSON |
| Patch | Data migration | One-time setup |
Python Patterns
Imports You Will See Everywhere
import frappe # The framework
from frappe import _ # Translation function
from frappe.utils import today, getdate, flt, cint # Utilities
The frappe.throw Pattern
if not self.customer:
frappe.throw(_("Customer is required"), title=_("Missing Field"))
Always use _("...") for translatable strings.
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| DocType names | Title Case | "Leave Application" |
| Field names | snake_case | "employee_name" |
| Python files | snake_case | "leave_application.py" |
| Class names | PascalCase | class LeaveApplication
|
Git-Mediawiki Workflow
How This Wiki Works
- Content is written as
.wikifiles (MediaWiki wikitext) - Files are stored in a git repository (
wiki-gi7b-org/) git pushsyncs to live MediaWiki at wiki.gi7b.org
File Naming
| Wiki Page | File Path |
|---|---|
PageName |
PageName.wiki
|
Namespace:Page |
Namespace/Page.wiki
|
Page/Subpage |
Page/Subpage.wiki
|
Commit Convention
# After completing a repo analysis
git add Repo/AppName/
git commit -m "Repo:AppName — full structural analysis (6 pages)"
git push
# After updating index
git add Repo_Index.wiki Repo_SharedConcepts.wiki
git commit -m "Repo:Index + SharedConcepts — updated after AppName analysis"
git push
Reading Dependencies
requirements.txt / pyproject.toml
When you see a dependency, ask:
- Is it a Frappe app? (
frappe,erpnext,payments)- These extend the framework
- Is it a Python library? (
requests,pandas)- These provide specific functionality
- Is it for development? (
pytest,black)- These are dev-only, not runtime
package.json (Node.js)
- Dependencies — Required at runtime
- devDependencies — Build/test only
- Look for
frappe-ui— Frappe's Vue component library
Development Workflow
Typical Edit Cycle
- Understand — Read relevant wiki page
- Locate — Find the file to edit (use DirectoryMap)
- Edit — Make minimal changes
- Test — Run
bench start, check browser - Migrate — If DocType changed:
bench migrate - Build — If JS/CSS changed:
bench build - Commit — Git commit with clear message
Common bench Commands
| Command | When to Use |
|---|---|
bench start |
Start development server |
bench migrate |
Sync DocTypes, run patches |
bench build |
Compile frontend assets |
bench --site [site] install-app [app] |
Install app on site |
bench --site [site] console |
Interactive Python shell |
bench --site [site] mariadb |
Direct database access |
Quick Decision Guide
"I want to add a field to a form"
- Is it a core field? → Edit DocType JSON
- Is it custom/temporary? → Use Customize Form (Desk UI)
- Needs to be version controlled? → Use fixtures
"I want to change validation logic"
- For standard DocTypes → Override class in
overrides/ - For custom DocTypes → Edit the DocType's
.pycontroller - For cross-cutting concerns → Use
doc_eventshook
"I want to run code on a schedule"
- Add to
scheduler_eventsin hooks.py - Choose frequency:
all,hourly,daily,weekly,monthly
"I want to add a web page"
- Public page → Create in
www/ - Auto-generated per record → Use
website_generators - API endpoint → Use
@frappe.whitelist()
Resources
- Frappe Framework Docs: https://frappeframework.com/docs
- ERPNext Docs: https://docs.erpnext.com
- Frappe School: https://frappe.school
- This Wiki: https://wiki.gi7b.org