[Tutorial] Attach Files from items in any document

I’ve created a client script to easily attach files from the selected items.

Basically, it creates a “Get Item Attachments” button to the current document. It lists file extensions and file counts which are attached to the items. You can select file extensions. Then all the files with selected extensions are added to the current document.

Source: This client script will attach files from the items by types to the current document. · GitHub

function GetFileAttachments(frm, arrItemCode) { 
    //Get files which are added to given items
    frappe.db.get_list("File", {
        filters: [
                ['attached_to_name', 'in', arrItemCode],
                ['attached_to_doctype', '=', "Item"]
            ],
    		fields: ["file_name", "file_url", "folder"],
    		limit: 9999
    	}).then((data) => {
    	    let arrFileExt = [];//File extensions
    	    let arrFileExtInfo = [];//json
    	    let strFileExt = "";
    	    let jsonFileInfo = {
    	        file_ext: [],
    	        file_count: []
    	    }
    	    let dIndex = -1;//File index search
    	    for(let i=0; i<data.length; i++) {
    	        strFileExt = data[i].file_name.substring(data[i].file_name.lastIndexOf(".")+1);
    	        if(arrFileExt.indexOf(strFileExt) === -1) {
    	            arrFileExt.push(strFileExt);
    	            arrFileExtInfo.push({'file_ext': strFileExt});
    	            jsonFileInfo.file_ext.push(strFileExt);
    	            jsonFileInfo.file_count.push(1);
    	        } else {
    	            dIndex = jsonFileInfo.file_ext.indexOf(strFileExt);
    	            jsonFileInfo.file_count[dIndex] = jsonFileInfo.file_count[dIndex] + 1;
    	        }
    	    }
    	    for(let i=0; i<arrFileExtInfo.length; i++) {
    	        strFileExt = arrFileExtInfo[i].file_ext;
    	        dIndex = jsonFileInfo.file_ext.indexOf(strFileExt);
    	        arrFileExtInfo[i].file_count = jsonFileInfo.file_count[dIndex];
    	    }
    	    
            const fields = [
            {
                label: 'Files',
                fieldtype: 'Table',
                fieldname: 'files',
                description: __('Select File Types'),
                fields: [
                {
                    fieldtype: 'Data',
                    fieldname: 'file_ext',
                    options: '',
                    reqd: 0,
                    readonly: 1,
                    label: __('File Type'),
                    in_list_view: 1
                },
                {
                    fieldtype: 'Int',
                    fieldname: 'file_count',
                    options: '',
                    reqd: 0,
                    readonly: 1,
                    label: __('File Count'),
                    in_list_view: 1
                }
            	],
                data: arrFileExtInfo,
                get_data: () => {
                    return r.message//ERR
                }
            }]
            var d = new frappe.ui.Dialog({
                title: __('Select File Types'),
                fields: fields,
                primary_action: function(dataTable) {
                    let arrPromFile = [];
                    for(let i=0; i<d.fields_dict.files.df.data.length; i++) {//Her dosya turu icin
                        strFileExt = d.fields_dict.files.df.data[i].file_ext;
                        //Does the file type is selected?
                        if (typeof d.fields_dict.files.df.data[i].__checked !== 'undefined') {
                            if (d.fields_dict.files.df.data[i].__checked === 1) {
                                //Select the files which have the same extension
                                for(let j=0; j<data.length; j++) {//For each file
                                    if (strFileExt == data[j].file_name.substring(data[j].file_name.lastIndexOf(".")+1)) {
                                        //Extensions match
                                        const docFile = frappe.model.get_new_doc('File');
                                        docFile.file_name = data[j].file_name;
                                        docFile.attached_to_doctype = frm.doctype;
                                        docFile.attached_to_name = frm.docname;
                                        docFile.file_url = data[j].file_url;
                                        docFile.folder = data[j].folder;
                                        arrPromFile.push(frappe.db.insert(docFile));
                                    }
                                }
                            }
                        }
                    }//File operations are completed.
                    d.hide();
                    Promise.all(arrPromFile).then((result) => {
                        frm.reload_doc();
                    })
            	},
            	primary_action_label: __('Attach Files')
            });
            d.show();
	    });
}

frappe.ui.form.on('Request for Quotation', {
	refresh(frm) {
	    frm.add_custom_button(__('Get Item Attachments'), function(){
                let arrItemCode = [];
            
                $.each(frm.doc.items,  function(i,  d) {
                    arrItemCode.push(d.item_code);
                });
                GetFileAttachments(frm, arrItemCode);
            });
	}
});

