# -*- coding: utf-8 -*- import logging from odoo import api, fields, models, tools, exceptions import datetime from dateutil.relativedelta import relativedelta from werkzeug import urls import functools import base64 _logger = logging.getLogger(__name__) try: from jinja2.sandbox import SandboxedEnvironment mako_template_env = SandboxedEnvironment( block_start_string="<%", block_end_string="%>", variable_start_string="${", variable_end_string="}", comment_start_string="<%doc>", comment_end_string="", line_statement_prefix="%", line_comment_prefix="##", trim_blocks=True, # do not output newline after blocks autoescape=True, # XML/HTML automatic escaping ) mako_template_env.globals.update({ 'str': str, 'quote': urls.url_quote, 'urlencode': urls.url_encode, 'datetime': datetime, 'len': len, 'abs': abs, 'min': min, 'max': max, 'sum': sum, 'filter': filter, 'reduce': functools.reduce, 'map': map, 'round': round, 'relativedelta': lambda *a, **kw: relativedelta(*a, **kw), }) except ImportError: _logger.warning("jinja2 not available, templating features will not work!") class DmsDocumentChoiseTemplate(models.TransientModel): _name = 'dms.choise_template' _description = 'mklab_dms_document_choise_template' temp_id = fields.Many2one(comodel_name='dms.template', string='Шаблон', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]") doc_id = fields.Many2one(comodel_name='dms.document', string='Документ') company_id = fields.Many2one(comodel_name='res.company', string='Компания', required=True, index=True, default=lambda self: self.env.company) def get_choise(self): for s in self: if s.temp_id: s.doc_id.text = s.temp_id.text class DmsDocumentTemplate(models.Model): _name = 'dms.template' _description = 'mklab_dms_document_template' name = fields.Char(string='Номер', required='1', tracking=True) # Номер — для исходящих с использованием нумератора, для входящих вносится вручную text = fields.Html(string='Текст', required='1', tracking=True) # Текст — hmtl company_id = fields.Many2one(comodel_name='res.company', string='Компания', required=True, index=True, default=lambda self: self.env.company, tracking=True) class DmsDocument(models.Model): _name = 'dms.document' _description = 'Документы' _inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin', 'utm.mixin'] _description = 'mklab_dms_document' def create_pdf(self): report_action = self.env.ref('mklab_dms_document.action_report_dms_document').sudo() for s in self: if s.type_document=='outgoing': pdf, _ = report_action._render_qweb_pdf(report_action.report_name, [s.id]) s.incoming_file = base64.b64encode(pdf) s.incoming_file_type = 'pdf' if not s.incoming_file and s.incoming_file_type == 'pdf': raise exceptions.UserError('Не указан файл для сохранения') if not s.incoming_file_other and s.incoming_file_type != 'pdf': raise exceptions.UserError('Не указан файл для сохранения') if not s.directory_id: raise exceptions.UserError('Не указана директория для сохранения файла') content = s.incoming_file if s.incoming_file_type == 'pdf' else s.incoming_file_other save_file = self.env['dms.file'].sudo().create({ 'name': s.name, 'directory_id': s.directory_id.id, 'content': content, }) s.file = save_file s.state = 'done' def open_choise_template(self): company_id = False if self.company_id: company_id = self.company_id.id return { 'type': 'ir.actions.act_window', 'name': "Выберите шаблон", 'res_model': 'dms.choise_template', 'target': 'new', 'view_mode': 'form', 'context': {'default_doc_id': self.id, 'company_id': company_id}, } def unlink(self): for s in self: if s.state=='done': raise exceptions.ValidationError('Нельзя удалить документ, записанный в хранилище') res = super().unlink() return res def get_number(self): type_document = str(self.env.context.get('default_type_document')) if self.type_document: type_document = self.type_document if type_document == 'incoming': self.name = self.env['ir.sequence'].next_by_code('dms.document_number') return self.env['ir.sequence'].next_by_code('dms.document_number') elif type_document == 'outgoing': self.name = self.env['ir.sequence'].next_by_code('dms.document_number_out') return self.env['ir.sequence'].next_by_code('dms.document_number_out') elif type_document == 'internal': self.name = self.env['ir.sequence'].next_by_code('dms.document_number_internal') return self.env['ir.sequence'].next_by_code('dms.document_number_internal') type_document = fields.Selection([('incoming','Входящий документ'),('outgoing','Исходящий документ'),('internal','Внутренний документ')], string='Тип', required="1", tracking=True) # Тип — список выбора (входящее, исходящее, внутреннее) name = fields.Char(string='Номер', default=lambda r: r.get_number(), tracking=True) # Номер — для исходящих с использованием нумератора, для входящих вносится вручную partner_id = fields.Many2one(comodel_name='res.partner', string='Контакт', required="1", domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True) # Контакт — от или для кого date = fields.Date(string='Дата документа', default=lambda r: datetime.datetime.today(), required="1", tracking=True) # Дата документа text = fields.Html(string='Текст') # Текст — hmtl incoming_file = fields.Binary(string='Входящий файл (PDF)') incoming_file_other = fields.Binary(string='Входящий файл') incoming_file_type = fields.Selection([('pdf','PDF'),('other','Другой')], string='Тип входящего файла', default='pdf',tracking=True) text_str = fields.Text(string='Текст') # Текст — hmtl text_render = fields.Html(string='Текст render') # Текст — hmtl file = fields.Many2one(comodel_name='dms.file', string='Связанный файл', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True) # Связанный dms.file link_model = fields.Many2one(comodel_name='ir.model', string='Связанная модель', tracking=True) # Связанная модель (не обязательно) res_id = fields.Integer(string='ID документа в связанной модели', tracking=True) # ID документа в связанной модели (res_id, не обязательно) # Эти поля потребуются только для заполнения переменных внутри текста print_head = fields.Boolean(string='Печатать шапку', default=False, tracking=True) # Признак печати шапки - булево state = fields.Selection([('draft','Черновик'),('done','Записано в хранилище')], string='Статус', default='draft', tracking=True) # Статус — черновик, записано в хранилище parent_id = fields.Many2one(comodel_name='dms.document', string='Ссылка на родительский документ', domain="[('id', '!=', id),'|',('partner_id', '=', partner_id),('partner_id.parent_id', '=', partner_id),'|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True) #Ссылка на другой (родительский) документ child_ids = fields.One2many(comodel_name='dms.document', inverse_name='parent_id', string='Direct subordinates') subordinate_ids = fields.One2many(comodel_name='dms.document', string='Subordinates', compute='_compute_subordinates', compute_sudo=True) company_id = fields.Many2one(comodel_name='res.company', string='Компания', required=True, index=True, default=lambda self: self.env.company, tracking=True) directory_id = fields.Many2one( comodel_name="dms.directory", string="Директория хранения", domain="[('permission_create', '=', True), '|', ('company_id', '=', False), ('company_id', '=', company_id)]", context={'dms_directory_show_path': True}, ondelete="restrict", auto_join=True, index=True, tracking=True #domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]" ) child_all_count = fields.Integer(string='Indirect Surbordinates Count', compute='_compute_subordinates', store=False, compute_sudo=True) def _get_subordinates(self, parents=None): if not parents: parents = self.env[self._name] indirect_subordinates = self.env[self._name] parents |= self direct_subordinates = self.child_ids - parents for child in direct_subordinates: child_subordinate = child._get_subordinates(parents=parents) indirect_subordinates |= child_subordinate return indirect_subordinates | direct_subordinates @api.depends('child_ids', 'child_ids.child_all_count') def _compute_subordinates(self): for doc in self: doc.subordinate_ids = doc._get_subordinates() doc.child_all_count = len(doc.subordinate_ids) def render_template(self): for s in self: template = s.text template_new = s.text model = s.link_model.model res_id = s.res_id render_result = 'Не удалось обработать текст' substrstart = "${" substrend = "}" substr_new_start = "<" substr_new_end = ">" flag = True start = 0 s.text_str = '' end = len(template)-1 while flag: try: #search for the item index = template.index(substrstart,start,end) start = index+len(substrstart) indexend = template.index(substrend,start,end) substr = template[index:indexend+1] substr_new_str = substr #s.text_str += str(substr)+ '; ' if substr.find(substr_new_start)>0 and substr.find(substr_new_end)>0: new_flag = True start_new = 0 end_new = len(substr)-1 while new_flag: try: #search for the item index_new = substr.index(substr_new_start,start_new,end_new) start_new = index_new+len(substr_new_start) indexend_new = substr.index(substr_new_end,start_new,end_new) substr_new = substr[index_new:indexend_new+1] #s.text_str += str(index_new) + '-' + str(indexend_new) +':'+str(substr_new)+ '; ' substr_new_str = substr_new_str.replace(substr_new,'') except ValueError: new_flag=False template_new = template_new.replace(substr,substr_new_str) except ValueError: flag = False parent = '' if s.parent_id: type_doc_parent = dict(self._fields['type_document'].selection).get(s.parent_id.type_document) parent = ' В ответ на {} {} от {}.'.format(type_doc_parent, s.parent_id.name,s.parent_id.date.strftime('%d.%m.%Y')) type_doc = dict(self._fields['type_document'].selection).get(s.type_document) s.text_str = '{} {} от {}.{}'.format(type_doc, s.name,s.date.strftime('%d.%m.%Y'), parent) template = template_new #res = re.findall(r"(${})", template) try: template = mako_template_env.from_string(tools.ustr(template)) except: template = mako_template_env.from_string(tools.ustr('')) user = self.env.user variables = { 'user': user } if model and res_id: record = self.env[model].browse(res_id) variables['object'] = record try: render_result = template.render(variables) except Exception: _logger.error("Failed to render template %r using values %r" % (template, variables)) render_result = u"" if render_result == u"False": render_result = u"" s.text_render = render_result return render_result # @api.model # def create(self, vals): # if 'name' in vals and 'type_document' in vals: # if vals.get('name', ('New')) == '/' or vals['name'] == '' or vals['name'] == False: # if vals['type_document'] == 'incoming': # vals['name'] = self.env['ir.sequence'].next_by_code('dms.document_number') # if vals['type_document'] == 'outgoing': # vals['name'] = self.env['ir.sequence'].next_by_code('dms.document_number_out') # if vals['type_document'] == 'internal': # vals['name'] = self.env['ir.sequence'].next_by_code('dms.document_number_internal') # for s in self: # if not s.name: # if s.type_document == 'incoming': # raise exceptions.ValidationError(self.env['ir.sequence'].next_by_code('dms.document_number')) # s.name = self.env['ir.sequence'].next_by_code('dms.document_number') # if s.type_document == 'outgoing': # s.name = self.env['ir.sequence'].next_by_code('dms.document_number_out') # if s.type_document == 'internal': # s.name = self.env['ir.sequence'].next_by_code('dms.document_number_internal') # res = super(mklab_dms_document, self).create(vals) # return res # def write(self, vals): # if 'name' in vals and 'type_document' in vals: # if vals.get('name', ('New')) == '/' or vals['name'] == '' or vals['name'] == False: # if vals['type_document'] == 'incoming': # vals['name'] = self.env['ir.sequence'].next_by_code('dms.document_number') # if vals['type_document'] == 'outgoing': # vals['name'] = self.env['ir.sequence'].next_by_code('dms.document_number_out') # if vals['type_document'] == 'internal': # vals['name'] = self.env['ir.sequence'].next_by_code('dms.document_number_internal') # if not self.name: # if self.type_document == 'incoming': # raise exceptions.ValidationError(self.env['ir.sequence'].next_by_code('dms.document_number')) # self.name = self.env['ir.sequence'].next_by_code('dms.document_number') # if self.type_document == 'outgoing': # self.name = self.env['ir.sequence'].next_by_code('dms.document_number_out') # if self.type_document == 'internal': # self.name = self.env['ir.sequence'].next_by_code('dms.document_number_internal') # res = super(mklab_dms_document, self).write(vals) # return res