How to Fetch Comprehensive Party Details with a Server Script in Frappe
Address and Contact DetailsLearn to create a whitelisted Python server script in Frappe to fetch comprehensive party details, including default addresses and contacts for Customers or Suppliers.
In Frappe and ERPNext development, a common requirement is to fetch a complete set of information for a party like a Customer or Supplier in a single, efficient server call. This often includes not just the main document's fields but also linked information like the default billing address and primary contact person's details.
This server script provides a robust, reusable, and permission-aware API endpoint to accomplish this. It consolidates data from multiple DocTypes (Customer, Supplier, Address, Contact) into one structured response, making it easy for client scripts or other backend services to consume.
1 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors 2 # License: GNU General Public License v3. See license.txt 3
4 from __future__ import unicode_literals 5
6 import frappe 7 from frappe import _ 8 from frappe.contacts.doctype.address.address import get_address_display, get_default_address 9 from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact 10
11
12 @frappe.whitelist() 13 def get_party_details(party=None, party_type="Customer", ignore_permissions=False): 14
15 if not party: 16 return {} 17
18 if not frappe.db.exists(party_type, party): 19 frappe.throw(_("{0}: {1} does not exists").format(party_type, party)) 20
21 return _get_party_details(party, party_type, ignore_permissions) 22
23 def _get_party_details(party=None, party_type="Customer", ignore_permissions=False): 24
25 out = frappe._dict({ 26 party_type.lower(): party 27 }) 28
29 party = out[party_type.lower()] 30
31 if not ignore_permissions and not frappe.has_permission(party_type, "read", party): 32 frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError) 33
34 party = frappe.get_doc(party_type, party) 35 36 set_address_details(out, party, party_type) 37 set_contact_details(out, party, party_type) 38 set_other_values(out, party, party_type) 39
40 return out 41
42 def set_address_details(out, party, party_type): 43 billing_address_field = "customer_address" if party_type == "Lead" else party_type.lower() + "_address" 44 out[billing_address_field] = get_default_address(party_type, party.name) 45 46 # address display 47 out.address_display = get_address_display(out[billing_address_field]) 48
49 def set_contact_details(out, party, party_type): 50 out.contact_person = get_default_contact(party_type, party.name) 51
52 if not out.contact_person: 53 out.update({ 54 "contact_person": None, 55 "contact_display": None, 56 "contact_email": None, 57 "contact_mobile": None, 58 "contact_phone": None, 59 "contact_designation": None, 60 "contact_department": None 61 }) 62 else: 63 out.update(get_contact_details(out.contact_person)) 64
65 def set_other_values(out, party, party_type): 66 # copy 67 if party_type=="Customer": 68 to_copy = ["customer_name", "customer_group", "territory", "language"] 69 else: 70 to_copy = ["supplier_name", "supplier_type", "language"] 71 for f in to_copy: 72 out[f] = party.get(f)
Understanding This Code
What It Does
Exposes a whitelisted API endpoint that fetches and consolidates key details for a specified party (e.g., Customer, Supplier), including their default address and contact information.
When To Use
Use this server script when you need to retrieve a party's complete details from a client script without making multiple database calls. It's ideal for populating forms or custom UIs with related party data.
Prerequisites
- •A working Frappe/ERPNext instance.
- •Basic understanding of Frappe Server Scripts and DocTypes.
Key Concepts
Important ideas to understand in this code
@frappe.whitelist()
A decorator that exposes a Python function to the client-side. This allows it to be called from JavaScript using `frappe.call`, effectively creating a secure API endpoint.
Learn morefrappe.get_doc()
A core Frappe API method used to load a full document (a record of a DocType) into memory as a document object. This provides access to all its fields and methods.
Learn morefrappe.has_permission()
Checks if the currently logged-in user has the specified permissions (e.g., 'read', 'write') for a given document. This is crucial for enforcing security rules in your custom APIs.
Learn moreget_default_address / get_default_contact
Standard Frappe utility functions that query the system to find the address or contact that has been marked as the default for a specific party DocType.
Learn moreStep-by-Step Tutorial
Follow along to understand how this code works
Create the Python File
Create a new Python file within your custom Frappe app. A good practice is to place utility scripts in a dedicated folder. For example, `my_app/my_app/utils/party.py`.
# In your custom app, create a file like:
# my_app/my_app/utils/party.py
# Paste the entire code snippet into this file.Import the Function in `__init__.py`
To make the function easily importable, add it to the `__init__.py` file in the same directory.
# In my_app/my_app/utils/__init__.py
from .party import get_party_detailsCall the Method from a Client Script
You can now call this whitelisted method from any Client Script using frappe.call. This example shows how to fetch details for a specific customer when a field changes.
// Example: Custom Client Script for Sales Order
frappe.ui.form.on('Sales Order', {
customer: function(frm) {
if (frm.doc.customer) {
frappe.call({
method: 'my_app.my_app.utils.party.get_party_details',
args: {
party: frm.doc.customer,
party_type: 'Customer'
},
callback: function(r) {
if (r.message) {
console.log(r.message);
frm.set_value('delivery_address', r.message.customer_address);
frm.set_value('contact_person', r.message.contact_person);
frm.refresh_fields();
}
}
});
}
}
});Test the API Endpoint
After saving your scripts, run `bench migrate` and `bench restart`. Then, trigger the client script in the UI (e.g., by changing the Customer in a Sales Order) and check the browser's console and the form fields to see the fetched data.
bench migrate
bench restartCommon Issues & Solutions
Troubleshoot problems you might encounter