Help needed to go through my custom script

Hello all,

I created a field in the sales invoice doctype, and I want to validate the field name so that a particular data cannot be used multiple times to create a sales invoice. That is to say that if a user wishes to select data that has already been used to raise a sales invoice, the application will prevent the user from selecting the data, and then prompt a user to select a different data out of several other options in a drop-down list of items.

Here is my custom python script:

def clean_unique_id(self):
      unique_id = self.clean_unique_id.get(‘unique_id’)
      if(unique_id == ‘’"):
            frappe.msgprint(‘This field cannot be left blank’)


    for doc in frappe.get_doc('Sales Invoice'):
        if doc.unique_id == unique_id:
            frappe.throw(title='Error', msg='This plot number is no longer available', exc=ValidationError)

After writing this script, I was still able to raise a sales invoice multi times.

I will be immensely grateful if you can correct my custom script if it’s wrong and guide me on how I can achieve my desired aim with this script.

Thank you very much.

Regards,

Where is this script, and how is it getting called? Also, please fix the code fence to make indentation appear properly.

There are a few obvious errors here. The first is that frappe.get_doc returns an individual document, so you can’t iterate through it. You probably want frappe.get_list or frappe.get_all.

Also, I’m not sure what `self.clean_unique_id.get(‘unique_id’) references.

1 Like

thanks for the awesome information.

Hi @peterg,
The clean_unique_id is referencing the unique_id which is a custom field name that I created in my Sales Invoice.

Ok, so I made some modifications to the script using your suggestions in your observations.

In the Sales Invoice python script, I inserted my function here:

def validate(self):
		super(SalesInvoice, self).validate()
		self.validate_auto_set_posting_time()

		if not self.is_pos:
			self.so_dn_required()

		self.validate_proj_cust()
		self.validate_pos_return()
		self.validate_with_previous_doc()
		self.validate_uom_is_integer("stock_uom", "stock_qty")
		self.validate_uom_is_integer("uom", "qty")
		self.check_sales_order_on_hold_or_close("sales_order")
		self.validate_debit_to_acc()
		self.clear_unallocated_advances("Sales Invoice Advance", "advances")
		self.add_remarks()
		self.validate_unique_id() #(This is where I inserted my function)
		self.validate_write_off_account()
		self.validate_account_for_change_amount()
		self.validate_fixed_asset()
		self.set_income_account_for_fixed_assets()
		validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_invoice_reference)


I then wrote my script modification as:

def validate_unique_id(self):
		unique_Id = self.validate_unique_id.get('unique_id')
		if(unique_Id == ''):
			frappe.msgprint('This field cannot be left blank')

			for doc in frappe.get_all("Sales Invoice", fields=["*"]):
				if doc.unique_id == "unique_id":
					frappe.throw(title='Error', msg='This plot number is no longer available', exc=ValidationError)

After writing this script, I’m still not able to validate the field. Can anyone help me resolve this?

The first task is to make sure that your script is running. At the moment, I suspect that it is not.

When you say that you inserted your function in the Sales Invoice python script, do you mean that you edited the erpnext base source code? If so, that’s not usually a recommended approach, as any changes you make will be wiped out on every update. A better approach would be to use either event hooks or server scripts.

That said, editing the source directly should work. If the method isn’t getting called, it’s likely that you’re running off of a cache. Are you running on in production or development mode?

Second, your python has a number of different errors.

  • self.validate_unique_id.get('unique_id') doesn’t make any sense. Do you mean just self.unique_id?
  • You refer at various times to unique_id and unique_Id (with capital). These are different variables. Do you mean them to be?
  • Your for loop is getting called only if unique_id is an empty string. That doesn’t seem to make sense.
  • you’re comparing doc.unique_id to the static string “unique_id”. If you’re meaning to compare it to the variable, you need to lose the quotes.

If you’re new to python, you might find it a lot easier to test some of this code in the console, which you can access from the command line via bench --site [site-name] console. There, you’ll get realtime feedback to all of the syntax errors here. Alternately, if you’re running frappe in development mode via bench start, you’ll get realtime feedback in the terminal. I would strongly advise starting there.

2 Likes

Also, have you considered just marking the custom field “unique”? I’m not entirely clear on what you’re trying to accomplish here, so I can’t say for sure that it will suit your needs, but the unique flag might do what you’re trying to do without custom code.

2 Likes

Hi @peterg,

Thanks for your response, I’m very grateful.

Yes, I am editing the erpnext source code itself, however, I am working on a development environment, on a VMware in my local machine. I’m aware of the server scripts, and I guess I may work with that. Thank you.

I will keep you posted on this thread. Thanks

Seems to me that all you need to do is set the field as ‘Unique’

…no need for any custom code at all!

Kind regards,

Hi Wale,
Thanks for your response.
When I try to set it as ‘Unique’, it throws me this error.
“Sales Invoice: Field ‘Unique ID’ cannot be set as Unique as it has non-unique values”. I guess it may be as a result of how I configured the field.

Not really. Whenever you set a field as unique, the system checks through all the existing records to be sure there are no duplicates already; if it does find an existing duplicate (ie 2 or more records that have the same value in that field), it throws the error you mentioned

You need to go through your existing records and remove/change any duplicates before setting the field as unique

Cheers

@wale can this validation be triggered on save rather than submit? if yes how can I achieve this?

Hi @flexy2ky

It validates on Save

Cheers

Oh thanks for the response. It works as you said however it doesn’t exactly help my scenario. Was hoping to use it to validate fields per transaction but it validates permanently so the data in that field must be unique in all transactions which is not what I intended. My use case it to compare values in two fields in a transaction to ensure they’re not the same but these values can be repeated in another transaction.

thanks for the awesome information.