Custom script not working: two attempts

Hi

I am trying to write a custom script that adds a button to Work Order, called “Stock Transfer”.
This button must create new Stock Entry and populate the items child-table in Stock Entry
( Stock Entry Detail ) , with the scrap items in the BOM related to the Work Order.
The new Stock entry is created but I have tried two approaches to populate the “items” table
but both dont work.

Here are my two attempts …

frappe.ui.form.on('Work Order', {
  refresh: function(frm) {
    if (!frm.doc.__islocal && frm.doc.docstatus === 1) {
      // Add custom button
      frm.add_custom_button('Stock Transfer', function() {
        frappe.show_alert("Button", 5);
        frappe.new_doc('Stock Entry', {
          work_order: frm.doc.name,
          stock_entry_type: 'Material Transfer',
          bom_no: frm.doc.bom_no,
        }, (newDoc) => {
          newDoc.parenttype = 'Work Order';
          newDoc.parentfield = 'required items';
          newDoc.parent = frm.doc.name;
//          newDoc.items[0].item_code = frm.doc.production_item;

          // Get the BOM for the work order
            const bom = frappe.get_doc('Bill of Materials', frm.doc.bom_no);
            frappe.show_alert("Get BOM",5 );
          // Iterate over the scrap items in the BOM
            bom.scrap_items.forEach((scrap_item) => {
            // Add a new row to the items child table in the Stock Entry
                frappe.show_alert("Iterate", 5);
                newDoc.items.push({
                    item_code: scrap_item.item_code,
			  item_name: scrap_item.item_name,
                    });
                });
            frappe.show_alert("Save",5 );    
            frappe.ui.form.make_quick_entry('Stock Entry', null, frm, newDoc);
            });
        });
    }
  }
});

Attempt 2

frappe.ui.form.on('Work Order', {
    refresh: function(frm) {
        if (!frm.doc.__islocal && frm.doc.docstatus === 1) {
            frm.add_custom_button('Stock Transfer', function() {
                frappe.show_alert("Button", 5);
                frappe.new_doc('Stock Entry', {
                    work_order: frm.doc.name,
                    stock_entry_type: 'Material Transfer',
                    bom_no: frm.doc.bom_no,
                }, (newDoc) => {
                    newDoc.parenttype = 'Work Order';
                    newDoc.parentfield = 'required items';
                    newDoc.parent = frm.doc.name;
			newDoc.items = [];
                    frappe.call({
                        method: 'frappe.client.get_list',
                	    args: {
			     parent : 'BOM',
                   	     doctype: 'BOM Scrap Item',
                   	     filters: {
                          	   parent: frm.doc.bom_no,
                       	    },
                            fieldname: ['item_code'],
                    },
                        callback: function(response) {
                            if (response.message) {
                                const scrapItems = response.message;

                                // Iterate
					  frappe.show_alert("Iterate",5 );
                                scrapItems.forEach(scrapItem => {
                                    newDoc.items.push({
                                        item_code: scrap_item.item_code;
                                        item_name: scrap_item.item_name, 
                                    });
                                });
                                newDoc.refresh();
                                frappe.show_alert("Save", 5);
                                frappe.ui.form.make_quick_entry('Stock Entry', null, frm, newDoc);
                            }
                        },
                    });
                });
            });
        }
    }
});

Would appreciate some assistance. Thank you

Hi @willspenc ,

The best way to do this is to write a server-side script and call it through the client script in the front end.

Python function:

