You are on page 1of 150

Frappe Framework Documentation

Frappe is a full stack, batteries-included, web framework written in Python and Javascript.
It is the framework which powers ERPNext. It is pretty generic and can be used to build
database driven apps.

Table of Contents
1. Introduction to Frappe Framework

2. Installing Bench and Frappe

3. Bench Architecture

4. Creating a Frappe App

5. Understanding Doctypes

6. Desk

7. Users and Permission

8. Reports

9. Printing

10. Portal Pages

11. Web Forms

12. Database Migrations

13. Deployment

14. Developer API

15. Testing

16. Debugging

17. Contribution Guidelines


Introduction
Frappe, pronounced fra-pay, is a full stack, batteries-included, web framework written in
Python and Javascript with MariaDB as the database. It is the framework which
powers ERPNext. It is pretty generic and can be used to build database driven apps.

The key difference in Frappe compared to other frameworks is that meta-data is also
treated as data and is used to build front-ends very easily. We believe in a monolithic
architecture, so Frappe comes with almost everything you need to build a modern web
application. It has a full blown Admin UI called the Desk that handles forms, navigation,
lists, menus, permissions, file attachment and much more out of the box.

Frappe is MIT Licensed and is hosted on GitHub.

Getting Started
Installation

Before you can use Frappe, you need to install it. We have a complete installation guide
which covers all possibilities, this guide will also help you understand the backend stack.

Frappe Installation Guide →

Documentation

The official documentation covers everything you need to know about the Frappe
Framework.

Read the docs →

Join the community


Hang out with our growing community on our forum, ask questions, help others and be
a part of the community.

Join Us →
Introduction
What is Frappe Framework?
Frappe is a full stack, batteries-included, web framework written in Python and Javascript.
It is the framework which powers ERPNext. It is pretty generic and can be used to build
database driven apps.

Meta-data driven

Meta-data is a first class citizen in Frappe. It is used to generate database tables, design
forms and configure a lot of features. Meta-data is stored in a Model which is known as
DocType in Frappe.

Let's take an example of a DocType called ToDo. It will contain fields


like status, date and description.

Here is what the todo.json may look like:

{
"name": "ToDo",
"module": "Desk",
"field_order": [
"status",
"date",
"description"
],
"fields": [
{
"default": "Open",
"fieldname": "status",
"fieldtype": "Select",
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"options": "Open\nClosed"
},
{
"fieldname": "date",
"fieldtype": "Date",
"in_standard_filter": 1,
"label": "Due Date"
},
{
"fieldname": "description",
"fieldtype": "Text Editor",
"in_global_search": 1,
"label": "Description",
"reqd": 1
}
]
}
A configuration like this will generate a database table whose schema might look like

MariaDB [_baa0f26509a564b6]> desc tabToDo;


+-----------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+---------+-------+
| name | varchar(140) | NO | PRI | NULL | |
| creation | datetime(6) | YES | | NULL | |
| modified | datetime(6) | YES | MUL | NULL | |
| modified_by | varchar(140) | YES | | NULL | |
| owner | varchar(140) | YES | | NULL | |
| docstatus | int(1) | NO | | 0 | |
| idx | int(8) | NO | | 0 | |
| status | varchar(140) | YES | | Open | |
| description | longtext | YES | | NULL | |
| date | date | YES | | NULL | |
+-----------------------+--------------+------+-----+---------+-------+

Rich Admin Interface

Frappe does not only manage the backend, it also comes with a feature rich admin
interface called the Desk. When you create a DocType in Frappe, a number of views are
generated for it. Here are some of them:
The List View supports paging, filtering, sorting and bulk editing
records.
List View

The Form View used for editing records also supports file attachments, PDF format,
comments, email,
etc.
Form View

The Report Builder supports adding columns, grouping, filtering, sorting and saving it as
a
configuration.
Report Builder

Users, Roles and Permissions

Frappe comes with User and Role management out of the box. A User is someone who
can login to the system and perform authorized actions like creating, updating or
deleting records. A Role is a mapping of DocTypes and actions allowed to perform on it.
Python, JS and MariaDB

Frappe Framework uses Python for the backend. It comes with a simple yet powerful
ORM as an abstraction over CRUD operations. The default database is MariaDB. Postgres
support is in beta.

doc = frappe.new_doc('ToDo')
doc.description = 'Buy Eggs'
doc.insert()
The front-end is an SPA built using Javascript (jQuery).

Realtime

Frappe also supports realtime pub/sub events using NodeJS and socketio.

# server
frappe.publish_realtime('update_progress', {
'progress': 42,
'total': 100
})

# client
frappe.realtime.on('update_progress', (data) => {
console.log(data)
});

Background Jobs

Frappe also supports background job queuing based on Python RQ.

frappe.enqueue('frappe.job.run_job', arg1='Test', arg2='Test2')

Email

Frappe supports sending and receiving emails, which can also be linked to individual
documents.
Printing

Frappe supports generating PDF print formats based on Jinja Templates. It also comes
with a drag-and-drop Print Format Builder.
Print Preview
Print Format Builder

Batteries Included

Frappe has tons of features that are essential to building a modern complex app. Only
the basic features are introduced here. The rest of this guide will cover them and other
advanced features with much finer detail, so make sure to read it all!
Installation
These steps assume you want to install Bench in developer mode. If you want install in
production mode, follow the Easy Install method.

System Requirements
This guide assumes you are using a personal computer, VPS or a bare-metal server. You
also need to be on a *nix system, so any Linux Distribution and MacOS is supported.
However, we officially support only the following distributions.

1. MacOS

2. Debian / Ubuntu

3. Arch Linux

4. CentOS

Learn more about the architecture here.

Pre-requisites
1. Python 2.7 (Python 3.5+ also supported)

2. MariaDB 10+

3. Nginx (for production)

4. Git

5. Nodejs

6. yarn

7. Redis

8. cron (crontab is required)

9. wkhtmltopdf with patched Qt (version 0.12.3) (for pdf generation)

MacOS

Install Homebrew. It makes it easy to install packages on macOS.


/usr/bin/ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"
Now, you can easily install the required packages by running the following command

brew install python


brew install git
brew install redis
brew install mariadb

brew tap caskroom/cask


brew cask install wkhtmltopdf
Install Node

We recommend installing node using nvm

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh |


bash
After nvm is installed, you may have to close your terminal and open another one. Now
run the following command to install node.

nvm install 8
Verify the installation, by running:

node -v
# output
v8.11.3
Finally, install yarn using npm

npm install -g yarn

Debian / Ubuntu

Install git, python, and redis

sudo apt install git python-dev redis-server


Install MariaDB

sudo apt-get install software-properties-common


sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80
0xF1656F24C74CD1D8
sudo add-apt-repository 'deb [arch=amd64,i386,ppc64el] http://ftp.ubuntu-
tw.org/mirror/mariadb/repo/10.3/ubuntu xenial main'
sudo apt-get update
sudo apt-get install mariadb-server-10.3
During this installation you'll be prompted to set the MySQL root password. If you are
not prompted, you'll have to initialize the MySQL server setup yourself. You can do that
by running the command:

mysql_secure_installation
Remember: only run it if you're not prompted the password during setup.
It is really important that you remember this password, since it'll be useful later on. You'll
also need the MySQL database development files.

sudo apt-get install libmysqlclient-dev


Now, edit the MariaDB configuration file.

sudo nano /etc/mysql/my.cnf


And add this configuration