PS: To make it work on document types other than the “Request for Quotation” you need to change line 105 to needed document type. Like “Purchase Order”.

16 Likes

Good one!

Quite amazing to see what can be done without opening the trunk !

Thanks for sharing,

1 Like

Very good contribution, very useful!

I see make a copy of the file, is a way tu use the same file of the item and not copy the file, like just make new file doc using an existing file?

image

Hi there. I didn’t understand the question here.

Hi @TurkerTunali
How can I bring the Attachments file in the Lead doctype to the Opportunity doctype?


Thank You!

Hi @Mohammadali,

Please apply the client script on opportunity doctype.

frappe.ui.form.on('Opportunity', {
    refresh: function(frm) {
        if (frm.doc.opportunity_from == "Lead") {
            frm.add_custom_button(__('Get Lead Attachments'), function () {
                const leadName = frm.doc.party_name;
                GetFileAttachments(frm, leadName);
            });
        }
    }
});

function GetFileAttachments(frm, leadName) {
    frappe.db.get_list("File", {
        filters: [
            ['attached_to_name', '=', leadName],
            ['attached_to_doctype', '=', "Lead"]
        ],
        fields: ["file_name", "file_url", "folder"]
    }).then((data) => {
        let arrFilesToAttach = [];
        for (let i = 0; i < data.length; i++) {
            const docFile = frappe.model.get_new_doc('File');
            docFile.file_name = data[i].file_name;
            docFile.attached_to_doctype = "Opportunity";
            docFile.attached_to_name = frm.docname;
            docFile.file_url = data[i].file_url;
            docFile.folder = data[i].folder;
            arrFilesToAttach.push(frappe.db.insert(docFile));
        }
        Promise.all(arrFilesToAttach).then(() => {
            frm.reload_doc();
        });
    });
}
2 Likes

Great solution. But managing this in server script would be more suitable for this sceneario I think.

Hmm right @TurkerTunali

I knew it.

DocType Event: Before Save

if doc.opportunity_from == "Lead" and doc.party_name:
    lead_attachments = frappe.get_all("File",
        filters={
            "attached_to_doctype": "Lead",
            "attached_to_name": doc.party_name
        },
        fields=["file_name", "file_url", "folder"]
    )

    if lead_attachments:
        for attachment in lead_attachments:
            new_file = frappe.get_doc({
                "doctype": "File",
                "file_name": attachment.file_name,
                "attached_to_doctype": "Opportunity",
                "attached_to_name": doc.name,
                "file_url": attachment.file_url,
                "folder": attachment.folder
            })
            new_file.insert(ignore_permissions=True)

Hope this helps :wink:

2 Likes

@NCP
Thanks for the quick response
My scenario is diff. I want to move record CRM to the opportunity default it will create same attachment instead of i want to do link to CRM

It’s creates same attachment for both places. Do not store multiple times. Only for same file use multiple time so storage location can not occupy more space.

Might be possible, but you can try your own way. You have to add the some logic in py side.

Yes, I have already tried with PY but not solved this query.
I am sure @NCP can help me in this case.

Thanks

I can provide some guidance. If you want to configure a system where, upon creating an opportunity from a lead, attachments are transferred to the opportunity and subsequently removed from the lead automatically, you’ll need to implement this logic in the code.

1 Like

Can you please some example with code

You have to set the delete doc logic, code is already shared in the above post. just you have to create idea and set them. :wink:

1 Like

i created a Child table "Documents " for a query doctype ,

where client can attach file of the raw materials, , but table order is not set as standrd as need

 "fields": [
  {
   "fieldname": "documents",
   "fieldtype": "Link",
   "label": "Documents",
   "options": "Supporting Documents",
   "permlevel": 1
  },
  {
   "fieldname": "column_break_rsbu",
   "fieldtype": "Column Break"
  },
  {
   "fieldname": "issue_date",
   "fieldtype": "Date",
   "label": "Issue Date",
   "permlevel": 1
  },
  {
   "fieldname": "column_break_fexr",
   "fieldtype": "Column Break"
  },
  {
   "fieldname": "expiry_date",
   "fieldtype": "Date",
   "label": "Expiry Date",
   "permlevel": 1
  },
  {
   "fieldname": "column_break_isjq",
   "fieldtype": "Column Break"
  },
  {
   "columns": 4,
   "fieldname": "attachment",
   "fieldtype": "Attach",
   "label": "Attachment",
   "permlevel": 1,
   "set_only_once": 1
  }
aftre load query doctype child table not load with this order , , there no colimn , and i have to set it menualy , i want to set it once for all user, 

2  in attachment i want to hide privet , ,we need just upload files, 

Thanks