Loading…
Loading…
How custom business apps are built on the Frappe framework: doctypes, APIs, workflows and connections to other systems.
Frappe is an open-source, full-stack web framework built with Python and JavaScript. It runs ERPNext, one of the most popular open-source ERP systems in the world, but you can build much more than ERP with it. It gives you what you need to build business applications quickly, without giving up flexibility or the ability to scale.
Frappe is metadata-driven. Instead of writing repetitive code for CRUD operations, forms and APIs, you define your data models (called DocTypes). The framework then generates the forms, REST APIs, database tables and more.
First, why consider Frappe for a custom business application?
DocTypes are the basic building block of any Frappe application. A DocType defines a data model. Think of it as a database table that comes with a form, an API and a place for business logic.
Single DocType: Stores only one record, useful for settings. Example: a "Quality Settings" DocType that holds global configuration for an app.
Regular DocType: Stores multiple records. Example: an "Incoming Inspection" DocType where each record is one inspection of a material batch.
Child DocType: Exists only within a parent DocType as a table field. Example: "Inspection Checks" as a child table within an "Incoming Inspection" DocType.
Virtual DocType: Does not create a database table; pulls data from external sources. Useful for integrating with third-party systems.
DocTypes can be created through the Frappe UI or defined in code as JSON files within your custom app. A DocType definition includes:
Install Frappe Bench, which is the CLI tool for managing Frappe sites and apps:
pip install frappe-bench
bench init my-bench
cd my-bench
bench new-site mysite.localhost
bench new-app my_custom_app
bench --site mysite.localhost install-app my_custom_app
This creates a standard app structure with directories for DocTypes, API endpoints, hooks, and more.
Create your data models either through the UI at /app/doctype or by defining JSON files in your app's module directory. Each DocType gets:
Frappe gives you several places to add custom logic:
Controller methods: Python classes that handle events on a DocType:
before_insert: Before a new record is savedvalidate: Validation before saveon_submit: After a record is submittedon_cancel: After a record is cancelledWhitelisted API methods: Create custom API endpoints:
@frappe.whitelist()
def calculate_total(inspection_id):
inspection = frappe.get_doc("Incoming Inspection", inspection_id)
# Custom calculation logic
return {"total": total}
Scheduled tasks: Run background jobs on a schedule:
# In hooks.py
scheduler_events = {
"daily": ["my_app.tasks.daily_cleanup"],
"hourly": ["my_app.tasks.sync_data"],
}
Frappe has a full Python API for working with data:
Creating records:
doc = frappe.get_doc({
"doctype": "Incoming Inspection",
"supplier": "ABC Castings",
"item_group": "Castings",
"checks": [
{"check_name": "Outer diameter", "method": "Vernier"},
{"check_name": "Hardness", "method": "IS 1500"}
]
})
doc.insert()
Querying data:
inspections = frappe.get_all("Incoming Inspection",
filters={"status": "Pending"},
fields=["name", "supplier", "item_group"],
order_by="creation desc",
limit=20
)
Updating records:
frappe.db.set_value("Incoming Inspection", "INSP-001", "status", "Completed")
Frappe's client-side API mirrors the server-side patterns:
Making API calls:
frappe.call({
method: "my_app.api.calculate_total",
args: { inspection_id: "INSP-001" },
callback: function(r) {
console.log(r.message);
}
});
Form customisation:
frappe.ui.form.on("Incoming Inspection", {
refresh: function(frm) {
if (frm.doc.status === "Pending") {
frm.add_custom_button("Start Inspection", function() {
frm.set_value("status", "In Progress");
frm.save();
});
}
}
});
Frappe has a built-in workflow engine. You can define the states a document moves through for any DocType:
A workflow consists of:
An incoming inspection might follow this workflow:
Each transition can require specific roles, trigger email notifications, and execute custom Python logic.
Connecting a Frappe app to other systems is straightforward:
Every DocType has auto-generated REST endpoints:
GET /api/resource/Incoming Inspection: List inspectionsPOST /api/resource/Incoming Inspection: Create an inspectionGET /api/resource/Incoming Inspection/INSP-001: Get one inspectionPUT /api/resource/Incoming Inspection/INSP-001: Update an inspectionConfigure webhooks to notify external systems when events occur in your Frappe app. Webhooks can be triggered on document events like creation, update, submission, or cancellation.
For heavy processing or calls to outside APIs, use Frappe's background jobs, built on Redis Queue (RQ). Long tasks then run without blocking the web interface.
Create custom print formats using HTML and Jinja templating. Print formats can generate PDFs for reports, invoices, labels, and any other document type your application needs.
Frappe applications can be deployed on any Linux server. The production stack typically includes:
For scaling, Frappe supports:
If you plan to build a custom business application, Frappe lets you build quickly on a base that is ready for production. A quality module, an ERP add-on or a dealer portal are all good fits.
Talk to us about a custom app.
In a free 30-minute call we map your process and tell you plainly what would change, whether that is Tanush LIMS, ERPNext or something built for you. We reply within 24 hours.