[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[mysql]
default-character-set = utf8mb4
Now, just restart the mysql service and you are good to good

sudo service mysql restart


Install Node

We recommend installing node using nvm

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh |


bash
After nvm is installed, you may have to close your terminal and open another one. Now
run the following command to install node.

nvm install 8
Verify the installation, by running:

node -v
# output
v8.11.3
Finally, install yarn using npm

npm install -g yarn


Install wkhtmltopdf

sudo apt-get install xvfb libfontconfig wkhtmltopdf

Arch Linux

Install packages using pacman

pacman -Syu
pacman -S mariadb redis python2-pip wkhtmltopdf git npm cronie nginx openssl
npm install -g yarn
Setup MariaDB

mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql


systemctl start mariadb
mysql_secure_installation
Edit the MariaDB configuration file

nano /etc/mysql/my.cnf
Add the following configuration

[mysqld]
innodb-file-format=barracuda
innodb-file-per-table=1
innodb-large-prefix=1
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

[mysql]
default-character-set = utf8mb4
Start services

systemctl start mariadb redis


If you don't have cron service enabled you would have to enable it.

systemctl enable cronie

Install Bench
Install bench as a non-root user

git clone https://github.com/frappe/bench bench-repo


pip install --user -e bench-repo
Confirm the bench installation by checking version

bench --version

# output
4.1.0
Create your first bench folder.

cd ~
bench init frappe-bench
After the frappe-bench folder is created, change your directory to it and run this
command

bench start
Congratulations, you have installed bench on to your system.
Bench Architecture
Bench is a command-line utility that helps you install apps, manage multiple sites and
update Frappe based apps on *nix based systems (macOS, CentOS, Debian, Ubuntu, etc)
for development and production. Bench will also create nginx and supervisor config files,
setup backups and much more.
Bench Architecture

For installation, check this guide

Directory structure

When you install bench, you will have a directory structure similar to

.
├── apps
│ ├── frappe
├── config
├── env
├── logs
├── Procfile
└── sites
├── apps.txt
├── assets
├── common_site_config.json
└── site1.local
├── private
├── public
└── site_config.json

Apps

A Frappe app is a python package that use the Frappe framework. Frappe apps live in a
directory called apps. As you can see, there is an app named frappe, since frappe is an
app which acts as the framework. A Frappe app should have an entry in apps.txt.

Sites

Frappe is a multitenant platform and each tenant is called a site. Sites exist in a directory
called sites, assumed as the current working directory when running a bench command.

Every site contains a private and public directory which stores private and public files
respectively. common_site_config.json is a configuration file common to all
sites. site_config.json is a configuration file specific to each site and will override
options from common_site_config.json.

Virtual Environment

Every frappe-bench directory is a python virtual environment. It holds the python


packages required by frappe apps.

Processes
Processes are defined in a file called Procfile. This is only for developement mode.

redis_cache: redis-server config/redis_cache.conf


redis_socketio: redis-server config/redis_socketio.conf
redis_queue: redis-server config/redis_queue.conf
web: bench serve --port 8000

socketio: /usr/bin/node apps/frappe/socketio.js

watch: bench watch

schedule: bench schedule


worker_short: bench worker --queue short --quiet
worker_long: bench worker --queue long --quiet
worker_default: bench worker --queue default --quiet
Let's see what each process is used for.

redis_cache:

Redis used for in-memory caching.

redis_socketio:

Redis used as a pub/sub between web and socketio processes for realtime
communication.

redis_queue:

Redis used for managing background jobs queuing.

web:

Python web server based on Werkzeug.

socketio:

Node server for a socketio connection with the browser for realtime communication.

watch:

Node server for bundling JS/CSS assets using Rollup. It will also rebuild files as they
change.

schedule:

Job Scheduler using Python RQ.

worker_short:

This worker has a short timeout (300).


worker_long:

This worker has a long timeout (1500).

worker_default:

This worker has a default timeout (300).


Creating a Frappe App
To create a new Frappe app, run the following command from the frappe-
bench directory.

frappe@frappe:~/frappe-bench$ bench new-app my-app


INFO:bench.app:creating new app my_app
App Title (default: My App):
App Description: App description
App Publisher: John Doe
App Email: app@email.com
App Icon (default 'octicon octicon-file-directory'):
App Color (default 'grey'):
App License (default 'MIT'):
'my_app' created at /home/frappe/frappe-bench/apps/my_app
INFO:bench.app:installing my_app
INFO:bench.utils:./env/bin/pip install -q -e ./apps/my_app

frappe@frappe:~/frappe-bench$ ls apps
frappe my_app
The my_app directory will now be created in the apps directory. It will also be added
to apps.txt.

To install an app, you must have a site. To install this app to a site, run the following
command.

frappe@frappe:~/frappe-bench$ bench --site site_name install-app my_app

Installing my_app...
frappe@frappe:~/frappe-bench$ bench --site site_name list-apps
frappe
my_app
frappe@frappe:~/frappe-bench$
Running the list-apps sub-command, you can verify if the app was installed correctly.
Understanding DocTypes
1. DocType

2. DocField

3. Naming

4. Document

5. Controllers

o Controller Methods

o Controller Hooks

6. Child DocType

7. Single DocType

DocType
A DocType is the core building block of any application based on the Frappe Framework.
It describes the Model and the View of your data. It contains what fields are stored for
your data, and how they behave with respect to each other. It contains information
about how your data is named. It also enables rich Object Relational Mapper
(ORM) pattern which we will discuss later in this guide. When you create a DocType, a
JSON object is created which in turn creates a database table.

ORM is just an easy way to read, write and update data in a database without writing
explicit SQL statements.

Conventions

To enable rapid application development, Frappe Framework follows some standard


conventions.

1. DocType is always singular. If you want to store a list of articles in the database,
you should name the doctype Article.

2. Table names are prefixed with tab. So the table name for Article doctype
is tabArticle.

The standard way to create a DocType is by typing new doctype in the search bar in
the Desk.
ToDo DocType

A DocType not only stores fields, but also other information about how your data
behaves in the system. We call this Meta. Since this meta-data is also stored in a
database table, it makes it easy to change meta-data on the fly without writing much
code. Learn more about Meta.

A DocType is also a DocType. This means that we store meta-data as the part of the
data.
After creating a DocType, Frappe can provide many features out-of-the-box. If you go
to /desk#List/ToDo you will be routed to the List View in the desk.
ToDo List

Similarly, you get a Form View at the route /desk#Form/ToDo/000001. The Form is used to
create new docs and view them.
ToDo Form

Module
A DocType will always belong to a module, to enable easier grouping of related models.
Frappe comes with a lot of built-in modules. For e.g

1. Core - contains doctypes like DocType, DocField, Report, System Settings, etc

2. Desk - contains doctypes like ToDo, Event, Note, Kanban Board, etc

3. Email - contains doctypes like Email Account, Newsletter, Email Group, etc

Modules also helps in grouping of code files in directories. The controller files generated
by a DocType will live in it's respective Module directory.

frappe
├── commands
├── config
├── core
│ ├── doctype
│ │ ├── doctype
│ │ ├── docfield
│ │ ├── report
│ │ ├── system_settings
│ │ ├── ...
│ │ └── ...
│ ├── page
│ ├── report
│ └── web_form
├── desk
│ ├── doctype
│ │ ├── ...
│ │ ├── event
│ │ ├── kanban_board
│ │ ├── note
│ │ └── todo
│ ├── form
│ ├── page

DocField
A DocField is a list of fields which describes what properties a DocType will have. For
instance, a ToDo doctype has fields description, status and priority. These ultimately
become columns in the database table tabToDo.

Example
The DocField stores meta-data about the field. Some of them are described below.

[
{
"label": "Description", // the value shown to the user (Form, Print,
etc)
"fieldname": "description", // the property name we refer in code, also
the column name
"fieldtype": "Text Editor", // the fieldtype which also decides how to
store this value
"reqd": 1 // whether this field is mandatory
},
{
"label": "Status",
"fieldname": "status",
"fieldtype": "Select",
"options": [
"Open",
"Pending",
"Closed"
]
},
{
"label": "Priority",
"fieldname": "priority",
"fieldtype": "Select",
"options": [ // list of options for select
"Low",
"Medium",
"High"
],
"default": "Low" // the default value to be set
}
]
Frappe comes with moret than 30 different fieldtypes out-of-the-box. These fieldtypes
serve a variety of use-cases. Learn more about Fieldtypes.

