459 lines
20 KiB
Python
459 lines
20 KiB
Python
import json, uuid, requests, os
|
|
from odoo import models, fields, api
|
|
from odoo.exceptions import UserError
|
|
from . import datasource
|
|
from PyPDF2 import PdfFileWriter, PdfFileReader
|
|
|
|
FIELD_TYPES = [(key, key) for key in sorted(fields.Field.by_type)]
|
|
import tempfile
|
|
|
|
#################
|
|
dir = tempfile.gettempdir()
|
|
# dir='/opt/odoo'
|
|
login = 'test'
|
|
password = 'test'
|
|
|
|
|
|
##################
|
|
|
|
class BaseModelExtend(models.AbstractModel):
|
|
_name = 'base.model.extend'
|
|
_description = '继承基类模型'
|
|
|
|
def _register_hook(self):
|
|
|
|
def GetUrl(self):
|
|
self._cr.execute("select value from ir_config_parameter where key ='web.base.url'")
|
|
dicts = self._cr.dictfetchall()
|
|
return str(dicts[0]['value'])
|
|
|
|
def getOdooToken(self):
|
|
|
|
url = GetUrl(self) + "/web/session/authenticate"
|
|
# 传入需要访问的数据库名称,登录账号与密码
|
|
data = {
|
|
"params": {
|
|
"db": self._cr.dbname,
|
|
"login": login,
|
|
"password": password,
|
|
}
|
|
}
|
|
|
|
headers = {'Content-type': 'application/json'}
|
|
response = requests.post(url, headers=headers, json=data)
|
|
return response.cookies["session_id"]
|
|
|
|
def report_pdf_prints(self, args, types):
|
|
'''
|
|
PDF打印公共方法
|
|
:param self:
|
|
:return:
|
|
'''
|
|
# 模型数据ID
|
|
# 模型名称
|
|
report_id = args
|
|
print_type = 'pdf' if types == 0 else 'xlsx'
|
|
data_id = self._context.get('active_ids', None)
|
|
report_report = self.env['report.report'].browse(report_id)
|
|
data_model = self._context.get('active_model', None)
|
|
data_model_details = report_report.model_detail_id.model if report_report.report_type == 'bill' else ''
|
|
if data_model is None:
|
|
raise UserError('获取数据源失败')
|
|
res_id = self.env[data_model].read_group([('id', 'in', data_id)], [report_report.group_field],
|
|
[report_report.group_field])
|
|
headers = {
|
|
'Connection': 'keep-alive',
|
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
|
|
'Cookie': 'session_id=' + getOdooToken(self)
|
|
}
|
|
|
|
inFileList = []
|
|
for s_code in res_id:
|
|
s_id = []
|
|
wheres_id = self.env[data_model].search(
|
|
[('id', 'in', data_id), (report_report.group_field, '=', s_code[report_report.group_field])])
|
|
for wid in wheres_id:
|
|
s_id.append(wid.id)
|
|
dic = {}
|
|
dic['report'] = json.loads(report_report.report_data)
|
|
dic['data'] = datasource.wms_datasource.get_data(self, report_id, s_id, data_model, data_model_details,
|
|
report_report.model_main_key)
|
|
dic['outputFormat'] = print_type
|
|
dic['isTestData'] = True
|
|
res = requests.post(self.GetUrl() + '/api/report/report_server/run',
|
|
data={'data': str(json.dumps(dic))}, headers=headers, allow_redirects=True)
|
|
print(res.text)
|
|
key = str(res.text.split(':')[1])
|
|
url = self.GetUrl() + '/api/report/report_server/run?key=' + key + '&outputFormat=' + print_type
|
|
r = requests.get(url, stream=True, cookies={'session_id': getOdooToken(self)})
|
|
pdf = "/%s.pdf" % key
|
|
# 获取temp临时文件路径
|
|
|
|
pdf = dir + pdf
|
|
inFileList.append(pdf)
|
|
with open(pdf, "wb") as pdf:
|
|
for chunk in r.iter_content(chunk_size=1024):
|
|
if chunk:
|
|
pdf.write(chunk)
|
|
filename = dir + "/print.pdf"
|
|
pdfFileWriter = PdfFileWriter()
|
|
|
|
for inFile in inFileList:
|
|
with open(inFile, 'rb') as f:
|
|
pdfReader = PdfFileReader(f)
|
|
numPages = pdfReader.getNumPages()
|
|
for index in range(0, numPages):
|
|
pageObj = pdfReader.getPage(index)
|
|
pdfFileWriter.addPage(pageObj)
|
|
|
|
pdfFileWriter.write(open(filename, 'wb'))
|
|
os.unlink(inFile)
|
|
with open(filename, 'rb') as pdf_document:
|
|
pdf_content = pdf_document.read()
|
|
os.unlink(filename)
|
|
key_uid = str(uuid.uuid4())
|
|
self.env['wms.print.list'].create({'p_key': key_uid, 'report_file_pdf': pdf_content})
|
|
if print_type == 'pdf':
|
|
# 打开新页面
|
|
# return {
|
|
# 'type': 'ir.actions.act_url',
|
|
# 'title':'打印预览',
|
|
# 'name': '打印预览',
|
|
# 'url': self.GetUrl() + '/api/report/report_server/run?key=' +key_uid+'&outputFormat=' + print_type + '&report_id='+str(args),
|
|
# 'target': 'new'
|
|
#
|
|
# }
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': '打印预览',
|
|
'res_model': 'wms.print.list',
|
|
'views': [[False, "form"]],
|
|
'target': 'new',
|
|
'context': {
|
|
'default_name': '<iframe src="' + self.GetUrl() + '/api/report/report_server/run?key=' + key_uid + '&outputFormat=' + print_type + '&report_id=' + str(
|
|
args) + '" marginheight="0" marginwidth="0" width="100%" height="700px" />'}
|
|
|
|
}
|
|
|
|
models.BaseModel.GetUrl = GetUrl
|
|
models.BaseModel.report_pdf_prints = report_pdf_prints
|
|
|
|
return super(BaseModelExtend, self)._register_hook()
|
|
|
|
|
|
class wmsprintlist(models.Model):
|
|
_name = 'wms.print.list'
|
|
_description = '打印服务表'
|
|
|
|
p_key = fields.Char('内容键')
|
|
report_file_pdf = fields.Binary('PDF', attachment=False)
|
|
report_file_xlsx = fields.Binary('XLSX', attachment=False)
|
|
print_status = fields.Boolean('打印状态', default=False)
|
|
|
|
name = fields.Html('预览', store=False, sanitize=False)
|
|
|
|
|
|
class ReportReport(models.Model):
|
|
_name = "report.report"
|
|
_description = "Report"
|
|
|
|
name = fields.Char(string='报表名称', required=True)
|
|
model_id = fields.Many2one('ir.model', string='主表模型', ondelete="cascade", required=True,
|
|
help='至少要存在一个数据模型')
|
|
model_detail_id = fields.Many2one('ir.model', string='明细表模型', ondelete="cascade")
|
|
report = fields.Text(string='报表')
|
|
model_main_key = fields.Char('主模型Key', help='主数据模型和明细模型的关联字段名称', default='order_id')
|
|
model_ids = fields.One2many('report.report.main', 'report_main_id', string='主表字段')
|
|
model_detail_ids = fields.One2many('report.report.detail', 'report_detail_id', string='明细字段')
|
|
display_complete = fields.Boolean(string='动作添加', default=False)
|
|
display_completes = fields.Boolean(string='批量动作添加', default=False)
|
|
reset_designer = fields.Boolean(string='重新设计', default=False, help='勾选上会进入空白模板,不再读取存储的配置')
|
|
report_data = fields.Text('模板数据')
|
|
data_source = fields.Char(string='数据源')
|
|
test_data = fields.Text('数据结构')
|
|
pdf_name = fields.Text('PDF名称')
|
|
report_type = fields.Selection(selection=[('bill', '单据'), ('report', '报表')], string='报表类型',
|
|
help='一般带主表,明细表模型的为单据类报表,单个模型的为纯报表类型')
|
|
group_field = fields.Char(string='套打字段', help='用于批量打印时,区分每页的关键字段')
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
res = super(ReportReport, self).default_get(fields)
|
|
res['data_source'] = 'a' + ''.join(str(uuid.uuid4()).split('-'))
|
|
return res
|
|
|
|
def add_print_actions(self):
|
|
self.del_print_actions()
|
|
res = self.env['report.report'].search([('id', '=', self.id)])
|
|
if res.group_field is False:
|
|
raise UserError('汇总打印字段不能为空')
|
|
model = self.env['ir.model'].search([('model', '=', 'report.report')])
|
|
res.write({'display_completes': True})
|
|
act_type = 'list,form' if res.report_type == 'bill' else 'list'
|
|
sql_pdf = "insert into ir_act_server (name,type,binding_model_id,binding_type,binding_view_types,usage,state,sequence,model_id,model_name,code,activity_date_deadline_range_type,activity_user_type) values ('" + json.dumps(
|
|
{'zh_CN': self.name}) + "','ir.actions.server', " + str(
|
|
res.model_id.id) + ",'action', '" + act_type + "','ir_actions_server','code',5, " + str(
|
|
model.id) + ",'report.report','action = model.report_pdf_prints(" + str(
|
|
self.id) + ",0)','days','specific');"
|
|
|
|
self._cr.execute(sql_pdf)
|
|
|
|
def del_print_actions(self):
|
|
res = self.env['report.report'].search([('id', '=', self.id)])
|
|
res.write({'display_completes': False})
|
|
|
|
self._cr.execute('delete from ir_act_server where name=%s', (json.dumps({'zh_CN': self.name}),))
|
|
|
|
def button_designer(self):
|
|
res = self.env['report.report'].search([('id', '=', self.id)])
|
|
# 重新设置模板参数
|
|
if res.reset_designer:
|
|
report_main = self.env['report.report.main'].search(
|
|
[('report_main_id', '=', self.id), ('is_report', '=', True)])
|
|
report_detail = self.env['report.report.detail'].search(
|
|
[('report_detail_id', '=', self.id), ('is_report', '=', True)])
|
|
|
|
res_main = []
|
|
res_detail = []
|
|
for r in report_main:
|
|
res_main.append(r.field_id.id)
|
|
for r2 in report_detail:
|
|
res_detail.append(r2.field_id.id)
|
|
|
|
# report_main_names = self.env['ir.model.fields'].search([('id', 'in', res_main)])
|
|
# report_detail_names = self.env['ir.model.fields'].search([('id', 'in', res_detail)])
|
|
|
|
parameters = [{
|
|
'id': 1, 'name': 'page_count', 'type': 'number', 'arrayItemType': 'string', 'eval': False,
|
|
'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': True,
|
|
'testData': ''
|
|
},
|
|
{
|
|
'id': 2, 'name': 'page_number', 'type': 'number', 'arrayItemType': 'string', 'eval': False,
|
|
'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': True,
|
|
'testData': ''
|
|
}]
|
|
data = {}
|
|
index = 3
|
|
|
|
# 主表
|
|
for rmn in report_main:
|
|
data[rmn.field_params] = ''
|
|
parameters.append({
|
|
'id': index,
|
|
'name': rmn.field_params,
|
|
'type': self.get_type(rmn.ttype),
|
|
'arrayItemType': self.get_type(rmn.ttype),
|
|
'eval': False,
|
|
'nullable': False,
|
|
'pattern': '',
|
|
'expression': '',
|
|
'showOnlyNameType': False,
|
|
'testData': ''
|
|
})
|
|
index += 1
|
|
|
|
# 序号
|
|
children = [{
|
|
'id': 888,
|
|
'name': 'index',
|
|
'type': 'string',
|
|
'arrayItemType': 'string',
|
|
'eval': False,
|
|
'nullable': False,
|
|
'pattern': '',
|
|
'expression': '',
|
|
'showOnlyNameType': False,
|
|
'testData': ''}]
|
|
test_data2 = []
|
|
# 明细表
|
|
r = {}
|
|
for rmn in report_detail:
|
|
r[rmn.field_params] = ''
|
|
children.append({
|
|
'id': index,
|
|
'name': rmn.field_params,
|
|
'type': self.get_type(rmn.ttype),
|
|
'arrayItemType': self.get_type(rmn.ttype),
|
|
'eval': False,
|
|
'nullable': False,
|
|
'pattern': '',
|
|
'expression': '',
|
|
'showOnlyNameType': False,
|
|
'testData': ''})
|
|
|
|
index += 1
|
|
test_data2.append(r)
|
|
parameters.append({
|
|
'id': 9999,
|
|
'name': 'res_detail',
|
|
'type': 'array',
|
|
'arrayItemType': 'array',
|
|
'eval': False,
|
|
'nullable': False,
|
|
'pattern': '',
|
|
'expression': '',
|
|
'showOnlyNameType': False,
|
|
'testData': '',
|
|
'children': children
|
|
})
|
|
data['res_detail'] = test_data2
|
|
r_data = res.report_data
|
|
if res.report_data is False:
|
|
r_data = str(json.dumps({'docElements': [], 'parameters': [
|
|
{
|
|
'id': 1, 'name': 'page_count', 'type': 'number', 'arrayItemType': 'string', 'eval': False,
|
|
'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': True,
|
|
'testData': ''
|
|
},
|
|
{
|
|
'id': 2, 'name': 'page_number', 'type': 'number', 'arrayItemType': 'string', 'eval': False,
|
|
'nullable': False, 'pattern': '', 'expression': '', 'showOnlyNameType': True,
|
|
'testData': ''
|
|
}],
|
|
'styles': [], 'version': 3,
|
|
'documentProperties': {
|
|
'pageFormat': 'A4', 'pageWidth': '', 'pageHeight': '',
|
|
'unit': 'mm', 'orientation': 'portrait', 'contentHeight': '',
|
|
'marginLeft': '', 'marginTop': '', 'marginRight': '',
|
|
'marginBottom': '', 'header': True, 'headerSize': '80',
|
|
'headerDisplay': 'always', 'footer': True, 'footerSize': '80',
|
|
'footerDisplay': 'always', 'patternLocale': 'en',
|
|
'patternCurrencySymbol': '$'}}))
|
|
report_data = json.loads(r_data)
|
|
report_data['parameters'] = parameters
|
|
res.write({'report_data': json.dumps(report_data), 'test_data': data, 'reset_designer': False})
|
|
|
|
self.env['report.designer'].sudo().create({'name': str(self.id)})
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'name': '报表设计',
|
|
'tag': 'report_designer',
|
|
'target': 'current',
|
|
'params': {
|
|
'report_id': self.id
|
|
}
|
|
}
|
|
|
|
def get_type(self, type_name):
|
|
res = {'char': 'string',
|
|
'text': 'string',
|
|
'one2many': 'string',
|
|
'many2one': 'string',
|
|
'many2many': 'string',
|
|
'integer': 'number',
|
|
'float': 'number',
|
|
'datetime': 'string',
|
|
'date': 'string',
|
|
'boolean': 'boolean',
|
|
'selection': 'string',
|
|
'monetary': 'number',
|
|
'binary': 'string',
|
|
'html': 'string',
|
|
'reference': 'string',
|
|
'image': 'image',
|
|
'image_url': 'string',
|
|
'json': 'string'
|
|
|
|
}
|
|
|
|
return res[type_name]
|
|
|
|
|
|
class ReportReportMain(models.Model):
|
|
_name = "report.report.main"
|
|
_description = "Report Main"
|
|
|
|
report_main_id = fields.Many2one('report.report', string='Report Main')
|
|
model_id = fields.Many2one('ir.model', string='Model', ondelete="cascade")
|
|
field_domain = fields.Many2many('ir.model.fields', string='Field Domain', compute='_compute_field_domain')
|
|
field_id = fields.Many2one('ir.model.fields', string='字段名称', ondelete="cascade", required=True)
|
|
field_description = fields.Char(string='字段', related='field_id.name')
|
|
field_params = fields.Char(string='参数')
|
|
field_value = fields.Char(string='字段取值', compute='_field_custom', store=True, inverse='_field_custom_inverse')
|
|
ttype = fields.Selection(selection=FIELD_TYPES, string='类型', related='field_id.ttype')
|
|
is_report = fields.Boolean(string='报表', default=True)
|
|
|
|
@api.depends('field_id')
|
|
def _field_custom(self):
|
|
for s in self:
|
|
s.field_params = s.field_description
|
|
if s.ttype in ['text', 'char', 'html']:
|
|
s.field_value = "str(s." + str(s.field_id.name) + ") or ''"
|
|
elif s.ttype in ['date', 'datetime', 'boolean']:
|
|
s.field_value = "str(s." + str(s.field_id.name) + ") or ''"
|
|
elif s.ttype in ['integer', 'float', 'monetary']:
|
|
s.field_value = "s." + str(s.field_id.name) + " or 0"
|
|
elif s.ttype in ['one2many', 'many2one', 'many2many']:
|
|
s.field_value = "str(s." + str(s.field_id.name) + ".name) or ''"
|
|
elif s.ttype == 'selection':
|
|
s.field_value = "str(dict(self.env['" + str(s.model_id.model) + "'].fields_get(allfields=['" + str(
|
|
s.field_id.name) + "'])['" + str(s.field_id.name) + "']['selection'])[s." + str(
|
|
s.field_id.name) + "]) or ''"
|
|
else:
|
|
s.field_value = "s." + str(s.field_id.name)
|
|
|
|
def _field_custom_inverse(self):
|
|
pass
|
|
|
|
@api.depends('model_id')
|
|
def _compute_field_domain(self):
|
|
for record in self:
|
|
fields = []
|
|
record.field_domain = self._get_fields(fields=fields, model=record.model_id)
|
|
|
|
def _get_fields(self, fields=None, model=None):
|
|
for field in model.field_id:
|
|
if field.ttype != 'one2many':
|
|
fields.append(field.id)
|
|
return fields
|
|
|
|
|
|
class ReportReportDetail(models.Model):
|
|
_name = "report.report.detail"
|
|
_description = "Report detail"
|
|
|
|
report_detail_id = fields.Many2one('report.report', string='Report detail')
|
|
model_id = fields.Many2one('ir.model', string='Model', ondelete="cascade")
|
|
field_domain = fields.Many2many('ir.model.fields', string='Field Domain', compute='_compute_field_domain')
|
|
field_id = fields.Many2one('ir.model.fields', string='字段名称', ondelete="cascade", required=True)
|
|
field_description = fields.Char(string='字段', related='field_id.name')
|
|
field_params = fields.Char(string='参数')
|
|
field_value = fields.Char(string='字段取值', compute='_field_custom', store=True, inverse='_field_custom_inverse')
|
|
ttype = fields.Selection(selection=FIELD_TYPES, string='类型', related='field_id.ttype')
|
|
is_report = fields.Boolean(string='报表', default=True)
|
|
|
|
@api.depends('field_id')
|
|
def _field_custom(self):
|
|
for s in self:
|
|
s.field_params = s.field_description
|
|
if s.ttype in ['text', 'char', 'html']:
|
|
s.field_value = "str(s." + str(s.field_id.name) + ") or ''"
|
|
elif s.ttype in ['date', 'datetime', 'boolean']:
|
|
s.field_value = "str(s." + str(s.field_id.name) + ") or ''"
|
|
elif s.ttype in ['integer', 'float', 'monetary']:
|
|
s.field_value = "s." + str(s.field_id.name) + " or 0"
|
|
elif s.ttype in ['one2many', 'many2one', 'many2many']:
|
|
s.field_value = "str(s." + str(s.field_id.name) + ".name) or ''"
|
|
elif s.ttype == 'selection':
|
|
s.field_value = "str(dict(self.env['" + str(s.model_id.model) + "'].fields_get(allfields=['" + str(
|
|
s.field_id.name) + "'])['" + str(s.field_id.name) + "']['selection'])[s." + str(
|
|
s.field_id.name) + "]) or ''"
|
|
else:
|
|
s.field_value = "s." + str(s.field_id.name)
|
|
|
|
def _field_custom_inverse(self):
|
|
pass
|
|
|
|
@api.depends('model_id')
|
|
def _compute_field_domain(self):
|
|
for record in self:
|
|
fields = []
|
|
record.field_domain = self._get_fields(fields=fields, model=record.model_id)
|
|
|
|
def _get_fields(self, fields=None, model=None):
|
|
for field in model.field_id:
|
|
if field.ttype != 'one2many':
|
|
fields.append(field.id)
|
|
return fields
|