@frappe.whitelist()
def create_stock_entry_for_scrap_items(work_order_name):
    work_order = frappe.get_doc('Work Order', work_order_name)

    if not work_order.bom_no:
        frappe.throw('Work Order does not have a valid BOM linked.')

    bom = frappe.get_doc('BOM', work_order.bom_no)

    if not bom:
        frappe.throw('BOM not found for this Work Order.')

    stock_entry = frappe.new_doc('Stock Entry')
    stock_entry.work_order = work_order_name
    stock_entry.stock_entry_type = 'Material Transfer'
    stock_entry.from_bom = '1'
    stock_entry.bom_no = work_order.bom_no

    for scrap_item in bom.scrap_items:
        stock_entry.append('items', {
            'item_code': scrap_item.item_code,
            'item_name': scrap_item.item_name,
            'qty': scrap_item.stock_qty,
            's_warehouse': 'Your Source Warehouse',
            't_warehouse': 'Your Target Warehouse',  # Specify your warehouse
        })

    stock_entry.insert()
    
    frappe.msgprint('Stock Entry created and items populated.')

The js:

frappe.ui.form.on("Work Order", {
  refresh: function (frm) {
    if (frm.doc.docstatus === 1 && frm.doc.bom_no) {
      frm.add_custom_button("Create Stock Entry", function () {
        frappe.call({
          method: "my_test_app.api.create_stock_entry_for_scrap_items",
          args: {
            work_order_name: frm.doc.name, // Use frm.doc.name to get the Work Order name
          },
          callback: function (r) {
            if (r.message) {
              frappe.msgprint(r.message);
            }
          },
        });
      });
    }
  },
});

Hope this Helps You,

Thanks.

Thank you @Balamurugan_Ravichan for your assistance

I have worked at this but I am battling a bit. I have tackled a lot of client scripts during the past few months in an attempts to learn, but this is my first custom-app, so please be patient.

The procedure I followed was

bench new-app scrap_item_app
bench --site site3.local install-app scrap_item_app
bench restart

( Developer mode active )

And this is where I am not sure. There are 3 x “scrap_item_app” folders under my "apps folder.
I am unsure in which of these I need to place my “create_stock_entry_for_scrap_items.py file”
I eventually put a file in each of them.
For each file I changed its permissions … chmod 777 create_stock_entry_for_scrap_items.py

I created a client script to load the .js code.
The frappe call I modified like this…

 frappe.call({
          method: "scrap_item_app.scrap_item_app.create_stock_entry_for_scrap_items",

The error I am getting …

Failed to get method for command scrap_item_app.scrap_item_app.create_stock_entry_for_scrap_items
with module scrap_item_app.scrap_item_app has no attribute "create_stock_entry_for_scrap_items.

So clearly it is not getting to my .py file

On the Desk, I can see my Module in “Module Def List”
in …/apps/sites/apps.txt , I can see my app listed

I have looked at a few videos to try and clear up my confusion over the folder-structure and the
method-structure of the frappe-call.

For one reason or another I have been unable to access the frappe-website to read the docs on
creating a new app.

If someone can assist I would greatly appreciate it.
thank you

Yes, I understand. What you can do is create a new api.py file in your custom app and call the function from there. You can create this file where the hooks.py or modules.txt is available, which is the second folder, I guess. once you create the file paste the code in the API and the route to call the function will be “scrap_item_app.api.create_stock_entry_for_scrap_items”

Thank you @Balamurugan_Ravichan , let me do that.

Update …
I have also been doing some googling and found a post in this forum that mentioned a nice tool, which I
am trying as well … bench console.

It seems i can “import” a method ? and also call it.

But le tme make the changes you suggest. Thank you

Thank you @Balamurugan_Ravichan again for your assistance

I implemented the suggestions you made in your previous post and all is working.

I still have another script to write but this involves a custom doctype. So there is going to be
another learning curve for me on that one but I am sure the info I got out of this project, will
assist me. If need be I shall open another thread for that.

Just one last thing I am wondering about : do I have to do
bench --site site3.local migrate
bench restart
for every change I make in the python code ???

It was also interesting to use the bench console to “import” my method and to actually call it as well.
It seems to display how the python code is executed. Never knew about this as well !!!

Later, I shall post the process that I used, incase it can help someone else.

Thank you

Hey @willspenc ,

I’m happy that you found it interesting and useful. Keep exploring it will get more exciting.

I will be happy to assist you in the future.

Bench migration and restart is not that necessary, the code should reflect the changes without these commands.

Thanks.