chickahoona
chickahoona

Reputation: 2034

How can I download an invoice from Odoo (v13) via xml rpc in Python?

I struggle currently a bit to download an invoice as PDF from Odoo 13 with xml rpc.

The closest that I could get is this:

model_name = 'ir.actions.report'
model_method = 'render_qweb_pdf'
report_id = 282
invoice_id = 4
args = [[report_id]]
kwargs = {'res_ids': [invoice_id]}

models = ServerProxy('{}/xmlrpc/2/object'.format(url))
return models.execute_kw(db, uid, password,
                         model_name, method_name,
                         args, kwargs)

Yet I always end up with this error:

  ...py", line 46, in execute_kw
    args, kwargs)
  File "/usr/lib/python3.6/xmlrpc/client.py", line 1112, in __call__
    return self.__send(self.__name, args)
  File "/usr/lib/python3.6/xmlrpc/client.py", line 1452, in __request
    verbose=self.__verbose
  File "/usr/lib/python3.6/xmlrpc/client.py", line 1154, in request
    return self.single_request(host, handler, request_body, verbose)
  File "/usr/lib/python3.6/xmlrpc/client.py", line 1170, in single_request
    return self.parse_response(resp)
  File "/usr/lib/python3.6/xmlrpc/client.py", line 1336, in parse_response
    p.feed(data)
  File "/usr/lib/python3.6/xmlrpc/client.py", line 439, in feed
    self._parser.Parse(data, 0)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 64, column 9

The data that its trying to parse there in this self._parser.Parse(data, 0)-line is

b"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><array><data>\n<value><string>%PDF-1.3\n1 0 obj\n&lt;&lt;\n/Type /Pages\n/Count 0\n/Kids [ ]\n&gt;&gt;\nendobj\n2 0 obj\n&lt;&lt;\n/Producer (PyPDF2)\n&gt;&gt;\nendobj\n3 0 obj\n&lt;&lt;\n/Type /Catalog\n/Pages 4 0 R\n/Outlines 23 0 R\n/PageMode /UseOutlines\n/Dests 25 0 R\n/Names &lt;&lt;\n/EmbeddedFiles &lt;&lt;\n/Names [ (factur\\055x\\056xml) &lt;&lt;\n/Type /Filespec\n/F (factur\\055x\\056xml)\n/EF &lt;&lt;\n/F 27 0 R\n&gt;&gt;\n&gt;&gt; ]\n&gt;&gt;\n&gt;&gt;\n&gt;&gt;\nendobj\n4 0 obj\n&lt;&lt;\n/Type /Pages\n/Kids [ 5 0 R ]\n/Count 1\n/ProcSet [ /PDF /Text /ImageB /ImageC ]\n&gt;&gt;\nendobj\n5 0 obj\n&lt;&lt;\n/Type /Page\n/Parent 4 0 R\n/Contents 6 0 R\n/Resources 7 0 R\n/Annots 22 0 R\n/MediaBox [ 0 0 595 842 ]\n&gt;&gt;\nendobj\n6 0 obj\n&lt;&lt;\n/Filter /FlateDecode\n/Length 2705\n&gt;&gt;\nstream\nx\xc2\x9c\xc3\xad]K\xc2\x8f\xc3\xa4\xc2\xb8\r\xc2\xbe\xc3\x97\xc2\xaf\xc3\xb09@\xc2\xbb\xc2\xad\xc2\xb7\x0c\x04\x0bL\xc2\xbf\xc2\x82\xc3\xa4\x10`0\r\xc3\xac!\xc3\x88!\xc2\x98\xc3\x9dM\xc2\xb0\xc2\x98\xc3\x9ed\xc2\xb2\xc2\x87\xc3\xbc\xc3\xbdH\xc2\xb2\xc3\xbc\xc2\x92\xc3\xab\xc2\x93m\xc2\xb5\xc3\xad\xc2\xb2\xc2\xabk\x1a\xc2\x98z\xc2\xb0$Q\x14I\xc2\x91\x14)\xc3\x9f\xc3\xbf\xc3\xa9\xc3\x8b?\xc2\xb2\x7f\xc3\xbe\xc2\x9e\xc3\x9d?~\xc3\xb9O\xc3\xb6\xc3\x95\xc2\xbf&gt;~9\x15\xc2\xb9.\xc3\xbc\xc2\xbf\xc3\x8c\xc3\xbe\xc3\x9d\xc3\xb5\xc2\xbfP\xc2\x84\xc3\xa7\xc2\xaa\xc2\xb4\xc3\xbf\xc2\xb2\xc2\xafo\xc2\xa7\xc3\xaf\xc3\x99\xc3\xb7\xc3\x93\xc3\xa7\xc3\x93g\xc3\xb3\xc2\xbf}\xc3\xbd~\xc2\xaa;"

So it actually looks quite good and so promising ... :(

Is there a better approach in Odoo 13 now? I checked and all the info for Odoo 12 and so on seems outdated as models / reports / functions / xrpc calls ... all don't exist anymore...

Upvotes: 3

Views: 1864

Answers (3)

Aditya Irri
Aditya Irri

Reputation: 55

I've tried this in Odoo 17 (SAAS). Posting this answer so that someone can get help. Below is the code.

Step By Step explanation of how this code works Step 1: Import all required packages

from flask import Flask, request, jsonify,send_file
import xmlrpc.client
import requests

Step 2: Initialize your Odoo connection details like URL, Database name, Username and Password. (Replace these with your own Odoo connection details)

url = 'https://your_odoo_instance_url.com'
db = 'your_odoo_database'
username = 'your_odoo_username'
password = 'your_odoo_password'

Step 3: This Python code sends a login request to a web service using JSON-RPC, passing the database name, username, and password for authentication, and retrieves the session ID from the server's response cookies.

session_url = f'{url}web/session/authenticate'
data = {
    'jsonrpc': '2.0',
    'method': 'call',
    'params': {
        "service": "common",
        "method": "login",
        'db': db,
        'login': username,
        'password': password,
    }
}
session_response = requests.post(session_url, json=data)
session_data = session_response.json()
session_id = session_response.cookies['session_id']

Step 4: The below Python code establishes connections to an XML-RPC server for authentication and object manipulation, retrieving the user ID after authentication.

common = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url))
uid = common.authenticate(db, username, password, {})
models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))

