Issue with UTF-8 encoding in XML files when sending via email

Hello everyone!

When attaching a UTF-8 encoded XML file to a Sales Invoice and sending it via email, the recipient receives the file without the special characters, which invalidates the document since these are signed files.

For now, I’ve resolved it using monkey patching in a custom app, but I’d like to know if I can contribute by submitting a PR in Frappe.

Looking forward to your feedback.

This issue is causing problems with Electronic Billing system in Costa Rica for invoices created using ERPNext, since the billing has special accent characters encoded in the XML.

When sending the invoice by mail from ERPNext, and attaching the signed XML files, the default main function in Frappe is replacing the UTF-8 accent characters in the XML (2 bytes), with plain ASCII characters (1 byte), invalidating the signature and causing systems to reject the XML files.

@GeraldiMejiaS, could you please post your proposed solution and where in Frappe framework this is happening, so others with the same problem can use this workaround?

Thanks!

Of course

The problem is in the add_attachment function in the email_body.py file

The original function in frappe is the following:

def add_attachment(fname, fcontent, content_type=None, parent=None, content_id=None, inline=False):
	...
	if maintype == "text":
		# Note: we should handle calculating the charset
		if isinstance(fcontent, str):
			fcontent = fcontent.encode("utf-8")
		part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
	elif maintype == "image":
		part = MIMEImage(fcontent, _subtype=subtype)
	elif maintype == "audio":
		part = MIMEAudio(fcontent, _subtype=subtype)
	else:
		part = MIMEBase(maintype, subtype)
		part.set_payload(fcontent)
		# Encode the payload using Base64
		from email import encoders

		encoders.encode_base64(part)
       ...
			

I propose adding one more condition to handle the xmls

def add_attachment(fname, fcontent, content_type=None, parent=None, content_id=None, inline=False):
	...
	if maintype == "text":
		# Note: we should handle calculating the charset
		if isinstance(fcontent, str):
			fcontent = fcontent.encode("utf-8")
		part = MIMEText(fcontent, _subtype=subtype, _charset="utf-8")
	elif maintype == "image":
		part = MIMEImage(fcontent, _subtype=subtype)
	elif maintype == "audio":
		part = MIMEAudio(fcontent, _subtype=subtype)
	elif content_type == "application/xml":
		if isinstance(fcontent, str):
			fcontent = fcontent.encode('utf-8')
		part = MIMEBase(maintype, subtype)
		part.set_payload(fcontent)
		from email import encoders
		encoders.encode_base64(part)
	else:
		part = MIMEBase(maintype, subtype)
		part.set_payload(fcontent)
		# Encode the payload using Base64
		from email import encoders

		encoders.encode_base64(part)
       ...
			

With Monkey Patching from a custom app the following must be added to the hooks.py file

frappe.email.email_body.add_attachment = custom_add_attachment
1 Like