|
-
-
-
- import random
- from docxtpl import DocxTemplate
- from datetime import datetime, timedelta
- import pytz
- import tempfile
- import os
- import subprocess
- import re
- import platform
- from odoo import models
- from odoo import fields
- from odoo.exceptions import UserError
- from odoo.tools import misc
-
-
-
-
-
-
-
-
-
-
-
-
- class DataModelProxy(object):
- '''使用一个代理类,来转发 model 的属性,用来消除掉属性值为 False 的情况
- 且支持 selection 字段取到实际的显示值
- '''
- DEFAULT_TZ = 'Asia/Shanghai'
-
- def __init__(self, data):
- self.data = data
-
- def _compute_by_selection(self, field, temp):
- if field and field.type == 'selection':
-
- selection = field._description_selection(self.data.env)
-
- try:
- return [value for _, value in selection if _ == temp][0]
- except KeyError:
- temp = ''
-
- return temp
-
- def _compute_by_datetime(self, field, temp):
- if field and field.type == 'datetime' and temp:
- tz = pytz.timezone(
- self.data.env.context.get('tz') or self.DEFAULT_TZ)
- temp_date = fields.Datetime.from_string(temp) + tz._utcoffset
- temp = fields.Datetime.to_string(temp_date)
-
- return temp
-
- def _compute_temp_false(self, field, temp):
- if not temp:
- if field and field.type in ('integer', 'float'):
- return 0
- if field.type == 'float' and int(temp) == temp:
- temp = int(temp)
-
- return temp or ''
-
- def __getattr__(self, key):
- if not self.data:
- return ""
-
-
- if isinstance(self.data, dict):
- value = self.data.get(key, '')
- if isinstance(value, (dict, models.Model, models.TransientModel)):
- value = DataModelProxy(value)
- return value
-
- temp = getattr(self.data, key)
- field = self.data._fields.get(key)
- if isinstance(temp, str) and (
- '&' in temp or '<' in temp or '>' in temp):
- temp = temp.replace('&', '&').replace(
- '<', '<').replace('>', '>')
-
-
- if isinstance(temp, (models.Model, models.TransientModel)):
- return DataModelProxy(temp)
-
-
- if callable(temp):
- return temp
-
- temp = self._compute_by_selection(field, temp)
- temp = self._compute_by_datetime(field, temp)
-
- return self._compute_temp_false(field, temp)
-
- def __getitem__(self, index):
- '''支持列表取值'''
- if isinstance(self.data, dict):
- return DataModelProxy(dict([list(self.data.items())[index]]))
- return DataModelProxy(self.data[index])
-
- def __iter__(self):
- '''支持迭代器行为'''
- return IterDataModelProxy(self.data)
-
- def __len__(self):
- '''支持返回长度'''
- if isinstance(self.data, dict):
- return len(self.data.items())
- return len(self.data)
-
- def __str__(self):
- '''支持直接在word 上写 many2one 字段'''
- name = ''
- if isinstance(self.data, dict):
- val = list(self.data.values())
- if len(val) > 0:
- name = str(val[0])
- return name
-
- if self.data and self.data.display_name:
- name = self.data.display_name
- if '&' in self.data.display_name:
- name = name.replace('&', '&')
- if '<' in self.data.display_name:
- name = name.replace('<', '<')
- if '>' in self.data.display_name:
- name = name.replace('>', '>')
- return name
-
-
- class IterDataModelProxy(object):
- '''迭代器类,用 next 函数支持 for in 操作'''
-
- def __init__(self, data):
- self.data = data
- if isinstance(self.data, dict):
- self.length = len(list(data.items()))
- else:
- self.length = len(data)
- self.current = 0
-
- def __next__(self):
- if self.current >= self.length:
- raise StopIteration()
-
- if isinstance(self.data, dict):
- temp = DataModelProxy(
- dict([list(self.data.items())[self.current]]))
- else:
- temp = DataModelProxy(self.data[self.current])
- self.current += 1
-
- return temp
-
-
- class ReportDocx(models.TransientModel):
-
- _name = 'gooderp.report.docx'
- _description = 'docx report'
-
- ir_actions_report_id = fields.Many2one(
- comodel_name="ir.actions.report",
- required=True
- )
-
- def generate_temp_file(self, tempname, suffix='docx'):
- return os.path.join(tempname, 'temp_%s_%s.%s' %
- (os.getpid(), random.randint(1, 10000), suffix))
-
- def create_report(self, res_ids, data):
-
-
-
-
- report_data = DataModelProxy(self.get_docx_data(
- self.ir_actions_report_id, res_ids, data))
-
- tempname = tempfile.mkdtemp()
- temp_out_file = self.generate_temp_file(tempname)
-
- doc = DocxTemplate(misc.file_open(
- self.ir_actions_report_id.template_file).name)
-
-
- from . import report_helper
-
- doc.render({'obj': report_data, 'tpl': doc}, report_helper.get_env())
- doc.save(temp_out_file)
-
- if self.ir_actions_report_id.output_type == 'pdf':
- temp_file = self.render_to_pdf(temp_out_file)
- else:
- temp_file = temp_out_file
-
- report_stream = ''
- with open(temp_file, 'rb') as input_stream:
- report_stream = input_stream.read()
- os.remove(temp_file)
- return report_stream, self.ir_actions_report_id.output_type
-
- def render_to_pdf(self, temp_file):
- folder = tempfile.mkdtemp()
- args = ['libreoffice', '--headless', '--convert-to',
- 'pdf', '--outdir', folder, temp_file]
-
-
-
-
-
-
-
-
- if platform.system() == "Windows":
- args = ["soffice",'--headless', '--convert-to',
- 'pdf', '--outdir', folder, temp_file]
- process = subprocess.run(
- args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- filename = re.search('-> (.*?) using filter', process.stdout.decode())
-
- if filename is None:
- raise UserError(process.stdout.decode())
- else:
- return filename.group(1)
-
- def get_docx_data(self, report, res_ids, data=None):
-
-
-
-
- try:
- message = str((datetime.now() + timedelta(hours=8)).strftime(
- '%Y-%m-%d %H:%M:%S')) + ' ' + self.env.user.name + u' 打印了该单据'
- for record in self.env.get(report.model).browse(res_ids):
- record.message_post(body=message)
- except Exception as e:
- pass
-
- res = self.env.get(report.model).sudo().browse(res_ids)
- if len(res) == 0:
- data = data and dict(data) or {}
- report_func = data.get('report_function', 'get_report_data')
- func = getattr(self.env.get(report.model), report_func)
- if callable(func):
- res = func(data=data)
- return res
-
- def _save_file(self, folder_name, file):
- out_stream = open(folder_name, 'wb')
- try:
- out_stream.writelines(file)
- finally:
- out_stream.close()
|