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