Use this file to discover all available pages before exploring further.
Hooks are Frappe’s mechanism for extending and customizing application behavior without modifying core code. They allow you to intercept document events, add custom permissions, override methods, and integrate with the framework lifecycle.
# my_app/customer.pyimport frappedef after_customer_insert(doc, method=None): """Called after a new Customer is inserted""" # Create welcome email frappe.sendmail( recipients=doc.email_id, subject='Welcome to our platform', message='Thank you for joining us!' )def on_customer_update(doc, method=None): """Called after Customer is updated""" # Update related documents if doc.has_value_changed('territory'): update_sales_orders(doc)def before_customer_submit(doc, method=None): """Called before Customer is submitted""" # Additional validation if not doc.tax_id: frappe.throw('Tax ID is required before submission')
# hooks.pyhas_permission = { "Sales Order": "my_app.permissions.sales_order_permission", "Project": "my_app.permissions.project_permission"}# my_app/permissions.pyimport frappedef sales_order_permission(doc, ptype='read', user=None, debug=False): """Custom permission check for Sales Order""" if not user: user = frappe.session.user # System Manager has full access if 'System Manager' in frappe.get_roles(user): return True # Sales Manager can access all if 'Sales Manager' in frappe.get_roles(user): return True # Sales Person can only access their territory if 'Sales Person' in frappe.get_roles(user): user_territory = frappe.db.get_value( 'Sales Person', {'user': user}, 'territory' ) if user_territory and doc.territory == user_territory: return True # Owner can read their own documents if ptype == 'read' and doc.owner == user: return True return False
# hooks.pypermission_query_conditions = { "Sales Order": "my_app.permissions.get_sales_order_conditions"}# my_app/permissions.pydef get_sales_order_conditions(user): """Return SQL WHERE conditions for Sales Order list""" if not user: user = frappe.session.user roles = frappe.get_roles(user) if 'System Manager' in roles or 'Sales Manager' in roles: return '' # No restrictions if 'Sales Person' in roles: territories = frappe.get_all( 'Sales Person', filters={'user': user}, pluck='territory' ) if territories: territory_list = "', '".join(territories) return f"(`tabSales Order`.territory IN ('{territory_list}'))" return '1=0' # No access
# hooks.pyhas_website_permission = { "Project": "my_app.permissions.project_website_permission"}# my_app/permissions.pydef project_website_permission(doc, ptype='read', user=None): """Permission check for portal users""" if not user: user = frappe.session.user # Check if user is project team member team_members = [d.user for d in doc.team_members] return user in team_members
def after_customer_insert(doc, method=None): try: send_welcome_email(doc) except Exception as e: frappe.log_error(f"Failed to send welcome email: {str(e)}") # Don't fail the main operation
Use appropriate hook types
Use validate for validation logic
Use before_save for derived field calculations
Use after_insert for async operations
Use on_submit for creating related documents
Document your hooks
def sales_order_permission(doc, ptype='read', user=None, debug=False): """Custom permission for Sales Order. Sales Person can only access orders in their territory. Sales Manager can access all orders. Args: doc: Sales Order document ptype: Permission type (read, write, etc.) user: User to check permission for debug: Enable debug logging Returns: bool: True if user has permission """ # Implementation