Public release from ruodoo-project: 19.0 - 2026-05-10 21:19:01 UTC

This commit is contained in:
CI Publish Bot
2026-05-10 21:19:11 +00:00
commit cbf9e6e6d6
1213 changed files with 183945 additions and 0 deletions

View 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