Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC
This commit is contained in:
294
mklab_dms_document/models/document.py
Normal file
294
mklab_dms_document/models/document.py
Normal file
@ -0,0 +1,294 @@
|
||||
# -*- 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="</%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='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
|
||||
|
||||
Reference in New Issue
Block a user