Naming
All docs in Frappe have a primary key called name. This is the unique id by which you will
be finding records and manipulating them using the ORM. You can configure
how docsshould be named when a new document is created. The following are the ways
you can setup naming in a DocType.

1. field:[fieldname]

The doc name is fetched from the value of the field provided.
2. [series]

You can provide a naming pattern which will be incremented automatically. For e.g, if
you set it as PRE.#####, the first document created will have the name as PRE00001, and
second one will be PRE00002 and so on.
3. naming_series:

The naming pattern is derived from a field in the document. For e.g, you have a
field naming_series in your document and it's value is set as PRE.#####, then that will be
the pattern used for generating the name. This value can change per document. So the
next document can have a different pattern.

This works only if you have a field called naming_series in your DocType.
4. Prompt

If you set it as Prompt, the name is required to be filled in manually.


5. Format
This is the most flexible one when it comes to configuring your naming schemes.

Let's say we have

EXAMPLE-{MM}-test-{fieldname1}-{fieldname2}-{#####}

Everything outside the curly braces are plain text. Keywords inside the curly braces will
be evaluated based on what they represent. In this case:

 MM: will be replaced by the current month

 fieldname1: will be replaced by the value of fieldname1 in the document

 #####: will generate a series, which starts with 00001

So the final name may look like, EXAMPLE-02-test-value1-value2-00001

Document
An instance of a DocType is called a doc (shorthand for document). Usually a doc directly
maps to a single row in the table.
Example

Let's say we have a DocType ToDo with the following fields:

 description

 status

 priority

Now, if we want to query a document from the database, we can use the ORM.

>>> doc = frappe.get_doc('ToDo', '0000001')


<frappe.desk.doctype.todo.todo.ToDo at 0x1128d35d0>

>>> doc.as_dict()
{u'creation': datetime.datetime(2018, 8, 14, 12, 57, 4, 568148),
u'description': u'Buy Groceries',
u'modified': datetime.datetime(2018, 8, 14, 12, 57, 16, 622779),
u'modified_by': u'faris@erpnext.com',
u'name': u'0000001',
u'owner': u'faris@erpnext.com',
u'priority': u'Medium',
u'status': u'Open',
...
}
You get the values of description, status and priority, but you also get fields
like creation, owner and modified_by which are fields added by default by the framework
on all docs.

Controllers
A Controller is a normal python class which extends from frappe.model.Document base
class. This base class is the core logic of a DocType. It handles how values are loaded
from the database, how they are parsed and saved back to the database.

When you create a DocType named Person, a python file is created by the
name person.pyand the contents look like:

# -*- coding: utf-8 -*-

from __future__ import unicode_literals


import frappe
from frappe.model.document import Document

class Person(Document):
pass
All the fields are available to the class as attributes.

Controller Methods

You can add custom methods to your Controller and it will be callable using
the doc object. For example,

# controller definition
class Person(Document):
def get_full_name(self):
"Returns the person's full name"
return self.first_name + ' ' + self.last_name

# somewhere in your code


>>> doc = frappe.get_doc('Person', '000001')
>>> doc.get_full_name()
John Doe
You can also override the pre-defined document methods to add your own behaviour.
For e.g to override the save() method,

class Person(Document):
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # call the base save method
do_something_else()
There are a lot of methods provided by default on the doc object. You can find the
complete list here.

Controller Hooks

To add custom behaviour during the lifecycle of a document, we have controller hooks.

Method Name
Description
before_submit Called before a document is submitted.
before_cancel This is called before a submitted document is cancelled.
before_update_after_submi This is called before a submitted document values are updated.
t
before_insert This is called before a document is inserted into the database.
before_naming This is called before the name property of the document is set.
autoname This is an optional method which is called only when it is defined in
the controller. Use this method to customize how the name property
of the document is set.
validate Use this method to throw any validation errors and prevent the
document from saving.
before_save This method is called before the document is saved.
Method Name
Description
after_insert This is called after the document is inserted into the database.
on_update This is called when values of an existing document is updated.
on_submit This is called when a document is submitted.
on_update_after_submit This is called when a submitted document values are updated.
on_cancel This is called when a submitted is cancelled.
on_change This is called to indicate that a document's values has been
changed.
on_trash This is called when a document is being deleted.
after_delete This is called after a document has been deleted.
To use a controller hook, just define a class method with that name. For e.g

class Person(Document):
def validate(self):
if self.age > 60:
frappe.throw('Age must be less than 60')

def after_insert(self):
frappe.sendmail(recipients=[self.email], message="Thank you for
registering!")

1. Create a document

To create a new document and save it to the database,

doc = frappe.get_doc({
'doctype': 'Person',
'first_name': 'John',
'last_name': 'Doe'
})
doc.insert()

doc.name # 000001

2. Load a document

To get an existing document from the database,

doc = frappe.get_doc('Person', '000001')

# doctype fields
doc.first_name # John
doc.last_name # Doe

# standard fields
doc.creation # datetime.datetime(2018, 9, 20, 12, 39, 34, 236801)
doc.owner # faris@erpnext.com
Child DocType
Up until now we have only seen DocTypes that can have a single value for each field.
However, there might be a need for storing multiple records against one record, also
known as many-to-one relationships. A Child DocType is doctype which can only be
linked to a parent DocType. To make a Child DocType make sure to check Is Child
Table while creating the doctype.
Child DocType records are directly attached to the parent doc.

>>> person = frappe.get_doc('Person', '000001')


>>> person.as_dict()
{
'first_name': 'John',
'last_name': 'Doe',
'qualifications': [
{'title': 'Frontend Architect', 'year': '2017'},
{'title': 'DevOps Engineer', 'year': '2016'},
]
}

Single DocType
A Single DocType is a DocType that holds only one record in the database. It is useful for
persisting things like System Settings, which don't make sense to have multiple records.
>>> settings = frappe.get_doc('System Settings')
>>> settings.notification_frequency
'Daily'
Desk
Frappe Framework comes with a rich admin interface called the Desk. It reads meta-data
from DocTypes and automatically builds list views, form views, report views, etc for your
DocTypes. Desk is to be used by users of the type "System User".

In this section we will discuss what views are provided by Desk and how to configure
them.

 Desktop

 Awesomebar

 List View

 Form View

 Report Builder

 Tree View

 Calendar View

 Gantt View

 Kanban View

Desktop
When you login to the Desk, the first screen you see is called the Desktop. Each card is
organized based on related functionalities and their DocTypes.
Desktop

Awesomebar
Awesomebar helps you to navigate anywhere in the system, create new records, search
in documents and even perform math operations.
Navigating ToDo using Awesomebar
List View
List View is generated for all DocTypes except which are Child Tables and Singles.

The List view is packed with features. Some of them are:

1. Filters

2. Sorting

3. Paging

4. Filter by tags

5. Switch view to Report, Calendar, Gantt, Kanban, etc.


List View

To customize the List View you must have a {doctype}_list.js file in the doctype
directory. Here are all the options that can be customized. This examples assumes the
Note DocType.

frappe.listview_settings['Note'] = {
// add fields to fetch
add_fields: ['title', 'public'],
// set default filters
filters: [
['public', '=', 1]
],
hide_name_column: true, // hide the last column which shows the `name`
onload(listview) {
// triggers once before the list is loaded
},
before_render() {
// triggers before every render of list records
},
get_indicator(doc) {
// customize indicator color
if(doc.public) {
return [__("Public"), "green", "public,=,Yes"];
} else {
return [__("Private"), "darkgrey", "public,=,No"];
}
},
primary_action() {
// triggers when the primary action is clicked
},
get_form_link(doc) {
// override the form route for this doc
},
// add a custom button for each row
button: {
show(doc) {
return doc.reference_name;
},
get_label() {
return 'View';
},
get_description(doc) {
return __('View {0}', [`${doc.reference_type} $
{doc.reference_name}`])
},
action(doc) {
frappe.set_route('Form', doc.reference_type, doc.reference_name);
}
},
// format how a field value is shown
formatters: {
title(val) {
return val.bold();
},
public(val) {
return val ? 'Yes' : 'No';
}
}
}

Form View
Form view is used to view the records in a Form Layout. This view has a lot of things
going on. But the primary purpose of it is to view and edit records. A document can be
assigned to or shared with other users and it can have arbitrary attachments and tags, all
of which can be seen in the form sidebar.
Form View

When you scroll down to the bottom of the form, you will see the form timeline. The
form timeline shows emails, comments, edits and other events in a reverse chronological
order.
Form Timeline

Report Builder
Report Builder is a generic tool to customize and build tabular data from a DocType. You
can select columns to show, filters to apply, sort order and save this configuration by
giving your report a name. You can also show Child Table data and also filter documents
by their child records. You can also apply Group By on a column with aggregation
methods like Count, Sum and Average.

Report Builder Features


Tree View
Frappe also supports tree structured records using the Nested set model. If a doctype is
configured to be a tree structure, it can be viewed in the Tree view.
Tree View

Calendar View
Calendar view can be configured for DocTypes with a start date and end date.
Calendar View

The configuration file should be named {doctype}_calendar.js and should exist in the
doctype directory. Here is an example configuration file for calendar view for Event
doctype.

frappe.views.calendar['Event'] = {
field_map: {
start: 'starts_on',
end: 'ends_on',
id: 'name',
allDay: 'all_day',
title: 'subject',
status: 'event_type',
color: 'color'
},
style_map: {
Public: 'success',
Private: 'info'
},
get_events_method: 'frappe.desk.doctype.event.event.get_events'
}
event_calendar.js

Gantt View
Gantt view uses the same configuration file as calendar, so every DocType that has a
Calendar view has a Gantt view too.
Gantt View

Kanban View
Kanban view can be created for any DocType that has a Select field with options. These
options become the column names for the Kanban Board.
Users and Permissions
Frappe comes with a user authentication system. It handles user accounts, role based
permissions and cookie based user sessions.

User authentication system in Frappe comes with a lot of features out of the box:

1. User

2. Role

3. DocType Permissions

4. Permission Level

5. Role Permission Manager

6. User Permissions

7. Restricting Views and Forms

8. Password Hashing

9. Password strength checking

10. Throttling of login attempts

11. Third party authentication like OAuth, Google, Facebook, etc

User and Role


A User record represents an authenticated user who can perform authorized actions in
the system. A User can have multiple roles assigned to it. A Role describes what actions a
User can perform on a DocType.
User Roles

For example, the role Blogger has read, write and create permission on the
doctype Blog Post, but only read permission on Blog Category.
Role Blogger
DocType Permissions
DocTypes can have a default set of Roles applied when you install your app. To configure
roles for a DocType you must add them in the Permissions table in DocType.
DocType Permissions
If you expand the row, you will see many more options that can be
configured.
DocType Permissions

Here is a list of them with their explanation:

Option Explanation
Level Permission Level assigned to this role
If the user is owner The restrictions will apply only if the user is the one who created that
document
Read Allow read access to the document
Write Allow edit access to the document
Create Allow create access to the document
Delete Allow user to delete the document
Submit Allow user to submit the document
Cancel Allow user to cancel the document
Amend Allow user to amend the document
Report Allow user to view the report view
Export Allow user to export records in Excel/CSV
Import Allow user to import records using the Data Import Tool
Set User Allow user to apply user permissions for other users
Permissions
Share Allow user to share the document with other users
Print Allow user to print the document or generate PDF
Email Allow user to send emails for that document

Permission Level
Permission Levels can be used to group fields in a document and apply separate roles to
each level. By default all fields have permlevel set as 0.
Permission Level

Role Permissions Manager


Role Permissions Manager is a user tool to manage role permissions. The default set of
permissions show up here and can be overridden.

User Permissions
User Permissions are another set of rules that can be applied per user basis. It can be
used to restrict documents which contain a specific value for a Link field.
For example, to restrict the User John such that he can only view Blog Posts that were
created by him, i.e, Blogger John. A user permission record with the following values
should be created.
User Permission Record
After creating the user permission configuration, when the User logs in to see the Blog
Post list, he will have a restricted view of blog posts that were created by him.
Restricted Blog Post List

Restricting Views and Forms


Frappe Framework allows you to configure what modules, doctypes and views are visible
to the user. To configure which modules are shown to a user go to the Allow
Modules section of the User form.
To hide a doctype from a User, remove the read permission from a Role using the Role
Permissions Manager.

To control permissions for Pages and Reports, use the Role Permission for Page and
Reporttool.
Password Hashing
Frappe handles password hashing out of the box. They are encrypted and saved in a
separate database table named __Auth.

MariaDB [_baa0f26509a564b6]> select * from __Auth;


+---------+------------------+-----------
+-----------------------------------------------
| doctype | name | fieldname | password
+---------+------------------+-----------
+-----------------------------------------------
| User | Administrator | password | $pbkdf2-
sha256$29000$Xss5pxSC8F5rDSHEOEdo7Q$in
| User | test@erpnext.com | password | $pbkdf2-
sha256$29000$y7mXMoZQau09RwiBsLaWsg$h.
+---------+------------------+-----------
+-----------------------------------------------

Password Policy
Frappe also supports password strength checking. It can be enabled from System
Settingsin the Security section. The Minimum Password Score field validates how strong
the password should be.
Password Policy

Login Attempts
Frappe allows you to configure how many consecutive login attempts should be allowed
before locking the account for a set time period.
Third Party Authentication
Frappe supports third party login providers. To setup a login provider you need to setup
a Social Login Key. Learn more about it here.
Reports
Frappe framework is best suited for business applications and reporting is an important
part of any business. Frappe supports 3 different ways to build reports depending on
their complexity.

Let's discuss each type and how to build them using Frappe.

Report Builder
Report Builder is the simplest type of Report and can be created without any code. It will
only show the records of single DocType as well as child records of Child Tables.

Saving Reports

By default a Report View is generated for all DocTypes. The user can also save multiple
Reports based on different combination of filters, ordering, columns, etc.
Report Builder

Printing

Reports can be printed from the Menu.


Report Builder Print

Group By

You can also apply group by clause on columns and use aggregate functions like Count,
Sum and Average.
Report Builder Group By

Keyboard Navigation and Editing

Since report builder is a view of single DocType, they can also be edited.
Report Builder Editing

Query Report
Query Reports are reports that can be generated using a single SQL query. The query
can be simple or complex as long as it generates columns and records. These reports can
only be created by a System Manager and are stored in the database.

To create a Query Report, type "new report" in the awesomebar and hit enter.

1. Set Report Type as "Query Report"


2. Set the Reference DocType - Users that have access to the Reference DocType
will have access to the report

3. Set the Module - The report will appear in the "Custom Reports" section of the
module.

4. Write your query

If you set Standard as "Yes" and Developer Mode is enabled, then a JSON file will be
generated which you will have to check in to your version control. You should do this
only if you want to bundle Query Reports with your app. The Module will decide where
the JSON file will go.
Here is what a query may look like:

SELECT
`tabWork Order`.name as "Work Order:Link/Work Order:200",
`tabWork Order`.creation as "Date:Date:120",
`tabWork Order`.production_item as "Item:Link/Item:150",
`tabWork Order`.qty as "To Produce:Int:100",
`tabWork Order`.produced_qty as "Produced:Int:100",
`tabWork Order`.company as "Company:Link/Company:"
FROM
`tabWork Order`
WHERE
`tabWork Order`.docstatus=1
AND ifnull(`tabWork Order`.produced_qty,0) < `tabWork Order`.qty
AND NOT EXISTS (SELECT name from `tabStock Entry` where work_order =`tabWork
Order`.name)
If you notice there is a special syntax for each column, we use this information to format
the Report View.

For example: The first column Work Order:Link/Work Order:200 will be rendered as a Link
Field with the DocType Work Order and the column width would be 200px.
Query Report View
Script Report
Anything that can't be achieved using Report Builder or Query Report can be achieved
using Script Reports. As the name suggests, these reports are built using Python scripts.
Since these reports give you unrestricted access via Python scripts, they can only be
created by Administrators. These reports are meant to be written during development
and be a part of your app.

To create Script Reports you must enable Developer Mode.


To create a Script Report, type "new report" in the awesomebar and hit enter.

1. Set Report Type as "Script Report"

2. Set "Is Standard" as "Yes"

3. Select the Module in which you want to add this report

4. In the module folder (for example if it is Accounts in ERPnext the folder will
be erpnext/accounts/report/[report-name] ) you will see that templates for the
report files will be created.

5. Write your python script in the generated {report-name}.py file.

6. You can add filters to your report by adding them to {report-name}.js


New Script Report

Writing the script

The generated .py file comes with a boilerplate for your report. There is one method
named execute which takes filters and returns columns and data. You can use any
combination of python modules and SQL queries to generate your report.

Here is what the shape of columns and data must look like:

import frappe
from frappe import _

def execute(filters=None):
columns = [
{
'fieldname': 'account',
'label': _('Account'),
'fieldtype': 'Link',
'options': 'Account'
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Link',
'options': 'Currency'
},
{
'fieldname': 'balance',
'label': _('Balance'),
'fieldtype': 'Currency',
'options': 'currency'
}
]

data = [
{
'account': 'Application of Funds (Assets)',
'currency': 'INR',
'balance': '15182212.738'
},
{
'account': 'Current Assets - GTPL',
'currency': 'INR',
'balance': '17117932.738'
},
...
]

return columns, data


Here is a script report from ERPNext: Balance Sheet
Adding filters

To add filters in your report define the fields and their fieldtypes in the {report-
name}.jsfile. The filter values will be available in the execute method as a dict.

frappe.query_reports['Balance Sheet'] = {
filters: [
{
fieldname: 'company',
label: __('Company'),
fieldtype: 'Link',
options: 'Company',
default: frappe.defaults.get_user_default('company')
},
{
fieldname: 'periodicity',
label: __('Periodicity'),
fieldtype: 'Select',
options: [
'Monthly',
'Quarterly',
'Half-Yearly',
'Yearly'
],
default: 'Yearly'
}
]
}
Balance Sheet

Protip: To navigate directly to a Report of any of the above type, type its name in the
awesomebar and hit enter.

Query Report View


For both Query Reports and Script Reports the same UI view is used to render them. So,
both of them benefit from the UI features like Print, PDF, Export to Excel/CSV, Auto Email
Report, etc.
Query Report Features
Printing
Frappe framework has first class support for generating print formats for documents and
also convert them into PDF. Frappe uses Jinja as the templating language for print
formats.

Print View
The Print View can be accessed from the form view of any document. A Standard print
format is generated for all DocTypes based on the form layout and mandatory fields in
it.
Print View

Print Format Builder


To Customize a print format you need to create a copy of the Standard Print format and
customize it using the Print Format Builder. These print formats are user editable and are
not bundled with the app as files.

Print Format Builder

Custom HTML
You can also add Custom HTML to your Print Format. Just drag and drop the Custom
HTML button in left sidebar into your Print Format Editor.

In the Custom HTML field you can use any valid HTML with Bootstrap 3 classes for
styling. You can also use Jinja Templating to add dynamic content to your HTML. See list
of methods available to use in Jinja templates.
Custom HTML

Custom CSS

To change styling in your Print Format you can also add custom CSS.

Click on Customize > Edit Properties to add Custom CSS


Custom CSS
Custom CSS Preview

Advanced Print Formats


Print Format Builder is limited if you want to completely change the layout of the Print
Format. You can also write your own HTML from scratch and build the print layout you
want.

To create a new Print Format, type "new print format" in awesomebar and hit enter.

1. Select a unique name for your format.

2. Set "Standard" as "No".

3. Check "Custom Format".

4. Select Print Format Type as "Jinja"

5. Write your custom HTML

If you set Standard as "Yes" and Developer Mode is enabled, then a JSON file will be
generated for your Print Format and you will have to check it in to your version control
with your app.
Custom HTML in Print Format

Print Formats for Reports


Frappe allows you to create custom Print Formats for your Query and Script Reports.
These print formats cannot be created using the UI.

To create a Print Format for reports, create a HTML file named {report-name}.html in the
Report folder.

For example, check General Ledger

JS Templating

These print formats are generated on the client side, so we can't use Jinja. We use an
adapted version of John Resig's Templating. It looks similar to Jinja so you don't need to
learn anything new.

Here's a snippet of JS Template.

{% for(var i=0, l=data.length; i<l; i++) { %}


<tr>
{% if(data[i].posting_date) { %}
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>
<td>
{% if(!(filters.party || filters.account)) { %}
{%= data[i].party || data[i].account %}
<br>
{% } %}

{{ __("Against") }}: {%= data[i].against %}


<br>{%= __("Remarks") %}: {%= data[i].remarks %}
</td>
{% } else { %}
<td><b>{%= frappe.format(data[i].account, {fieldtype: "Link"}) ||
"&nbsp;" %}</b></td>
<td style="text-align: right">
{%= data[i].account && format_currency(data[i].debit,
filters.presentation_currency) %}
</td>
{% } %}
</tr>
{% } %}
Portal Pages
Frappe Framework not only provides a rich admin interface via the Desk which is an SPA
but also static server rendered web pages. These pages are generally built for your
website visitors. They can be public or can require login.

Adding pages
Every frappe app including frappe comes with a www folder which directly maps to
website urls. Here is what the directory structure looks like:

frappe/www
├── about.html
├── about.py
├── contact.html
├── contact.py
├── desk.html
├── desk.py
├── login.html
├── login.py
├── me.html
└── me.py
This structure enables the routes /about, /contact, /desk, /login and /me.

To add your own page, just add an HTML file in the www folder of your app. There are
multiple ways to organize these portal pages. For example,

custom_app/www
├── custom_page.html
└── custom_page.py
Will be rendered on the route /custom_page.

To add subpages you can convert your main page into a folder and add its content in an
index file. For example,

custom_app/www
└── custom_page
├── index.html
├── index.py
├── subpage.html
└── subpage.py

Will still be rendered on the route /custom_page and /custom_page/subpage will also be
available.

You can write .md files instead of .html for simple static pages like documentation. This
documentation you are reading is written as a markdown file.
Overriding standard pages

Frappe also allows you to override standard pages through your custom app. For
example, to override the standard /about provided by frappe, just add a file
named about.html in the www folder of your app and it will take precedence.

Templating

You can add dynamic content to Portal Pages using Jinja templates. All of the portal
pages extend from the base template frappe/templates/web.html which itself extends
from frappe/templates/base.html.

Here is what a sample page might look like:

<!-- about.html -->


{% extends "templates/web.html" %}

{% block title %}{{ _("About Us") }}{% endblock %}

{% block page_content %}
<h1>{{ _("About Us") }}</h1>
<div class="row">
<div class="col-sm-6">
We believe that great companies are driven by excellence,
and add value to both its customers and society.
You will find our team embibes these values.
</div>
</div>
{% endblock %}
You can also omit the extend and block if you want to the use the default base template.

<!-- about.html -->


<h1>{{ _("About Us") }}</h1>
<div class="row">
<div class="col-sm-6">
We believe that great companies are driven by excellence,
and add value to both its customers and society.
You will find our team embibes these values.
</div>
</div>

Context

Every portal page can have a python controller which will build context for the page. The
controller should have the same name as the .html or .md file with a .py extension.

custom_app/www
├── custom_page.html
└── custom_page.py
The controller should have a get_context method which takes a context dict, adds any
data to it and then returns it. Here is what a sample page controller might look like:

# about.py
import frappe

def get_context(context):
context.about_us_settings = frappe.get_doc('About Us Settings')
return context
Usage in template

<!-- about.html -->


<h1>{{ _("About Us") }}</h1>
<div class="row">
<div class="col-sm-6">
We believe that great companies are driven by excellence,
and add value to both its customers and society.
You will find our team embibes these values.
</div>

{% if about_us_settings.show_contact_us %}
<a href="/contact" class="btn btn-primary">Contact Us</a>
{% endif %}
</div>
Since Portal Pages are built using Jinja, frappe provides a standard API to use in jinja
templates.

Custom CSS and JS

You can add custom CSS and JS for your pages by dropping a .css or .js file of the
same name.

custom_app/www
├── custom_page.html
├── custom_page.css
├── custom_page.js
└── custom_page.py

Magic Comments

You can configure some functionalities by adding magic comments in your pages.

For example by adding <!-- add-breadcrumbs --> to your .html or .md file, frappe will
automatically generate breadcrumbs based on folder structure.

<!-- add-breadcrumbs -->

<h1>{{ _("About Us") }}</h1>


<div class="row">
<div class="col-sm-6">
We believe that great companies are driven by excellence,
and add value to both its customers and society.
You will find our team embibes these values.
</div>

{% if about_us_settings.show_contact_us %}
<a href="/contact" class="btn btn-primary">Contact Us</a>
{% endif %}
</div>
Here is a list of all magic comments and their functionalities.

Comment Functionality
<!-- add-breadcrumbs --> Add breadcrumbs to page
<!-- no-breadcrumbs --> Remove breadcrumbs from page
<!-- show-sidebar --> Show web sidebar
<!-- no-cache --> Disable caching for this page
<!-- no-sitemap --> Don't include page in sitemap
<!-- sitemap --> Include page in sitemap
<!-- add-next-prev-links --> Add Next and Previous navigation
buttons
<!-- title: Custom Title --> Set the page title
<!-- base_template: custom_app/path/to/custom_base.html Override base_template for this page
-->
Web Forms
Frappe provides an easy way to generate forms for your website with very little
configuration. These forms may be public (anyone can fill them up) or can be configured
to require login.
Creating a Web Form
To create a Web Form, type "new web form" in awesomebar and hit enter.

1. Enter Title

2. Select DocType for which the record should be created.

3. Select fields for your web form. These fields are populated from the DocType.

For detailed explanation, check user manual


Standard Web Forms

If you check the "Is Standard" checkbox, a new folder will be created in the module of the
Web Form. In this folder, you will see a .py and .js file that you can use to configure the
web form. These files need to be checked into version control with your custom app. You
can install this app on any site and it will have this web form installed.

Custom Script

Introduced in Version 11
You can also add a custom client script to the web form

Event Handler

Write an event handler to do actions when a field is changed.

frappe.web_form.on([fieldname], [handler]);

Get Value

Get value of a particular field

value = frappe.web_form.get_value([fieldname]);

Set Value

Set value of a particular field

frappe.web_form.set_value([fieldname], [value])

Validate

frappe.web_form.validate is called before the web_form is saved. Add custom validation


by overriding the validate method. To stop the user from saving, return false;

frappe.web_form.validate = () => {
// return false if not valid
}

Set Field Property

frappe.web_form.set_field_property([fieldname], [property], [value]);

Trigger script when form is loaded

Initialize form with customisation after it is loaded

frappe.web_form.after_load = () => {
// init script here
}

Examples

Reset value if invalid

frappe.web_form.on('amount', (field, value) => {


if (value < 1000) {
frappe.msgprint('Value must be more than 1000');
field.set_value(0);
}
});

Custom Validation

frappe.web_form.validate = () => {
let data = frappe.web_form.get_values();
if (data.amount < 1000) {
frappe.msgprint('Value must be more than 1000');
return false;
}
});

Hide a field based on value

frappe.web_form.on('amount', (field, value) => {


if (value < 1000) {
frappe.web_form.set_field_property('rate', 'hidden', 1);
}
});

Show a message on startup

frappe.web_form.after_load = () => {
frappe.msgprint('Please fill all values carefully');
}

Breadcrumbs

You can customize the breadcrumbs in a Web Form by adding JSON object.

Example:

[{"label": "Home", "route":"/" }]


Database Migrations
A project often undergoes changes related to database schema during course of its
development. It may also require patching of existing data. Frappe comes with a
migration and patch system tools to handle these scenarios.

When there are schema changes in your app, to migrate your existing site's database to
the new schema, you should run the command.

bench --site [sitename] migrate

Schema changes
You can edit a DocType to add, remove or change fields. On saving a DocType, a JSON
file containing the DocType data is added to source tree of your app. When you add an
app to a site, the DocTypes are installed (database tables are created) using this JSON
file.

For making schema changes, you must enable Developer Mode.


On running a migrate, DocTypes in the system are synced to their latest version from the
JSON files in the app.

When you remove or rename fields in the DocType, the corresponding database
columns are not removed from the database table, but they will not be visible in the
form view. This is done to avoid any potential data loss situations and to allow you write
related data migrations (patches) which might need values from old fields.

Frappe doesn't support reverse schema migrations.

Data Migrations
On introducing data related changes, you might want to run one off scripts to change
existing data to match expectations as per new code. We call these scripts patch in
frappe.

Writing a patch

To write a patch, you must write an execute method in a python script and add it
topatches.txt of your app.

It is recommended to make a file with a patch number and name in its path and add it to
a patches package (directory) in your app. You can then add a line with dotted path to
the patch module to patches.txt.

The directory structure followed in Frappe is as below


frappe
└── patches
└── v12_0
└── my_awesome_patch.py
The patch can then be added to patches.txt by its dotted path.

frappe.patches.v12_0.my_awesome_patch

Schema during patch

The DocType meta available in the execute function will be as per the old JSON. This is
so that you can write migration code assuming you still have the old fields. After the
patch is run, the new schema is applied to the DocType.

If you want to have the new schema during your patch execution, use
the reload_docmethod.

import frappe

def execute():
frappe.reload_doc(module_name, "doctype", doctype_name)

# your patch code here

One off Python statements

You can also add one off python statements in patches.txt using the syntax,

frappe.patches.v12_0.my_awesome_patch
execute:frappe.delete_doc('Page', 'applications', ignore_missing=True)

Patch execution order

Patches run in the order they are defined. All lines in patches.txt have to be unique. If a
patch has been run before, it won't run again. If you want to run a patch again, add a
comment that will make the line appear as new.

For Example,

frappe.patches.v12_0.my_awesome_patch #2019-09-08
Deployment
Bench is the CLI tool to manage deployments for sites based on Frappe Framework. Here
are steps to deploy your frappe based sites on production.

Install Bench
Deploying frappe sites is not too different from setting it up on your local system. Install
bench using the Easy Install script if your server is one of the supported linux
distributions (Debian, Ubuntu, CentOS). Make sure you pass the --production flag to the
script.

sudo python install.py --production --user [frappe-user]

Setup sites and apps


# change directory to frappe-bench
cd frappe-bench

# create a new site


bench new-site example.com

# download frappe apps or your custom-apps


bench get-app erpnext
bench get-app https://github.com/yourremote/yourapp.git

# install apps onto your site


bench --site example.com install-app erpnext yourapp

Check supervisor
If everything is setup properly, you should see frappe processes as the supervisor status
output.

$ sudo supervisorctl status


frappe-bench-redis:frappe-bench-redis-cache RUNNING pid 6467,
uptime 10 days, 8:12:09
frappe-bench-redis:frappe-bench-redis-queue RUNNING pid 6466,
uptime 10 days, 8:12:09
frappe-bench-redis:frappe-bench-redis-socketio RUNNING pid 6468,
uptime 10 days, 8:12:09
frappe-bench-web:frappe-bench-frappe-web RUNNING pid 8856,
uptime 10 days, 4:32:18
frappe-bench-web:frappe-bench-node-socketio RUNNING pid 8858,
uptime 10 days, 4:32:18
frappe-bench-workers:frappe-bench-frappe-default-worker-0 RUNNING pid 8823,
uptime 10 days, 4:32:19
frappe-bench-workers:frappe-bench-frappe-long-worker-0 RUNNING pid 8824,
uptime 10 days, 4:32:19
frappe-bench-workers:frappe-bench-frappe-schedule RUNNING pid 8822,
uptime 10 days, 4:32:19
frappe-bench-workers:frappe-bench-frappe-short-worker-0 RUNNING pid 8825,
uptime 10 days, 4:32:19
If you own example.com and it is mapped to the IP Address of your server, your site
should be live on example.com.

Updating
To update your sites, just run the following command. It will update all of your apps ( git
pull), run patches on all sites, build JS and CSS assets and restart supervisor.

# update everything
bench update

# update apps
bench update --pull

# run patches only


bench update --patch

# build assets only


bench update --build

# update bench (the cli)


bench update --bench

# update python packages and node_modules


bench update --requirements
Developer API
1. Server Side API (Python)

2. Client Side API (JavaScript)

3. Jinja API (Python like)


Testing
Frappe provides some basic tooling to write automated tests. There are some basic rules:

1. Test can be anywhere in your repository but must begin with test_ and should be
a .py file.

2. The test runner will automatically build test records for dependent DocTypes
identified by the Link type field (Foreign Key).

3. For non-DocType tests, you can write simple unit tests and prefix your file names
with test_.

Writing Tests
When you create a new DocType (in developer mode), the boilerplate files also contain
the test_{doctype}.py file. The test file should handle creating dependencies and
cleaning them up.

Here is a sample test file referred from test_event.py.

import frappe
import unittest

def create_events():
if frappe.flags.test_events_created:
return

frappe.set_user("Administrator")
doc = frappe.get_doc({
"doctype": "Event",
"subject":"_Test Event 1",
"starts_on": "2014-01-01",
"event_type": "Public"
}).insert()

doc = frappe.get_doc({
"doctype": "Event",
"subject":"_Test Event 3",
"starts_on": "2014-01-01",
"event_type": "Public"
"event_individuals": [{
"person": "test1@example.com"
}]
}).insert()

frappe.flags.test_events_created = True
class TestEvent(unittest.TestCase):
def setUp(self):
create_events()

def tearDown(self):
frappe.set_user("Administrator")

def test_allowed_public(self):
frappe.set_user("test1@example.com")
doc = frappe.get_doc("Event", frappe.db.get_value("Event",
{"subject":"_Test Event 1"}))
self.assertTrue(frappe.has_permission("Event", doc=doc))

def test_not_allowed_private(self):
frappe.set_user("test1@example.com")
doc = frappe.get_doc("Event", frappe.db.get_value("Event",
{"subject":"_Test Event 2"}))
self.assertFalse(frappe.has_permission("Event", doc=doc))

Running Tests
Run the following command to run all your tests. It will build all the test dependencies
once and run your tests. You should run tests from frappe_bench folder.

# run all tests


bench --site [sitename] run-tests

# run tests for only frappe app


bench --site [sitename] run-tests --app frappe

# run tests for the Task doctype


bench --site [sitename] run-tests --doctype "Task"

# run a test using module path


bench --site [sitename] run-tests --module frappe.tests.test_api

# run a specific test from a test file


bench --site [sitename] run-tests --module frappe.tests.test_api --test
test_insert_many

# run tests without creating test records


bench --site [sitename] run-tests --skip-test-records --doctype "Task"

# profile tests and show a report after tests execute


bench --site [sitename] run-tests --profile --doctype "Task"
.
----------------------------------------------------------------------
Ran 1 test in 0.010s

OK

9133 function calls (8912 primitive calls) in 0.011 seconds


Ordered by: cumulative time

ncalls tottime percall cumtime percall filename:lineno(function)


2 0.000 0.000 0.008 0.004 /home/frappe/frappe-
bench/apps/frappe/frappe/model/document.py:187(insert)
1 0.000 0.000 0.003 0.003 /home/frappe/frappe-
bench/apps/frappe/frappe/model/document.py:386(_validate)
13 0.000 0.000 0.002 0.000 /home/frappe/frappe-
bench/apps/frappe/frappe/database.py:77(sql)
255 0.000 0.000 0.002 0.000 /home/frappe/frappe-
bench/apps/frappe/frappe/model/base_document.py:91(get)
12 0.000 0.000 0.002 0.000

# verbose log level for tests


bench --site [sitename] --verbose run-tests
Debugging
Server
When you run the bench start command during development, the log from each
process of the Procfile is logged in the terminal window.

▶ bench start
14:55:17 system | redis_cache.1 started (pid=4085)
14:55:17 system | redis_socketio.1 started (pid=4086)
14:55:17 system | redis_queue.1 started (pid=4088)
14:55:17 system | web.1 started (pid=4089)
14:55:17 system | socketio.1 started (pid=4090)
14:55:17 system | watch.1 started (pid=4094)
14:55:17 system | worker_short.1 started (pid=4096)
14:55:17 system | schedule.1 started (pid=4095)
14:55:17 redis_queue.1 | 4088:C 22 May 14:55:17.257 # oO0OoO0OoO0Oo Redis is
starting oO0OoO0OoO0Oo
14:55:17 redis_queue.1 | 4088:C 22 May 14:55:17.264 # Redis version=4.0.11,
bits=64, commit=00000000, modified=0, pid=4088, just started
14:55:17 redis_queue.1 | 4088:C 22 May 14:55:17.264 # Configuration loaded
14:55:17 redis_queue.1 | 4088:M 22 May 14:55:17.265 * Increased maximum number
of open files to 10032 (it was originally set to 4864).
14:55:17 redis_cache.1 | 4085:C 22 May 14:55:17.262 # oO0OoO0OoO0Oo Redis is
starting oO0OoO0OoO0Oo
14:55:17 redis_cache.1 | 4085:C 22 May 14:55:17.268 # Redis version=4.0.11,
bits=64, commit=00000000, modified=0, pid=4085, just started
14:55:17 redis_cache.1 | 4085:C 22 May 14:55:17.268 # Configuration loaded
14:55:17 redis_cache.1 | 4085:M 22 May 14:55:17.269 * Increased maximum number
of open files to 10032 (it was originally set to 4864).
14:55:17 redis_socketio.1 | 4086:C 22 May 14:55:17.262 # oO0OoO0OoO0Oo Redis is
starting oO0OoO0OoO0Oo
14:55:17 redis_socketio.1 | 4086:C 22 May 14:55:17.270 # Redis version=4.0.11,
bits=64, commit=00000000, modified=0, pid=4086, just started
14:55:17 redis_socketio.1 | 4086:C 22 May 14:55:17.270 # Configuration loaded
14:55:17 redis_socketio.1 | 4086:M 22 May 14:55:17.272 * Increased maximum number
of open files to 10032 (it was originally set to 4864).
14:55:17 redis_queue.1 | 4088:M 22 May 14:55:17.285 * Running mode=standalone,
port=11002.
14:55:17 redis_queue.1 | 4088:M 22 May 14:55:17.285 # Server initialized
14:55:17 redis_queue.1 | 4088:M 22 May 14:55:17.286 * Ready to accept
connections
14:55:17 redis_cache.1 | 4085:M 22 May 14:55:17.287 * Running mode=standalone,
port=13002.
14:55:17 redis_cache.1 | 4085:M 22 May 14:55:17.292 # Server initialized
14:55:17 redis_cache.1 | 4085:M 22 May 14:55:17.292 * Ready to accept
connections
14:55:17 redis_socketio.1 | 4086:M 22 May 14:55:17.294 * Running mode=standalone,
port=12002.
14:55:17 redis_socketio.1 | 4086:M 22 May 14:55:17.294 # Server initialized
14:55:17 redis_socketio.1 | 4086:M 22 May 14:55:17.295 * Ready to accept
connections
14:55:17 system | worker_long.1 started (pid=4098)
14:55:17 system | worker_default.1 started (pid=4100)
14:55:18 socketio.1 | listening on *: 9002
14:55:20 socketio.1 | { Error: connect ECONNREFUSED 0.0.0.0:8002
14:55:20 socketio.1 | at TCPConnectWrap.afterConnect [as oncomplete]
(net.js:1191:14)
14:55:20 socketio.1 | errno: 'ECONNREFUSED',
14:55:20 socketio.1 | code: 'ECONNREFUSED',
14:55:20 socketio.1 | syscall: 'connect',
14:55:20 socketio.1 | address: '0.0.0.0',
14:55:20 socketio.1 | port: 8002,
14:55:20 socketio.1 | response: undefined }
14:55:24 web.1 | * Running on http://0.0.0.0:8002/ (Press CTRL+C to
quit)
14:55:24 web.1 | * Restarting with fsevents reloader
14:55:24 watch.1 | yarn run v1.10.1
14:55:24 watch.1 | $ node rollup/watch.js
14:55:25 web.1 | * Debugger is active!
14:55:25 web.1 | * Debugger PIN: 321-355-865
14:55:26 watch.1 |
14:55:26 watch.1 | Rollup Watcher Started
14:55:26 watch.1 |
14:55:26 watch.1 | Watching...
14:55:26 watch.1 | Rebuilding frappe-web.css
14:55:27 watch.1 | Rebuilding frappe-web-b4.css
14:55:27 watch.1 | Rebuilding chat.js
14:55:28 web.1 |
14:55:28 web.1 | test print
When you write any print statements in your python code, it will show up in
the web:process log if it is a request/response, or in one of worker_ processes if the code
runs in a background job.

