如何通过 Python 中的 xml rpc 从 Odoo (v13) 下载发票?

问题描述 投票:0回答:4

我目前很难使用 xml rpc 从 Odoo 13 下载 PDF 格式的发票。

我能得到的最接近的是:

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)

但我总是遇到这个错误:

  ...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

它试图在这

self._parser.Parse(data, 0)
行中解析的数据是

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;"

所以它实际上看起来相当不错,很有前途......:(

Odoo 13 现在有更好的方法吗?我检查了一下,Odoo 12 等的所有信息似乎都已过时,因为模型/报告/函数/xrpc 调用...全部都不存在了...

odoo odoo-13
4个回答
2
投票

发现 jsonrpc 后,我终于能够下载发票了...希望这对没有 xmlrpc 解决方案的人有所帮助。 (我仍在寻找 xml rpc 解决方案。)

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"]

1
投票

谢谢您的回答!

我找到了以下适用于 XML RPC 的解决方案:

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

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')

0
投票

感谢您的回复。我还尝试使用外部 API 从 Odoo 16 获取 PDF 报告。在

XMLRPC
OdooRPC
失败后,我尝试了你的方法。我能够获取 PDF 内容,但当我尝试打开 PDF 时,它只是一个白页。该文件的大小为
444 kb
,并且包含如下数据:

Type /XObject
/Subtype /Image
/Width 2962
/Height 886
/BitsPerComponent 8
/ColorSpace /DeviceRGB
/Length 7 0 R
/Filter /DCTDecode
>>
stream
ÿØÿàJFIFÿÛCÿÛCÿv
                ’ÿÄ

        ÿÄg
          
!"1?AQX—#Ö×?2BVa‘–˜ð3Wq¡±ÔÕ$4RT•ÁÑ%6CYb”¨Øáñ&'DU57GrFHS(9Ec‚†’¢¥ÿÄ?ÿÄ,
1QÑ!2Aq‘¡±ðaÁáñ"RÿÚ
                  ?ŸÀÿêþ·ýh^\È#¹.|¨Ð¢²žoI–ûq£´ún<òÛiûÍj"/¿ÐPÖµ§«lÖWxÃ4o~éV²…J6øÿ™N®°iáëË‘—·qËc?ë¬'ëïBt¥­ºžµ4•Nq¾<Ñ?QØr?ÑÈöO4?y6iä~‰å·#ô-‚ØÆ:Àãr:–ô䆮µÿ¢hÊ%)¾2uW‚™WqÂA“·âOšŒŸá'í ¶1Ž°=ˆýHºwLÛÊkÛE²y#ºŸ/ª\÷&ωw
                                                                                                                                                                                      µ}(Í?~"ø~$|[¨‚ØÆ:ÀæTmnè¾ãáìö®ôÃ]¿¡³î)ªþF#vHäG·Í;ì_p[?ÇXÕld¬s{™Ù7q(¹$틪…_#OÞ_EO–[>ûïè:9©ü¾ïö—õ°·Üöûyã¿ü?óÛó3þŒ¿§×þÿM¶ùŸ§Þ@?¿ý%þ¯ôüÿœ¾ð@|ºÕb›nÑê×jctú5
:qí9Ö$èÙøÓ¢~Nê+öB?tãÚr¬Iѳñ§DüÔWì„4éÇ´äX“£gãN‰ù;¨¯iÓiÈ>±'FÏÆòwQ_²Ó§Ó}bNŸ:'äd!§N=§ úÄ?tOÉÝE~ÈCNœ{NCaÚQÖ>›µÁŽ*ywKy-Œ«Ž¨÷…JÁ¨\±­‹ÖÔe‹¶H ×jt”À¾­Ë^­'ÊÒîj•O‹Oz˜µM8¬Ír\Y¬GÔMºàd؇q\ÖåŸFqÝ×Ö·©ŒœŠ•vã«@¡Ñ©ÌÍùÕJœˆ°¢2_k’i~›ŸÌ-ˆ¾l?˜Ï]zúOéíÙ´û—W¶-÷_‡Í    a?òîDn¿)uÛ&”)
BÑWºi­¶âTÓŽ%Ý3§N=§!§Óã

您知道可能出现什么问题吗?仅供参考:方法

render_qweb_pdf
在 Odoo 16 中更改为私有方法
_render_qweb_pdf
,我需要更改它以便能够通过外部 API 调用它。

代码:

import json
import random
import urllib.request
import config
import sys
import os

HOST = config.url
DB = config.db
USER = config.username
PASS = config.password

def json_rpc(url, method, params):
    data = {"jsonrpc": "2.0", 
           "method": method,
           "params": params,
           "id": random.randint(0, 1000000000),
            }
    req = urllib.request.Request(url=url, 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"]

def call(url, service, method, *args):
    return json_rpc(url, "call", {"service": service, "method": method, "args": args})

url = "http://%s/jsonrpc" % (HOST)
uid = call(url, "common", "login", DB, USER, PASS)

model_name = 'ir.actions.report'
method_name = 'render_qweb_pdf'
report_id = 784
invoice_id = [1827]
method = "call"
params = {
    "service": "object",
    "method": "execute",
    "args": [DB, uid, PASS, model_name, method_name, report_id, report_id, invoice_id],
}
data = {
    "jsonrpc": "2.0",
    "method": method,
    "params": params,
    "id": random.randint(0, 1000000000),
}
req = urllib.request.Request(url="URL", data=json.dumps(data).encode(), headers={
    "Content-Type": "application/json",
})

reply = json.loads(urllib.request.urlopen(req).read().decode('UTF-8'))

#print(reply)

if reply.get("error"):
    raise Exception(reply["error"])

#print(reply["result"])

report_data = (reply['result'])[0]

#print(report_data)

#print(sys.getsizeof(reply))

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

0
投票

我在 Odoo 17 中尝试过此操作。发布此答案以便有人可以获得帮助。 下面是代码。

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

# 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'

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']

common = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url))
uid = common.authenticate(db, username, password, {})
# Create a new XML-RPC client object
models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))
invoice_id = 21566
# Download the PDF file for the first invoice in the list
invoice = models.execute_kw(db, uid, password, 'account.move', 'read', [invoice_id], {'fields': ['name']})

pdf_file = models.execute_kw(db, uid, password, 'account.move.send', 'action_send_and_print', [4,[invoice_id]])
# Call the URL and send authorization
# Set the cookie value in the request headers
headers = {'Cookie': f'session_id={session_id}'}

# Call the URL with the specified cookie value
download_url = f'{url}{pdf_file["url"]}'
response = requests.get(download_url, headers=headers)

pdf_content = response.content
# Save the PDF file to disk
filename = '{}.pdf'.format(invoice[0]['name'])
with open(filename, 'wb') as f:
    f.write(pdf_content)
© www.soinside.com 2019 - 2024. All rights reserved.