Step 5: Initialize the Invoice ID which you want to download.

invoice_id = 21566

Step 6: In the below code invoice gets the name of the Invoice so that we can name the PDF file. pdf_file_url outputs the URL extension where you can download the PDF file.

invoice = models.execute_kw(db, uid, password, 'account.move', 'read', [invoice_id], {'fields': ['name']})

pdf_file_url = models.execute_kw(db, uid, password, 'account.move.send', 'action_send_and_print', [4,[invoice_id]])

Step 7: In the below code we call the URL to download the PDF file and for that we have to give Cookie in headers and for Odoo the cookie value should look solrthing like this session_id=xxxxxxxxxxxxxxxxxx and we already got our session ID from session_id above, so we are going to use that here. The pdf_content variable will have the binary value of the file.

headers = {'Cookie': f'session_id={session_id}'}

download_url = f'{url}{pdf_file_url["url"]}'
response = requests.get(download_url, headers=headers)

pdf_content = response.content

Step 8: Now save the file to your desired location (For me I'm saving in the same directory of the file)

filename = '{}.pdf'.format(invoice[0]['name'])
with open(filename, 'wb') as f:
    f.write(pdf_content)

Complete Code

# Import necessary modules from Flask framework and other libraries
from flask import Flask, request, jsonify, send_file
import xmlrpc.client
import requests

# Set up credentials and URL for Odoo instance
url = 'https://your_odoo_instance_url.com'
db = 'your_odoo_database'
username = 'your_odoo_username'
password = 'your_odoo_password'

# Authenticate and get session ID from Odoo
session_url = f'{url}web/session/authenticate'
data = {
    'jsonrpc': '2.0',
    'method': 'call',
    'params': {
        "service": "common",
        "method": "login",
        'db': db,
        'login': username,
        'password': password,
    }
}
session_response = requests.post(session_url, json=data)
session_data = session_response.json()
session_id = session_response.cookies['session_id']

# Set up XML-RPC connection to Odoo
common = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url))
uid = common.authenticate(db, username, password, {})
models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))

# Specify the ID of the invoice to retrieve
invoice_id = 21566

# Retrieve information about the specified invoice from Odoo
invoice = models.execute_kw(db, uid, password, 'account.move', 'read', [invoice_id], {'fields': ['name']})

# Trigger the action to send and print the invoice, and retrieve the URL of the generated PDF
pdf_file_url = models.execute_kw(db, uid, password, 'account.move.send', 'action_send_and_print', [4, [invoice_id]])

# Set up headers with the session ID for downloading the PDF
headers = {'Cookie': f'session_id={session_id}'}
download_url = f'{url}{pdf_file_url["url"]}'

# Download the PDF content of the invoice
response = requests.get(download_url, headers=headers)
pdf_content = response.content

# Specify the filename for the downloaded PDF based on the invoice name
filename = '{}.pdf'.format(invoice[0]['name'])

# Save the PDF content to a file
with open(filename, 'wb') as f:
    f.write(pdf_content)

Upvotes: 1

Nate
Nate

Reputation: 11

Thanks for your answer!

I have found the following solution which works with XML RPC:

Code in Odoo 16:

class InvoiceXMLRPC(models.Model):
    _inherit = "ir.actions.report"

    def render_qweb_pdf_inherit(self, report_ref, res_ids=None, data=None):
        pdf_content = self._render_qweb_pdf(report_ref, res_ids=res_ids, data=data)
        pdf_content_encoded = base64.b64encode(pdf_content[0]) # needs to be encoded to be able to access with xmlrpc
        return pdf_content_encoded

Code on the Raspberry Pi:

result = models.execute_kw(db, uid, password,
    'ir.actions.report', 'render_qweb_pdf_inherit',[REPORT_ID, REPORT_ID],{'res_ids': invoice_ids})

invoice_content = base64.b64decode(result)


with open('/home/pi/InvoicePrintO16/invoices.pdf', 'wb') as report_file:
        report_file.write(invoice_content)
    
os.system('lp invoices.pdf')

Upvotes: 1

chickahoona
chickahoona

Reputation: 2034

After discovering jsonrpc I was finally able to download the invoice ... Hope this helps someone in the absence of a solution with xmlrpc. (I am still looking for an xml rpc solution.)

import urllib.request
import json
import random

model_name = 'ir.actions.report'
method_name = 'render_qweb_pdf'
report_id = 282
invoice_id = 4
method = "call"
params = {
    "service": "object",
    "method": "execute",
    "args": [db, uid, password, model_name, method_name, report_id, invoice_id],
}
data = {
    "jsonrpc": "2.0",
    "method": method,
    "params": params,
    "id": random.randint(0, 1000000000),
}
req = urllib.request.Request(url=f"{self.url}/jsonrpc", data=json.dumps(data).encode(), headers={
    "Content-Type": "application/json",
})
reply = json.loads(urllib.request.urlopen(req).read().decode('UTF-8'))
if reply.get("error"):
    raise Exception(reply["error"])
return reply["result"]

Upvotes: 2

Related Questions