Pass entire object from client side to server side and use

so i have found somewhat similar answers on forum , but none are exact and cant get it to work .

i am trying to pass a items array object to server side , perform operations on it and pass the changed values back .
problem is i cant get access to individual fields of the item .

here is my code :

js

promo_rule(){
		let invoice_items = this.frm.doc.items;
		// console.log(invoice_items);
		frappe.call({
			method: "promo_rule.promo_rule.doctype.promotion_rule.promotion_rule.apply_promo_rule",
			args: {
				items: invoice_items
			}
		}).then(r => {
			if(r.message) {
				console.log("front side", this.frm.doc.items);
				this.update_cart_data(r.message);
				}
			});

	}

the object which is passed is like below in object array format with all the details of the item

now what i want to do is based on some condition , change the discount_percentage for the item and send back the updated–

but deciding condition aside , i cant seem to change the discount_percentage .

python code below

@frappe.whitelist(allow_guest=True)
def apply_promo_rule(items):
	local_item = json.loads(items)
	# frappe.errprint(local_item.discount_percentage)
	for x in local_item:
		# x.discount_percentage =50
		if x =="discount_percentage":
			x = 50
	# frappe.errprint(local_item)
	return local_item

as you see from error prints , i have tried multiple ways of accessing the discount percentage and manipulate it (statically for now ) - but cant get it .

any help is appreciated ,

thanks.

1 Like

First:
If you are calling this from the promo doctype itself (which is what it looks like), consider using the following:

frappe.call({
		method: "apply_promo_rule",
		doc: frm.doc, // this thing here
		args: // additional arguments if any

Otherwise if basically passes the in-class method a self argument and you’re on your way.

Second:
If you are not calling the method from promo doctype itself, consider passing a document name only and the use a frappe.get_doc to load the document and operate on it. Be aware that docstatus still applies - you won’t be able to apply a discount percentage on an already submitted document (and you don’t want to change that document because it’s against the majority of accounting conventions (and is in some places illegal)). You are better off handling it as a discount on the payment, but then it has become a business workflow problem and not necessarily a programming problem.

Third:
To anybody who’s interested, this is a well-asked question: a detailed problem statement and all the code that applies to the problem you’re working. Nice job @Taher_Khalil.

1 Like

That matters the most. Could there be a guidance post you could sticky so that people could follow that format and make helping them a lot more easier ?

thanks @tmatteson ,

Actually i am passing this from an external point of sale page ,
so until the invoice is submitted , the docname is not there , its just new Sales Invoice 1 ,
so cant get the document ,

i have a button on the POS page (online) , which fetches all the items in the cart ,send it to the function which checks the items and applies discount on certain items according to certain conditions .

then its sent back to apply the discount on those items .
Hence i need to be able to manupilate the items object i get ,

but once it comes in python it does’t seem to be in key-value pair , else i would have been able to access via items.discount_percentage,

Any idea is helpful ,
thanks

Sure! I’ll try to carve out some time for it this week.

There’a builtin for this problem: frappe.model.make_new_doc_and_get_name - search github and the forum for examples, I don’t have the arguments memorized.

I think this is a different problem. One of the hitches in learning the Frappe API is that for the arguments to be deserailized from JS callback -> JSON correctly is that that Python method arguments must exactly match the JS args in the callback.
This will not work:
"args": {"doc": "S0-00001", "customer": "Taher" ... }def tahers_method(document, customer_name):`

This will:
"args": {"doc": "S0-00001", "customer": "Taher" ... }def tahers_method(doc, customer):`

Put another way, you cannot rename variables via JSON and expect them to deserialize correctly: they have to have a python method argument to map to.

So it sounds like you might be up against two different issues here. Can you post your refactored code or solution?

1 Like

so , i figured something out ,

if i was passing only the items object , it went as an array and converted to list type on json.loads
instead if i pass the whole document it goes in as object then converts to Dictionary ,
https://pythonspot.com/json-encoding-and-decoding-with-python/

then i can access the items via doc['items'] and i can manipulate from there ,

then pass back the entire doc ,then can iterate the doc to get all the items .
.
your replies are appreciated .
.
@root13F , yes , questioning in proper format and with all the info is quite important to even the one asking as at most times forming the question can prove to be a pretty good Rubber Duck. , like in this case ,

Thanks

1 Like