If you are a VSCode user, you can debug right in your editor by setting breakpoints in
your code. Follow these steps to set it up.

Console

To play with Python API, bench provides an iPython shell. After you run the following
command, it will import frappe, initialize it and also connect to database.

▶ bench --site [sitename] console

In [1]: frappe.get_doc('Task', 'TASK00004')


Out[1]: <erpnext.projects.doctype.task.task.Task at 0x10825d710>
Learn more about the Python API here.

Profiling
Bench's execute command runs a dotted path to method and it also supports profiling.

bench --site [sitename] --profile execute


erpnext.projects.doctype.task.task.set_tasks_as_overdue

Client
Client side debugging is as simple as adding a debugger statement in your JS file. You
must open your DevTools in your browser for it to pause on the statement.

frappe.db.get_value('Task', 'TASK00004', 'status')


.then(values => {
debugger
console.log(values);
})

Console

To play with Client API, you can open your browser's console and use the globally
available frappe object to explore and run methods and access properties.
Browser Console

Learn more about the Client API here


Contribution Guidelines
Rushabh Mehta edited this page on Feb 18 · 22 revisions

Introduction (for first timers)


Thank you for your interest in contributing to an open source project! Our world
works on people taking initiative to contribute to the "commons" and contributing to
open source means you are contributing to make things better for not only yourself,
but everyone else too! So thank you for taking this initiative.

