How to get saved state of the doctype while validating?

Hi all!

I’m validating “Payment Entry” on the server side with Python.

How to get saved state of doctype I’m validating now to check what fields are changed?

Maybe there is some mechanism in frappe to get only changed fields?

Any help apriciated!

You can check what fields were changed or new values were added via Version

There is a hidden field called data in that latest Version instance of your Payment Entry which contains a string which is a dictionary which records fields were newly added and which ones were updated. Here’s an example of an update:

"{
 "added": [], 
 "changed": [
  [
   "workflow_state", 
   "Approved", 
   "Invoice Generated"
  ]
 ], 
 "removed": [], 
 "row_changed": []
}"

One final step before you can use this will be to convert this string to an accessible Python dictionary

You can use this answer to convert string to a usable data structure:

1 Like

Hi! Thanks!

This is exactly what I need, but when I do following:

frappe.msgprint(str(doc.data))

I’m getting this error

AttributeError: 'PaymentEntry' object has no attribute 'data'

Which are correct way to get version data from doc?

Here is my completely simplified code from method executed on validate event:

import frappe

def transfer_confirmation_check(doc,event):
    frappe.msgprint(str(doc.data))

The error is saying that your Payment Entry object has no such attribute called data.

This field data is not a field in the Payment Entry but the Version instance of this Payment Entry. You need to fetch the latest version document of your current Payment Entry. Try something like this:

pe_last_change = frappe.get_doc('Version',{'ref_doctype':self.doctype , 'docname': self.name})

This method will get you the Version object which contains the changes done to this payment entry. Then you can use pe_last_change.data You can ofcourse use object names as per your requirement and the context to make your code more readable.

Clarification for usage of self in the above method

If you already are well verse with Python then you can skip this one. If not please read.

Just to clarify, that you can use self.doctype only if this is a function defined in the Class itself. BUT this function is called from JS or any function out of the scope of class, then make sure that you pass the Payment Entry name field which makes the usage of get_doc or other Frappe methods to get values easier. If it is the latter case, then use the appropriate object name you defined.

I’m on the right way now, this code:

def transfer_confirmation_check(doc,event):
    pe_last_change = frappe.get_doc('Version', {'ref_doctype': doc.doctype, 'docname': doc.name})
    frappe.msgprint(str(pe_last_change.data))

retuns now
Version {'docname': u'PE-00020', 'ref_doctype': u'Payment Entry'} not found

I checked doctype Payment Entry for track changes option - it’s enabled.

I’m replaced self.doctype and self.name in your code with the same values passed to the method with doc object. Maybe it’s my mistake and values in your code string means somthing different?

Okay so this is a hook function.

Is this doc a Payment Entry object ? i.e. is it mapped to Payment Entry events in doc_events in your hooks.py ?

No you have correctly written this.

Yes, it’s a hook function mapped to Payment Entry’s ‘validate’ event in hooks.py.

From my hooks.py:

doc_events = {
	"Payment Entry": {
		"validate": "unium_transfers.unium_transfers.payment_entry_custom.transfer_confirmation_check"
	},
}

I think this function being called at validate may be one issue. Do you want to check it after each save or after the final submit ?

After each save.

I looked through some frappe related sources and discovered that it can be done with code like this:

if frappe.get_value("Payment entry", doc.name, "fieldname") != doc.fieldname:
    // do necessary things here

I expect this code must compare saved state of field fieldname with one passed from frontend for validation.
Is it right?

I haven’t tried it myself. Does it work ?

Finally I ended up with this code for my needs:

def transfer_confirmation_check(doc,event):
    if not doc.get('__unsaved'):
        saved_doc = frappe.get_doc('Payment Entry', doc.name)
        if saved_doc.creditdecision != doc.creditdecision:
            # do something if creditdecision field is changed by user
    else:
        # Some validation if doc item is new here

Thank u for ur help and time.

2 Likes