Files
public/mklab_dms_document/models/document.py

292 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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="</%doc>",
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=True, tracking=True)
text = fields.Html(string='Текст', required=True, tracking=True)
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 = 'Документ DMS'
_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
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},
}
@api.ondelete(at_uninstall=False)
def _unlink_except_done(self):
# Штатный механизм запрета удаления (вместо raise прямо в unlink):
# корректно уживается с каскадами ondelete и групповыми операциями.
for s in self:
if s.state == 'done':
raise exceptions.ValidationError('Нельзя удалить документ, записанный в хранилище')
# Соответствие типа документа коду последовательности нумерации.
_SEQUENCE_BY_TYPE = {
'incoming': 'dms.document_number',
'outgoing': 'dms.document_number_out',
'internal': 'dms.document_number_internal',
}
def _next_document_number(self, type_document):
"""Следующий номер по типу документа из соответствующей последовательности."""
code = self._SEQUENCE_BY_TYPE.get(type_document)
if not code:
return False
return self.env['ir.sequence'].next_by_code(code)
type_document = fields.Selection(
[('incoming', 'Входящий документ'),
('outgoing', 'Исходящий документ'),
('internal', 'Внутренний документ')],
string='Тип', required=True, tracking=True)
name = fields.Char(string='Номер', copy=False, tracking=True)
partner_id = fields.Many2one(
comodel_name='res.partner', string='Контакт', required=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
tracking=True)
date = fields.Date(
string='Дата документа', default=fields.Date.context_today,
required=True, tracking=True)
text = fields.Html(string='Текст')
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='Текст шапки')
text_render = fields.Html(string='Обработанный текст')
file = fields.Many2one(
comodel_name='dms.file', string='Связанный файл',
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
tracking=True)
link_model = fields.Many2one(
comodel_name='ir.model', string='Связанная модель', tracking=True)
res_id = fields.Integer(
string='ID документа в связанной модели', tracking=True)
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='Дочерние документы')
subordinate_ids = fields.One2many(
comodel_name='dms.document', string='Все связанные документы',
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='Количество связанных документов',
compute='_compute_subordinates', store=False, compute_sudo=True)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if not vals.get('name'):
type_document = vals.get('type_document') \
or self.env.context.get('default_type_document')
number = self._next_document_number(type_document)
if number:
vals['name'] = number
return super().create(vals_list)
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 = ""
if render_result == "False":
render_result = ""
s.text_render = render_result
return render_result