Great projects also work because of great quality. Open source or not, the user really
cares that things should work as they are advertised, and consistently. New features
should follow the same pattern and so that users don't have to learn things again
and again.

Developers who maintain open source also expect that you follow certain guidelines.
These guidelines ensure that developers are able to quickly give feedback on your
contribution and how to make it better. Most probably you might have to go back
and change a few things, but it will be in the interest of making this process better
for everyone. So be prepared for some back and forth.

Contribution Guides
Please read the following guidelines carefully when contributing:

1. Preparing a Contribution Proposal

2. Module Maintainers

3. Naming Guidelines

4. Form Design Guidelines

5. Model Design Guidelines

6. Integration Design Guidelines

7. Updating Documentation

8. Coding Standards

9. Pull Request Checklist

10. Supported Versions


11. Conventional Commits

Feedback Policy
We will strive for a "Zero Pull Request Pending" policy, inspired by "Zero Inbox". This
means, that if the pull request is good, it will be merged within a day and if it does
not meet the requirements, it will be closed.

What if my Pull Request is closed?

Don't worry, fix the problem and re-open it!

Why do we follow this policy?

This is because ERPNext is at a stage where it is being used by thousands of


companies and introducing breaking changes can be harmful for everyone. Also, we
do not want to stop the speed of contributions and the best way to encourage
contributors is to give fast feedback.

Happy contributing!

You might also like