Hooks
Hooks allow you to "hook" into functionality and events of core parts of the Frappe Framework. This page documents all of the hooks provided by the framework.
Jump to list of all available hooks in Frappe.
How does hooks work?
Hooks are places in the core code that allow an app to override the standard implementation or extend it. Hooks are defined in hooks.py of your app.
Let's learn by example. Add the following hooks in your app's hooks.py .
test_string = "value"
test_list = ["value"]
test_dict = {
"key": "value"
}
Now, open the python console by running the command
bench --site sitename console
and run the following lines:
❯ bench --site sitename console
Apps in this namespace:
frappe, frappe_docs
In [1]: frappe.get_hooks("test_string")
Out[1]: ["value"]
In [2]: frappe.get_hooks("test_dict")
Out[2]: {"key": ["value"]}
In [3]: frappe.get_hooks("test_list")
Out[3]: ["value"]
When you call
frappe.get_hooks
, it will convert all the values in a list. This means that if the hook is defined in multiple apps, the values will be collected from those apps. This is what enables the cascading nature of hooks.
Now, the hook value can be consumed in different ways. For example, for including JS assets using
app_include_js
, all of the values are included. But for overriding whitelisted method, the last value in the list is used.
So the implementation of the hook is totally dependent on how the author of the feature intended it to be used.
How are conflicting hooks resolved?
Hooks are resolved using "last writer wins" strategy. Last installed app on site will have highest priority over others.
-
When the hook overrides existing behaviour like overriding a class then only overrides from last app will work.
-
When the hook extends behaviour then extensions will be applied in order of installation on the site.
> If you need to change this order you can do so by going to "Installed Applications" page and clicking on "Update Hooks Resolution Order"
App Meta Data
These are automatically generated when you create a new app. Most of the time you don't need to change anything here.
-
app_name
- slugified name of the app -
app_title
- presentable app name -
app_publisher
-
app_description
-
app_version
-
app_icon
-
app_color
Javascript / CSS Assets
The following hooks allow you to inject static JS and CSS assets in various parts of your site.
Desk
These hooks allow you to inject JS / CSS in
desk.html
which renders the
Desk
.
# injected in desk.html
app_include_js = "assets/js/app.min.js"
app_include_css = "assets/js/app.min.css"
# All of the above support a list of paths too
app_include_js = ["assets/js/app1.min.js", "assets/js/app2.min.js"]
Portal
These hooks allow you to inject JS / CSS in
web.html
which renders the
Portal
.
# injected in the web.html
web_include_js = "assets/js/app-web.min.js"
web_include_css = "assets/js/app-web.min.css"
# All of the above support a list of paths too
web_include_js = ["assets/js/web1.min.js", "assets/js/web2.min.js"]
Web Form
These hooks allow you to add inject static JS and CSS assets in
web_form.html
which is used to render Web Forms. These will work only for Standard Web Forms.
webform_include_js = {"ToDo": "public/js/custom_todo.js"}
webform_include_css = {"ToDo": "public/css/custom_todo.css"}
> For user created Web Forms, you can directly write the script in the form > itself.
Page
These hooks allow you to inject JS assets in Standard Desk Pages.
page_js = {"page_name" : "public/js/file.js"}
For e.g., Background Jobs is a standard page that is part of Core module in Frappe Framework. To add custom behaviour in that page you can add a JS file in your custom app
custom_app/public/js/custom_background_jobs.js
and add the following line in your hooks file.
custom_app/hooks.py
page_js = {"background_jobs": "public/js/custom_background_jobs.js"}
Sounds
Frappe ships with a set of audio notifications for events like a success action, document submission, error, etc. You can add your own sounds using the
sounds
hook.
app/hooks.py
sounds = [
{"name": "ping", "src": "/assets/app/sounds/ping.mp3", "volume": 0.2}
]
You can play your added sound using the client utility method:
frappe.utils.play_sound("ping")
Install Hooks
These hooks allow you to run code before and after installation of your app. For example, ERPNext has these defined .
# python module path
before_install = "app.setup.install.before_install"
after_install = "app.setup.install.after_install"
after_sync = "app.setup.install.after_sync"
app/setup/install.py
# will run before app is installed on site
def before_install():
pass
# will run after app is installed on site
def after_install():
pass
# will run after app fixtures are synced
def after_sync():
pass
Uninstall Hooks
These hooks allow you to run code before and after uninstallation of your app.
app/hooks.py
before_uninstall = "app.setup.uninstall.before_uninstall"
after_uninstall = "app.setup.uninstall.after_uninstall"
app/setup/uninstall.py
# will run before app is uninstalled from site
def before_uninstall():
pass
# will run after app is uninstalled from site
def after_uninstall():
pass
Migrate Hooks
These hooks allow you to run code before and after a migration is run on your site via the command
bench --site sitename migrate
.
app/hooks.py
before_migrate = "app.migrate.before_migrate"
after_migrate = "app.migrate.after_migrate"
app/migrate.py
def after_migrate():
# run code after site migration
pass
Test Hooks
This hook allows you to run code before tests are run on a site. You can use this hook to add seed data to your database which will be available to your tests.
app/hooks.py
before_tests = "app.tests.before_tests"
app/migrate.py
def before_tests():
# add seed data to the database
pass
File Hooks
These hooks allows you to change the implementation of handling user uploaded files.
app/hooks.py
before_write_file = "app.overrides.file.before_write"
write_file = "app.overrides.file.write_file"
delete_file_data_content = "app.overrides.file.delete_file"
app/overrides/file.py
# will run before file is written to disk
def before_write():
pass
# will override the implementation of writing file to disk
# can be used to upload files to a CDN instead of writing
# the file to disk
def write_file():
pass
# will override the implementation of deleting file from disk
# can be used to delete uploaded files from a CDN instead of
# deleting file from disk
def delete_file():
pass
Email Hooks
These hooks allows you to change the default email module implementation of sending emails and setting default sender address.
app/hooks.py
override_email_send = "app.overrides.email.send"
get_sender_details = "app.overrides.email.get_sender_details"
By default frappe uses the currently logged in users name and id as sender details on all emails. This can be overriden with
get_sender_details
hook. And if you want to extend the email modules functionality by using a thirdy party server or app for sending emails then you can use
override_email_send
hook. This hook will send all the email information (sender, recipient, content(mime)) to a function in custom_app.
app/overrides/email.py
# will be edited as "John Doe <johndoe@gmail.com>"
def get_sender_details():
return "John Doe", "johndoe@gmail.com"
# self - EmailQueue object refrence for updating status
def send(self, sender, recipient, msg):
# smtp or http request
self.update_status("Sending")
> Note: You'll have to handle the status change of email queue in your custom app depending on the webhook response you get from your mail provider/server
Extend Bootinfo
After a successful login, the Desk is injected with a dictionary of global values called
bootinfo
. The
bootinfo
is available as a global object in Javascript as
frappe.boot
.
The
bootinfo
dict contains a lot of values including:
-
System defaults
-
Notification status
-
Permissions
-
User settings
-
Language and timezone info
You can add global values that makes sense for your app via the
extend_bootinfo
hook.
# python module path
extend_bootinfo = "app.boot.boot_session"
The method is called with one argument
bootinfo
, on which you can directly add/update values.
app/boot.py
def boot_session(bootinfo):
bootinfo.my_global_key = "my_global_value"
Now, you can access the value anywhere in your client side code.
console.log(frappe.boot.my_global_key)
Website Context
When a Portal Page is rendered, a dictionary is built with all of the possible variables that the page might need to render. This dict is also known as
context
. You can use these hooks to add or modify values in this dict.
app/hooks.py
website_context = {
"favicon": "/assets/app/image/favicon.png"
}
update_website_context = "app.overrides.website_context"
The
website_context
hook is a simple dict of key value pairs. Use this hook for simple value overrides.
You can use the
update_website_context
hook for more complex scenarios as it allows you to manipulate the context dict in a python method. The method is called with one argument, which is the
context
dict. You can either modify the context directly by mutating it or return a dict that will be merged with
context
.
app/overrides.py
def website_context(context):
context.my_key = "my_value"
Website Controller Context
Frappe ships with standard web pages like
/404
and
/about
. If you want to extend the controller context for these pages you can use the
extend_website_page_controller_context
hook.
app/hooks.py
extend_website_page_controller_context = {
"frappe.www.404": "app.pages.context_404"
}
The above hook configuration will allow you to extend the context of the 404 page so that you can add your own keys or modify existing ones.
app/pages.py
def context_404(context):
# context of the 404 page
context.my_key = "my_value"
Web pages with dynamic routes
Dynamic routes are the routes with dynamic values in them.
Example:
/profile/<name>
here "name" is the dynamic part but same profile page is rendered. By default, Frappe supports dynamic routes from documents in "Web Page" doctype. To add more dynamic routes,
get_web_pages_with_dynamic_routes
can be used.
app/ hooks.py
get_web_pages_with_dynamic_routes = "script.get_web_pages_with_dynamic_routes"
script.py
def get_web_pages_with_dynamic_routes():
return [{
"doctype": "Custom Web Page", // Doctype extended from WebsiteGenerator /frappe/en/guides/portal-development/generators
"route": "/profile/<name>",
"name": "profile-page" // name of the web view document to render
}, ...]
Website Clear Cache
Frappe Framework caches a lot of static web pages for fast subsequent rendering. If you have created web pages that use cached values, and you want to invalidate the cache, this hook is place to do it.
app/hooks.py
website_clear_cache = "app.overrides.clear_website_cache"
The method is called with one argument
path
.
path
is set when cache is being cleared for one route, and is
None
when cache is cleared for all routes. You need to handle this case if your cache is page specific.
app/overrides.py
def clear_website_cache(path=None):
if path:
# clear page related cache
else:
# clear all cache
Website Redirects
Website Redirects allow you to define redirects from one route to another. Frappe will generate a 304 Redirect response when the source URL is requested and redirect to the target URL. You can redirect plain URLs or you can use regex to match your URLs.
app/hooks.py
website_redirects = [
{"source": "/compare", "target": "/comparison"},
{"source": "/docs(/.*)?", "target": "https://docs.tennismart.com/\1"},
{"source": r'/items/item\?item_name=(.*)', "target": '/items/\1', match_with_query_string=True},
]
The above configuration will result in following redirects:
-
/compare
to/comparison
-
/docs/getting-started
tohttps://docs.tennismart.com/getting-started
-
/docs/help
tohttps://docs.tennismart.com/help
-
/items/item?item_name=racket
tohttps://docs.tennismart.com/items/racket
Website Route Rules
Website Route Rules allow you to map URLs to custom controllers. This is commonly used to generate clean URLs for pages.
Let's say you want to have
/projects
route to display list of projects. This can be done by creating a
projects.html
and
projects.py
in
www
folder.
You also want to have
/project/<name>
route to show a project page where name is the dynamic. To do this you can use the
website_route_rules
hook.
app/hooks.py
website_route_rules = [
{"from_route": "/projects/<name>", "to_route": "app/projects/project"},
]
Now, you can create your controller files in
app/projects
folder.
app/projects/project.py
def get_context(context):
project_name = frappe.form_dict.name
project = frappe.get_doc("Project", project_name)
context.project = project
app/projects/project.html
<h1>{{ project.title }}</h1>
<p>{{ project.description }}</p>
Website Path Resolver
Frappe does some standard route resolving eg. any request to "/profile" gets internally converted to "/me".
website_path_resolver
can be used to override this behaviour.
app/ hooks.py
website_path_resolver = "path.to.custom_resolver_method"
Note: Your custom_resolver_method will get the requested route, is expected to return a processed route.
Website 404
Frappe renders a default
/404
route when a page is not found. You can change this using the
website_catch_all
hook.
app/hooks.py
website_catch_all = "not_found"
The above configuration will render
/not_found
when a 404 is occurred. It is upto you to implement the template
www/not_found.html
and controller
www/not_found.py
.
Default Homepage
Homepage is the page which is rendered when you visit the root URL (
/
) of your site. There are multiple ways to configure what page is rendered as the default homepage.
By default, the homepage is
index
. So, frappe will try to render
index.html
from
www
folder. This can be overridden using the
homepage
hook.
app/hooks.py
homepage = "homepage"
The above configuration will load the
www/homepage.html
as the default homepage.
You can also have role based homepage by using the
role_home_page
hook.
app/hooks.py
role_home_page = {
"Customer": "orders",
"Supplier": "bills"
}
The above configuration will make
/orders
the default homepage for users with the
Customer
role and
/bills
for users with the
Supplier
role.
You can have even more control over the logic by using the
get_website_user_home_page
hook.
app/hooks.py
get_website_user_home_page = "app.website.get_home_page"
app/website.py
def get_home_page(user):
if is_projects_user(user):
return "projects"
if is_partner(user):
return "partner-dashboard"
return "index"
> If all of these hooks are defined, the
get_website_user_home_page
will have > higher priority over the others, and
role_home_page
will have higher > priority over
homepage
.
Portal Sidebar
Some Portal views are shown with a sidebar with links to quickly jump to pages. These sidebar items can be customized via hooks.
app/hooks.py
portal_menu_items = [
{"title": "Dashboard", "route": "/dashboard", "role": "Customer"},
{"title": "Orders", "route": "/orders", "role": "Customer"},
]
The above configuration will add two sidebar links for users with the role Customer.
These sidebar items are hardcoded in your app so they are not customizable from Desk. For e.g., if you want to hide a sidebar link temporarily you will have to make changes in your code.
There is another hook called
standard_portal_menu_items
which allows you to do that. The sidebar links set in
standard_portal_menu_items
hook will be synced with the database.
app/hooks.py
standard_portal_menu_items = [
{"title": "Dashboard", "route": "/dashboard", "role": "Website Manager"},
{"title": "Orders", "route": "/orders", "role": "Website Manager"},
]
The above configuration will sync sidebar items to the Portal Settings which can later be edited by any System User.
Brand HTML
This hook allows you to customize the brand logo in the navbar of your website.
app/hooks.py
brand_html = '<div><img src="tennismart.png"/> TennisMart</div>'
If the
brand_html
is defined, it will override the default brand html in the navbar. It is not recommended to use hooks to change your brand logo, unless you want to version control it, otherwise you can use Website Settings to change it.
Base Template
When a web page is rendered, it extends
templates/base.html
by default. You can override the base template by overriding the
base_template
hook.
app/hooks.py
base_template = "app/templates/my_custom_base.html"
You can also customize base templates based on routes. For e.g., if you want to use a different base template for all the routes that start with
docs/*
then you can use the
base_template_map
hook. The key must be a regex that matches the route. All other routes will fallback to the default base template.
app/hooks.py
base_template_map = {
r"docs.*": "app/templates/doc_template.html"
}
Integrations
These hooks allow you to customize behaviour of 3rd-party integrations in Frappe.
Braintree Success Page
This hook allows you to override the default redirect URL on successful payment of Braintree transaction.
app/hooks.py
braintree_success_page = "app.integrations.braintree_success_page"
The method is called with one argument
data
which has the meta data of the payment.
app/integrations.py
def braintree_success_page(data):
# data.reference_doctype
# data.reference_docname
return "/thank-you"
Calendars
The calendar hook is a list of doctype names which are shown as menu items for quick navigation from the Calendar page in Desk.
app/hooks.py
calendars = ["Appointment"]
Clear Cache
This hook allows you to clear your app specific cache values when the global cache is being cleared by frappe.
app/hooks.py
clear_cache = "app.cache.clear_cache"
You can use this hook to clear your app specific cache. The method is called without any arguments.
app/cache.py
def clear_cache():
frappe.cache().hdel("app_specific_cache")
Default Mail Footer
If you want to set the default footer of all the emails that are sent out by Frappe, you can use the
default_mail_footer
hook.
app/hooks.py
default_mail_footer = """
<div>
Sent via <a href="https://tennismart.com" target="_blank">TennisMart</a>
</div>
"""
Now, all the emails will have Sent via TennisMart in the footer.
Session Hooks
These hooks are triggered over the login lifecycle of a user.
on_login
is triggered immediately after a successful login,
on_session_creation
is triggered after the session is setup,
on_logout
is triggered after the user logs out.
app/hooks.py
on_login = "app.overrides.successful_login"
on_session_creation = "app.overrides.allocate_free_credits"
on_logout = "app.overrides.clear_user_cache"
The method will be called with one argument
login_manager
.
app/overrides.py
def allocate_free_credits(login_manager):
# allocate free credits to frappe.session.user
pass
Auth Hooks
These hooks are triggered during request authentication. Custom headers, Authorization headers can be validated here, user is verified and mapped to the request using
frappe.set_user()
. Use
frappe.request
and
frappe.*
to validate request and map user.
app/hooks.py
auth_hooks = ["app.overrides.validate_custom_jwt"]
The method will be called during request authentication.
app/overrides.py
def validate_custom_jwt():
# validate jwt from header, verify signature, set user from jwt.
pass
Use this method to check for incoming request header, verify the header and map the user to the request. If header verification fails DO NOT throw error to continue with other hooks. Unverified request is treated as "Guest" request by default. You may use third party server, shared database or any alternative of choice to verify and map request and user.
Fixtures
Fixtures are database records that are synced using JSON files when you install and update your site.
Let's say you want to create a set of categories in the database whenever you install your app. To do that, create the set of categories in your local site, and add the doctype name in the
fixtures
hook.
fixtures = [
# export all records from the Category table
"Category"
]
Now, run the following command:
bench --site sitename export-fixtures
This command will create a JSON file for each doctype which will contain the data to generate list of records. You can test this by creating a new site and by installing your app on that site.
You can also add conditions for exporting records.
fixtures = [
# export all records from the Category table
"Category",
# export only those records that match the filters from the Role table
{"dt": "Role", "filters": [["role_name", "like", "Admin%"]]},
]
Some fields are for internal use only. They will be set and kept up-to-date by the system automatically. These will not get exported:
modified_by
,
creation
,
owner
,
idx
,
lft
and
rgt
. For child table records, the following fields will not get exported:
docstatus
,
doctype
,
modified
and
name
.
Document Hooks
Modify List Query
You can customize how list of records are queried for a DocType by adding custom match conditions using the
permission_query_conditions
hook. This match condition must be a valid WHERE clause fragment for an SQL query.
app/hooks.py
permission_query_conditions = {
"ToDo": "app.permissions.todo_query",
}
The method is called with a single argument
user
which can be
None
. The method should return a string that is a valid SQL WHERE clause.
app/permissions.py
def todo_query(user):
if not user:
user = frappe.session.user
# todos that belong to user or assigned by user
return "(`tabToDo`.owner = {user} or `tabToDo`.assigned_by = {user})".format(user=frappe.db.escape(user))
Now, if you use the
frappe.db.get_list
method, your WHERE clause will be appended to the query.
todos = frappe.db.get_list("ToDo", debug=1)
# output
'''
select `tabToDo`.`name`
from `tabToDo`
where ((`tabToDo`.owner = 'john@doe.com' or `tabToDo`.assigned_by = 'john@doe.com'))
order by `tabToDo`.`modified` DESC
'''
> This hook will only affect the result of
frappe.db.get_list
method and not >
frappe.db.get_all
.
Document Permissions
You can modify the behaviour of
doc.has_permission
document method for any DocType and add custom permission checking logic using the
has_permission
hook.
app/hooks.py
has_permission = {
"Event": "app.permissions.event_has_permission",
}
The method will be passed the
doc
,
user
and
permission_type
as arguments. It should return
True
or a
False
value. If
None
is returned, it will fallback to default behaviour.
app/permissions.py
def event_has_permission(doc, user=None, permission_type=None):
# when reading a document allow if event is Public
if permission_type == "read" and doc.event_type == "Public":
return True
# when writing a document allow if event owned by user
if permission_type == "write" and doc.owner == user:
return True
return False
Override DocType Class
You can override/extend the class for standard doctypes by using the
override_doctype_class
hook.
app/hooks.py
override_doctype_class = {
"ToDo": "app.overrides.todo.CustomToDo"
}
app/overrides/todo.py
from frappe.desk.doctype.todo.todo import ToDo
class CustomToDo(ToDo):
def on_update(self):
self.my_custom_code()
super().on_update()
def my_custom_code(self):
pass
> It is recommended that you extend the standard class of the doctype, otherwise > you will have to implement all of the core functionality.
Override Form Scripts
You can override/extend
Standard Form Scripts
by using the
doctype_js
hook.
app/hooks.py
doctype_js = {
"ToDo": "public/js/todo.js",
}
app/public/js/todo.js
frappe.ui.form.on("Todo", {
refresh: function(frm) {
frm.trigger("my_custom_code");
},
my_custom_code: function(frm){
console.log(frm.doc.name)
}
});
> The events/functions defined in
app/public/todo.js
will extend > those in the standard form script of
ToDo
doctype.
CRUD Events
You can hook into various CRUD events of any doctype using the
doc_events
hook.
app/hooks.py
doc_events = {
"*": {
# will run after any DocType record is inserted into database
"after_insert": "app.crud_events.after_insert_all"
},
"ToDo": {
# will run before a ToDo record is inserted into database
"before_insert": "app.crud_events.before_insert_todo",
}
}
The method will be passed the doc and the method name as arguments.
app/crud_events.py
def after_insert_all(doc, method=None):
pass
def before_insert_todo(doc, method=None):
pass
> See Controller Hooks > for a list of all available hooks.
Override Whitelisted Methods
Whitelisted Methods are python methods that are accessible on a REST endpoint and consumed by a client. You can override standard whitelisted methods that are part of the core framework using the
override_whitelisted_methods
hook.
app/hooks.py
override_whitelisted_methods = {
"frappe.client.get_count": "app.whitelisted.custom_get_count"
}
The method should have the same signature as the original method.
app/whitelisted.py
def custom_get_count(doctype, filters=None, debug=False, cache=False):
# your custom implementation of the standard get_count method provided by frappe
pass
Ignore Links on Delete
To ignore links to specific DocTypes when deleting documents, you can specify them in the
ignore_links_on_delete
hook like so:
app/hooks.py
ignore_links_on_delete = ["Communication", "ToDo"]
Form Timeline
The timeline section of form view of a document shows an audit trail of actions performed on that document like views, value changes, comments and related communications, etc.
Apart from these standard actions, there might arise a situation where you need to add your own custom actions. You can do this via
additional_timeline_content
hook.
additional_timeline_content: {
# show in each document's timeline
"*": ["app.timeline.all_timeline"]
# only show in ToDo's timeline
"ToDo": ["app.timeline.todo_timeline"]
}
The method will be passed the doctype and docname as arguments. You can perform queries and return actions related to that document as a list of dicts as shown in the example. Each dict in the list must have a
creation
value which will be used to sort the item in the timeline.
def todo_timeline(doctype, docname):
# this method should return a list of dicts
return [
{
# this will be used to sort the content in the timeline
"creation": "22-05-2020 18:00:00",
# this JS template will be rendered in the timeline
"template": "custom_timeline_template",
# this data will be passed to the template.
"template_data": {"key": "value"},
},
...
]
Scheduler Events
You can use Scheduler Events for running tasks periodically in the background using the
scheduler_events
hook.
app/hooks.py
scheduler_events = {
"hourly": [
# will run hourly
"app.scheduled_tasks.update_database_usage"
],
}
app/scheduled_tasks.py
def update_database_usage():
pass
> After changing any scheduled events in
hooks.py
, you need to run
bench migrate
for changes to take effect.
Available Events
-
hourly
,daily
,weekly
,monthly
These events will trigger every hour, day, week and month respectively.
-
hourly_long
,daily_long
,weekly_long
,monthly_long
Same as above but these jobs are run in the long worker suitable for long running jobs.
-
all
The
all
event is triggered every 60 seconds. This can be configured via thescheduler_tick_interval
key incommon_site_config.json
-
cron
A valid cron string that can be parsed by croniter .
Usage Examples:
scheduler_events = {
"daily": [
"app.scheduled_tasks.manage_recurring_invoices"
],
"daily_long": [
"app.scheduled_tasks..take_backups_daily"
],
"cron": {
"15 18 * * *": [
"app.scheduled_tasks..delete_all_barcodes_for_users"
],
"*/6 * * * *": [
"app.scheduled_tasks..collect_error_snapshots"
],
"annual": [
"app.scheduled_tasks.collect_error_snapshots"
]
}
}
Jinja Customization
Frappe provides a list of
global utility methods
in Jinja templates. To add your own methods and filters you can use the
jinja
hook.
app/hooks.py
jinja = {
"methods": [
"app.jinja.methods",
"app.utils.get_fullname"
],
"filters": [
"app.jinja.filters",
"app.utils.format_currency"
]
}
app/jinja/methods.py
def sum(a, b):
return a + b
def multiply(a, b):
return a * b
> If the path is a module path, all the methods in that module will be added.
app/utils.py
def get_fullname(user):
first_name, last_name = frappe.db.get_value("User", user, ["first_name", "last_name"])
return first_name + " " + last_name
def format_currency(value, currency):
return currency + " " + str(value)
Now, you can use these utilities in your Jinja templates like so:
<h1>Hi, {{ get_fullname(frappe.session.user) }}</h1>
<p>Your account balance is {{ account_balance | format_currency("INR") }}</p>
<p>1 + 2 = {{ sum(1, 2) }}</p>
Prevent Auto Cancellation of Linked Documents
To prevent documents of a specific DocType from being automatically cancelled on the cancellation of any linked documents you can use the
auto_cancel_exempted_doctypes
hook.
app/hooks.py
auto_cancel_exempted_doctypes = ["Payment Entry"]
In the above example, if any document (for e.g Sales Invoice) that is linked with Payment Entry is cancelled, it will skip the auto-cancellation of the linked Payment Entry document.
Notification configurations
The notification configuration hook is used to customize the items shown in the Notification dropdown in Desk. It can be configured by the
notification_config
hook.
app/hooks.py
notification_config = "app.notification.get_config"
The method is called without any arguments.
app/notification.py
def get_config():
return {
"for_doctype": {
"Issue": {"status":"Open"},
"Issue": {"status":"Open"},
},
"for_module_doctypes": {
"ToDo": "To Do",
"Event": "Calendar",
"Comment": "Messages"
},
"for_module": {
"To Do": "frappe.core.notifications.get_things_todo",
"Calendar": "frappe.core.notifications.get_todays_events",
"Messages": "frappe.core.notifications.get_unread_messages"
}
}
The above configuration has three parts:
-
for_doctype
part of the above configuration marks any "Issue" or "Customer Issue" as unread if its status is Open -
for_module_doctypes
maps doctypes to module's unread count. -
for_module
maps modules to functions to obtain its unread count. The functions are called without any argument.
Required Apps
When building apps, you might create apps that build on top of other apps. To make sure dependent apps are installed when someone installs your app, you can use the
required_apps
hook.
app/hooks.py
required_apps = ["erpnext"]
The above configuration will make sure
erpnext
is installed when someone installs your app.
User Data Protection & Privacy
User Data Privacy features like personal data download and personal data deletion come out of the box in Frappe. What constitutes as personal data may be defined by the App publisher in the application's
hooks.py
file as
user_data_fields
.
app/hooks.py
user_data_fields = [
{"doctype": "Access Log"},
{"doctype": "Comment", "strict": True},
{
"doctype": "Contact",
"filter_by": "email_id",
"rename": True,
},
{"doctype": "Contact Email", "filter_by": "email_id"},
{
"doctype": "File",
"filter_by": "attached_to_name",
"redact_fields": ["file_name", "file_url"],
},
{"doctype": "Email Unsubscribe", "filter_by": "email", "partial": True},
]
DocTypes that have user data should be mapped under this hook using the above format. Upon data deletion or download requests from users, this hook will be utilized to map over the specified DocTypes. The options available to modify documents are:
Field |
Description |
---|---|
|
DocType that contains user data. |
|
Docfield to filter the documents by. If unset, defaults to
|
|
If set, all text fields are parsed and user's full name and username references will be redacted. |
|
Fields that have to be redacted. If unspecified, it considers partial data redaction from all text fields. |
|
If document name contains user data, set this field to rename document to anonymize it. |
|
If set to True, any user data will be redacted from all documents of current DocType. If unset, it defaults to False which means it only filters through documents in which user is the owner. |
> Note: Personal Data Download only utilizes the doctype and filter_by fields defined in
user_data_fields
Related Topics:
Signup Form Template
If you want to add additional fields to the signup form you can use this hook. Create a template file which contains the custom signup form. Pass this template to the custom signup hook.
signup_form_template = "school/templates/signup-form.html"
> Note: If you want custom fields in signup form, it will require additional fields in the user doctype. You will have to add these fields using fixtures. Also you will have to write your own submit handler for this signup form and a function on the server side which will signup the user. This way you can also write validations for the custom fields you add.
List of available hooks
Hook Name |
Explanation |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Deprecated |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Deprecated |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DocTypes that use TreeView as the default view (instead of ListView) |
|
|
|
Deprecated
(Use
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Deprecated (Use Has Web View in DocType instead) |
|
|
|
|
|
Deprecated
(Use
|
|
|
|
Deprecated |
|
</name></name></johndoe@gmail.com>