Search code examples
javascriptwidgetmulti-selectodoo-9qweb

Upload multiple attachment at once in Odoo 9


I'm trying to make a feature in Odoo that allows a user to upload multiple files from an attachment form, and when he submits it, it creates as many attachments as selected documents.

I created a widget for the 'binary' field that allows the multi selection (Adding 'multiple' argument to 'input' tag in a Qweb template), but at this point i'm struggling with attachments creation and linking them with the concerned field from JS.

my widget :

odoo.define('lettermgmt', function(require)
{
    'use strict';
    var core = require('web.core');
    var FieldBinaryFile = core.form_widget_registry.get('binary');
    var FieldBinaryMultipleFiles = FieldBinaryFile.extend({
        template : 'FieldBinaryMultipleFiles',
    });
    core.form_widget_registry.add('binary_multiple_files',FieldBinaryMultipleFiles);

});

My first guess was to override on_file_change method, loop over "e.target.files" and make a POST request for each file on '/web/binary/upload_attachment'. this creates the attachments but i couldn't figure out how to link them to my model (relational field).

I'll appreciate any help :)


Solution

  • I finally found a solution :

    The idea is to create a custom char widget with specific behavior. JS Code here :

    odoo.define('cutom_module', function(require)
    {
        'use strict';
        var core = require('web.core');
        var Char = core.form_widget_registry.get('char');
    
        var import_button = Char.extend({
           template : "import_button",
            events : {
               'change' : 'import_files',
            },
            init : function(){
               this._super.apply(this,arguments);
               this._start = null;
            },
            start : function() {
            },
            import_files : function(event){
                var self = this;
                var files = event.target.files; // Get Selected files
                var attachment_ids = self.getParent().fields[ 'attachment_ids' ]; // Get existing attachments
                var data64 = null;
                var values_list = [];
                _.each(files, function(file){
                    // Check if the file is already in the attachments, must specify field name here :/
                    if(self.already_attached(attachment_ids.get_value(),file.name)){
                        return;
                    }
                    var filereader = new FileReader();
                    filereader.readAsDataURL(file);
                    filereader.onloadend = function(upload) {
                            var data = upload.target.result;
                            data64 = data.split(',')[1];
                            var values = {
                                'name' : file.name,
                                'type' : 'binary',
                                'datas' : data64,
                            };
                            values_list.push([ 0, 0, values]);
                            if(values_list.length == files.length){
                                attachment_ids.set_value(values_list);
                            }
                    };
                });
            },
            already_attached : function (attachments,filename) {
                  for(var i=0;i<attachments.length;i++){
                      if(attachments[i][2]['name'] == filename){
                          return true;
                      }
                  }
                  return false;
            },
        });
    
        core.form_widget_registry.add('import_button',import_button);
    
    });
    

    Qweb for import_button template :

    <?xml version="1.0" encoding="UTF-8"?>
    
    <templates xml:space="preserve">
        <t t-name="HiddenInputMultipleFiles">
            <div t-attf-class="oe_hidden_input_file #{fileupload_class or ''}" t-att-style="fileupload_style">
                <form class="o_form_binary_form" t-att-target="fileupload_id"
                      method="post" enctype="multipart/form-data" t-att-action="'/web/binary/upload_attachment'">
                    <input type="hidden" name="csrf_token" t-att-value="csrf_token"/>
                    <input type="hidden" name="session_id" value="" t-if="widget.session.override_session"/>
                    <input type="hidden" name="callback" t-att-value="fileupload_id"/>
                    <t t-raw="0"/>
                    <input type="file" class="o_form_input_file" name="ufile" t-if="widget.widget!='image'" multiple="multiple"/>
                    <input type="file" class="o_form_input_file" name="ufile" accept="image/*" t-if="widget.widget=='image'"/>
                </form>
                <iframe t-att-id="fileupload_id" t-att-name="fileupload_id" style="display: none"/>
            </div>
        </t>
    
        <div  t-name="import_button" t-attf-class="base #{cls}" class="oe_web_example">
            <p>
                <t t-call="HiddenInputMultipleFiles">
    
                </t>
            </p>
        </div>
    </templates>
    

    View :

    ....
    <field name="import_files" widget="import_button"/>
    <field name="attachment_ids" widget="one2many_list"
                            >
                                <tree create="0">
                                    <field name="name" string="File name"/>
                                </tree>
                        </field>
    ....
    

    and the model :

    ....
    attachment_ids = fields.Many2many(comodel_name="ir.attachment",string="Documents")
    import_files = fields.Char(string=" ")
    ....
    

    If you have questions or suggestions for improvements let me know :)