π― Who This Is For & the Learning Journey
This guide is written for anyone who needs to go from zero knowledge of ERPNext to being productive with it professionally: ERPNext beginners, Python/Django and full-stack developers, system administrators, database developers, ERP implementers, business analysts, technical support engineers, and anyone building a custom ERPNext module.
Every diagram in this guide labels the same underlying chain: User β Frontend β Backend β DocType β Database β Integration. That chain is the single idea the whole course is built around β once it clicks, every ERPNext screen, table, and API call reads as an instance of it.
β οΈ Version & Accuracy Note
- Verified Frappe/ERPNext convention β core, long-stable behavior (e.g. the
tab<DocType>table naming rule, thebenchcommand family, doc-event hooks). - Conceptual / illustrative architecture β diagrams and table lists built to teach the relationships, not copied from a specific installation's schema browser.
π§ What Is ERPNext? Frappe vs ERPNext
ERP (Enterprise Resource Planning) software exists to solve one recurring business problem: sales, purchasing, inventory, accounting, and HR are all run as separate spreadsheets or disconnected tools, so nobody can see one true, current picture of the business. An ERP unifies them into one database with one shared set of master data (customers, items, accounts) so every transaction updates the same source of truth.
ERPNext is an open-source ERP application. It is not a standalone codebase β it is built on top of Frappe Framework, a full-stack, metadata-driven web application framework (Python backend, JavaScript frontend, MariaDB database) that ERPNext uses for everything: authentication, permissions, forms, reports, workflows, and the REST API. This is the single most important distinction for a developer to internalize before touching ERPNext code.
| Term | What it means |
|---|---|
| Frappe | The underlying framework: auth, permissions, ORM, UI rendering, REST API, background jobs |
| ERPNext | A Frappe app that implements ERP business logic on top of the framework |
| App | An installable Python package (Frappe or ERPNext itself, or a custom app) that bundles DocTypes, code, and assets |
| Module | A logical grouping of DocTypes within an app (e.g. "Selling", "Stock") |
| DocType | A metadata definition of a business object β its fields, permissions, and behavior; the framework generates a database table and a UI form from it |
| Document | One saved record/row of a DocType (e.g. one specific Sales Invoice) |
| Field | One attribute on a DocType (Data, Link, Currency, Table, Select, β¦) |
| Child Table | A DocType marked "Is Child Table", used to store repeatable rows inside a parent document (e.g. invoice line items) |
| Workspace | A configurable landing page/dashboard grouping shortcuts, charts, and links for a module or role |
| Report | A saved query/script that reads DocType data and renders it as a table, chart, or dashboard |
| Page | A custom, code-driven screen that isn't a standard DocType list/form (used for dashboards, POS, etc.) |
| Web Form | A public-facing, simplified form that lets portal/external users create or edit specific DocTypes |
ποΈ Frappe/ERPNext Architecture
Browser
ERPNext UI
- Desk, forms, list views
Frappe Framework
- auth, ORM, permissions
DocTypes
- metadata + schema
APIs
- REST / RPC endpoints
Controllers
- Python classes, hooks
MariaDB
- ERPNext Database
Behind that request/response path, a full ERPNext deployment runs several supporting processes together:
| Component | Role |
|---|---|
| Python | Backend language β DocType controllers, server scripts, API logic |
| JavaScript | Frontend logic β client scripts, form behavior, the Desk UI itself |
| MariaDB | Primary relational database; one table per DocType |
| Redis | Caching, background job queue, and real-time pub/sub backing |
| Background workers | Process queued/long-running jobs (emails, reports, bulk updates) outside the request cycle |
| Scheduler | Triggers scheduler_events hooks on a cron-like cadence (daily, hourly, etc.) |
| Web server | Gunicorn (app server) typically fronted by Nginx in production |
| Socket.IO | Real-time updates β live notifications, document locks, desk refresh |
| REST API | /api/resource/<DocType> β the framework-generated API surface for every DocType |
| File storage | Attachments and generated files, stored under the site's public/files / private/files directories |
βοΈ Installation & Dependencies
ERPNext can be installed for local development (bench on bare metal or WSL), via Docker, or directly on a production VPS. All paths converge on the same tool: Frappe Bench, the CLI that manages sites, apps, and processes.
Linux
Python
Node.js
MariaDB
Redis
Git
Yarn
wkhtmltopdf
Frappe Bench
Frappe
ERPNext
The core bench workflow, in order:
# install bench itself (via pip/pipx), then:
bench init frappe-bench # scaffold a new bench (downloads the Frappe app)
cd frappe-bench
bench new-site site1.local # create a site: its own database + config
bench get-app erpnext # fetch the ERPNext app source
bench --site site1.local install-app erpnext # install ERPNext into that site
bench start # run web server + workers + scheduler for development
Each command has a distinct job: bench init sets up the shared bench (apps + environment); bench new-site creates one isolated tenant (its own MariaDB database, own config, own set of installed apps); get-app vs install-app is the same split as downloading a package vs actually enabling it for a specific site β one bench can host many sites, each with a different combination of installed apps.
| Path | Contents |
|---|---|
apps/ | Source of every installed app β apps/frappe/, apps/erpnext/, and any custom apps |
sites/ | One folder per site; sites/common_site_config.json holds bench-wide settings, sites/site1.local/ holds that site's config and files |
config/ | Generated Nginx/Supervisor config for production setups |
logs/ | Web, worker, and scheduler logs |
env/ | The Python virtual environment bench runs inside |
Procfile | Process list bench start uses in development (web, workers, scheduler, socketio) |
π Production Deployment Architecture
In production, Supervisor keeps Gunicorn, the background workers, and the scheduler running (and restarts them on crash or reboot), while Nginx handles TLS (typically via Let's Encrypt / Certbot for free auto-renewing HTTPS certificates), serves static assets directly, and proxies dynamic requests to Gunicorn. A production checklist also includes a configured firewall (only 80/443/22 open), a scheduled backup job, and a DNS record pointed at the server before requesting a certificate.
π First Login & Initial Setup
The first login into a freshly installed site runs the Setup Wizard, which walks through the minimum master data ERPNext needs before any transaction can be created:
| Step | Why it's required first |
|---|---|
| Company | Almost every transaction and account belongs to a Company β it's the top of the accounting hierarchy |
| Currency & Fiscal Year | Every ledger entry needs a currency and an open accounting period to post into |
| Chart of Accounts | Generated automatically from a country/industry template, then customized β every GL Entry needs an Account |
| Warehouse | Stock can't move without at least one warehouse to move into/out of |
| Customers / Suppliers | Optional at setup, but needed before the first Sales/Purchase document |
| Users & Roles | Determines who can do what from day one β see Users, Roles & Permissions |
π₯οΈ ERPNext UI Concepts
Almost the entire application is one interface, called the Desk, built from a small set of reusable view types:
| # | UI element | What it's for |
|---|---|---|
| 1 | Sidebar | Module and workspace navigation |
| 2 | Workspace | A configurable dashboard/landing page per module or role |
| 3 | Awesome Bar (search) | Global fuzzy search across DocTypes, documents, and even commands ("new sales invoice") |
| 4 | User menu | Profile, settings, "switch to desk/portal", logout |
| 5 | Notifications | Assignments, mentions, and document events targeted at the current user |
| 6 | List View | Filterable, sortable table of documents for one DocType |
| 7 | Filters | Standard and saved filters on any list or report |
| 8 | Form View | The single-document editing screen β fields, child tables, and actions |
| 9 | Actions (Submit/Cancel/Print/Duplicateβ¦) | Document-level operations available from the form |
| β | Report View | Spreadsheet-like grouped/aggregated view over a DocType's data |
| β | Dashboard / Kanban / Calendar / Tree view | Alternate visualizations of the same underlying documents, chosen per DocType |
π₯ Users, Roles & Permission Flow
ERPNext ships a set of standard roles that map to job functions, all ultimately subordinate to the System Manager role, which has unrestricted administrative access:
Access is resolved through a layered chain, not a single flag β this is the same "many small typed layers, not one field" principle that shows up in ERP accounting status modeling:
A Role Profile bundles a set of roles for fast assignment to new users; Document Share grants ad-hoc access to one specific document outside the normal role rules; and workflow-state permissions (see Workflow Engine) can further restrict who may transition a document, independent of its base role permissions.
π§© Core Modules at a Glance
ERPNext ships as a set of business modules sharing one database and one permission system. Every module follows the same internal shape β Purpose β Master Data β Transactions β Child Tables β Workflow β Database β Accounting/Stock Impact β Other-Module Connections β Reports β API β so once you understand one module deeply, reading a new one is mostly about learning its specific documents, not a new mental model.
| Module | Purpose | Key master data | Core transaction flow | Connects to |
|---|---|---|---|---|
| Accounting | Books of record β every module ultimately posts here | Company, Account, Cost Center, Fiscal Year, Tax Template | Sales/Purchase Invoice β Payment Entry / Journal Entry β GL Entry | All modules |
| Selling | Quote-to-cash for customers | Customer, Territory, Sales Person, Price List | Lead β Opportunity β Quotation β Sales Order β Delivery Note β Sales Invoice | Stock, Accounting, CRM |
| Buying | Procure-to-pay from suppliers | Supplier, Item, Buying Price List | Material Request β RFQ β Supplier Quotation β Purchase Order β Purchase Receipt β Purchase Invoice | Stock, Accounting |
| Stock | Inventory quantity & valuation | Item, Item Group, Warehouse, Batch, Serial No | Stock Entry / Delivery Note / Purchase Receipt β Stock Ledger Entry β Bin | Selling, Buying, Manufacturing, Accounting |
| Manufacturing | Convert raw materials into finished goods | BOM, Workstation, Operation, Routing | BOM β Work Order β Material Transfer β Job Card β Finished Goods β Stock Entry | Stock, Accounting |
| CRM | Pre-sales relationship management | Lead, Opportunity, Contact, Address | Lead β Opportunity β Quotation β Customer | Selling |
| Projects | Track billable and internal work | Project, Task, Activity Type | Project β Task β Timesheet β Sales Invoice | HR, Accounting |
| Assets | Fixed asset lifecycle & depreciation | Asset Category, Asset | Purchase Invoice β Asset β Depreciation β Asset Movement/Disposal | Accounting |
| HR | Employee lifecycle & attendance | Employee, Department, Designation | Employee β Attendance β Leave Application | Payroll |
| Payroll | Salary calculation & disbursement | Salary Structure, Employee Grade | Salary Structure β Salary Slip β Payroll Entry | HR, Accounting |
| Quality | Inspection & conformance | Quality Procedure, Quality Goal | Quality Inspection β Purchase Receipt / Delivery Note / Manufacturing | Stock, Manufacturing, Buying |
| Support | Post-sales customer service | Issue, Service Level Agreement | Customer β Issue β Assignment β Communication β Resolution | CRM, Selling |
π Flagship Document Flows
Selling: Lead to Cash
Child tables carry the line items at every stage: Quotation Item, Sales Order Item, Delivery Note Item, Sales Invoice Item β each new document typically pulls its rows from the previous one so item, price, and quantity stay traceable end to end.
Buying: Procure to Pay
Manufacturing: BOM to Finished Goods
A BOM (Bill of Materials) can itself reference another Item that has its own BOM β a multi-level BOM β so manufacturing a finished item can cascade into manufacturing its sub-assemblies first.
HR & Payroll: Employee to Accounting
Assets: Purchase to Disposal
GL Entry table β it's arguably the single most important transaction table in ERPNext, because it's where every module's financial impact becomes comparable in one ledger. For the full accounting theory behind why invoices, payments, and GL postings are deliberately kept as separate records, see Invoice vs Payment: The Complete ERP Accounting Architecture.
πΈοΈ Module Interconnection Master Diagram
CRM
Selling
Sales Order
Stock
- Delivery Note
Accounting
- GL Entry
Payment
| Source module | Feeds into |
|---|---|
| Buying | Stock, Accounting |
| Manufacturing | Stock, Accounting |
| HR | Payroll |
| Payroll | Accounting |
| Projects | Timesheet β Billing β Accounting |
Notice the pattern: almost every module is a source that eventually drains into Stock and/or Accounting. Those two are the shared "settlement layer" every other module's transactions converge on β the ERPNext equivalent of the subledger/GL relationship described in the accounting curriculum's ERP Architecture level.
ποΈ Database Architecture: DocType β Table
This is a verified, stable Frappe convention: every non-child, non-virtual DocType gets a MariaDB table named tab + the DocType name, spaces included β e.g. tabCustomer, tabSupplier, tabItem, tabSales Order, tabSales Invoice. Changing a DocType's fields in the UI ("Customize Form" or the DocType editor) triggers a schema migration that adds/alters the corresponding column β this is why DocTypes are described as "metadata-driven": the schema is a byproduct of the metadata, not hand-written SQL.
| Concept | Definition |
|---|---|
| DocType | The schema + behavior definition (fields, permissions, controller class) |
| Document | One instance/row of a DocType |
| Child table | A DocType flagged "Is Child Table"; its rows always belong to exactly one parent document |
| Link field | A foreign-key-style reference to another DocType's name (primary key) |
| Dynamic Link | A Link field whose target DocType is itself chosen by another field (used for polymorphic references, e.g. an Address linking to a Customer or a Supplier) |
| Table field | The field type on a parent DocType that embeds a child table |
π Master Tables vs Transaction Tables
| Type | Examples | Behavior |
|---|---|---|
| Master | Customer, Supplier, Item, Warehouse, Account, Employee | Created once, referenced repeatedly by many transactions; rarely deleted, usually just deactivated |
| Transaction | Sales Order, Purchase Order, Sales Invoice, Delivery Note, Payment Entry, Stock Entry | Created per business event, generally follow a submit/cancel lifecycle, and are the rows that actually move quantities and money |
Master data exists so transactions don't repeat information β a Sales Order doesn't store a customer's address text, it stores a Link to the Customer, and the Customer record is the single place that address gets corrected. This is the same "don't duplicate the fact, reference it" principle behind normalized database design generally.
Child Tables Explained
A child table row always carries four framework-managed fields that tie it back to its parent, in addition to whatever fields the child DocType itself defines:
| Field | Purpose |
|---|---|
parent | The name of the owning parent document |
parenttype | The parent's DocType (needed because a child table can, in principle, be reused by more than one parent DocType) |
parentfield | Which Table field on the parent this row belongs to |
idx | The row's display order within the table |
tabSales Order
β
β parent (1) ββββ (N) rows
βΌ
tabSales Order Item
parent = "SAL-ORD-2026-00042"
parenttype = "Sales Order"
parentfield = "items"
idx = 1
item_code = "WIDGET-001"
qty = 10
Illustrative Table Map (Verify Per Version)
The following are well-known, long-stable core DocTypes grouped by module, to illustrate the naming convention β not an exhaustive schema dump. Confirm exact names and any version differences against your installed site (e.g. via bench --site SITE console or the DocType list in Developer Mode) before relying on them in code.
| Area | Representative tables |
|---|---|
| Core | tabUser, tabRole, tabDocType, tabDocField, tabWorkflow, tabFile, tabCommunication, tabToDo |
| CRM / Selling | tabLead, tabOpportunity, tabCustomer, tabQuotation, tabSales Order, tabSales Order Item, tabDelivery Note, tabSales Invoice, tabSales Invoice Item |
| Buying | tabSupplier, tabMaterial Request, tabPurchase Order, tabPurchase Order Item, tabPurchase Receipt, tabPurchase Invoice, tabPurchase Invoice Item |
| Stock | tabItem, tabItem Group, tabWarehouse, tabBin, tabStock Entry, tabStock Ledger Entry, tabSerial No, tabBatch |
| Accounting | tabAccount, tabGL Entry, tabPayment Entry, tabJournal Entry, tabCost Center, tabCompany |
| Manufacturing | tabBOM, tabBOM Item, tabWork Order, tabJob Card |
| HR | tabEmployee, tabDepartment, tabAttendance, tabLeave Application, tabSalary Slip, tabPayroll Entry |
π ER Diagrams: Customer, Item, Accounting
Customer Relationships
Item Relationships
Accounting Relationships
π Internal vs External Connections
Internal (module β module, inside one database)
| Selling β Stock | Selling β Accounting |
| Buying β Stock | Buying β Accounting |
| Manufacturing β Stock | Manufacturing β Accounting |
| HR β Payroll | Projects β Accounting |
External (ERPNext β the outside world)
π REST API Basics
Frappe auto-generates a REST-style endpoint for every DocType at /api/resource/<DocType>, supporting the standard HTTP verbs, authenticated with an API key/secret pair generated per user:
# list/read
GET /api/resource/Customer
GET /api/resource/Customer/CUST-00001
# create
POST /api/resource/Customer
# update
PUT /api/resource/Customer/CUST-00001
# delete
DELETE /api/resource/Customer/CUST-00001
# Python (requests)
import requests
headers = {"Authorization": "token API_KEY:API_SECRET"}
r = requests.get("https://site.example.com/api/resource/Customer", headers=headers)
// JavaScript (fetch)
fetch("https://site.example.com/api/resource/Customer", {
headers: { "Authorization": "token API_KEY:API_SECRET" }
}).then(r => r.json());
π οΈ Customization Decision Tree
ERPNext offers a deliberate ladder of customization tools, from no-code to full custom application β always reach for the lowest rung that solves the problem:
π¦ Building a Custom Frappe App
bench new-app my_custom_app
bench --site site1.local install-app my_custom_app
my_custom_app/
βββ hooks.py # app config: doc_events, scheduler_events, includes, fixtures
βββ modules.txt # modules this app defines
βββ patches.txt # data migration scripts run on update
βββ public/ # JS/CSS/images served to the browser
βββ templates/ # Jinja templates for web pages/emails
βββ my_custom_app/
βββ my_module/
βββ doctype/ # one folder per custom DocType (json + python + js)
βββ report/ # custom reports
βββ page/ # custom Desk pages
Doc-event hooks are how a custom app plugs into the lifecycle of any document β including ERPNext's own β without editing ERPNext source:
Other key hook categories: scheduler_events (cron-style background jobs), app_include_js/app_include_css (global frontend assets), fixtures (data β like custom fields or roles β exported and reapplied on install), permission_query_conditions and has_permission (row-level access rules beyond the standard permission engine), and override_doctype_class (replacing a standard DocType's controller logic without editing its file).
π§ͺ Custom Module Case Study: Laboratory Management
A realistic custom app example β extending ERPNext for a diagnostic lab β illustrates how a domain-specific module plugs into ERPNext's core rather than replacing it:
New DocTypes (Patient, Sample, Test Request, etc.) link back into ERPNext core via ordinary Link fields β Sample.customer β Customer, Sample.company β Company, Test.item β Item, Test Request.employee β Employee β so a lab report can bill through the standard Sales Invoice flow instead of the custom app reinventing billing.
Integration happens on three levels, and a well-designed custom app touches all three deliberately rather than only the first:
| Level | Mechanism |
|---|---|
| 1. UI Integration | Workspace, module icon, dashboard cards for the new DocTypes |
| 2. Data Integration | Link/Dynamic Link fields and child tables tying custom records to Customer, Item, Company, Employee |
| 3. Business Logic Integration | Hooks, doc events, controller methods, and APIs that trigger ERPNext-side actions (e.g. auto-creating a Sales Invoice when a Lab Report is approved) |
π Workflow Engine
A Frappe Workflow defines a set of Workflow States, the Actions/Transitions allowed between them, and which Role may perform each transition β letting a document (any DocType, standard or custom) require a multi-step approval chain without writing code:
π‘οΈ Security, Backup & Troubleshooting
Layer that with HTTPS everywhere, a real password policy, server-side validation (never trust client scripts alone), and the audit trail every document already carries (owner, modified_by, version history) β plus scheduled, tested backups:
| Symptom | Likely cause | First command to run |
|---|---|---|
| Site doesn't open | Web process/Nginx down, or wrong host entry | bench start / check Nginx logs |
| MariaDB connection error | DB not running or wrong credentials in site config | bench mariadb |
| Redis error | Redis cache/queue process not running | check redis-server status/logs |
| Scheduled jobs not firing | Scheduler disabled or worker not running | bench doctor |
| Migration failure | Conflicting patch or schema change | bench migrate (read the traceback first) |
| Build/asset failure | Node/Yarn version mismatch | bench build |
| Nginx 502 | Gunicorn/app server not running | check Supervisor status |
π» Developer Command Cheat Sheet
bench start # run dev processes (web, workers, scheduler)
bench restart # restart supervisor-managed processes (production)
bench update # pull latest app code + run migrations
bench migrate # apply pending schema/data migrations
bench build # rebuild frontend JS/CSS assets
bench clear-cache # clear Frappe's Redis cache
bench clear-website-cache # clear rendered website page cache
bench console # interactive Python shell with the site bootstrapped
bench mariadb # open a MariaDB shell for the current site
bench doctor # diagnose common environment problems
bench --site SITE list-apps # list apps installed on a site
bench --site SITE install-app APP # install an app into a site
bench --site SITE uninstall-app APP # remove an app from a site
bench --site SITE backup # take a database (+ optional files) backup
β Best Practices
- Never modify ERPNext (or Frappe) core source β every core change gets silently discarded or conflicts on the next
bench update. - Extend via a Custom App + hooks + Custom DocTypes + Custom Fields + Workflows + APIs instead.
- Use fixtures and patches to move customizations between dev, staging, and production predictably.
- Keep development, staging, and production as separate sites/environments, and test every upgrade in staging first.
- Back up the database before running
bench migrateorbench updatein production. - Prefer the REST API for external integrations over direct database access.
- Design permissions through Roles and User Permissions rather than hard-coding access checks in custom scripts.
- Keep everything in version control, including fixtures exported from the UI.
π’ Real-World Scenarios
Retail: ABC Electronics Ltd.
Running in parallel on the purchasing side β Supplier β Purchase Order β Purchase Receipt β Purchase Invoice β Payment β with Stock and Accounting updating automatically from both sides without either team touching the other's documents directly.
Manufacturing Company
Custom Module: Laboratory Extension
The same Lab Management case study above, closing with billing: once a Lab Report is approved, the custom app's business-logic hook creates a Sales Invoice against the linked Customer, feeding straight back into the Accounting module covered earlier.
πΊοΈ Learning Roadmap & What's Next
This article is Part 1 of a 9-part series, covering the foundations (architecture, installation, modules, and database design) in enough depth to navigate the rest of ERPNext confidently. The planned follow-ups go deeper on each area:
If your interest in ERPNext is specifically the accounting side β how GL Entry, Payment Entry, and invoice settlement actually work under the hood β that ground is covered in full depth, framework-agnostically, in Invoice vs Payment: The Complete ERP Accounting Architecture and the Accounting to ERP curriculum.