Public release from ruodoo-project: 19.0 - 2026-05-10 21:19:01 UTC
This commit is contained in:
93
mklab_dms_document/README.md
Normal file
93
mklab_dms_document/README.md
Normal file
@ -0,0 +1,93 @@
|
||||
# mklab_dms_document
|
||||
|
||||
|
||||
|
||||
## Getting started
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin https://gitlab.inf-centre.ru/mklab-base/documents/mklab_dms_document.git
|
||||
git branch -M master
|
||||
git push -uf origin master
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
|
||||
- [ ] [Set up project integrations](https://gitlab.inf-centre.ru/mklab-base/documents/mklab_dms_document/-/settings/integrations)
|
||||
|
||||
## Collaborate with your team
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
|
||||
## Test and Deploy
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
|
||||
***
|
||||
|
||||
# Editing this README
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
3
mklab_dms_document/__init__.py
Normal file
3
mklab_dms_document/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
34
mklab_dms_document/__manifest__.py
Normal file
34
mklab_dms_document/__manifest__.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "mklab_dms_document",
|
||||
|
||||
'summary': """
|
||||
Работа с входящими и исходящими документами и их хранение""",
|
||||
|
||||
'description': """
|
||||
Работа с входящими и исходящими документами и их хранение
|
||||
""",
|
||||
|
||||
'author': "MK.Lab",
|
||||
'website': "http://www.inf-centre.ru",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/13.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '13.20230927',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base','dms', 'utm', 'base_tier_validation'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/data.xml',
|
||||
'views/views.xml',
|
||||
'views/report.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo/demo.xml',
|
||||
],
|
||||
}
|
||||
3
mklab_dms_document/controllers/__init__.py
Normal file
3
mklab_dms_document/controllers/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
21
mklab_dms_document/controllers/controllers.py
Normal file
21
mklab_dms_document/controllers/controllers.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from odoo import http
|
||||
|
||||
|
||||
# class MklabDmsDocument(http.Controller):
|
||||
# @http.route('/mklab_dms_document/mklab_dms_document/', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/mklab_dms_document/mklab_dms_document/objects/', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('mklab_dms_document.listing', {
|
||||
# 'root': '/mklab_dms_document/mklab_dms_document',
|
||||
# 'objects': http.request.env['mklab_dms_document.mklab_dms_document'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/mklab_dms_document/mklab_dms_document/objects/<model("mklab_dms_document.mklab_dms_document"):obj>/', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('mklab_dms_document.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
25
mklab_dms_document/data/data.xml
Normal file
25
mklab_dms_document/data/data.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<odoo>
|
||||
<record id="seq_ir_dms_document" model="ir.sequence">
|
||||
<field name="name">Входящий документ</field>
|
||||
<field name="code">dms.document_number</field>
|
||||
<field name="prefix">InDoc</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="seq_ir_dms_document_out" model="ir.sequence">
|
||||
<field name="name">Исходящий документ</field>
|
||||
<field name="code">dms.document_number_out</field>
|
||||
<field name="prefix">OutDoc</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="seq_ir_dms_document_internal" model="ir.sequence">
|
||||
<field name="name">Внутренний документ документ</field>
|
||||
<field name="code">dms.document_number_internal</field>
|
||||
<field name="prefix">InternalDoc</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
</odoo>
|
||||
55
mklab_dms_document/demo/demo.xml
Normal file
55
mklab_dms_document/demo/demo.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- Шаблон документа -->
|
||||
<record id="demo_dms_template_letter" model="dms.template">
|
||||
<field name="name">Шаблон исходящего письма</field>
|
||||
<field name="text"><p>Уважаемые коллеги,</p><p>Настоящим сообщаем вам о ...</p><p>С уважением,<br/>${user.name}</p></field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
<record id="demo_dms_template_request" model="dms.template">
|
||||
<field name="name">Шаблон запроса документов</field>
|
||||
<field name="text"><p>Просим предоставить следующие документы:</p><ul><li>Счёт-фактура</li><li>Товарная накладная</li></ul></field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
<!-- Входящий документ -->
|
||||
<record id="demo_dms_document_incoming" model="dms.document">
|
||||
<field name="type_document">incoming</field>
|
||||
<field name="name">ВХ-2026-001</field>
|
||||
<field name="partner_id" ref="base.res_partner_1"/>
|
||||
<field name="date">2026-01-10</field>
|
||||
<field name="text"><p>Входящее письмо от поставщика с коммерческим предложением.</p></field>
|
||||
<field name="incoming_file_type">pdf</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
<!-- Исходящий документ -->
|
||||
<record id="demo_dms_document_outgoing" model="dms.document">
|
||||
<field name="type_document">outgoing</field>
|
||||
<field name="name">ИСХ-2026-001</field>
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field name="date">2026-01-12</field>
|
||||
<field name="text"><p>Исходящее письмо покупателю с подтверждением заказа.</p></field>
|
||||
<field name="incoming_file_type">pdf</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
<!-- Внутренний документ -->
|
||||
<record id="demo_dms_document_internal" model="dms.document">
|
||||
<field name="type_document">internal</field>
|
||||
<field name="name">ВН-2026-001</field>
|
||||
<field name="partner_id" ref="base.main_partner"/>
|
||||
<field name="date">2026-01-15</field>
|
||||
<field name="text"><p>Внутренняя служебная записка о порядке работы с документами.</p></field>
|
||||
<field name="incoming_file_type">pdf</field>
|
||||
<field name="state">draft</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
6
mklab_dms_document/models/__init__.py
Normal file
6
mklab_dms_document/models/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import res_partner
|
||||
from . import document
|
||||
from . import tier_definition
|
||||
from . import tier_dms
|
||||
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
|
||||
|
||||
18
mklab_dms_document/models/res_partner.py
Normal file
18
mklab_dms_document/models/res_partner.py
Normal file
@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models
|
||||
|
||||
|
||||
class DmsResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
def action_show_document_incoming(self):
|
||||
self.ensure_one()
|
||||
document_action = self.env['ir.actions.act_window'].for_xml_id('mklab_dms_document','action_window_incoming')
|
||||
document_action['domain'] = str([('partner_id','=',self.id),('type_document','=','incoming')])
|
||||
return document_action
|
||||
|
||||
def action_show_document_outgoing(self):
|
||||
self.ensure_one()
|
||||
document_action = self.env['ir.actions.act_window'].for_xml_id('mklab_dms_document','action_window_outgoing')
|
||||
document_action['domain'] = str([('partner_id','=',self.id),('type_document','=','outgoing')])
|
||||
return document_action
|
||||
11
mklab_dms_document/models/tier_definition.py
Normal file
11
mklab_dms_document/models/tier_definition.py
Normal file
@ -0,0 +1,11 @@
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class TierDefinition(models.Model):
|
||||
_inherit = "tier.definition"
|
||||
|
||||
@api.model
|
||||
def _get_tier_validation_model_names(self):
|
||||
res = super()._get_tier_validation_model_names()
|
||||
res.append("dms.document")
|
||||
return res
|
||||
9
mklab_dms_document/models/tier_dms.py
Normal file
9
mklab_dms_document/models/tier_dms.py
Normal file
@ -0,0 +1,9 @@
|
||||
from odoo import models
|
||||
|
||||
|
||||
class TirerDmsDocument(models.Model):
|
||||
_name = "dms.document"
|
||||
_inherit = ["dms.document", "tier.validation"]
|
||||
_state_field = "state"
|
||||
_state_from = ["draft"]
|
||||
_state_to = ['done']
|
||||
5
mklab_dms_document/security/ir.model.access.csv
Normal file
5
mklab_dms_document/security/ir.model.access.csv
Normal file
@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_dms_document,dms.document,model_dms_document,base.group_user,1,1,1,1
|
||||
access_dms_template_admin,dms_template_admin,model_dms_template,dms.group_dms_manager,1,1,1,1
|
||||
access_dms_template,dms_template,model_dms_template,base.group_user,1,0,0,0
|
||||
access_dms_choise_template,dms_choise_template,model_dms_choise_template,base.group_user,1,1,1,1
|
||||
|
2
mklab_dms_document/tests/__init__.py
Normal file
2
mklab_dms_document/tests/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import test_dms_document
|
||||
114
mklab_dms_document/tests/test_dms_document.py
Normal file
114
mklab_dms_document/tests/test_dms_document.py
Normal file
@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for mklab_dms_document — DMS document lifecycle, PDF creation,
|
||||
and template rendering.
|
||||
|
||||
Validates: Requirements 18.1, 18.2, 18.3, 18.4
|
||||
"""
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _make_partner(env):
|
||||
"""Create a minimal company partner."""
|
||||
return env['res.partner'].create({
|
||||
'name': 'Test DMS Partner',
|
||||
'is_company': True,
|
||||
})
|
||||
|
||||
|
||||
def _make_document(env, partner, state='draft', text=None):
|
||||
"""Create a minimal dms.document record."""
|
||||
vals = {
|
||||
'type_document': 'incoming',
|
||||
'partner_id': partner.id,
|
||||
'name': 'TEST-DOC-001',
|
||||
'state': state,
|
||||
}
|
||||
if text is not None:
|
||||
vals['text'] = text
|
||||
return env['dms.document'].create(vals)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestDmsDocument
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestDmsDocument(TransactionCase):
|
||||
"""
|
||||
Tests for dms.document: unlink restrictions, create_pdf validation,
|
||||
and render_template output.
|
||||
|
||||
Validates: Requirements 18.1, 18.2, 18.3, 18.4
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.partner = _make_partner(self.env)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Requirement 18.1 — unlink of a 'done' document raises ValidationError
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_unlink_done_raises(self):
|
||||
"""
|
||||
Req 18.1 — deleting a document in state 'done' must raise
|
||||
ValidationError.
|
||||
"""
|
||||
doc = _make_document(self.env, self.partner, state='done')
|
||||
with self.assertRaises(ValidationError):
|
||||
doc.unlink()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Requirement 18.2 — unlink of a draft document succeeds
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_unlink_draft_ok(self):
|
||||
"""
|
||||
Req 18.2 — deleting a document in state 'draft' must succeed
|
||||
without raising any exception.
|
||||
"""
|
||||
doc = _make_document(self.env, self.partner, state='draft')
|
||||
# Should not raise
|
||||
doc.unlink()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Requirement 18.3 — create_pdf without directory raises UserError
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_create_pdf_no_directory_raises(self):
|
||||
"""
|
||||
Req 18.3 — calling create_pdf on a document with no directory_id
|
||||
set must raise UserError.
|
||||
"""
|
||||
doc = _make_document(self.env, self.partner, state='draft')
|
||||
# Ensure no directory is set
|
||||
doc.directory_id = False
|
||||
with self.assertRaises(UserError):
|
||||
doc.create_pdf()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Requirement 18.4 — render_template with a simple template returns
|
||||
# a non-empty string
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_render_template_basic(self):
|
||||
"""
|
||||
Req 18.4 — render_template on a document with a simple Mako/Jinja2
|
||||
template must return a non-empty string.
|
||||
"""
|
||||
doc = _make_document(
|
||||
self.env,
|
||||
self.partner,
|
||||
state='draft',
|
||||
text='<p>Hello World</p>',
|
||||
)
|
||||
result = doc.render_template()
|
||||
self.assertTrue(
|
||||
result and len(result) > 0,
|
||||
"render_template should return a non-empty string for a simple template",
|
||||
)
|
||||
78
mklab_dms_document/views/report.xml
Normal file
78
mklab_dms_document/views/report.xml
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="mklab_dms_document.report_dms_document">
|
||||
<t t-call="web.basic_layout">
|
||||
<t t-foreach="docs" t-as="o">
|
||||
<t t-if="o and 'company_id' in o">
|
||||
<t t-set="company" t-value="o.company_id"/>
|
||||
</t>
|
||||
<t t-if="not o or not 'company_id' in o">
|
||||
<t t-set="company" t-value="res_company"/>
|
||||
</t>
|
||||
<t t-set="context" t-value="o._context"/>
|
||||
<div class="header">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 10pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 10pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; font-weight: normal;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 10pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: normal;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
|
||||
<t t-set="render" t-value="o.render_template()"/>
|
||||
<td class="R0C0">
|
||||
<t t-if='o.print_head'>
|
||||
<t t-esc="o.text_str"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="page">
|
||||
<span>
|
||||
<t t-raw="o.text_render"/>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="mklab_dms_document.paperformat_a4" model="report.paperformat">
|
||||
<field name="name">A4</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="format">A4</field>
|
||||
<field name="page_height">0</field>
|
||||
<field name="page_width">0</field>
|
||||
<field name="orientation">Portrait</field>
|
||||
<field name="margin_top">15</field>
|
||||
<field name="margin_bottom">20</field>
|
||||
<field name="margin_left">7</field>
|
||||
<field name="margin_right">7</field>
|
||||
<field name="header_line" eval="False"/>
|
||||
<field name="header_spacing">10</field>
|
||||
<field name="dpi">90</field>
|
||||
</record>
|
||||
|
||||
<report id="mklab_dms_document.action_report_dms_document"
|
||||
string="Документ"
|
||||
model="dms.document"
|
||||
report_type="qweb-pdf"
|
||||
file="mklab_dms_document.report_dms_document"
|
||||
name="mklab_dms_document.report_dms_document"
|
||||
print_report_name="'Документ - %s' % (object.name)"
|
||||
/>
|
||||
<record id="mklab_dms_document.action_report_dms_document" model="ir.actions.report">
|
||||
<field name="name">DMS Документ</field>
|
||||
<field name="model">dms.document</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">mklab_dms_document.report_dms_document</field>
|
||||
<field name="paperformat_id" ref="mklab_dms_document.paperformat_a4"/>
|
||||
</record>
|
||||
</odoo>
|
||||
272
mklab_dms_document/views/views.xml
Normal file
272
mklab_dms_document/views/views.xml
Normal file
@ -0,0 +1,272 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="mklab_dms_document.res_partner_inherit" model="ir.ui.view">
|
||||
<field name="name">mklab_dms_document.res_partner_inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button class="oe_stat_button" type="object" icon="fa-book" name="action_show_document_incoming"
|
||||
string="Входящие документы"/>
|
||||
<button class="oe_stat_button" type="object" icon="fa-book" name="action_show_document_outgoing"
|
||||
string="Исходящие документы"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mklab_dms_document.rule_company_document" model="ir.rule">
|
||||
<field name="name">Документы</field>
|
||||
<field name="model_id" ref="model_dms_document"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mklab_dms_document.form_template_wiz">
|
||||
<field name="name">mklab_dms_document form_template_wiz</field>
|
||||
<field name="model">dms.choise_template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="temp_id"/>
|
||||
<field name="doc_id" invisible='1'/>
|
||||
<field name="company_id" invisible='1'/>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button string="Выбрать" name='get_choise' type="object"/>
|
||||
<button string="Отмена" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mklab_dms_document.list_template">
|
||||
<field name="name">mklab_dms_document list_template</field>
|
||||
<field name="model">dms.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="company_id"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mklab_dms_document.list">
|
||||
<field name="name">mklab_dms_document list</field>
|
||||
<field name="model">dms.document</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="name"/>
|
||||
<field name="date"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="state"/>
|
||||
<field name="company_id"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mklab_dms_document.form_template">
|
||||
<field name="name">mklab_dms_document form_template</field>
|
||||
<field name="model">dms.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
</header>
|
||||
<sheet>
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
<field name="text"/>
|
||||
<group>
|
||||
<field name="company_id"/>
|
||||
</group>
|
||||
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mklab_dms_document.form">
|
||||
<field name="name">mklab_dms_document form</field>
|
||||
<field name="model">dms.document</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<!--button name='render_template' type='object' string='render'/-->
|
||||
<button name='open_choise_template' type='object' string='Создать текст из шаблона'
|
||||
invisible="type_document != 'outgoing' or state =='done'"/>
|
||||
<button name='create_pdf' type='object' string='Сформировать PDF и записать в хранилище'
|
||||
invisible="type_document != 'outgoing' or state == 'done'"/>
|
||||
<button name='create_pdf' type='object' string='Записать в хранилище'
|
||||
invisible="type_document != 'incoming' or state == 'done'"/>
|
||||
<button
|
||||
name="request_validation"
|
||||
string="Request Validation"
|
||||
invisible="need_validation != True or rejected == True or state not in ['draft']"
|
||||
type="object"
|
||||
/>
|
||||
<button
|
||||
name="restart_validation"
|
||||
string="Restart Validation"
|
||||
invisible="not review_ids or state not in ['draft']"
|
||||
type="object"
|
||||
/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<field name="need_validation" invisible="1"/>
|
||||
<field name="validated" invisible="1"/>
|
||||
<field name="rejected" invisible="1"/>
|
||||
<div
|
||||
class="alert alert-warning"
|
||||
role="alert"
|
||||
invisible="validated == True or state not in ['draft'] or rejected == True or not review_ids"
|
||||
style="margin-bottom:0px;"
|
||||
>
|
||||
<p><i class="fa fa-info-circle"/>Требуется валидация
|
||||
<field name="can_review" invisible="1"/>
|
||||
<button
|
||||
name="validate_tier"
|
||||
string="Validate"
|
||||
invisible="can_review == False"
|
||||
type="object"
|
||||
class="oe_inline oe_button btn-success"
|
||||
icon="fa-thumbs-up"
|
||||
/>
|
||||
<button
|
||||
name="reject_tier"
|
||||
string="Reject"
|
||||
invisible="can_review == False"
|
||||
type="object"
|
||||
class="btn-icon btn-danger"
|
||||
icon="fa-thumbs-down"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="alert alert-success"
|
||||
role="alert"
|
||||
invisible="validated != True or state not in ['draft'] or not review_ids"
|
||||
style="margin-bottom:0px;"
|
||||
>
|
||||
<p>
|
||||
<i class="fa fa-thumbs-up"/>
|
||||
<b>Валидация прошла успешно</b>!
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="alert alert-danger"
|
||||
role="alert"
|
||||
invisible="rejected != True or state not in ['draft'] or not review_ids"
|
||||
style="margin-bottom:0px;"
|
||||
>
|
||||
<p>
|
||||
<i class="fa fa-thumbs-down"/>
|
||||
Валидация <b>отклонена</b>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<sheet>
|
||||
<h1>
|
||||
<field name="name" readonly="state == 'done'"/>
|
||||
</h1>
|
||||
<group>
|
||||
<group>
|
||||
<field name="date" readonly="state == 'done'"/>
|
||||
<field name="partner_id" readonly="state == 'done'"/>
|
||||
<field name="type_document" readonly='True'/>
|
||||
<field name="print_head" readonly="state == 'done'"/>
|
||||
<field name="company_id" readonly="state == 'done'"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="file" readonly="state == 'done'"/>
|
||||
<field name="directory_id" readonly="state == 'done'"/>
|
||||
<field name="link_model" readonly="state == 'done'"/>
|
||||
<field name="res_id" readonly="state == 'done'"/>
|
||||
<field name="parent_id" readonly="state == 'done'"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<notebook>
|
||||
<page name='page_document' string='Документ'>
|
||||
<field name="text"
|
||||
invisible="type_document != 'outgoing'" readonly="state == 'done'"/>
|
||||
<field name="text_str" invisible='1'/>
|
||||
<field name="text_render" invisible='1'/>
|
||||
<field name="text_render" invisible='1'/>
|
||||
<group>
|
||||
<field name="incoming_file_type"
|
||||
invisible="type_document != 'incoming'" readonly="state == 'done'"/>
|
||||
<field name="incoming_file"
|
||||
invisible="type_document != 'incoming' or incoming_file_type != 'pdf'" readonly="state == 'done'"
|
||||
widget="pdf_viewer"/>
|
||||
<field name="incoming_file" string="Сформированный документ"
|
||||
invisible="type_document == 'incoming' and state != 'done'" readonly="state == 'done'"
|
||||
widget="pdf_viewer"/>
|
||||
<field name="incoming_file_other"
|
||||
invisible="type_document == 'incoming' or state != 'done'" readonly="state == 'done'"/>
|
||||
</group>
|
||||
</page>
|
||||
<page name='page_chain_document' string='Цепочка документов'>
|
||||
<field name="child_ids" readonly="state == 'done'"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
<label for="create_uid" string="Автор документа: "/>
|
||||
<field name="create_uid"/>
|
||||
</sheet>
|
||||
<field
|
||||
name="review_ids"
|
||||
widget="tier_validation"
|
||||
invisible="not review_ids"
|
||||
/>
|
||||
<div class="oe_chatter"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="mklab_dms_document.action_window_template">
|
||||
<field name="name">Шаблоны</field>
|
||||
<field name="res_model">dms.template</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="mklab_dms_document.action_window_incoming">
|
||||
<field name="name">Входящие</field>
|
||||
<field name="res_model">dms.document</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'default_type_document':'incoming'}</field>
|
||||
<field name="domain">[('type_document', '=', 'incoming')]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="mklab_dms_document.action_window_outgoing">
|
||||
<field name="name">Исходящие</field>
|
||||
<field name="res_model">dms.document</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'default_type_document':'outgoing'}</field>
|
||||
<field name="domain">[('type_document', '=', 'outgoing')]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="mklab_dms_document.action_window_internal">
|
||||
<field name="name">Внутренние</field>
|
||||
<field name="res_model">dms.document</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'default_type_document':'internal'}</field>
|
||||
<field name="domain">[('type_document', '=', 'internal')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Документы" id="mklab_dms_document.documents" parent="dms.main_menu_dms"/>
|
||||
<menuitem name="Шаблоны" id="mklab_dms_document.template_menu" parent="mklab_dms_document.documents"
|
||||
action="mklab_dms_document.action_window_template"/>
|
||||
<menuitem name="Входящие" id="mklab_dms_document.incoming" parent="mklab_dms_document.documents"
|
||||
action="mklab_dms_document.action_window_incoming"/>
|
||||
<menuitem name="Исходящие" id="mklab_dms_document.outgoing" parent="mklab_dms_document.documents"
|
||||
action="mklab_dms_document.action_window_outgoing"/>
|
||||
<!--menuitem name="Внутренние" id="mklab_dms_document.internal" parent="mklab_dms_document.documents" action="mklab_dms_document.action_window_internal"/-->
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user