pymysql.err.IntegrityError and DuplicateEntryError when adding user and contact from a custom app method

Hi, I have been trying to write a short custom_app to create users and contacts linked to each other (later customers too) via API.

I’m running
Frappe Framework: v12.3.0 (version-12)
ERPNext: v12.5.2 (version-12)
in development mode

So, I have a custom app say custom_app and I added a file login_custom.py inside custom_app/custom_app/custom_app with a whitelisted function as below:

> @frappe.whitelist(allow_guest=True)
> def sign_up_test(email, mobile, full_name):
> 	userlist = frappe.db.get_all("User", or_filters = {"email": email,"mobile_no": mobile})
> 	if userlist:
> 		return userlist, _("already exists")
> 	else:
> 		from frappe.utils import random_string
> 		user = frappe.get_doc({
> 			"doctype":"User",
> 			"email": email,
> 			"mobile_no": mobile,
> 			"first_name": full_name,
> 			"enabled": 1,
> 			"new_password": random_string(10),
> 			"user_type": "Website User"
> 		})
> 		user.flags.ignore_permissions = True
> 		user.flags.ignore_password_policy = True
> 		user.add_roles("Customer")
> 		user.insert()
> 		return user, _("added new")

Now, I also want to create a contact record for this user and for that, I added another function as below in the same file

> def create_contact(user, ignore_links=False, ignore_mandatory=True):
> 	contact = frappe.get_doc({
> 		"doctype": "Contact",
> 		"first_name": user.first_name,
> 		"last_name": user.last_name,
> 		"user": user.name,
> 		"gender": user.gender,
> 	})
> 
> 	if user.email:
> 		contact.add_email(user.email, is_primary=True)
> 
> 	if user.phone:
> 		contact.add_phone(user.phone, is_primary_phone=True)
> 
> 	if user.mobile_no:
> 		contact.add_phone(user.mobile_no, is_primary_mobile_no=True)
> 	contact.insert(ignore_permissions=True, ignore_links=ignore_links, ignore_mandatory=ignore_mandatory)

To execute this function whenever a user is inserted, a added a doc event hook in the hooks.py file of the custom_app

> doc_events = {
> 	"User": {
> 		"after_insert": "custom_app.custom_app.login_custom.create_contact"
> 	}
> }

Now, using Postman, I am hitting the following endpoint http://site1.local:8001/api/method/custom_app.custom_app.login_custom.sign_up_test with the required params.

Without the “create_contact” hook event, it adds the user and returns it successfully. But, with the hook event added, I get the following error trace:

> Server Error
> Traceback (most recent call last):
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/model/base_document.py", line 324, in db_insert
>     ), list(d.values()))
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/database/database.py", line 156, in sql
>     self._cursor.execute(query, values)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/cursors.py", line 170, in execute
>     result = self._query(query)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/cursors.py", line 328, in _query
>     conn.query(q)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/connections.py", line 517, in query
>     self._affected_rows = self._read_query_result(unbuffered=unbuffered)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/connections.py", line 732, in _read_query_result
>     result.read()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/connections.py", line 1075, in read
>     first_packet = self.connection._read_packet()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/connections.py", line 684, in _read_packet
>     packet.check_error()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/protocol.py", line 220, in check_error
>     err.raise_mysql_exception(self._data)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/env/lib/python3.7/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
>     raise errorclass(errno, errval)
> pymysql.err.IntegrityError: (1062, "Duplicate entry 'test456@example.com' for key 'PRIMARY'")
> 
> During handling of the above exception, another exception occurred:
> 
> Traceback (most recent call last):
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/app.py", line 60, in application
>     response = frappe.api.handle()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/api.py", line 55, in handle
>     return frappe.handler.handle()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/handler.py", line 22, in handle
>     data = execute_cmd(cmd)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/handler.py", line 61, in execute_cmd
>     return frappe.call(method, **frappe.form_dict)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/__init__.py", line 1051, in call
>     return fn(*args, **newargs)
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/custom_app/custom_app/custom_app/login_custom.py", line 73, in sign_up_test
>     user.insert()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/model/document.py", line 248, in insert
>     raise e
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/model/document.py", line 245, in insert
>     self.db_insert()
>   File "/Users/shubhamgupta/FrappeDev/fi-bench/apps/frappe/frappe/model/base_document.py", line 334, in db_insert
>     raise frappe.DuplicateEntryError(self.doctype, self.name, e)
> frappe.exceptions.DuplicateEntryError: ('User', 'test456@example.com', IntegrityError(1062, "Duplicate entry 'test456@example.com' for key 'PRIMARY'"))

I cant figure out what is wrong, is it my code or it could be some bug. Can someone please help or at least nudge me in the right direction :slight_smile:

Okay so I’ve even upgraded to the latest version:
Frappe Framework: v12.5.0 (version-12)
ERPNext: v12.7.0 (version-12)

and the issue still persists.

I also checked the database and there are no actual duplicate entries. Can’t figure out how inserting a Contact doctype record is triggering an error for User doctype.

Okay I finally solved this myself.

It was these two lines.

user.add_roles("Customer")
user.insert()

It was supposed to be the insert function first and then the add_role function (which calls a save function again)

user.insert()
user.add_roles("Customer")
1 Like