Frappe Server ScriptPython

How to Fetch Comprehensive Party Details with a Server Script in Frappe

Address and Contact Details

Learn 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.

party_utils.pypython
1# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2# License: GNU General Public License v3. See license.txt
3
4from __future__ import unicode_literals
5
6import frappe
7from frappe import _
8from frappe.contacts.doctype.address.address import get_address_display, get_default_address
9from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
10
11
12@frappe.whitelist()
13def 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
23def _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
42def 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
49def 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
65def 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 more

frappe.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 more

frappe.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 more

get_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 more

Step-by-Step Tutorial

Follow along to understand how this code works

1

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`.

bash
# In your custom app, create a file like:
# my_app/my_app/utils/party.py

# Paste the entire code snippet into this file.
Next Step
2

Import the Function in `__init__.py`

To make the function easily importable, add it to the `__init__.py` file in the same directory.

python
# In my_app/my_app/utils/__init__.py

from .party import get_party_details
Next Step
3

Call 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.

javascript
// 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();
                    }
                }
            });
        }
    }
});
Next Step
4

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.

bash
bench migrate
bench restart

Common Issues & Solutions

Troubleshoot problems you might encounter