How to add attachment to ir.attachment through json_rpc

Hi,

I am trying to add attachment/picture to ir.attachment to json_rpc.
At first, I am using Flutter/Dart as client side.
Firstly, using base64Encode to encode the bytes to string.

String value = base64Encode(byte);
vlist  = {
"data": "{'__class__': 'bytes', 'base64':" + value + "}"
}

using json_rpc, create method.

When passing to server, I get this error:
Unicode-objects must be encoded before hashing

May I know what is wrong ? and what object should pass to attachment data?

Using flask_tryton I was able to add an attachment just reading the file with ‘rb’ and passing the binary data of the file.

Have you tried that ?

Hi german,

Thank you, are you able to show me some example from your code?

I did inside tryton module like this:
I’m sure from josn-rpc should be very similar

Attachment = Pool().get('ir.attachment')
with open('my_file', "rb") as f:
     data= f.read()
attach,  = Attachment.create([{'create_uid': 1, 'resource':str(invoice),'name': 'my_fname','data':data},])

Hope it helps

It is strange to name this vlist when it is just a dictionary.
I guess it will be good to show exactly the RPC call you are doing.

Hi @ced,

Below is my code, please help

   File f = File(el.imageFile.path);
    String value = base64Encode(f.readAsBytesSync());
    String kk = "{'__class__': 'bytes', 'base64':" + value+ "}";
    List<Map<String, dynamic>> vlist = List<Map<String, dynamic>>();  
       vlist.add({
        "resource": 'wms.receiving,6',
        "name": 'Ref_doc_.jpg' ,
        "data": value
      });
   Future<TrytonResponse> create(List vlist, {Map context}) async {
       var ctx = await getContext();
    var params = [vlist, ctx];
    String method = "model.ir.attachment.create";
    var body = json.encode({"id": new Uuid().v1(),
      "method": method,
      "params": params});
    final res = await callRequest(payload);      
    return res;
  }
    Future<TrytonResponse> callRequest(payload) async {
    var auth = '$_userName:$_uid:$_sessionId';
    var bytes = utf8.encode(auth);
    var base64 = base64Encode(bytes);
    String sessionAuth = 'Session  $base64';
    Dio client = Dio();
    client.options.headers["Content-type"] = "application/json; charset=UTF-8";
    client.options.headers["Cookie"] = _sessionId;
    client.options.headers["authorization"] = sessionAuth;
    final response = await client.post(_serverURL, data: body);
    TrytonResponse res = TrytonResponse(response);
    res.checkError();
    return res;
  }

Hi @german,

Thank you for your help.
I think there is difference between json_rpc and tryton-flask.

Seem like, the code provided is using the connector which able to direct send the raw file as attachment.

Thank you

This string representing a JSON will be encoded a second times.

You are using the base64 value instead of the JSON representation.

Sorry for long posting this thread. When posting using JSOn we need to user base64.
When go back to python, we need to change back to byte so it can be read.
Here I posted the solution. Hope it can help to others:

class Attachment(metaclass=PoolMeta):
    __name__ = "ir.attachment"

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls.__rpc__.update(
            {
                "rft_attachment": RPC(readonly=False),
            }
        )

    @classmethod
    def rft_attachment(cls, values, *args):
        if not values:
            return
        pool = Pool()
        Attachment = pool.get("ir.attachment")
        val = values[0]
        base64data = val["data"]
        filename = val["filename"]
        resource = val["resource"]
        imgdata = base64.b64decode(base64data)
        (attach,) = Attachment.create(
            [
                {
                    "resource": resource,
                    "name": filename,
                    "data": imgdata,
                }
            ]
        )
        return [{"value": True}]
1 Like

Markus,
I found a solution without having to override Attachment :

 String imagePath = "images/test.jpeg";
    var imageData = await rootBundle.load(imagePath);
    String value = base64Encode(imageData.buffer.asUint8List());
    var kk = {'__class__': 'bytes', 'base64': value};
    List<Map<String, dynamic>> vlist = <Map<String, dynamic>>[];
    var params = {
      "resource": 'product.template,43',
      "name": 'test.jpg',
      "data": kk
    };
    vlist.add(params);
    call("model.ir.attachment", "create", [vlist, {}]);

You then can list the attachment like this :

 call("model.ir.attachment", "search_read", [
      ['resource', '=', 'product.template,43'],
      0,
      null,
      null,
      ['id', 'name'],
      {}
    ]);
1 Like

Hi @DaZD ,

Eh… I did try this as per ced did mention.
But it failed too.
Nevermind, i try later.

Thank you… cheers

Yes ced gave the solution but to make it work with flutter the trick is to give an object instead of a string :

var kk = {'__class__': 'bytes', 'base64': value};

instead of :

String kk = "{'__class__': 'bytes', 'base64':" + value+ "}";

With a string value I had the same error message :

Unicode-objects must be encoded before hashing

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.