Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC
This commit is contained in:
160
l10n_ru_contract/.gitignore
vendored
Normal file
160
l10n_ru_contract/.gitignore
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
.idea/
|
||||
30
l10n_ru_contract/README.md
Normal file
30
l10n_ru_contract/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Российская локализация - Договоры
|
||||
name: l10n_ru_contract
|
||||
|
||||
|
||||
## Описание
|
||||
Создание договоров с клиентами и поставщиками в системе.
|
||||
Возможность разделения договоров на виды, отслеживание статуса договора. Вы также можете распечатать договор из системы.
|
||||
|
||||
### Создание вида договора клиента(поставщика):
|
||||
1. Меню Продажи (Покупки) - Договоры - Виды договора - кнопка "Создать";
|
||||
2. На форме указываем:
|
||||
|
||||
2.1. Журнал и счета дебетовой и кредиторской задолженности;
|
||||
|
||||
2.2. Присваиваем новое название.
|
||||
|
||||
### Создание договора клиента (поставщика):
|
||||
1. Меню Продажи (Покупки) - Договоры - кнопка "Создать";
|
||||
2. На форме указываем основные и дополнительные условия договора:
|
||||
|
||||
2.1. Контрагент - клиент (поставщик);
|
||||
|
||||
2.2. Тип контрагента;
|
||||
|
||||
2.3. Компанию, от лица которой будет подписан договор;
|
||||
|
||||
2.4. Вид договора.
|
||||
|
||||
### Для печати:
|
||||
1. Открываем созданную запись договора - Действие - "Договор".
|
||||
3
l10n_ru_contract/__init__.py
Normal file
3
l10n_ru_contract/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
from . import report
|
||||
57
l10n_ru_contract/__manifest__.py
Normal file
57
l10n_ru_contract/__manifest__.py
Normal file
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'Российская локализация - Договоры',
|
||||
'summary': """
|
||||
Создание договоров, их видов и печать
|
||||
""",
|
||||
|
||||
'description': """
|
||||
Создание договоров с клиентами и поставщиками. Возможность разделения на виды договоров, отслеживание статуса договора и его печатью
|
||||
Создание вида договора клиента(поставщика):
|
||||
1. Меню Продажи (Покупки) - Договоры - Виды договора - кнопка "Создать";
|
||||
2. На форме указываем:
|
||||
2.1. Журнал и счета дебетовой и кредиторской задолженности;
|
||||
2.2. Присваиваем новое название.
|
||||
|
||||
Создание договора клиента (поставщика):
|
||||
1. Меню Продажи (Покупки) - Договоры - кнопка "Создать";
|
||||
2. На форме указываем основные и дополнительные условия договора:
|
||||
2.1. Контрагент - клиент (поставщик);
|
||||
2.2. Тип контрагента;
|
||||
2.3. Компанию, от лица которой будет подписан договор;
|
||||
2.4. Вид договора.
|
||||
|
||||
Для печати:
|
||||
1. Открываем созданную запись договора - Действие - "Договор".
|
||||
""",
|
||||
|
||||
'version': '19.0.1.0.0',
|
||||
'sequence': 0,
|
||||
'author': 'MK.Lab',
|
||||
'website': 'https://www.inf-centre.ru/',
|
||||
'depends': [
|
||||
'base',
|
||||
'mail',
|
||||
'l10n_ru_base',
|
||||
'report_weasyprint',
|
||||
],
|
||||
"external_dependencies": {
|
||||
"python": ["weasyprint"],
|
||||
},
|
||||
'data': [
|
||||
'data/data.xml',
|
||||
'report/report_contract_simple.xml',
|
||||
'views/contract_customer_view.xml',
|
||||
"views/contract_header_templates.xml",
|
||||
'views/res_company_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'views/contract_profile_views.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'report/report_contract.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo/demo.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
21
l10n_ru_contract/data/data.xml
Normal file
21
l10n_ru_contract/data/data.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="contract_customer_sequence" model="ir.sequence">
|
||||
<field name="name">Договор последовательность клиент</field>
|
||||
<field name="code">partner.contract.customer.sequence</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field eval="True" name="use_date_range"/>
|
||||
<field name="padding">5</field>
|
||||
</record>
|
||||
<record id="contract_supplier_sequence" model="ir.sequence">
|
||||
<field name="name">Договор последовательность поставщик</field>
|
||||
<field name="code">partner.contract.supplier.sequence</field>
|
||||
<field eval="1" name="number_next"/>
|
||||
<field eval="1" name="number_increment"/>
|
||||
<field eval="True" name="use_date_range"/>
|
||||
<field name="padding">5</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
101
l10n_ru_contract/demo/demo.xml
Normal file
101
l10n_ru_contract/demo/demo.xml
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<!-- ===== СПРАВОЧНИКИ ===== -->
|
||||
|
||||
<!-- Виды договоров -->
|
||||
<record id="demo_contract_profile_supply" model="contract.profile">
|
||||
<field name="name">Договор поставки</field>
|
||||
</record>
|
||||
<record id="demo_contract_profile_service" model="contract.profile">
|
||||
<field name="name">Договор оказания услуг</field>
|
||||
</record>
|
||||
<record id="demo_contract_profile_agency" model="contract.profile">
|
||||
<field name="name">Агентский договор</field>
|
||||
</record>
|
||||
|
||||
<!-- Дни доставки/отгрузки -->
|
||||
<record id="demo_day_monday" model="contract.day">
|
||||
<field name="name">Понедельник</field>
|
||||
</record>
|
||||
<record id="demo_day_wednesday" model="contract.day">
|
||||
<field name="name">Среда</field>
|
||||
</record>
|
||||
<record id="demo_day_friday" model="contract.day">
|
||||
<field name="name">Пятница</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== ПАРТНЁРЫ ===== -->
|
||||
|
||||
<!-- Покупатель -->
|
||||
<record id="demo_partner_buyer" model="res.partner">
|
||||
<field name="name">ООО "Торговый Дом Альфа"</field>
|
||||
<field name="is_company" eval="True"/>
|
||||
<field name="street">ул. Ленина, д. 10</field>
|
||||
<field name="city">Москва</field>
|
||||
<field name="zip">101000</field>
|
||||
<field name="inn">7701234567</field>
|
||||
<field name="kpp">770101001</field>
|
||||
<field name="ogrn">1027700000001</field>
|
||||
<field name="okpo">12345678</field>
|
||||
</record>
|
||||
|
||||
<!-- Поставщик -->
|
||||
<record id="demo_partner_supplier" model="res.partner">
|
||||
<field name="name">ООО "Производственная Компания Бета"</field>
|
||||
<field name="is_company" eval="True"/>
|
||||
<field name="street">пр. Мира, д. 25</field>
|
||||
<field name="city">Санкт-Петербург</field>
|
||||
<field name="zip">190000</field>
|
||||
<field name="inn">7802345678</field>
|
||||
<field name="kpp">780201001</field>
|
||||
<field name="ogrn">1027800000002</field>
|
||||
<field name="okpo">87654321</field>
|
||||
</record>
|
||||
|
||||
<!-- ===== ДОГОВОРЫ ===== -->
|
||||
|
||||
<!-- Договор с покупателем -->
|
||||
<record id="demo_contract_customer_001" model="partner.contract.customer">
|
||||
<field name="date_start">2026-01-01</field>
|
||||
<field name="date_end">2026-12-31</field>
|
||||
<field name="partner_id" ref="demo_partner_buyer"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="profile_id" ref="demo_contract_profile_supply"/>
|
||||
<field name="type">customer</field>
|
||||
<field name="state">signed</field>
|
||||
<field name="partner_type">company</field>
|
||||
</record>
|
||||
|
||||
<!-- Договор с поставщиком -->
|
||||
<record id="demo_contract_supplier_001" model="partner.contract.customer">
|
||||
<field name="date_start">2026-01-01</field>
|
||||
<field name="date_end">2026-12-31</field>
|
||||
<field name="partner_id" ref="demo_partner_supplier"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="profile_id" ref="demo_contract_profile_service"/>
|
||||
<field name="type">supplier</field>
|
||||
<field name="state">signed</field>
|
||||
<field name="partner_type">company</field>
|
||||
</record>
|
||||
|
||||
<!-- Строки договора -->
|
||||
<record id="demo_contract_line_001" model="contract.line">
|
||||
<field name="contract_id" ref="demo_contract_customer_001"/>
|
||||
<field name="sequence">10</field>
|
||||
<field name="name">1.1</field>
|
||||
<field name="punct"><p>Поставщик обязуется поставить товар в соответствии с условиями настоящего договора.</p></field>
|
||||
</record>
|
||||
<record id="demo_contract_line_002" model="contract.line">
|
||||
<field name="contract_id" ref="demo_contract_customer_001"/>
|
||||
<field name="sequence">20</field>
|
||||
<field name="name">1.2</field>
|
||||
<field name="punct"><p>Покупатель обязуется принять и оплатить товар в установленные сроки.</p></field>
|
||||
</record>
|
||||
|
||||
<!-- account.move убран: модуль account не является зависимостью l10n_ru_contract,
|
||||
поэтому account.move недоступна при загрузке демо этого модуля -->
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
9
l10n_ru_contract/models/__init__.py
Normal file
9
l10n_ru_contract/models/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from . import partner_contract_customer
|
||||
from . import dop_field
|
||||
from . import res_partner
|
||||
from . import res_partner_bank
|
||||
from . import res_company
|
||||
from . import contract_day
|
||||
from . import contract_allowed_profiles
|
||||
from . import contract_line
|
||||
from . import contract_profile
|
||||
50
l10n_ru_contract/models/account_move.py
Normal file
50
l10n_ru_contract/models/account_move.py
Normal file
@ -0,0 +1,50 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
mt_contract_id = fields.Many2one('partner.contract.customer', string=_('Номер договора'))
|
||||
sf_number = fields.Char(string=_('Номер с/ф'))
|
||||
osnovanie = fields.Char(string=_('Основание'))
|
||||
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент'), store=True, compute='_compute_get_pid')
|
||||
stamp = fields.Boolean(string=_('Печать и подпись'), related='mt_contract_id.stamp')
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_get_pid(self):
|
||||
for s in self:
|
||||
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
|
||||
|
||||
@api.onchange('mt_contract_id')
|
||||
def set_ons(self):
|
||||
if self.mt_contract_id:
|
||||
self.osnovanie = 'Договор № ' + self.mt_contract_id.name + ' от ' + fields.Datetime.from_string(
|
||||
self.mt_contract_id.date_start).strftime("%d.%m.%Y")
|
||||
|
||||
@api.constrains('state')
|
||||
def invoice_fields_check(self):
|
||||
for s in self:
|
||||
if s.state == 'posted':
|
||||
if s.mt_contract_id:
|
||||
errors_list = []
|
||||
journal_in_contract = s.mt_contract_id.profile_id.journal_id
|
||||
payment_term_in_contract = s.mt_contract_id.profile_id.payment_term_id
|
||||
receivable_in_contract = s.mt_contract_id.profile_id.receivable_account_id
|
||||
|
||||
if journal_in_contract != s.journal_id:
|
||||
errors_list.append(f'Отличается Журнал - [{s.journal_id.name}] '
|
||||
f'и указанный в договоре №{s.mt_contract_id.name} '
|
||||
f'Журнал - [{journal_in_contract.name}]\n\n')
|
||||
|
||||
if payment_term_in_contract != s.invoice_payment_term_id:
|
||||
errors_list.append(f'Отличается поле "Условие оплаты" в инвойсе '
|
||||
f'[Условие оплаты - {s.invoice_payment_term_id.name}] '
|
||||
f'и указанный в договоре №{s.mt_contract_id.name} '
|
||||
f'[Условие оплаты - {payment_term_in_contract.name}]\n\n')
|
||||
|
||||
if receivable_in_contract not in s.line_ids.account_id:
|
||||
errors_list.append(f'Отличается поле "Счет дебиторской задолженности" в инвойсе '
|
||||
f'и указанный в договоре №{s.mt_contract_id.name}')
|
||||
|
||||
if errors_list:
|
||||
raise exceptions.ValidationError(''.join(errors_list))
|
||||
16
l10n_ru_contract/models/contract_allowed_profiles.py
Normal file
16
l10n_ru_contract/models/contract_allowed_profiles.py
Normal file
@ -0,0 +1,16 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
class AllowedProfiles(models.Model):
|
||||
_name = 'contract.allowed.profiles'
|
||||
name = fields.Char(string='Одновременно включены следующие виды договоров:')
|
||||
allowed_profiles = fields.Many2many('contract.profile', string='Виды договоров', required=True)
|
||||
|
||||
@api.onchange('allowed_profiles')
|
||||
def set_name(self):
|
||||
for s in self:
|
||||
s.name = ''
|
||||
for profile in s.allowed_profiles:
|
||||
s.name += profile.name + ' + '
|
||||
if s.name:
|
||||
if s.name[-2] == '+':
|
||||
s.name = s.name[:-2]
|
||||
498
l10n_ru_contract/models/contract_customer.py
Normal file
498
l10n_ru_contract/models/contract_customer.py
Normal file
@ -0,0 +1,498 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, exceptions, tools, _
|
||||
import pymorphy2
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from .crutch_fields_header import IP_CONTACT_HEADER, ENTITY_CONTRACT_HEADER, INDIVIDUAL_CONTRACT_HEADER
|
||||
from odoo.exceptions import UserError
|
||||
import re
|
||||
import base64
|
||||
from io import BytesIO
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Inches
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
|
||||
|
||||
class PartnerContractCustomer(models.Model):
|
||||
_name = 'partner.contract.customer'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'mail.render.mixin']
|
||||
|
||||
def get_dateend(self):
|
||||
for s in self:
|
||||
if s.date_start:
|
||||
six_months = fields.Datetime.from_string(s.date_start) + relativedelta(months=+11)
|
||||
else:
|
||||
six_months = datetime.today() + relativedelta(months=+11)
|
||||
return fields.Datetime.to_string(six_months)
|
||||
|
||||
name = fields.Char(string='Номер')
|
||||
date_start = fields.Date(string='Дата договора', required=True, default=fields.Datetime.now())
|
||||
partner_id = fields.Many2one('res.partner', string=_('Контрагент'), required=True)
|
||||
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент как в заказе'))
|
||||
company_id = fields.Many2one('res.company', string=_('Компания'), required=True)
|
||||
name_print = fields.Char(string=_('Имя для печати'), compute='_get_name_print')
|
||||
name_print1 = fields.Char(string=_('Имя для печати, И.П.'), compute='_get_name_printip')
|
||||
date_end = fields.Date(string=_('Дата окончания'), required=True, default=get_dateend)
|
||||
name_dirprint = fields.Char(string=_('Имя нашего директора для печати'))
|
||||
name_dirprint1 = fields.Char(string=_('Имя нашего директора для печати И.П.'))
|
||||
lines = fields.One2many('contract.line', 'contract_id', string=_('Договорные цены'))
|
||||
type = fields.Selection(
|
||||
[('customer', 'С покупателем'),
|
||||
('supplier', 'С поставщиком'),
|
||||
('other', 'Прочие расчеты'),
|
||||
|
||||
],
|
||||
string=_('Тип договора'), default='customer', required=True)
|
||||
saleorder_id = fields.Many2one('sale.order', string=_('Заказ/Сделка'))
|
||||
stamp = fields.Boolean(string=_('Печать и подпись'))
|
||||
signed = fields.Boolean(string=_('Договор подписан'))
|
||||
state = fields.Selection(
|
||||
[('draft', 'Черновик'),
|
||||
('progress', 'На согласовании'),
|
||||
('signed', 'Подписан, действует'),
|
||||
('closed', 'Истёк'),
|
||||
],
|
||||
string=_('Статус'), default='draft', group_expand='_expand_states', index=True
|
||||
)
|
||||
is_template = fields.Boolean(_('Это шаблон'))
|
||||
copy_from = fields.Many2one('partner.contract.customer', string=_('Копировать из этого шаблона'))
|
||||
profile_id = fields.Many2one('contract.profile', string=_('Вид договора'), required=True)
|
||||
credit_limit = fields.Float(string=_('Лимит кредита'))
|
||||
guid_1s = fields.Char(_('Код договора из 1С'))
|
||||
buh_code = fields.Char(_('Код договора из бухгалтерии'))
|
||||
payment_term_id = fields.Many2one('account.payment.term', string=_('Условие оплаты'))
|
||||
manager_id = fields.Many2one('res.users', string=_('Менеджер по продажам'))
|
||||
accountant_id = fields.Many2one('res.users', string=_('Бухгалтер по взаиморасчетам'))
|
||||
time_to_delivery_from = fields.Datetime(_('Время доставки от'))
|
||||
time_to_delivery_to = fields.Datetime(_('Время доставки до'))
|
||||
day_of_delivery = fields.Float(_('Дни доставки'))
|
||||
day_of_otgruzki = fields.Float(_('Дни отгрузки'))
|
||||
|
||||
channel_id = fields.Many2one('saleorder.channel', string=_('Канал продаж'))
|
||||
team_id = fields.Many2one('crm.team', string=_('Команда продаж'))
|
||||
order_days_ids = fields.Many2many(comodel_name='contract.day', relation='orderdays', string=_('Дни доставки'),
|
||||
column1='contract_id', column2='day_id')
|
||||
shipment_days_ids = fields.Many2many(comodel_name='contract.day', relation='shipmentdays', string=_('Дни отгрузки'),
|
||||
column1='contract_id',
|
||||
column2='day_id')
|
||||
# Доработка хедера договора
|
||||
partner_type = fields.Selection(string=_('Тип контрагента'), selection=[
|
||||
('person', 'Физ. лицо'),
|
||||
('company_ip', 'ИП'),
|
||||
('company', 'Юр. лицо')
|
||||
], required=True)
|
||||
contract_header = fields.Html(_('Шапка договора'))
|
||||
|
||||
@api.onchange('partner_type')
|
||||
def generate_contract_header(self):
|
||||
self.ensure_one()
|
||||
self.render_model = 'partner.contract.customer'
|
||||
if self.partner_type == 'company_ip':
|
||||
self.contract_header = IP_CONTACT_HEADER
|
||||
elif self.partner_type == 'person':
|
||||
self.contract_header = INDIVIDUAL_CONTRACT_HEADER
|
||||
else:
|
||||
self.contract_header = ENTITY_CONTRACT_HEADER
|
||||
# # Рендер Jinja выражение типа {{object.field}}
|
||||
result = self._render_template(self.contract_header, self.render_model, res_ids=[self.id])
|
||||
result = tools.html_sanitize(result[self.id])
|
||||
self.contract_header = result
|
||||
|
||||
@api.onchange('sec_partner_id')
|
||||
def set_pid(self):
|
||||
for s in self:
|
||||
s.partner_id = s.sec_partner_id.parent_id if s.sec_partner_id.parent_id else s.sec_partner_id
|
||||
|
||||
def _expand_states(self, states, domain, order):
|
||||
return [key for key, val in type(self).state.selection]
|
||||
|
||||
def copy_it(self):
|
||||
for s in self:
|
||||
if s.copy_from:
|
||||
for line in s.copy_from.lines:
|
||||
line.copy({'contract_id': s.id})
|
||||
|
||||
def _get_name_print(self):
|
||||
morph = pymorphy2.MorphAnalyzer()
|
||||
for s in self:
|
||||
s.name_print = False
|
||||
director = self.env['res.partner'].search([('parent_id', '=', s.partner_id.id), ('type', '=', 'director')],
|
||||
limit=1)
|
||||
if director:
|
||||
if len(director.name.split(' ')) == 3:
|
||||
lastname_old, firstname_old, middlename_old = director.name.split(' ')
|
||||
|
||||
if lastname_old:
|
||||
lastname_n = morph.parse(lastname_old)[0]
|
||||
if lastname_n.inflect({'gent'}):
|
||||
lastname_n = lastname_n.inflect({'gent'}).word
|
||||
else:
|
||||
lastname_n = lastname_old
|
||||
else:
|
||||
lastname_n = ''
|
||||
|
||||
if firstname_old:
|
||||
firstname_n = morph.parse(firstname_old)[0]
|
||||
firstname_n = firstname_n.inflect({'gent'}).word
|
||||
else:
|
||||
firstname_n = ''
|
||||
|
||||
if middlename_old:
|
||||
middlename_n = morph.parse(middlename_old)[0]
|
||||
middlename_n = middlename_n.inflect({'gent'}).word
|
||||
else:
|
||||
middlename_n = ''
|
||||
|
||||
name_print = lastname_n + ' ' + firstname_n + ' ' + middlename_n
|
||||
|
||||
s.name_print = name_print.title()
|
||||
|
||||
def _get_name_print1(self):
|
||||
morph = pymorphy2.MorphAnalyzer()
|
||||
for s in self:
|
||||
director = s.company_id.chief_id.partner_id if s.company_id.chief_id else False
|
||||
# raise exceptions.UserError(str(director))
|
||||
s.name_dirprint = False
|
||||
if director:
|
||||
if len(director.name.split(' ')) == 3:
|
||||
lastname_old, firstname_old, middlename_old = director.name.split(' ')
|
||||
|
||||
if lastname_old:
|
||||
lastname_n = morph.parse(lastname_old)[0]
|
||||
lastname_n = lastname_n.inflect({'gent'}).word
|
||||
else:
|
||||
lastname_n = ''
|
||||
|
||||
if firstname_old:
|
||||
firstname_n = morph.parse(firstname_old)[0]
|
||||
firstname_n = firstname_n.inflect({'gent'}).word
|
||||
else:
|
||||
firstname_n = ''
|
||||
|
||||
if middlename_old:
|
||||
middlename_n = morph.parse(middlename_old)[0]
|
||||
middlename_n = middlename_n.inflect({'gent'}).word
|
||||
else:
|
||||
middlename_n = ''
|
||||
|
||||
name_print = lastname_n + ' ' + firstname_n + ' ' + middlename_n
|
||||
|
||||
s.name_dirprint = name_print.title()
|
||||
|
||||
@api.model
|
||||
def create(self, vals_list):
|
||||
if isinstance(vals_list, dict):
|
||||
vals_list = [vals_list]
|
||||
|
||||
records = super().create(vals_list)
|
||||
|
||||
for record, vals in zip(records, vals_list):
|
||||
if vals.get('is_template'):
|
||||
continue
|
||||
if vals.get('type') == 'customer':
|
||||
sequence_code = 'partner.contract.customer.sequence'
|
||||
elif vals.get('type') == 'supplier':
|
||||
sequence_code = 'partner.contract.supplier.sequence'
|
||||
else:
|
||||
continue
|
||||
name = self.env['ir.sequence'].next_by_code(sequence_code)
|
||||
record.name = name
|
||||
|
||||
return records
|
||||
|
||||
# @api.model
|
||||
def write(self, values):
|
||||
|
||||
if 'state' in values:
|
||||
if self.state != values['state']:
|
||||
msg = 'Статус: ' + dict(self._fields['state'].selection).get(self.state) + ' -> ' + dict(
|
||||
self._fields['state'].selection).get(values['state'])
|
||||
self.message_post(body=msg)
|
||||
res = super(PartnerContractCustomer, self).write(values)
|
||||
return res
|
||||
|
||||
def _get_name_print1ip(self):
|
||||
for s in self:
|
||||
s.name_dirprint1 = s.company_id.chief_id.partner_id.name if s.company_id.chief_id else False
|
||||
|
||||
def _get_name_printip(self):
|
||||
for s in self:
|
||||
s.name_print1 = False
|
||||
director = self.env['res.partner'].search([('parent_id', '=', s.partner_id.id), ('type', '=', 'director')],
|
||||
limit=1)
|
||||
if director:
|
||||
s.name_print1 = director.name
|
||||
|
||||
def print_supp(self):
|
||||
# self.filtered(lambda s: s.state == 'draft').write({'state': 'sent'})
|
||||
return self.env['report'].get_action(self, 'mta_base.action_mtacontractsupp_report')
|
||||
|
||||
def print_contract_cust(self):
|
||||
for s in self:
|
||||
if s.saleorder_id:
|
||||
return s.saleorder_id.print_contract()
|
||||
else:
|
||||
raise exceptions.UserError(
|
||||
'Вы не можете напечатать договор с Клиентом, потому что нет связи с Заказом. Нужно зайти в Заказ и привязать этот договор.')
|
||||
|
||||
def contract_action_confirm(self):
|
||||
for s in self:
|
||||
if s.state == 'draft':
|
||||
s.state = 'progress'
|
||||
elif s.state == 'progress':
|
||||
s.state = 'signed'
|
||||
|
||||
def contract_in_draft(self):
|
||||
for s in self:
|
||||
s.state = 'draft'
|
||||
|
||||
@api.onchange('name')
|
||||
def set_comp_and_partn(self):
|
||||
for s in self:
|
||||
context = s._context
|
||||
order_id = context.get('sale_order_id')
|
||||
if order_id:
|
||||
sale_order = self.env['sale.order'].browse(order_id)
|
||||
|
||||
s.company_id = sale_order.company_id
|
||||
s.partner_id = sale_order.partner_id
|
||||
|
||||
@api.onchange('profile_id')
|
||||
def set_payment(self):
|
||||
for s in self:
|
||||
if s.profile_id.payment_term_id:
|
||||
s.payment_term_id = s.profile_id.payment_term_id
|
||||
|
||||
# @api.constrains('name')
|
||||
def check_name(self):
|
||||
obj = self.search([('name', '=', self.name), ('id', '!=', self.id), ('state', '!=', 'closed')])
|
||||
if obj:
|
||||
raise exceptions.ValidationError('Договор с таким номером уже существует')
|
||||
|
||||
"""
|
||||
@api.constrains('profile_id')
|
||||
def check_profile_id(self):
|
||||
contracts = self.search([('partner_id', '=', self.partner_id.id), ('id', '!=', self.id)])
|
||||
if contracts:
|
||||
profiles_in_contracts = contracts.profile_id
|
||||
# raise exceptions.ValidationError(profiles_in_contracts.ids)
|
||||
if profiles_in_contracts:
|
||||
ads = self.env['contract.allowed.profiles'].search(
|
||||
[('allowed_profiles', 'in', profiles_in_contracts.ids)])
|
||||
if ads:
|
||||
raise exceptions.ValidationError((self.profile_id.name, ads.name))
|
||||
# raise exceptions.ValidationError(contracts)
|
||||
"""
|
||||
|
||||
|
||||
def action_report_contract_with_format(self, report_format):
|
||||
if report_format == 'docx':
|
||||
file_data, filename = self._generate_contract_docx()
|
||||
mimetype = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
elif report_format == 'md':
|
||||
file_data, filename = self._generate_contract_md()
|
||||
mimetype = 'application/md'
|
||||
else:
|
||||
raise UserError("Неверный формат отчёта.")
|
||||
|
||||
attachment = self.env['ir.attachment'].create({
|
||||
'name': filename,
|
||||
'datas': base64.b64encode(file_data),
|
||||
'res_model': self._name,
|
||||
'res_id': self.id,
|
||||
'mimetype': mimetype or 'application/octet-stream',
|
||||
})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': '/web/content/%s?download=true' % attachment.id,
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def _strip_html_tags(self, html):
|
||||
clean = re.compile('<.*?>')
|
||||
return re.sub(clean, '', html)
|
||||
|
||||
def _generate_contract_docx(self):
|
||||
doc = Document()
|
||||
style = doc.styles['Normal']
|
||||
font = style.font
|
||||
font.name = 'Times New Roman'
|
||||
font.size = Pt(12)
|
||||
company = self.env.company
|
||||
partner = self.partner_id
|
||||
|
||||
table_header = doc.add_table(rows=1, cols=2)
|
||||
rc_header = table_header.rows[0].cells
|
||||
run_left = rc_header[0].paragraphs[0].add_run(f"{company.name or ''}")
|
||||
run_left.font.bold = True
|
||||
header_paragraph_right = rc_header[1].paragraphs[0]
|
||||
header_paragraph_right.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
||||
header_run_right = header_paragraph_right.add_run(f"Договор {self.name or ''} от {self.date_start or ''}")
|
||||
header_run_right.font.bold = True
|
||||
|
||||
header_paragraph_centre = doc.add_paragraph(f"Договор №{self.name or ''} от {self.date_start or ''}\n на поставку продукции")
|
||||
header_paragraph_centre.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
|
||||
table_city_date = doc.add_table(rows=1, cols=2)
|
||||
row = table_city_date.rows[0].cells
|
||||
run_city = row[0].paragraphs[0].add_run(company.city or '')
|
||||
run_city.font.bold = True
|
||||
paragraph_date = row[1].paragraphs[0]
|
||||
paragraph_date.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
||||
run_date = paragraph_date.add_run(self.get_date_text(self.date_start) or '')
|
||||
run_date.font.bold = True
|
||||
|
||||
text_contract_header = self._strip_html_tags(self.contract_header)
|
||||
doc.add_paragraph(text_contract_header)
|
||||
|
||||
for line in self.lines.sorted(key=lambda r: r.sequence, reverse=False):
|
||||
doc.add_paragraph(line.name or '')
|
||||
doc.add_paragraph(line.punct or '')
|
||||
|
||||
doc.add_paragraph("Реквизиты и подписи сторон.").alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
|
||||
table = doc.add_table(rows=7, cols=6)
|
||||
table.style = 'Table Grid'
|
||||
table.cell(0, 0).merge(table.cell(0, 2)).text = "Поставщик:"
|
||||
table.cell(0, 3).merge(table.cell(0, 5)).text = "Покупатель:"
|
||||
table.cell(1, 0).merge(table.cell(1, 2)).text = company.name or ''
|
||||
table.cell(1, 3).merge(table.cell(1, 5)).text = partner.name or ''
|
||||
table.cell(2, 0).merge(table.cell(2, 2)).text = f"ИНН {company.inn or ''}"
|
||||
table.cell(2, 3).merge(table.cell(2, 5)).text = f"ИНН {partner.inn or ''}"
|
||||
table.cell(3, 0).merge(table.cell(3, 1)).text = f"ОГРН {company.partner_id.ogrn or ''}"
|
||||
table.cell(3, 3).merge(table.cell(3, 5)).text = f"ОГРН {partner.ogrn or ''}"
|
||||
table.cell(4, 0).merge(table.cell(4, 2)).text = f"КПП {company.kpp or ''}"
|
||||
table.cell(4, 3).merge(table.cell(4, 5)).text = f"КПП {partner.kpp or ''}"
|
||||
company_address = self.address(company) or ''
|
||||
partner_address = self.address(partner) or ''
|
||||
table.cell(5, 0).merge(table.cell(5, 2)).text = f"Юридический адрес: {company_address}"
|
||||
table.cell(5, 3).merge(table.cell(5, 5)).text = f"Юридический адрес: {partner_address}"
|
||||
table.cell(6, 0).merge(table.cell(6, 2)).text = "Подпись Исполнителя:"
|
||||
table.cell(6, 3).merge(table.cell(6, 5)).text = "Подпись Заказчика:"
|
||||
|
||||
f = BytesIO()
|
||||
doc.save(f)
|
||||
f.seek(0)
|
||||
return f.read(), 'Договор (DOCX).docx'
|
||||
|
||||
|
||||
def _generate_contract_md(self):
|
||||
company = self.env.company
|
||||
partner = self.partner_id
|
||||
|
||||
lines = []
|
||||
|
||||
lines.append(f"| **{company.name or ''}** | **Договор {self.name or ''} от {self.date_start or ''}** |")
|
||||
lines.append("| --- | --- |")
|
||||
|
||||
lines.append(f"**Договор {self.name or ''} от {self.date_start or ''}**\n")
|
||||
lines.append("**на поставку продукции**\n")
|
||||
|
||||
lines.append("| Город | Дата |")
|
||||
lines.append("| --- | --- |")
|
||||
lines.append(f"| {company.city or ''} | {self.get_date_text(self.date_start)} |\n")
|
||||
|
||||
text_contract_header = self._strip_html_tags(self.contract_header or "")
|
||||
lines.append(text_contract_header + "\n")
|
||||
|
||||
for line in self.lines.sorted(key=lambda r: r.sequence):
|
||||
lines.append(f"**{line.name or ''}**")
|
||||
lines.append((line.punct or '') + "\n")
|
||||
|
||||
lines.append("**Реквизиты и подписи сторон.**\n")
|
||||
|
||||
lines.append("| Поставщик | | Покупатель | |")
|
||||
lines.append("| --- | --- | --- | --- |")
|
||||
|
||||
lines.append(f"| {company.name or ''} | | {partner.name or ''} | |")
|
||||
lines.append(f"| ИНН {company.inn or ''} | | ИНН {partner.inn or ''} | |")
|
||||
|
||||
lines.append(f"| ОГРН {company.ogrn or ''} | | ОГРН {partner.ogrn or ''} | |")
|
||||
lines.append(f"| КПП {company.kpp or ''} | | КПП {partner.kpp or ''} | |")
|
||||
|
||||
company_address = self.address(company) or ''
|
||||
partner_address = self.address(partner) or ''
|
||||
lines.append(f"| Юридический адрес: {company_address} | | Юридический адрес: {partner_address} | |")
|
||||
lines.append(f"| Подпись Исполнителя | | Подпись Заказчика | |\n")
|
||||
|
||||
md_content = "\n".join(lines)
|
||||
return md_content.encode('utf-8'), 'Договор (MD).md'
|
||||
|
||||
def action_print_by_template(self):
|
||||
return {
|
||||
'name': 'Печать по шаблону',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'report.template.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {'default_contract_id': self.id},
|
||||
}
|
||||
|
||||
class Partner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
contract_count = fields.Integer(string='Договоры', compute='get_count_contract')
|
||||
pol = fields.Selection(string="Пол", selection=[('m', 'Муж.'), ('j', 'Жен'), ], required=False)
|
||||
type = fields.Selection(selection_add=[('director', 'Директор')])
|
||||
|
||||
def get_count_contract(self):
|
||||
contract = self.env['partner.contract.customer']
|
||||
self.contract_count = contract.search_count([('partner_id', '=', self.id)])
|
||||
|
||||
def action_view_contract(self):
|
||||
action = self.env.ref('contract.contract_customer_action').read()[0]
|
||||
|
||||
action['domain'] = [('partner_id', '=', self.id)]
|
||||
|
||||
return action
|
||||
|
||||
|
||||
class ContractLine(models.Model):
|
||||
_name = 'contract.line'
|
||||
contract_id = fields.Many2one('partner.contract.customer', string='Order Reference', required=True,
|
||||
ondelete='cascade', index=True, copy=False)
|
||||
_order = "sequence desc"
|
||||
# name = fields.Text(string='Название для договора')
|
||||
# price_unit = fields.Float('Цена', default=0.0)
|
||||
# product_uom = fields.Many2one('uom.uom', string='Единица измерения')
|
||||
# product_id = fields.Many2one('product.product', string='Услуга', domain=[('sale_ok', '=', True)], change_default=True, ondelete='restrict')
|
||||
sequence = fields.Integer('Порядок')
|
||||
name = fields.Char('Номер пункта')
|
||||
punct = fields.Html('Текст пункта')
|
||||
|
||||
@api.onchange('product_id')
|
||||
def set_name(self):
|
||||
for s in self:
|
||||
s.name = s.product_id.name
|
||||
|
||||
|
||||
class AllowedProfiles(models.Model):
|
||||
_name = 'contract.allowed.profiles'
|
||||
name = fields.Char(string='Одновременно включены следующие виды договоров:')
|
||||
allowed_profiles = fields.Many2many('contract.profile', string='Виды договоров', required=True)
|
||||
|
||||
@api.onchange('allowed_profiles')
|
||||
def set_name(self):
|
||||
for s in self:
|
||||
s.name = ''
|
||||
for profile in s.allowed_profiles:
|
||||
s.name += profile.name + ' + '
|
||||
if s.name:
|
||||
if s.name[-2] == '+':
|
||||
s.name = s.name[:-2]
|
||||
|
||||
|
||||
class ContractProfile(models.Model):
|
||||
_name = 'contract.profile'
|
||||
name = fields.Char(string='Вид договора', required=True)
|
||||
payable_account_id = fields.Many2one('account.account', string='Счет кредиторской задолженности', required=True)
|
||||
receivable_account_id = fields.Many2one('account.account', string='Счет дебиторской задолженности', required=True)
|
||||
max_receivable_id = fields.Float(string='Максимальная деб. задолженность', required=True)
|
||||
payment_term_id = fields.Many2one('account.payment.term', string='Условие оплаты', required=True)
|
||||
journal_id = fields.Many2one('account.journal', string='Журнал', required=True)
|
||||
|
||||
|
||||
class ContractDay(models.Model):
|
||||
_name = 'contract.day'
|
||||
name = fields.Char('День')
|
||||
@ -0,0 +1,20 @@
|
||||
from odoo import models, fields, api
|
||||
import os
|
||||
|
||||
class ReportTemplate(models.Model):
|
||||
_name = 'contract.report.template'
|
||||
_description = 'Шаблоны отчетов'
|
||||
|
||||
name = fields.Char(string='Название', required=True)
|
||||
attachment = fields.Binary(string='Файл шаблона', required=True)
|
||||
attachment_filename = fields.Char(string='Имя файла')
|
||||
typeformat = fields.Char(string='Формат', compute='_compute_format', store=True)
|
||||
create_date = fields.Datetime(string='Дата создания', default=fields.Datetime.now)
|
||||
|
||||
@api.depends('attachment_filename')
|
||||
def _compute_typeformat(self):
|
||||
for rec in self:
|
||||
if rec.attachment_filename:
|
||||
rec.typeformat = os.path.splitext(rec.attachment_filename)[1].lstrip('.').lower()
|
||||
else:
|
||||
rec.typeformat = False
|
||||
5
l10n_ru_contract/models/contract_day.py
Normal file
5
l10n_ru_contract/models/contract_day.py
Normal file
@ -0,0 +1,5 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
class ContractDay(models.Model):
|
||||
_name = 'contract.day'
|
||||
name = fields.Char(_('День'))
|
||||
12
l10n_ru_contract/models/contract_line.py
Normal file
12
l10n_ru_contract/models/contract_line.py
Normal file
@ -0,0 +1,12 @@
|
||||
from odoo import api, fields, models, exceptions, tools, _
|
||||
|
||||
class ContractLine(models.Model):
|
||||
_name = 'contract.line'
|
||||
_order = "sequence desc"
|
||||
|
||||
|
||||
contract_id = fields.Many2one('partner.contract.customer', string='Order Reference', required=True,
|
||||
ondelete='cascade', index=True, copy=False)
|
||||
sequence = fields.Integer('Порядок')
|
||||
name = fields.Char('Номер пункта')
|
||||
punct = fields.Html('Текст пункта')
|
||||
6
l10n_ru_contract/models/contract_profile.py
Normal file
6
l10n_ru_contract/models/contract_profile.py
Normal file
@ -0,0 +1,6 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
class ContractProfile(models.Model):
|
||||
_name = 'contract.profile'
|
||||
|
||||
name = fields.Char(string=_('Вид договора'), required=True)
|
||||
33
l10n_ru_contract/models/crutch_fields_header.py
Normal file
33
l10n_ru_contract/models/crutch_fields_header.py
Normal file
@ -0,0 +1,33 @@
|
||||
ENTITY_CONTRACT_HEADER = """
|
||||
<b>{{object.company_id.partner_id.name}}</b>,
|
||||
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
|
||||
{{(object.company_id.chief_id.partner_id.function or '').lower()}}
|
||||
{{(object.name_dirprint1 or '').title()}},
|
||||
действующего на основании ОГРНИП № {{object.company_id.company_registry or ''}}, с одной стороны, и <b>{{object.partner_id.name or ''}}</b>,
|
||||
именуемое в дальнейшем <b>«Покупатель»</b>, в лице
|
||||
{{(object.get_function_partner1(object.partner_id.id) or '').lower()}}
|
||||
{{(object.name_print1 or '').title()}}, действующего на основании устава общества, с другой стороны, вместе именуемые в дальнейшем <b>«Стороны»</b> заключили
|
||||
настоящий Договор о нижеследующем:
|
||||
"""
|
||||
IP_CONTACT_HEADER = """
|
||||
<b>{{object.company_id.partner_id.name}}</b>,
|
||||
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
|
||||
{{(object.company_id.chief_id.partner_id.function or '').lower()}}
|
||||
{{(object.name_dirprint1 or '').title()}},
|
||||
действующего на основании ОГРНИП № {{object.company_id.company_registry or ''}}, с одной стороны, и <b>{{object.partner_id.name or ''}}</b>,
|
||||
именуемое в дальнейшем <b>«Покупатель»</b>, в лице
|
||||
{{(object.get_function_partner1(object.partner_id.id) or '').lower()}}
|
||||
{{(object.name_print1 or '').title()}}, действующего на основании ОГРНИП №{{object.partner_id.ogrn or ''}},
|
||||
с другой стороны, вместе именуемые в дальнейшем <b>«Стороны»</b> заключили
|
||||
настоящий Договор о нижеследующем:
|
||||
"""
|
||||
|
||||
INDIVIDUAL_CONTRACT_HEADER = """
|
||||
<b>{{object.company_id.partner_id.name}}</b>,
|
||||
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
|
||||
{{(object.company_id.chief_id.partner_id.function or '').lower()}}
|
||||
{{(object.name_dirprint1 or '').title()}},
|
||||
действующего на основании ОГРНИП № {{object.company_id.company_registry or ''}}, с одной стороны, и <b>{{object.partner_id.name or ''}}</b>,
|
||||
именуемое в дальнейшем <b>«Покупатель»</b>, вместе именуемые в дальнейшем <b>«Стороны»</b> заключили
|
||||
настоящий Договор о нижеследующем:
|
||||
"""
|
||||
313
l10n_ru_contract/models/dop_field.py
Normal file
313
l10n_ru_contract/models/dop_field.py
Normal file
@ -0,0 +1,313 @@
|
||||
from odoo import models
|
||||
from datetime import datetime
|
||||
import re
|
||||
import pymorphy2
|
||||
from odoo.tools import pycompat
|
||||
|
||||
FRACTIONS = (
|
||||
(u"десятая", u"десятых", u"десятых"),
|
||||
(u"сотая", u"сотых", u"сотых"),
|
||||
(u"тысячная", u"тысячных", u"тысячных"),
|
||||
(u"десятитысячная", u"десятитысячных", u"десятитысячных"),
|
||||
(u"стотысячная", u"стотысячных", u"стотысячных"),
|
||||
(u"миллионная", u"милллионных", u"милллионных"),
|
||||
(u"десятимиллионная", u"десятимилллионных", u"десятимиллионных"),
|
||||
(u"стомиллионная", u"стомилллионных", u"стомиллионных"),
|
||||
(u"миллиардная", u"миллиардных", u"миллиардных"),
|
||||
)
|
||||
|
||||
ONES = {
|
||||
0: (u"", u"", u""),
|
||||
1: (u"один", u"одна", u"одно"),
|
||||
2: (u"два", u"две", u"два"),
|
||||
3: (u"три", u"три", u"три"),
|
||||
4: (u"четыре", u"четыре", u"четыре"),
|
||||
5: (u"пять", u"пять", u"пять"),
|
||||
6: (u"шесть", u"шесть", u"шесть"),
|
||||
7: (u"семь", u"семь", u"семь"),
|
||||
8: (u"восемь", u"восемь", u"восемь"),
|
||||
9: (u"девять", u"девять", u"девять"),
|
||||
}
|
||||
|
||||
TENS = {
|
||||
0: u"",
|
||||
10: u"десять",
|
||||
11: u"одиннадцать",
|
||||
12: u"двенадцать",
|
||||
13: u"тринадцать",
|
||||
14: u"четырнадцать",
|
||||
15: u"пятнадцать",
|
||||
16: u"шестнадцать",
|
||||
17: u"семнадцать",
|
||||
18: u"восемнадцать",
|
||||
19: u"девятнадцать",
|
||||
2: u"двадцать",
|
||||
3: u"тридцать",
|
||||
4: u"сорок",
|
||||
5: u"пятьдесят",
|
||||
6: u"шестьдесят",
|
||||
7: u"семьдесят",
|
||||
8: u"восемьдесят",
|
||||
9: u"девяносто",
|
||||
}
|
||||
|
||||
HUNDREDS = {
|
||||
0: u"",
|
||||
1: u"сто",
|
||||
2: u"двести",
|
||||
3: u"триста",
|
||||
4: u"четыреста",
|
||||
5: u"пятьсот",
|
||||
6: u"шестьсот",
|
||||
7: u"семьсот",
|
||||
8: u"восемьсот",
|
||||
9: u"девятьсот",
|
||||
}
|
||||
|
||||
MALE = 1
|
||||
FEMALE = 2
|
||||
|
||||
import sys
|
||||
import types
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
|
||||
class Report_contract_customer(models.Model):
|
||||
_inherit = 'partner.contract.customer'
|
||||
|
||||
def img(self, img, type='png', width=0, height=0):
|
||||
if width:
|
||||
width = "width='%spx'" % (width)
|
||||
else:
|
||||
width = " "
|
||||
if height:
|
||||
height = "height='%spx'" % (height)
|
||||
else:
|
||||
height = " "
|
||||
toreturn = "<img %s %s src='data:image/%s;base64,%s' />" % (
|
||||
width,
|
||||
height,
|
||||
type,
|
||||
str(pycompat.to_text(img)))
|
||||
return toreturn
|
||||
|
||||
def numer(self, name):
|
||||
if name:
|
||||
numeration = re.findall('\d+$', name)
|
||||
if numeration: return numeration[0]
|
||||
return ''
|
||||
|
||||
def ru_date(self, date):
|
||||
if date and date != 'False':
|
||||
return dt.ru_strftime(u'"%d" %B %Y года', date=datetime.strptime(str(date), "%Y-%m-%d"), inflected=True)
|
||||
return ''
|
||||
|
||||
def ru_date2(self, date):
|
||||
if date and date != 'False':
|
||||
return dt.ru_strftime(u'%d %B %Y г.', date=datetime.strptime(str(date), "%Y-%m-%d %H:%M:%S"),
|
||||
inflected=True)
|
||||
return ''
|
||||
|
||||
def in_words(self, number):
|
||||
return numeral.in_words(number)
|
||||
|
||||
def rubles(self, sum):
|
||||
"Transform sum number in rubles to text"
|
||||
text_rubles = self.numeral_rubles(int(sum))
|
||||
copeck = round((sum - int(sum)) * 100)
|
||||
text_copeck = self.numeral_choose_plural(int(copeck), (u"копейка", u"копейки", u"копеек"))
|
||||
return ("%s %02d %s") % (text_rubles, copeck, text_copeck)
|
||||
|
||||
def numeral_rubles(self, amount, zero_for_kopeck=False):
|
||||
self.check_positive(amount)
|
||||
pts = []
|
||||
amount = round(amount, 2)
|
||||
pts.append(self.sum_string(int(amount), 1, (u"рубль", u"рубля", u"рублей")))
|
||||
remainder = self._get_float_remainder(amount, 2)
|
||||
iremainder = int(remainder)
|
||||
|
||||
if iremainder != 0 or zero_for_kopeck:
|
||||
if iremainder < 10 and len(remainder) == 1:
|
||||
iremainder *= 10
|
||||
pts.append(self.sum_string(iremainder, 2,
|
||||
(u"копейка", u"копейки", u"копеек")))
|
||||
return u" ".join(pts)
|
||||
|
||||
def _get_float_remainder(self, fvalue, signs=9):
|
||||
self.check_positive(fvalue)
|
||||
if isinstance(fvalue, integer_types):
|
||||
return "0"
|
||||
if isinstance(fvalue, Decimal) and fvalue.as_tuple()[2] == 0:
|
||||
return "0"
|
||||
|
||||
def sum_string(self, amount, gender, items=None):
|
||||
if isinstance(items, text_type):
|
||||
items = split_values(items)
|
||||
if items is None:
|
||||
items = (u"", u"", u"")
|
||||
try:
|
||||
one_item, two_items, five_items = items
|
||||
except ValueError:
|
||||
raise ValueError("Items must be 3-element sequence")
|
||||
self.check_positive(amount)
|
||||
if amount == 0:
|
||||
return u"ноль %s" % five_items
|
||||
into = u''
|
||||
tmp_val = amount
|
||||
into, tmp_val = self._sum_string_fn(into, tmp_val, gender, items)
|
||||
into, tmp_val = self._sum_string_fn(into, tmp_val, FEMALE,
|
||||
(u"тысяча", u"тысячи", u"тысяч"))
|
||||
into, tmp_val = self._sum_string_fn(into, tmp_val, MALE,
|
||||
(u"миллион", u"миллиона", u"миллионов"))
|
||||
into, tmp_val = self._sum_string_fn(into, tmp_val, MALE,
|
||||
(u"миллиард", u"миллиарда", u"миллиардов"))
|
||||
if tmp_val == 0:
|
||||
return into
|
||||
else:
|
||||
raise ValueError("Cannot operand with numbers bigger than 10**11")
|
||||
|
||||
def _sum_string_fn(self, into, tmp_val, gender, items=None):
|
||||
if items is None:
|
||||
items = (u"", u"", u"")
|
||||
one_item, two_items, five_items = items
|
||||
self.check_positive(tmp_val)
|
||||
if tmp_val == 0:
|
||||
return into, tmp_val
|
||||
words = []
|
||||
rest = tmp_val % 1000
|
||||
tmp_val = tmp_val // 1000
|
||||
if rest == 0:
|
||||
if into == u"":
|
||||
into = u"%s " % five_items
|
||||
return into, tmp_val
|
||||
end_word = five_items
|
||||
words.append(HUNDREDS[rest // 100])
|
||||
rest = rest % 100
|
||||
rest1 = rest // 10
|
||||
tens = rest1 == 1 and TENS[rest] or TENS[rest1]
|
||||
words.append(tens)
|
||||
if rest1 < 1 or rest1 > 1:
|
||||
amount = rest % 10
|
||||
end_word = self.numeral_choose_plural(amount, items)
|
||||
words.append(ONES[amount][gender - 1])
|
||||
words.append(end_word)
|
||||
words.append(into)
|
||||
words = filter(lambda x: len(x) > 0, words)
|
||||
return u" ".join(words).strip(), tmp_val
|
||||
|
||||
def check_positive(self, value, strict=False):
|
||||
if not strict and value < 0:
|
||||
raise ValueError("Value must be positive or zero, not %s" % str(value))
|
||||
if strict and value <= 0:
|
||||
raise ValueError("Value must be positive, not %s" % str(value))
|
||||
|
||||
def numeral_choose_plural(self, amount, variants):
|
||||
if isinstance(variants, text_type):
|
||||
variants = split_values(variants)
|
||||
self.check_length(variants, 3)
|
||||
amount = abs(amount)
|
||||
if amount % 10 == 1 and amount % 100 != 11:
|
||||
variant = 0
|
||||
elif amount % 10 >= 2 and amount % 10 <= 4 and \
|
||||
(amount % 100 < 10 or amount % 100 >= 20):
|
||||
variant = 1
|
||||
else:
|
||||
variant = 2
|
||||
return variants[variant]
|
||||
|
||||
def check_length(self, value, length):
|
||||
_length = len(value)
|
||||
if _length != length:
|
||||
raise ValueError("length must be %d, not %d" % \
|
||||
(length, _length))
|
||||
|
||||
def initials(self, fio):
|
||||
if fio:
|
||||
return (fio.split()[0] + ' ' + ''.join([fio[0:1] + '.' for fio in fio.split()[1:]])).strip()
|
||||
return ''
|
||||
|
||||
def address(self, partner):
|
||||
repr = []
|
||||
if partner.zip: repr.append(partner.zip)
|
||||
if partner.city: repr.append(partner.city)
|
||||
if partner.street: repr.append(partner.street)
|
||||
if partner.street2: repr.append(partner.street2)
|
||||
return ', '.join(repr)
|
||||
|
||||
def address_delivery(self, partner):
|
||||
if partner:
|
||||
addr = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'delivery')], limit=1)
|
||||
repr = []
|
||||
if addr:
|
||||
if addr.zip: repr.append(addr.zip)
|
||||
if addr.city: repr.append(addr.city)
|
||||
if addr.street: repr.append(addr.street)
|
||||
if addr.street2: repr.append(addr.street2)
|
||||
return ', '.join(repr)
|
||||
|
||||
def get_function_print(self, function):
|
||||
morph = pymorphy2.MorphAnalyzer()
|
||||
if function:
|
||||
f = morph.parse(function)[0]
|
||||
f = f.inflect({'gent'}).word
|
||||
return f.title()
|
||||
|
||||
def get_function_partnerip(self, partner):
|
||||
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
|
||||
if director:
|
||||
if director.function:
|
||||
return director.function
|
||||
|
||||
def get_function_partner(self, partner):
|
||||
res = []
|
||||
morph = pymorphy2.MorphAnalyzer()
|
||||
if partner:
|
||||
director = self.env['res.partner'].search([('parent_id', '=', partner), ('type', '=', 'director')], limit=1)
|
||||
if director:
|
||||
if director.function:
|
||||
list_f = str(director.function).split(' ')
|
||||
for func in list_f:
|
||||
f = morph.parse(func)[0]
|
||||
f = f.inflect({'gent'}).word
|
||||
res.append(f)
|
||||
return ' '.join(res)
|
||||
|
||||
|
||||
|
||||
def get_date_text(self, date):
|
||||
month_list = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября',
|
||||
'ноября', 'декабря']
|
||||
if date:
|
||||
date_list = str(date).split('-')
|
||||
if date_list[0] and date_list[1] and date_list[2]:
|
||||
return ('"' + date_list[2] + '" ' + month_list[int(date_list[1]) - 1] + ' ' + date_list[0] + ' г.')
|
||||
|
||||
def get_bank(self, partner):
|
||||
repr = []
|
||||
bank = None
|
||||
if partner.bank_ids:
|
||||
bank = partner.bank_ids[0]
|
||||
elif partner.parent_id.bank_ids:
|
||||
bank = partner.parent_id.bank_ids[0]
|
||||
if bank and bank.bank_name: repr.append(bank.bank_name)
|
||||
if bank and bank.acc_number: repr.append(u"Р/счет " + bank.acc_number)
|
||||
if bank and bank.bank_bic: repr.append(u"БИК " + bank.bank_bic)
|
||||
if bank and bank.bank_corr_acc: repr.append(u"к/с " + bank.bank_corr_acc)
|
||||
return '<br/>'.join(repr)
|
||||
130
l10n_ru_contract/models/invoice_saleorder.py
Normal file
130
l10n_ru_contract/models/invoice_saleorder.py
Normal file
@ -0,0 +1,130 @@
|
||||
from odoo import api, fields, models, exceptions
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class contract_sale_order(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
mt_contractid = fields.Many2one('partner.contract.customer', string='Номер договора')
|
||||
sec_partner_id = fields.Many2one('res.partner', string='Контрагент', store=True, compute='get_pid')
|
||||
stamp = fields.Boolean(string='Печать и подпись', related='mt_contractid.stamp')
|
||||
|
||||
@api.depends('partner_id')
|
||||
def get_pid(self):
|
||||
for s in self:
|
||||
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
|
||||
|
||||
@api.onchange('mt_contractid')
|
||||
def set_ons(self):
|
||||
for s in self:
|
||||
if s.mt_contractid:
|
||||
s.payment_term_id = s.mt_contractid.payment_term_id
|
||||
|
||||
@api.constrains('state')
|
||||
def late_payment_check(self):
|
||||
for s in self:
|
||||
if s.mt_contractid:
|
||||
if s.state == 'sale':
|
||||
late_invoices_count = 0
|
||||
max_receivable = s.mt_contractid.profile_id.max_receivable_id # макс. деб. задолженность в договоре
|
||||
# ищу просроченные инвойсы контрагента указанного в заказе со стейтом "Подтверждено"
|
||||
invoices_obj = self.env['account.move'].search([('partner_id', '=', s.partner_id.id),
|
||||
('state', '=', 'posted'),
|
||||
('payment_state', 'not in', ['paid','reversed']),
|
||||
('move_type','in',['out_invoice']),
|
||||
('invoice_date_due', '<', datetime.now().date())])
|
||||
|
||||
for invoice in invoices_obj:
|
||||
late_invoices_count += invoice.amount_residual # складываю деб. задолженность по просроченным инвойсам
|
||||
|
||||
if late_invoices_count > max_receivable:
|
||||
raise exceptions.ValidationError(
|
||||
f'Нельзя подтвердить заказ, так как у контрагента {s.sec_partner_id.name} нарушено '
|
||||
f'условие по дебиторской задолженности.\n\n'
|
||||
f'Контрагент {s.sec_partner_id.name} должен {late_invoices_count}руб.\n'
|
||||
f'Максимальная дебиторская задолженность указанная в '
|
||||
f'договоре №{s.mt_contractid.name} - {max_receivable}руб.\n\n'
|
||||
f'Проверьте следующие неоплаченные счета контрагента:\n'
|
||||
f'{", ".join([invoice.name for invoice in invoices_obj])}')
|
||||
|
||||
# def _get_invoice_grouping_keys(self):
|
||||
# res = super(contract_sale_order, self)._get_invoice_grouping_keys()
|
||||
# res.append('mt_contractid')
|
||||
# return res
|
||||
|
||||
def _prepare_invoice(self):
|
||||
invoice_vals = super(contract_sale_order, self)._prepare_invoice()
|
||||
for s in self:
|
||||
if s.mt_contractid:
|
||||
invoice_vals['mt_contractid'] = s.mt_contractid.id
|
||||
invoice_vals['osnovanie'] = 'Договор № ' + s.mt_contractid.name + ' от ' + fields.Datetime.from_string(
|
||||
s.mt_contractid.date_start).strftime("%d.%m.%Y")
|
||||
return invoice_vals
|
||||
|
||||
|
||||
class ContractCreateInvoice(models.TransientModel):
|
||||
_inherit = 'sale.advance.payment.inv'
|
||||
|
||||
def _prepare_invoice_values(self, order, so_lines):
|
||||
invoice_vals = super(ContractCreateInvoice, self)._prepare_invoice_values(order, so_lines)
|
||||
if order.mt_contractid:
|
||||
invoice_vals['mt_contractid'] = order.mt_contractid.id
|
||||
invoice_vals['osnovanie'] = 'Договор № ' + order.mt_contractid.name + ' от ' + fields.Datetime.from_string(order.mt_contractid.date_start).strftime("%d.%m.%Y")
|
||||
return invoice_vals
|
||||
|
||||
class contract_invoice(models.Model):
|
||||
_inherit = 'account.move'
|
||||
mt_contractid = fields.Many2one('partner.contract.customer', string='Номер договора')
|
||||
sf_number = fields.Char(string='Номер с/ф')
|
||||
osnovanie = fields.Char(string='Основание')
|
||||
sec_partner_id = fields.Many2one('res.partner', string='Контрагент', store=True, compute='get_pid')
|
||||
stamp = fields.Boolean(string='Печать и подпись', related='mt_contractid.stamp')
|
||||
|
||||
@api.depends('partner_id')
|
||||
def get_pid(self):
|
||||
for s in self:
|
||||
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
|
||||
|
||||
@api.onchange('mt_contractid')
|
||||
def set_ons(self):
|
||||
if self.mt_contractid:
|
||||
self.osnovanie = 'Договор № ' + self.mt_contractid.name + ' от ' + fields.Datetime.from_string(
|
||||
self.mt_contractid.date_start).strftime("%d.%m.%Y")
|
||||
|
||||
@api.constrains('state')
|
||||
def invoice_fields_check(self):
|
||||
for s in self:
|
||||
if s.state == 'posted':
|
||||
if s.mt_contractid:
|
||||
errors_list = []
|
||||
journal_in_contract = s.mt_contractid.profile_id.journal_id
|
||||
payment_term_in_contract = s.mt_contractid.profile_id.payment_term_id
|
||||
receivable_in_contract = s.mt_contractid.profile_id.receivable_account_id
|
||||
|
||||
if journal_in_contract != s.journal_id:
|
||||
errors_list.append(f'Отличается Журнал - [{s.journal_id.name}] '
|
||||
f'и указанный в договоре №{s.mt_contractid.name} '
|
||||
f'Журнал - [{journal_in_contract.name}]\n\n')
|
||||
|
||||
if payment_term_in_contract != s.invoice_payment_term_id:
|
||||
errors_list.append(f'Отличается поле "Условие оплаты" в инвойсе '
|
||||
f'[Условие оплаты - {s.invoice_payment_term_id.name}] '
|
||||
f'и указанный в договоре №{s.mt_contractid.name} '
|
||||
f'[Условие оплаты - {payment_term_in_contract.name}]\n\n')
|
||||
|
||||
if receivable_in_contract not in s.line_ids.account_id:
|
||||
errors_list.append(f'Отличается поле "Счет дебиторской задолженности" в инвойсе '
|
||||
f'и указанный в договоре №{s.mt_contractid.name}')
|
||||
|
||||
if errors_list:
|
||||
raise exceptions.ValidationError(''.join(errors_list))
|
||||
|
||||
|
||||
class contact_purchase_order(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
mt_contractid = fields.Many2one('partner.contract.customer', string='Номер договора')
|
||||
sec_partner_id = fields.Many2one('res.partner', string='Контрагент', store=True, compute='get_pid')
|
||||
|
||||
@api.depends('partner_id')
|
||||
def get_pid(self):
|
||||
for s in self:
|
||||
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
|
||||
231
l10n_ru_contract/models/partner_contract_customer.py
Normal file
231
l10n_ru_contract/models/partner_contract_customer.py
Normal file
@ -0,0 +1,231 @@
|
||||
from odoo import api, fields, models, tools, _
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class PartnerContractCustomer(models.Model):
|
||||
_name = 'partner.contract.customer'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'mail.render.mixin']
|
||||
|
||||
name = fields.Char(string=_('Номер'))
|
||||
date_start = fields.Date(string=_('Дата договора'), required=True, default=fields.Datetime.now())
|
||||
date_end = fields.Date(string=_('Дата окончания'), required=True)
|
||||
type = fields.Selection(
|
||||
[('customer', 'С покупателем'),
|
||||
('supplier', 'С поставщиком'),
|
||||
('other', 'Прочие расчеты'),
|
||||
|
||||
],
|
||||
string=_('Тип договора'), default='customer', required=True)
|
||||
profile_id = fields.Many2one('contract.profile', string=_('Вид договора'), required=True)
|
||||
partner_id = fields.Many2one(
|
||||
'res.partner',
|
||||
string='Контрагент',
|
||||
required=True,
|
||||
domain="[('id', 'in', possible_partner_ids)]",
|
||||
)
|
||||
|
||||
partner_type = fields.Selection(string=_('Тип контрагента'), selection=[
|
||||
('person', 'Физ. лицо'),
|
||||
('company_ip', 'ИП'),
|
||||
('company', 'Юр. лицо')
|
||||
], required=True)
|
||||
company_id = fields.Many2one('res.company', string=_('Компания'), required=True)
|
||||
state = fields.Selection(
|
||||
[
|
||||
('draft', 'Черновик'),
|
||||
('progress', 'На согласовании'),
|
||||
('signed', 'Подписан, действует'),
|
||||
('closed', 'Истёк'),
|
||||
],
|
||||
string=_('Статус'),
|
||||
default='draft',
|
||||
group_expand=lambda self, states, domain: [
|
||||
key for key, _ in self._fields['state'].selection
|
||||
],
|
||||
)
|
||||
stamp = fields.Boolean(string=_('Печать и подпись'))
|
||||
signed = fields.Boolean(string=_('Договор подписан'))
|
||||
lines_ids = fields.One2many('contract.line', 'contract_id', string=_('Пункты договора'))
|
||||
|
||||
is_template = fields.Boolean(_('Это шаблон'))
|
||||
copy_from = fields.Many2one('partner.contract.customer', string=_('Копировать из этого шаблона'))
|
||||
|
||||
director_name_partner = fields.Char(
|
||||
string=_('ФИО директора (от партнёра)'),
|
||||
related='partner_id.parent_id.name',
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
director_name_company = fields.Char(
|
||||
string=_('ФИО руководителя (от компании)'),
|
||||
related='company_id.chief_id.partner_id.name',
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
contract_header = fields.Html(
|
||||
string=_("Шапка договора"),
|
||||
compute="_compute_contract_header",
|
||||
)
|
||||
|
||||
contract_header_template_id = fields.Many2one(
|
||||
"ir.ui.view",
|
||||
string=_("Шаблон шапки договора"),
|
||||
domain='['
|
||||
'("type", "=", "qweb"), '
|
||||
'"|", '
|
||||
'"&", ("model", "!=", False), ("model", "=", "partner.contract.customer"), '
|
||||
'"&", ("model", "=", False), ("key", "ilike", "l10n_ru_contract.contract_header_")'
|
||||
']',
|
||||
help=_("QWeb-шаблон, по которому генерируется поле «Шапка договора»."),
|
||||
)
|
||||
|
||||
use_custom_contract_header = fields.Boolean(
|
||||
string=_("Использовать кастомный шаблон"),
|
||||
default=False,
|
||||
help=_(
|
||||
"Если включено, шапка договора редактируется вручную и "
|
||||
"не перегенерируется автоматически при изменении реквизитов."
|
||||
),
|
||||
)
|
||||
|
||||
possible_partner_ids = fields.Many2many(
|
||||
'res.partner',
|
||||
compute='_compute_possible_partners',
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
@api.depends('partner_type')
|
||||
def _compute_possible_partners(self):
|
||||
company_ids = self.env.companies.ids
|
||||
internal_user_partners = self.env['res.users'].sudo().search([
|
||||
('share', '=', False)
|
||||
]).mapped('partner_id.id')
|
||||
|
||||
for rec in self:
|
||||
if rec.partner_type in ('company', 'company_ip'):
|
||||
rec.possible_partner_ids = self.env['res.partner'].search([
|
||||
('is_company', '=', True),
|
||||
('id', 'not in', company_ids),
|
||||
])
|
||||
elif rec.partner_type == 'person':
|
||||
rec.possible_partner_ids = self.env['res.partner'].search([
|
||||
('is_company', '=', False),
|
||||
('id', 'not in', internal_user_partners),
|
||||
])
|
||||
else:
|
||||
rec.possible_partner_ids = self.env['res.partner'].search([])
|
||||
|
||||
@api.onchange('partner_type')
|
||||
def _onchange_partner_type(self):
|
||||
if self.partner_id:
|
||||
if self.partner_type in ('company', 'company_ip'):
|
||||
if not self.partner_id.is_company or self.partner_id.id in self.env.companies.ids:
|
||||
self.partner_id = False
|
||||
elif self.partner_type == 'person':
|
||||
if self.partner_id.is_company or self.partner_id.id in self.env['res.users'].sudo().search(
|
||||
[('share', '=', False)]).mapped('partner_id.id'):
|
||||
self.partner_id = False
|
||||
|
||||
def _get_default_header_xmlid(self):
|
||||
self.ensure_one()
|
||||
if self.partner_type == "company_ip":
|
||||
xmlid = "l10n_ru_contract.contract_header_ip"
|
||||
elif self.partner_type == "person":
|
||||
xmlid = "l10n_ru_contract.contract_header_individual"
|
||||
else:
|
||||
xmlid = "l10n_ru_contract.contract_header_entity"
|
||||
|
||||
return xmlid
|
||||
|
||||
def _render_header_qweb(self):
|
||||
self.ensure_one()
|
||||
view = self.contract_header_template_id
|
||||
if not view:
|
||||
xmlid = self._get_default_header_xmlid()
|
||||
view = self.env.ref(xmlid, raise_if_not_found=False)
|
||||
self.contract_header_template_id = view
|
||||
try:
|
||||
html = self.env["ir.qweb"]._render(view.key, {"object": self})
|
||||
except Exception as e:
|
||||
return ""
|
||||
return tools.html_sanitize(html or "")
|
||||
|
||||
@api.depends(
|
||||
"partner_type",
|
||||
"partner_id",
|
||||
"company_id",
|
||||
"contract_header_template_id",
|
||||
"use_custom_contract_header",
|
||||
)
|
||||
def _compute_contract_header(self):
|
||||
for rec in self:
|
||||
rec.contract_header = rec._render_header_qweb()
|
||||
|
||||
@api.onchange("partner_type")
|
||||
def _onchange_partner_type_set_template(self):
|
||||
for rec in self:
|
||||
xmlid = rec._get_default_header_xmlid()
|
||||
view = rec.env.ref(xmlid, raise_if_not_found=False)
|
||||
rec.contract_header_template_id = view
|
||||
|
||||
def generate_contract_header(self):
|
||||
for rec in self:
|
||||
rec.contract_header = rec._render_header_qweb()
|
||||
|
||||
@api.onchange("use_custom_contract_header")
|
||||
def _onchange_use_custom_contract_header(self):
|
||||
for rec in self:
|
||||
if rec.use_custom_contract_header:
|
||||
if not rec.contract_header:
|
||||
rec.contract_header = rec._render_header_qweb()
|
||||
else:
|
||||
rec.contract_header = rec._render_header_qweb()
|
||||
|
||||
@api.onchange('sec_partner_id')
|
||||
def set_pid(self):
|
||||
for s in self:
|
||||
s.partner_id = s.sec_partner_id.parent_id if s.sec_partner_id.parent_id else s.sec_partner_id
|
||||
|
||||
def copy_it(self):
|
||||
for s in self:
|
||||
if s.copy_from:
|
||||
for line in s.copy_from.lines_ids:
|
||||
line.copy({'contract_id': s.id})
|
||||
|
||||
@api.model
|
||||
def create(self, vals_list):
|
||||
if not isinstance(vals_list, list):
|
||||
vals_list = [vals_list]
|
||||
|
||||
for vals in vals_list:
|
||||
if not vals.get('is_template'):
|
||||
if vals.get('type') == 'customer':
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('partner.contract.customer.sequence')
|
||||
elif vals.get('type') == 'supplier':
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('partner.contract.supplier.sequence')
|
||||
|
||||
records = super().create(vals_list)
|
||||
return records
|
||||
|
||||
def write(self, values):
|
||||
|
||||
if 'state' in values:
|
||||
if self.state != values['state']:
|
||||
msg = 'Статус: ' + dict(self._fields['state'].selection).get(self.state) + ' -> ' + dict(
|
||||
self._fields['state'].selection).get(values['state'])
|
||||
self.message_post(body=msg)
|
||||
return super().write(values)
|
||||
|
||||
|
||||
def action_set_on_approval(self):
|
||||
for rec in self:
|
||||
rec.state = 'progress'
|
||||
|
||||
def action_confirm(self):
|
||||
for rec in self:
|
||||
rec.state = 'signed'
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
for rec in self:
|
||||
rec.state = 'draft'
|
||||
13
l10n_ru_contract/models/purchase_order.py
Normal file
13
l10n_ru_contract/models/purchase_order.py
Normal file
@ -0,0 +1,13 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
mt_contract_id = fields.Many2one('partner.contract.customer', string=_('Номер договора'))
|
||||
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент'), store=True, compute='_compute_get_pid')
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_get_pid(self):
|
||||
for s in self:
|
||||
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
|
||||
10
l10n_ru_contract/models/res_company.py
Normal file
10
l10n_ru_contract/models/res_company.py
Normal file
@ -0,0 +1,10 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
class Company(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
inn = fields.Char(related='partner_id.inn', readonly=False)
|
||||
kpp = fields.Char(related='partner_id.kpp', readonly=False)
|
||||
okpo = fields.Char(related='partner_id.okpo', readonly=False)
|
||||
chief_id = fields.Many2one('res.users', 'Имя директора')
|
||||
stamp = fields.Binary("Stamp")
|
||||
24
l10n_ru_contract/models/res_partner.py
Normal file
24
l10n_ru_contract/models/res_partner.py
Normal file
@ -0,0 +1,24 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
class Partner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
ogrn = fields.Char('ОГРН')
|
||||
okpo = fields.Char('ОКПО')
|
||||
inn = fields.Char('ИНН')
|
||||
kpp = fields.Char('KPP')
|
||||
passport = fields.Char('Паспорт')
|
||||
|
||||
contract_count = fields.Integer(string=_('Договоры'), compute='get_count_contract')
|
||||
pol = fields.Selection(string=_("Пол"), selection=[('m', 'Муж.'), ('j', 'Жен'), ], required=False)
|
||||
type = fields.Selection(selection_add=[('director', 'Директор')])
|
||||
|
||||
def get_count_contract(self):
|
||||
contract = self.env['partner.contract.customer']
|
||||
for s in self:
|
||||
s.contract_count = contract.search_count([('partner_id', '=', s.id)])
|
||||
|
||||
def action_view_contract(self):
|
||||
action = self.env.ref('l10n_ru_contract.contract_customer_action').read()[0]
|
||||
action['domain'] = [('partner_id', '=', self.id)]
|
||||
return action
|
||||
6
l10n_ru_contract/models/res_partner_bank.py
Normal file
6
l10n_ru_contract/models/res_partner_bank.py
Normal file
@ -0,0 +1,6 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
|
||||
|
||||
class Partner_Bank(models.Model):
|
||||
_inherit = 'res.partner.bank'
|
||||
bank_corr_acc = fields.Char('Кор.счет')
|
||||
14
l10n_ru_contract/models/sale_make_invoice_advance.py
Normal file
14
l10n_ru_contract/models/sale_make_invoice_advance.py
Normal file
@ -0,0 +1,14 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ContractCreateInvoice(models.TransientModel):
|
||||
_inherit = 'sale.advance.payment.inv'
|
||||
|
||||
# при выбора счета "Авансовый платеж"
|
||||
@api.model
|
||||
def _create_invoice(self, order, so_line, amount):
|
||||
res = super(ContractCreateInvoice, self)._create_invoice(order, so_line, amount)
|
||||
if order.mt_contract_id:
|
||||
res.write({'mt_contract_id': order.mt_contract_id,
|
||||
'journal_id': order.mt_contract_id.profile_id.journal_id, })
|
||||
return res
|
||||
53
l10n_ru_contract/models/sale_order.py
Normal file
53
l10n_ru_contract/models/sale_order.py
Normal file
@ -0,0 +1,53 @@
|
||||
from odoo import api, fields, models, exceptions, _
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
mt_contract_id = fields.Many2one('partner.contract.customer', string=_('Номер договора'))
|
||||
sec_partner_id = fields.Many2one('res.partner', string=_('Контрагент'), store=True, compute='_compute_get_pid')
|
||||
stamp = fields.Boolean(string=_('Печать и подпись'), related='mt_contract_id.stamp')
|
||||
|
||||
@api.depends('partner_id')
|
||||
def _compute_get_pid(self):
|
||||
for s in self:
|
||||
s.sec_partner_id = s.partner_id.parent_id if s.partner_id.parent_id else s.partner_id
|
||||
|
||||
@api.onchange('mt_contract_id')
|
||||
def set_ons(self):
|
||||
if self.mt_contract_id:
|
||||
self.payment_term_id = self.mt_contract_id.payment_term_id
|
||||
|
||||
@api.constrains('state')
|
||||
def late_payment_check(self):
|
||||
if self.mt_contract_id:
|
||||
if self.state == 'sale':
|
||||
late_invoices_count = 0
|
||||
max_receivable = self.mt_contract_id.profile_id.max_receivable_id # макс. деб. задолженность в договоре
|
||||
# ищу просроченные инвойсы контрагента указанного в заказе со стейтом "Подтверждено"
|
||||
invoices_obj = self.env['account.move'].search([('partner_id', '=', self.partner_id.id),
|
||||
('state', '=', 'posted'),
|
||||
('invoice_date_due', '<', datetime.now().date())])
|
||||
|
||||
for invoice in invoices_obj:
|
||||
late_invoices_count += invoice.amount_residual # складываю деб. задолженность по просроченным инвойсам
|
||||
|
||||
if late_invoices_count > max_receivable:
|
||||
raise exceptions.ValidationError(
|
||||
f'Нельзя подтвердить заказ, так как у контрагента {self.sec_partner_id.name} нарушено '
|
||||
f'условие по дебиторской задолженности.\n\n'
|
||||
f'Контрагент {self.sec_partner_id.name} должен {late_invoices_count}руб.\n'
|
||||
f'Максимальная дебиторская задолженность указанная в '
|
||||
f'договоре №{self.mt_contract_id.name} - {max_receivable}руб.\n\n'
|
||||
f'Проверьте следующие неоплаченные счета контрагента:\n'
|
||||
f'{", ".join([invoice.name for invoice in invoices_obj])}')
|
||||
|
||||
# при выбора счета "Обычный счет"
|
||||
@api.model
|
||||
def _create_invoices(self, grouped=False, final=False, date=None):
|
||||
res = super(SaleOrder, self)._create_invoices(grouped, final, date)
|
||||
if self.mt_contract_id:
|
||||
res.write({'mt_contract_id': self.mt_contract_id,
|
||||
'journal_id': self.mt_contract_id.profile_id.journal_id})
|
||||
return res
|
||||
2
l10n_ru_contract/report/__init__.py
Normal file
2
l10n_ru_contract/report/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import report_contract
|
||||
14
l10n_ru_contract/report/report_contract.py
Normal file
14
l10n_ru_contract/report/report_contract.py
Normal file
@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, models
|
||||
|
||||
class ContractCustomerReport(models.AbstractModel):
|
||||
_name = 'contract.customer.report'
|
||||
|
||||
|
||||
def get_report_values(self, docids, data=None):
|
||||
docs = self.env['partner.contract.customer'].browse(docids)
|
||||
return {
|
||||
'doc_ids': docs.ids,
|
||||
'doc_model': 'partner.contract.customer',
|
||||
'docs': docs,
|
||||
}
|
||||
398
l10n_ru_contract/report/report_contract.xml
Normal file
398
l10n_ru_contract/report/report_contract.xml
Normal file
@ -0,0 +1,398 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="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">30</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>
|
||||
|
||||
<record id="action_report_contract_customer" model="ir.actions.report">
|
||||
<field name="name">Договор</field>
|
||||
<field name="model">partner.contract.customer</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_ru_contract.report_contract_customer</field>
|
||||
<field name="report_file">l10n_ru_contract.report_contract_customer</field>
|
||||
<field name="print_report_name">'Договор - %s' % (object.name)</field>
|
||||
<field name="binding_model_id" ref="l10n_ru_contract.model_partner_contract_customer" />
|
||||
<field name="paperformat_id" ref="paperformat_a4" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<template id="report_contract_customer">
|
||||
<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: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0">
|
||||
<t t-esc="o.company_id.name or ''"/>
|
||||
</td>
|
||||
<td class="R0C1">Договор
|
||||
<t t-esc="o.name or ''"/>
|
||||
от
|
||||
<t
|
||||
t-esc="o.date_start or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="page">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: center; vertical-align: medium;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium;}
|
||||
tr.R0 td.R0C2{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium;}
|
||||
tr.R0 td.R0C3{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium; margin: 40px;}
|
||||
p.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align: center;
|
||||
vertical-align: medium; font-weight: bold;}
|
||||
div.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium;}
|
||||
tr.R0 td.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium;}
|
||||
tr.R0 td.R0C6rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-top: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid; border-right:
|
||||
#000000 1px solid}
|
||||
tr.R0 td.R0C6r{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-right: #000000 1px solid}
|
||||
tr.R0 td.R0C6l{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
text-align: left; vertical-align: medium; border-left: #000000 1px solid;}
|
||||
tr.R0 td.R0C6b{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-top: #000000 1px solid;}
|
||||
tr.R0 td.R0C6t{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-top: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-top: #000000 1px solid;
|
||||
border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -30px; left: 80px; }
|
||||
tr.R0 td.R0C6tS { overflow: visible; }
|
||||
tr.R0 td.R0C6tS span { position: relative; }
|
||||
tr.R0 td.R0C6tS span img { position: absolute; width: 140px; top: -40px; left: -200px; }
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: auto;">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="2">Договор №
|
||||
<t t-esc="o.name"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="2">на поставку продукции</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1">
|
||||
<t t-esc="company.partner_id.city"/>
|
||||
</td>
|
||||
<td class="R0C2">
|
||||
<t t-esc="o.get_date_text(o.date_start)"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C3" colspan="2">
|
||||
<span>
|
||||
<t t-raw="o.contract_header or ''"/>
|
||||
</span>
|
||||
</td>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<!--t t-foreach="o.lines.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="2"><t t-esc="line.name"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="2"><span><t t-raw="line.punct"/></span></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="2"></td>
|
||||
</tr>
|
||||
</t-->
|
||||
</tbody>
|
||||
</table>
|
||||
<t t-foreach="o.lines_ids.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<p class="R0C4">
|
||||
<t t-esc="line.name or ''"/>
|
||||
</p>
|
||||
<div class="R0C5">
|
||||
<span>
|
||||
<t t-raw="line.punct or ''"/>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: avoid;">
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="6">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="6"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rtl" colspan="3">Поставщик:</td>
|
||||
<td class="R0C6rtl" colspan="3">Покупатель:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">
|
||||
<t t-esc="o.company_id.name or ''"/>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">
|
||||
<t t-esc="o.partner_id.name or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">ИНН
|
||||
<t t-esc="o.company_id.inn or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">ИНН
|
||||
<t t-esc="o.partner_id.inn or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="2">ОГРН
|
||||
<t t-esc="o.company_id.company_registry or ''"/>
|
||||
</td>
|
||||
<td class="R0C6r" rowspan="2">
|
||||
<t t-esc="o.director_name_company or ''"/>
|
||||
</td>
|
||||
<t t-if="o.partner_type in ['company', 'company_ip']">
|
||||
<td class="R0C7rl" colspan="3">ОГРН
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3">Паспорт
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">КПП
|
||||
<t t-esc="o.company_id.kpp or ''"/>
|
||||
</td>
|
||||
<t t-if="o.partner_type == 'company'">
|
||||
<td class="R0C7rl" colspan="3">КПП
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3"></td>
|
||||
</t>
|
||||
</tr>
|
||||
<!-- <tr class="R0">-->
|
||||
<!-- <td class="R0C6rl" colspan="3">Расчетный счет:</td>-->
|
||||
<!-- <td class="R0C6rl" colspan="3">Расчетный счет:</td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <tr class="R0">-->
|
||||
<!-- <td class="R0C6l" colspan="3">-->
|
||||
<!-- <t t-raw="o.get_bank(o.company_id.partner_id) or ''"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="R0C7rl" colspan="3">-->
|
||||
<!-- <t t-raw="o.get_bank(o.partner_id) or ''"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- </tr>-->
|
||||
<!-- <td class="R0C6tI">-->
|
||||
<!-- <SPAN>-->
|
||||
<!-- <t-->
|
||||
<!-- t-raw="o.stamp and o.img(company.chief_id.facsimile) or ''"/>-->
|
||||
<!-- </SPAN>-->
|
||||
<!-- </td>-->
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">
|
||||
<t t-esc="o.address(o.company_id) or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">
|
||||
<t t-esc="o.address(o.partner_id) or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.img(o.company_id.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6tS">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.img(o.company_id.stamp) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6b"></td>
|
||||
<td class="R0C6r">
|
||||
<t t-esc="''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 30px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: bottom;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: right; vertical-align: bottom;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: top; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -5px; left: 5px;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0">Подпись Исполнителя</td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t
|
||||
t-raw="o.stamp and o.img(company.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C0"></td>
|
||||
<td class="R0C1">Подпись Заказчика</td>
|
||||
<td class="R0C6tI"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-xs-3 pull-right">
|
||||
<small style="font-size:18px;">
|
||||
<span>Страница</span>
|
||||
<span class="page"/>
|
||||
из
|
||||
<span class="topage"/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
24
l10n_ru_contract/report/report_contract_docx.xml
Normal file
24
l10n_ru_contract/report/report_contract_docx.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="action_contract_generate_docx" model="ir.actions.server">
|
||||
<field name="name">Договор DOCX</field>
|
||||
<field name="model_id" ref="l10n_ru_contract.model_partner_contract_customer"/>
|
||||
<field name="binding_model_id" ref="l10n_ru_contract.model_partner_contract_customer"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = records.action_report_contract_with_format('docx')
|
||||
</field>
|
||||
<field name="binding_type">action</field>
|
||||
</record>
|
||||
|
||||
<record id="action_contract_generate_md" model="ir.actions.server">
|
||||
<field name="name">Договор MD</field>
|
||||
<field name="model_id" ref="l10n_ru_contract.model_partner_contract_customer"/>
|
||||
<field name="binding_model_id" ref="l10n_ru_contract.model_partner_contract_customer"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = records.action_report_contract_with_format('md')
|
||||
</field>
|
||||
<field name="binding_type">action</field>
|
||||
</record>
|
||||
</odoo>
|
||||
15
l10n_ru_contract/report/report_contract_invoice.py
Normal file
15
l10n_ru_contract/report/report_contract_invoice.py
Normal file
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ContractCustomerReportInvoice(models.AbstractModel):
|
||||
_name = 'contract.customer.report_invoice'
|
||||
|
||||
|
||||
def get_report_values(self, docids, data=None):
|
||||
docs = self.env['account.move'].browse(docids)
|
||||
return {
|
||||
'doc_ids': docs.ids,
|
||||
'doc_model': 'account.move',
|
||||
'docs': docs,
|
||||
}
|
||||
592
l10n_ru_contract/report/report_contract_invoice.xml
Normal file
592
l10n_ru_contract/report/report_contract_invoice.xml
Normal file
@ -0,0 +1,592 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_contract_customer_invoice">
|
||||
<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: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0"><t t-esc="o.mt_contract_id.company_id.name or ''"/></td>
|
||||
<td class="R0C1">Договор <t t-esc="o.mt_contract_id.name or ''"/> от <t
|
||||
t-esc="o.mt_contract_id.date_start or ''"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="page">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: center; vertical-align: medium;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium;}
|
||||
tr.R0 td.R0C2{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium;}
|
||||
tr.R0 td.R0C3{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium; margin: 40px;}
|
||||
tr.R0 td.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
start; vertical-align: medium;}
|
||||
p.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align: center;
|
||||
vertical-align: medium; font-weight: bold;}
|
||||
div.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium;}
|
||||
tr.R0 td.R0C6rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-top: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid; border-right:
|
||||
#000000 1px solid}
|
||||
tr.R0 td.R0C6r{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-right: #000000 1px solid}
|
||||
tr.R0 td.R0C6l{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
text-align: left; vertical-align: medium; border-left: #000000 1px solid;}
|
||||
tr.R0 td.R0C6b{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6t{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-top: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-top: #000000 1px solid;
|
||||
border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -30px; left: 80px; }
|
||||
tr.R0 td.R0C6tS { overflow: visible; }
|
||||
tr.R0 td.R0C6tS span { position: relative; }
|
||||
tr.R0 td.R0C6tS span img { position: absolute; width: 140px; top: -40px; left: -200px; }
|
||||
tr.R0 td.R0C8{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C9box{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid; border-top: #000000 1px solid;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: auto;">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="2">Договор № <t t-esc="o.mt_contractid.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="2">на поставку продукции</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"><t t-esc="o.mt_contractid.company_id.partner_id.city or ''"/></td>
|
||||
<td class="R0C2"><t
|
||||
t-esc="o.mt_contractid.get_date_text(o.mt_contractid.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C3" colspan="2">
|
||||
<span>
|
||||
<t t-raw="o.mt_contractid.contract_header or ''"/>
|
||||
</span>
|
||||
</td>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<!--t t-foreach="o.mt_contractid.lines.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="2"><t t-esc="line.name"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="2"><t t-raw="line.punct"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="2"></td>
|
||||
</tr>
|
||||
</t-->
|
||||
<!--tr class="R0">
|
||||
<td class="R0C4" colspan="2">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr-->
|
||||
</tbody>
|
||||
</table>
|
||||
<t t-foreach="o.mt_contractid.lines.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<p class="R0C4"><t t-esc="line.name or ''"/></p>
|
||||
<div class="R0C5"><span><t t-raw="line.punct or ''"/></span></div>
|
||||
</t>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: avoid;">
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="6">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="6"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rtl" colspan="3">Поставщик:</td>
|
||||
<td class="R0C6rtl" colspan="3">Покупатель:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contractid.company_id.name or ''"/></td>
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contractid.partner_id.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">ИНН
|
||||
<t t-esc="o.mt_contractid.company_id.inn or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">ИНН <t
|
||||
t-esc="o.mt_contractid.partner_id.inn or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="2">ОГРН
|
||||
<t t-esc="o.company_id.company_registry or ''"/>
|
||||
</td>
|
||||
<td class="R0C6r" rowspan="2">
|
||||
<t t-esc="o.mt_contractid.name_dirprint1 or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contractid.partner_type in ['company', 'company_ip']">
|
||||
<td class="R0C7rl" colspan="3">ОГРН
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3">Паспорт
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">КПП
|
||||
<t t-esc="o.mt_contractid.company_id.kpp or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contractid.partner_type == 'company'">
|
||||
<td class="R0C7rl" colspan="3">КПП
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3"></td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">
|
||||
<t t-esc="o.mt_contractid.address(o.company_id) or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">
|
||||
<t t-esc="o.mt_contractid.address(o.partner_id) or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contractid.img(o.company_id.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6tS">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contractid.img(o.company_id.stamp) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6b"></td>
|
||||
<td class="R0C6r">
|
||||
<t t-esc="''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="page-break-before:always;"></p>
|
||||
<!--p style="page-break-after:always"></p>
|
||||
<div style="page-break-after: auto;"><span style="display: none;"> </span></div>
|
||||
<p style="page-break-inside: avoid">
|
||||
<div style="page-break-inside: auto"></div-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C8" colspan="8">Приложение<br/>к договору № <t
|
||||
t-esc="o.mt_contractid.name"/><br/>на поставку продукции<br/>от <t
|
||||
t-esc="o.mt_contractid.get_date_text(o.mt_contractid.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8">Спецификация № 1</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="4"><t
|
||||
t-esc="o.mt_contractid.company_id.partner_id.city or ''"/></td>
|
||||
<td class="R0C2" colspan="4"><t
|
||||
t-esc="o.mt_contractid.get_date_text(o.mt_contractid.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C3" colspan="8">
|
||||
<span>
|
||||
<t t-raw="o.mt_contractid.contract_header or ''"/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="8">1. По договору на поставку продукции <t
|
||||
t-esc="o.mt_contractid.name or ''"/> от <t
|
||||
t-esc="o.mt_contractid.get_date_text(o.mt_contractid.date_start) or ''"/>.
|
||||
Поставщик обязуется поставить, а Покупатель оплатить в полном объеме следующее:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C9box">Номенклатура</td>
|
||||
<!-- <td class="R0C9box">*ГОСТ</td>-->
|
||||
<td class="R0C9box">Ед.изм.</td>
|
||||
<td class="R0C9box">Кол-во</td>
|
||||
<td class="R0C9box">Цена/руб.<br/>без НДС</td>
|
||||
<td class="R0C9box">Стоимость/руб.<br/>без НДС</td>
|
||||
<!-- <td class="R0C9box">НДС</td>-->
|
||||
<!-- <td class="R0C9box">Стоимость/руб.<br/>c НДС</td>-->
|
||||
</tr>
|
||||
<t t-set="invoice_line_ids" t-value="o.invoice_line_ids.filtered(lambda s: 'аванс' not in s.name.lower() and 'депозит' not in s.name.lower())"/>
|
||||
|
||||
<t t-set="quantity" t-value="sum([line.quantity for line in invoice_line_ids])"/>
|
||||
<t t-set="sum_full"
|
||||
t-value="sum([line.quantity*line.price_unit for line in invoice_line_ids])"/>
|
||||
<t t-set="sum_nds"
|
||||
t-value="sum([line.quantity*line.price_unit*1.2 for line in invoice_line_ids])"/>
|
||||
<tr class="R0" t-foreach="invoice_line_ids" t-as="line">
|
||||
<td class="R0C9box"><t t-esc="line.product_id.name or ''"/></td>
|
||||
<!-- <td class="R0C9box"><!–ГОСТ–></td>-->
|
||||
<td class="R0C9box"><t t-esc="line.product_id.uom_id.name or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.quantity or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.price_unit or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.price_unit*line.quantity or ''"/></td>
|
||||
<!-- <td class="R0C9box">20%</td>-->
|
||||
<!-- <td class="R0C9box"><t t-esc="line.price_unit*line.quantity*1.2 or ''"/></td>-->
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td></td>
|
||||
<!-- <td></td>-->
|
||||
<td>ИТОГО</td>
|
||||
<td class="R0C9box"><t t-esc="quantity or ''"/></td>
|
||||
<td class="R0C9box">x</td>
|
||||
<td class="R0C9box"><t t-esc="sum_full or ''"/></td>
|
||||
<!-- <td class="R0C9box">x</td>-->
|
||||
<!-- <td class="R0C9box"><t t-esc="sum_nds or ''"/></td>-->
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="8">Итого: <t t-esc="sum_full or ''"/> рублей (<t
|
||||
t-esc="o.mt_contractid.rubles(sum_full) or ''"/>). В стоимость продукции входит стоимость
|
||||
тары и стоимость транспортных расходов, связанных с доставкой продукции до станции
|
||||
назначения.<br/>
|
||||
2. Базис поставки (пункт назначения) по отгрузочным реквизитам:<br/>
|
||||
<t t-esc="o.mt_contractid.address_delivery(o.mt_contractid.partner_id.id) or ''"/><br/>
|
||||
Грузополучатель: <t t-esc="o.mt_contractid.partner_id.name or ''"/>, код <t
|
||||
t-esc="o.mt_contractid.partner_id.id or ''"/>, ОКПО <t
|
||||
t-esc="o.mt_contractid.partner_id.okpo or ''"/>.<br/>
|
||||
3. Условия оплаты: <t t-esc="o.invoice_payment_term_id.name or ''"/> согласно
|
||||
выставленному счету Поставщика.<br/>
|
||||
4. Срок отгрузки: <t
|
||||
t-esc="o.mt_contractid.get_date_text(o.mt_contractid.date_end) or ''"/><br/>
|
||||
5. Срок действия настоящей спецификации ограничен и равен сроку отгрузки по условиям
|
||||
данной спецификации.
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: avoid;">
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="6">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="6"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rtl" colspan="3">Поставщик:</td>
|
||||
<td class="R0C6rtl" colspan="3">Покупатель:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contractid.company_id.name or ''"/></td>
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contractid.partner_id.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">ИНН
|
||||
<t t-esc="o.mt_contractid.company_id.inn or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">ИНН <t
|
||||
t-esc="o.mt_contractid.partner_id.inn or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="2">ОГРН
|
||||
<t t-esc="o.company_id.company_registry or ''"/>
|
||||
</td>
|
||||
<td class="R0C6r" rowspan="2">
|
||||
<t t-esc="o.mt_contractid.name_dirprint1 or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contractid.partner_type in ['company', 'company_ip']">
|
||||
<td class="R0C7rl" colspan="3">ОГРН
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3">Паспорт
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">КПП
|
||||
<t t-esc="o.mt_contractid.company_id.kpp or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contractid.partner_type == 'company'">
|
||||
<td class="R0C7rl" colspan="3">КПП
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3"></td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">
|
||||
<t t-esc="o.mt_contractid.address(o.company_id) or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">
|
||||
<t t-esc="o.mt_contractid.address(o.partner_id) or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contractid.img(o.company_id.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6tS">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contractid.img(o.company_id.stamp) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6b"></td>
|
||||
<td class="R0C6r">
|
||||
<t t-esc="''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 30px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: bottom;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: right; vertical-align: bottom;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: top; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -5px; left: 5px;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0">Подпись Исполнителя</td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t
|
||||
t-raw="o.stamp and o.mt_contractid.img(company.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C0"></td>
|
||||
<td class="R0C1">Подпись Заказчика</td>
|
||||
<td class="R0C6tI"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-xs-3 pull-right">
|
||||
<small style="font-size:18px;">
|
||||
<span>Страница</span>
|
||||
<span class="page"/>
|
||||
из
|
||||
<span class="topage"/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="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">30</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>
|
||||
|
||||
<record id="action_report_contract_customer_invoce" model="ir.actions.report">
|
||||
<field name="name">Договор со спецификацией</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_ru_contract.report_contract_customer_invoice</field>
|
||||
<field name="report_file">l10n_ru_contract.report_contract_customer_invoice</field>
|
||||
<field name="print_report_name">'Договор со спецификацией - %s' % (object.name)</field>
|
||||
<field name="binding_model_id" ref="account.model_account_move" />
|
||||
<field name="paperformat_id" ref="paperformat_a4" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
18
l10n_ru_contract/report/report_contract_order.py
Normal file
18
l10n_ru_contract/report/report_contract_order.py
Normal file
@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ContractCustomerReportOrder(models.AbstractModel):
|
||||
_name = 'contract.customer.report_order'
|
||||
|
||||
|
||||
def get_report_values(self, docids, data=None):
|
||||
docs = self.env['sale.order'].browse(docids)
|
||||
return {
|
||||
'doc_ids': docs.ids,
|
||||
'doc_model': 'sale.order',
|
||||
'docs': docs,
|
||||
}
|
||||
|
||||
|
||||
|
||||
594
l10n_ru_contract/report/report_contract_order.xml
Normal file
594
l10n_ru_contract/report/report_contract_order.xml
Normal file
@ -0,0 +1,594 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_contract_customer_order">
|
||||
<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: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0"><t t-esc="o.mt_contract_id.company_id.name or ''"/></td>
|
||||
<td class="R0C1">Договор <t t-esc="o.mt_contract_id.name or ''"/> от <t
|
||||
t-esc="o.mt_contract_id.date_start or ''"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="page">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: center; vertical-align: medium;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium;}
|
||||
tr.R0 td.R0C2{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium;}
|
||||
tr.R0 td.R0C3{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium; margin: 40px;}
|
||||
tr.R0 td.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
start; vertical-align: medium;}
|
||||
p.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align: center;
|
||||
vertical-align: medium; font-weight: bold;}
|
||||
div.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium;}
|
||||
tr.R0 td.R0C6rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-top: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid; border-right:
|
||||
#000000 1px solid}
|
||||
tr.R0 td.R0C6r{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-right: #000000 1px solid}
|
||||
tr.R0 td.R0C6l{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
text-align: left; vertical-align: medium; border-left: #000000 1px solid;}
|
||||
tr.R0 td.R0C6b{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6t{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-top: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-top: #000000 1px solid;
|
||||
border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -30px; left: 80px; }
|
||||
tr.R0 td.R0C6tS { overflow: visible; }
|
||||
tr.R0 td.R0C6tS span { position: relative; }
|
||||
tr.R0 td.R0C6tS span img { position: absolute; width: 140px; top: -40px; left: -200px; }
|
||||
tr.R0 td.R0C8{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C9box{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid; border-top: #000000 1px solid;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%;">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="2">Договор № <t t-esc="o.mt_contract_id.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="2">на поставку продукции</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"><t t-esc="o.mt_contract_id.company_id.partner_id.city or ''"/></td>
|
||||
<td class="R0C2"><t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C3" colspan="2">
|
||||
<span>
|
||||
<t t-raw="o.mt_contract_id.contract_header or ''"/>
|
||||
</span>
|
||||
</td>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr>
|
||||
</tr>
|
||||
<!--t t-foreach="o.mt_contract_id.lines.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="2"><t t-esc="line.name"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="2"><t t-raw="line.punct"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="2"></td>
|
||||
</tr>
|
||||
</t-->
|
||||
<!--tr class="R0">
|
||||
<td class="R0C4" colspan="2">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1"></td>
|
||||
<td class="R0C2"></td>
|
||||
</tr-->
|
||||
</tbody>
|
||||
</table>
|
||||
<t t-foreach="o.mt_contract_id.lines.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<p class="R0C4"><t t-esc="line.name or ''"/></p>
|
||||
<div class="R0C5"><span><t t-raw="line.punct or ''"/></span></div>
|
||||
</t>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: avoid;">
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="6">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="6"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rtl" colspan="3">Поставщик:</td>
|
||||
<td class="R0C6rtl" colspan="3">Покупатель:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contract_id.company_id.name or ''"/></td>
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contract_id.partner_id.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">ИНН
|
||||
<t t-esc="o.mt_contract_id.company_id.inn or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">ИНН <t
|
||||
t-esc="o.mt_contract_id.partner_id.inn or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="2">ОГРН
|
||||
<t t-esc="o.company_id.company_registry or ''"/>
|
||||
</td>
|
||||
<td class="R0C6r" rowspan="2">
|
||||
<t t-esc="o.mt_contract_id.name_dirprint1 or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contract_id.partner_type in ['company', 'company_ip']">
|
||||
<td class="R0C7rl" colspan="3">ОГРН
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3">Паспорт
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">КПП
|
||||
<t t-esc="o.mt_contract_id.company_id.kpp or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contract_id.partner_type == 'company'">
|
||||
<td class="R0C7rl" colspan="3">КПП
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3"></td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">
|
||||
<t t-esc="o.mt_contract_id.address(o.company_id) or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">
|
||||
<t t-esc="o.mt_contract_id.address(o.partner_id) or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contract_id.img(o.company_id.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6tS">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contract_id.img(o.company_id.stamp) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6b"></td>
|
||||
<td class="R0C6r">
|
||||
<t t-esc="''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="page-break-before:always;"></p>
|
||||
<!--p style="page-break-after:always"></p>
|
||||
<div style="page-break-after: auto;"><span style="display: none;"> </span></div>
|
||||
<p style="page-break-inside: avoid">
|
||||
<div style="page-break-inside: auto"></div-->
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C8" colspan="8">Приложение<br/>к договору № <t
|
||||
t-esc="o.mt_contract_id.name"/><br/>на поставку продукции<br/>от <t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8">Спецификация № 1</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="4"><t
|
||||
t-esc="o.mt_contract_id.company_id.partner_id.city or ''"/></td>
|
||||
<td class="R0C2" colspan="4"><t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C3" colspan="8">
|
||||
<span>
|
||||
<t t-raw="o.mt_contract_id.contract_header or ''"/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="8">1. По договору на поставку продукции <t
|
||||
t-esc="o.mt_contract_id.name or ''"/> от <t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/>.
|
||||
Поставщик обязуется поставить, а Покупатель оплатить в полном объеме следующее:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C9box">Номенклатура</td>
|
||||
<!-- <td class="R0C9box">*ГОСТ</td>-->
|
||||
<td class="R0C9box">Ед.изм.</td>
|
||||
<td class="R0C9box">Кол-во</td>
|
||||
<td class="R0C9box">Цена/руб.<br/>без НДС</td>
|
||||
<td class="R0C9box">Стоимость/руб.<br/>без НДС</td>
|
||||
<!-- <td class="R0C9box">НДС</td>-->
|
||||
<!-- <td class="R0C9box">Стоимость/руб.<br/>c НДС</td>-->
|
||||
</tr>
|
||||
<t t-set="order_line" t-value="o.order_line.filtered(lambda s: 'аванс' not in s.name.lower() and 'депозит' not in s.name.lower())"/>
|
||||
<t t-set="quantity" t-value="sum([line.product_uom_qty for line in order_line])"/>
|
||||
<t t-set="sum_full"
|
||||
t-value="sum([line.product_uom_qty*line.price_unit for line in order_line])"/>
|
||||
<t t-set="sum_nds"
|
||||
t-value="sum([line.product_uom_qty*line.price_unit*1.2 for line in order_line])"/>
|
||||
<tr class="R0" t-foreach="order_line" t-as="line">
|
||||
<td class="R0C9box"><t t-esc="line.product_id.name or ''"/></td>
|
||||
<!-- <td class="R0C9box"><!–ГОСТ–></td>-->
|
||||
<td class="R0C9box"><t t-esc="line.product_id.uom_id.name or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.product_uom_qty or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.price_unit or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.price_unit*line.product_uom_qty or ''"/></td>
|
||||
<!-- <td class="R0C9box">20%</td>-->
|
||||
<!-- <td class="R0C9box"><t t-esc="line.price_unit*line.product_uom_qty*1.2 or ''"/></td>-->
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td></td>
|
||||
<!-- <td></td>-->
|
||||
<td>ИТОГО</td>
|
||||
<td class="R0C9box"><t t-esc="quantity or ''"/></td>
|
||||
<td class="R0C9box">x</td>
|
||||
<td class="R0C9box"><t t-esc="sum_full or ''"/></td>
|
||||
<!-- <td class="R0C9box">x</td>-->
|
||||
<!-- <td class="R0C9box"><t t-esc="sum_nds or ''"/></td>-->
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="9">Итого: <t t-esc="sum_full or ''"/> рублей (<t
|
||||
t-esc="o.mt_contract_id.rubles(sum_full) or ''"/>). В стоимость продукции входит
|
||||
стоимость
|
||||
тары и стоимость транспортных расходов, связанных с доставкой продукции до станции
|
||||
назначения.<br/>
|
||||
2. Базис поставки (пункт назначения) по отгрузочным реквизитам:<br/>
|
||||
<t t-esc="o.mt_contract_id.address_delivery(o.mt_contract_id.partner_id.id) or ''"/><br/>
|
||||
Грузополучатель: <t t-esc="o.mt_contract_id.partner_id.name or ''"/>, код <t
|
||||
t-esc="o.mt_contract_id.partner_id.id or ''"/>, ОКПО <t
|
||||
t-esc="o.mt_contract_id.partner_id.okpo or ''"/>.<br/>
|
||||
3. Условия оплаты: <t t-esc="o.payment_term_id.name or ''"/> согласно выставленному
|
||||
счету
|
||||
Поставщика.<br/>
|
||||
4. Срок отгрузки: <t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_end) or ''"/>
|
||||
<br/>
|
||||
5. Срок действия настоящей спецификации ограничен и равен сроку отгрузки по
|
||||
условиям данной спецификации.
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: avoid;">
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="6">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="6"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rtl" colspan="3">Поставщик:</td>
|
||||
<td class="R0C6rtl" colspan="3">Покупатель:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contract_id.company_id.name or ''"/></td>
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contract_id.partner_id.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">ИНН
|
||||
<t t-esc="o.mt_contract_id.company_id.inn or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">ИНН <t
|
||||
t-esc="o.mt_contract_id.partner_id.inn or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="2">ОГРН
|
||||
<t t-esc="o.company_id.company_registry or ''"/>
|
||||
</td>
|
||||
<td class="R0C6r" rowspan="2">
|
||||
<t t-esc="o.mt_contract_id.name_dirprint1 or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contract_id.partner_type in ['company', 'company_ip']">
|
||||
<td class="R0C7rl" colspan="3">ОГРН
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3">Паспорт
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">КПП
|
||||
<t t-esc="o.mt_contract_id.company_id.kpp or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contract_id.partner_type == 'company'">
|
||||
<td class="R0C7rl" colspan="3">КПП
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3"></td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">
|
||||
<t t-esc="o.mt_contract_id.address(o.company_id) or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">
|
||||
<t t-esc="o.mt_contract_id.address(o.partner_id) or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contract_id.img(o.company_id.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6tS">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contract_id.img(o.company_id.stamp) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6b"></td>
|
||||
<td class="R0C6r">
|
||||
<t t-esc="''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 30px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: bottom;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: right; vertical-align: bottom;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: top; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -5px; left: 5px;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0">Подпись Исполнителя</td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t
|
||||
t-raw="o.stamp and o.mt_contract_id.img(company.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C0"></td>
|
||||
<td class="R0C1">Подпись Заказчика</td>
|
||||
<td class="R0C6tI"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-xs-3 pull-right">
|
||||
<small style="font-size:18px;">
|
||||
<span>Страница</span>
|
||||
<span class="page"/>
|
||||
из
|
||||
<span class="topage"/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="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">30</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>
|
||||
|
||||
<record id="action_report_contract_customer_order" model="ir.actions.report">
|
||||
<field name="name">Договор со спецификацией</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_ru_contract.report_contract_customer_order</field>
|
||||
<field name="report_file">l10n_ru_contract.report_contract_customer_order</field>
|
||||
<field name="print_report_name">'Договор со спецификацией - %s' % (object.name)</field>
|
||||
<field name="binding_model_id" ref="sale.model_sale_order" />
|
||||
<field name="paperformat_id" ref="paperformat_a4" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
406
l10n_ru_contract/report/report_contract_order1.xml
Normal file
406
l10n_ru_contract/report/report_contract_order1.xml
Normal file
@ -0,0 +1,406 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_contract_customer_order1">
|
||||
<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: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0"><t t-esc="o.mt_contract_id.company_id.name or ''"/></td>
|
||||
<td class="R0C1">Договор <t t-esc="o.mt_contract_id.name or ''"/> от <t
|
||||
t-esc="o.mt_contract_id.date_start or ''"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="page">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 15px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: center; vertical-align: medium;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium;}
|
||||
tr.R0 td.R0C2{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium;}
|
||||
tr.R0 td.R0C3{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium; margin: 40px;}
|
||||
tr.R0 td.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
start; vertical-align: medium;}
|
||||
p.R0C4{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align: center;
|
||||
vertical-align: medium; font-weight: bold;}
|
||||
div.R0C5{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
justify; vertical-align: medium;}
|
||||
tr.R0 td.R0C6rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-top: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid; border-right:
|
||||
#000000 1px solid}
|
||||
tr.R0 td.R0C6r{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-right: #000000 1px solid}
|
||||
tr.R0 td.R0C6l{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
text-align: left; vertical-align: medium; border-left: #000000 1px solid;}
|
||||
tr.R0 td.R0C6b{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6t{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-top: #000000 1px solid;}
|
||||
tr.R0 td.R0C6rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal;
|
||||
font-weight: bold; text-align: left; vertical-align: medium; border-left: #000000 1px solid;
|
||||
border-right: #000000 1px solid; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rtl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-top: #000000 1px solid;
|
||||
border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;}
|
||||
tr.R0 td.R0C7rbl{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
left; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: medium; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -30px; left: 80px; }
|
||||
tr.R0 td.R0C6tS { overflow: visible; }
|
||||
tr.R0 td.R0C6tS span { position: relative; }
|
||||
tr.R0 td.R0C6tS span img { position: absolute; width: 140px; top: -40px; left: -200px; }
|
||||
tr.R0 td.R0C8{ font-family: Times new roman; font-size: 11pt; font-style: normal; text-align:
|
||||
right; vertical-align: medium; font-weight: bold;}
|
||||
tr.R0 td.R0C9box{ font-family: Times new roman; font-size: 12pt; font-style: normal; text-align:
|
||||
center; vertical-align: medium; border-left: #000000 1px solid; border-right: #000000 1px solid;
|
||||
border-bottom: #000000 1px solid; border-top: #000000 1px solid;}
|
||||
</STYLE>
|
||||
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C8" colspan="8">Приложение<br/>к договору № <t
|
||||
t-esc="o.mt_contract_id.name"/><br/>на поставку продукции<br/>от <t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8">Спецификация № 1</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="4"><t
|
||||
t-esc="o.mt_contract_id.company_id.partner_id.city or ''"/></td>
|
||||
<td class="R0C2" colspan="4"><t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C3" colspan="8">
|
||||
<span>
|
||||
<t t-raw="o.mt_contract_id.contract_header or ''"/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="8">1. По договору на поставку продукции <t
|
||||
t-esc="o.mt_contract_id.name or ''"/> от <t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_start) or ''"/>.
|
||||
Поставщик обязуется поставить, а Покупатель оплатить в полном объеме следующее:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C9box">Номенклатура</td>
|
||||
<!-- <td class="R0C9box">*ГОСТ</td>-->
|
||||
<td class="R0C9box">Ед.изм.</td>
|
||||
<td class="R0C9box">Кол-во</td>
|
||||
<td class="R0C9box">Цена/руб.<br/>без НДС</td>
|
||||
<td class="R0C9box">Стоимость/руб.<br/>без НДС</td>
|
||||
<!-- <td class="R0C9box">НДС</td>-->
|
||||
<!-- <td class="R0C9box">Стоимость/руб.<br/>c НДС</td>-->
|
||||
</tr>
|
||||
<t t-set="order_line" t-value="o.order_line.filtered(lambda s: 'аванс' not in s.name.lower() and 'депозит' not in s.name.lower())"/>
|
||||
<t t-set="quantity" t-value="sum([line.product_uom_qty for line in order_line])"/>
|
||||
<t t-set="sum_full"
|
||||
t-value="sum([line.product_uom_qty*line.price_unit for line in order_line])"/>
|
||||
<t t-set="sum_nds"
|
||||
t-value="sum([line.product_uom_qty*line.price_unit*1.2 for line in order_line])"/>
|
||||
<tr class="R0" t-foreach="order_line" t-as="line">
|
||||
<td class="R0C9box"><t t-esc="line.product_id.name or ''"/></td>
|
||||
<!-- <td class="R0C9box"><!–ГОСТ–></td>-->
|
||||
<td class="R0C9box"><t t-esc="line.product_id.uom_id.name or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.product_uom_qty or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.price_unit or ''"/></td>
|
||||
<td class="R0C9box"><t t-esc="line.price_unit*line.product_uom_qty or ''"/></td>
|
||||
<!-- <td class="R0C9box">20%</td>-->
|
||||
<!-- <td class="R0C9box"><t t-esc="line.price_unit*line.product_uom_qty*1.2 or ''"/></td>-->
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td></td>
|
||||
<!-- <td></td>-->
|
||||
<td>ИТОГО</td>
|
||||
<td class="R0C9box"><t t-esc="quantity or ''"/></td>
|
||||
<td class="R0C9box">x</td>
|
||||
<td class="R0C9box"><t t-esc="sum_full or ''"/></td>
|
||||
<!-- <td class="R0C9box">x</td>-->
|
||||
<!-- <td class="R0C9box"><t t-esc="sum_nds or ''"/></td>-->
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C5" colspan="8">Итого: <t t-esc="sum_full or ''"/> рублей (<t
|
||||
t-esc="o.mt_contract_id.rubles(sum_full) or ''"/>). В стоимость продукции входит стоимость
|
||||
тары и стоимость транспортных расходов, связанных с доставкой продукции до станции
|
||||
назначения.<br/>
|
||||
2. Базис поставки (пункт назначения) по отгрузочным реквизитам:<br/>
|
||||
<t t-esc="o.mt_contract_id.address_delivery(o.mt_contract_id.partner_id.id) or ''"/><br/>
|
||||
Грузополучатель: <t t-esc="o.mt_contract_id.partner_id.name or ''"/>, код <t
|
||||
t-esc="o.mt_contract_id.partner_id.id or ''"/>, ОКПО <t
|
||||
t-esc="o.mt_contract_id.partner_id.okpo or ''"/>.<br/>
|
||||
3. Условия оплаты: <t t-esc="o.payment_term_id.name or ''"/> согласно выставленному счету
|
||||
Поставщика.<br/>
|
||||
4. Срок отгрузки: <t
|
||||
t-esc="o.mt_contract_id.get_date_text(o.mt_contract_id.date_end) or ''"/><br/>
|
||||
5. Срок действия настоящей спецификации ограничен и равен сроку отгрузки по условиям
|
||||
данной спецификации.
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C0" colspan="8"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%; page-break-inside: avoid;">
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<COL WIDTH="5%"/>
|
||||
<COL WIDTH="30%"/>
|
||||
<COL WIDTH="15%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C4" colspan="6">Реквизиты и подписи сторон.</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C1" colspan="6"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rtl" colspan="3">Поставщик:</td>
|
||||
<td class="R0C6rtl" colspan="3">Покупатель:</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contract_id.company_id.name or ''"/></td>
|
||||
<td class="R0C6rl" colspan="3"><t t-esc="o.mt_contract_id.partner_id.name or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">ИНН
|
||||
<t t-esc="o.mt_contract_id.company_id.inn or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">ИНН <t
|
||||
t-esc="o.mt_contract_id.partner_id.inn or ''"/></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="2">ОГРН
|
||||
<t t-esc="o.company_id.company_registry or ''"/>
|
||||
</td>
|
||||
<td class="R0C6r" rowspan="2">
|
||||
<t t-esc="o.mt_contract_id.name_dirprint1 or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contract_id.partner_type in ['company', 'company_ip']">
|
||||
<td class="R0C7rl" colspan="3">ОГРН
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3">Паспорт
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">КПП
|
||||
<t t-esc="o.mt_contract_id.company_id.kpp or ''"/>
|
||||
</td>
|
||||
<t t-if="o.mt_contract_id.partner_type == 'company'">
|
||||
<td class="R0C7rl" colspan="3">КПП
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</td>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<td class="R0C7rl" colspan="3"></td>
|
||||
</t>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3">Юридический адрес:
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l" colspan="3">
|
||||
<t t-esc="o.mt_contract_id.address(o.company_id) or ''"/>
|
||||
</td>
|
||||
<td class="R0C7rl" colspan="3">
|
||||
<t t-esc="o.mt_contract_id.address(o.partner_id) or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contract_id.img(o.company_id.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6tS">
|
||||
<SPAN>
|
||||
<t t-raw="o.stamp and o.mt_contract_id.img(o.company_id.stamp) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6l"></td>
|
||||
<td class="R0C6b"></td>
|
||||
<td class="R0C6r">
|
||||
<t t-esc="''"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
<td class="R0C6rl" colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="R0">
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
<td class="R0C6rbl" colspan="3"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<STYLE TYPE="text/css">
|
||||
body {background: #ffffff; margin: 0; font-family: Times new roman; font-size: 12pt; font-style:
|
||||
normal;}
|
||||
tr.R0{height: 30px;}
|
||||
tr.R0 td.R0C0{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: bottom;}
|
||||
tr.R0 td.R0C1{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: right; vertical-align: bottom;}
|
||||
tr.R0 td.R0C6tI{ font-family: Times new roman; font-size: 11pt; font-style: normal; font-weight:
|
||||
bold; text-align: left; vertical-align: top; border-bottom: #000000 1px solid;}
|
||||
tr.R0 td.R0C6tI { overflow: visible; }
|
||||
tr.R0 td.R0C6tI span { position: relative; }
|
||||
tr.R0 td.R0C6tI span img { position: absolute; width: 100px; top: -5px; left: 5px;}
|
||||
</STYLE>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<COL WIDTH="20%"/>
|
||||
<tbody>
|
||||
<tr class="R0">
|
||||
<td class="R0C0">Подпись Исполнителя</td>
|
||||
<td class="R0C6tI">
|
||||
<SPAN>
|
||||
<t
|
||||
t-raw="o.stamp and o.mt_contract_id.img(company.chief_id.facsimile) or ''"/>
|
||||
</SPAN>
|
||||
</td>
|
||||
<td class="R0C0"></td>
|
||||
<td class="R0C1">Подпись Заказчика</td>
|
||||
<td class="R0C6tI"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-xs-3 pull-right">
|
||||
<small style="font-size:18px;">
|
||||
<span>Страница</span>
|
||||
<span class="page"/>
|
||||
из
|
||||
<span class="topage"/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="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">30</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>
|
||||
|
||||
<record id="action_report_contract_customer_order1" model="ir.actions.report">
|
||||
<field name="name">Спецификация</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_ru_contract.report_contract_customer_order1</field>
|
||||
<field name="report_file">l10n_ru_contract.report_contract_customer_order1</field>
|
||||
<field name="print_report_name">'Спецификация - %s' % (object.name)</field>
|
||||
<field name="binding_model_id" ref="sale.model_sale_order" />
|
||||
<field name="paperformat_id" ref="paperformat_a4" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
343
l10n_ru_contract/report/report_contract_simple.xml
Normal file
343
l10n_ru_contract/report/report_contract_simple.xml
Normal file
@ -0,0 +1,343 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="action_report_contract_simple" model="ir.actions.report">
|
||||
<field name="name">Договор (WeasyPrint)</field>
|
||||
<field name="model">partner.contract.customer</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_ru_contract.report_contract_customer_modern</field>
|
||||
<field name="report_file">l10n_ru_contract.report_contract_customer_modern</field>
|
||||
<field name="print_report_name">'Договор - %s' % (object.name or '')</field>
|
||||
<field name="binding_model_id" ref="l10n_ru_contract.model_partner_contract_customer" />
|
||||
<field name="use_weasyprint">True</field>
|
||||
</record>
|
||||
|
||||
<template id="report_contract_customer_modern">
|
||||
<t t-call="web.html_container">
|
||||
<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-call="web.external_layout">
|
||||
|
||||
<!-- Корневой блок, передаём номер договора в string-set -->
|
||||
<div class="page contract-root" t-att-data-contract-number="o.name or ''">
|
||||
<style type="text/css">
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 18mm 20mm 22mm 20mm;
|
||||
|
||||
@bottom-right {
|
||||
content: "Страница " counter(page) " из " counter(pages);
|
||||
font-size: 9pt;
|
||||
font-family: "DejaVu Serif", "Times New Roman", serif;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "DejaVu Serif", "Times New Roman", serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.45;
|
||||
color: #000;
|
||||
background: #ffffff;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.contract-root {
|
||||
string-set: contract_number attr(data-contract-number);
|
||||
}
|
||||
|
||||
.contract-main-title {
|
||||
text-align: center;
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4mm;
|
||||
}
|
||||
|
||||
.contract-subtitle {
|
||||
text-align: center;
|
||||
font-size: 11pt;
|
||||
color: #444;
|
||||
margin-bottom: 6mm;
|
||||
}
|
||||
|
||||
.meta-table {
|
||||
width: 100%;
|
||||
margin-bottom: 6mm;
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
.meta-city { text-align: left; }
|
||||
.meta-date { text-align: right; }
|
||||
|
||||
.parties-block {
|
||||
margin-bottom: 10mm;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.clauses-root {
|
||||
counter-reset: clause;
|
||||
}
|
||||
|
||||
.clauses-columns {
|
||||
column-count: 2;
|
||||
column-gap: 12mm;
|
||||
column-rule: 0.2mm solid #999;
|
||||
}
|
||||
|
||||
.clause {
|
||||
counter-increment: clause;
|
||||
margin-bottom: 4mm;
|
||||
break-inside: avoid-column;
|
||||
}
|
||||
|
||||
.clause-title::before {
|
||||
content: counter(clause) ". ";
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.clause-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
|
||||
.clause-body {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.requisites-title {
|
||||
margin-top: 12mm;
|
||||
margin-bottom: 4mm;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.requisites-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 48% 48%;
|
||||
column-gap: 4%;
|
||||
row-gap: 6mm;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.requisites-card {
|
||||
border: 1px solid #000;
|
||||
padding: 4mm;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
.requisites-card-title {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 3mm;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.requisites-line {
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
|
||||
.signature-block {
|
||||
margin-top: 12mm;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 12mm;
|
||||
}
|
||||
|
||||
.signature-side-title {
|
||||
font-size: 10pt;
|
||||
font-weight: bold;
|
||||
margin-bottom: 3mm;
|
||||
}
|
||||
|
||||
.signature-line {
|
||||
border-bottom: 1px solid #000;
|
||||
height: 6mm;
|
||||
margin-bottom: 2mm;
|
||||
width: 60mm;
|
||||
}
|
||||
|
||||
.signature-name {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 4mm;
|
||||
}
|
||||
|
||||
.signature-stamp {
|
||||
margin-top: 4mm;
|
||||
height: 30mm;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.signature-stamp img {
|
||||
position: absolute;
|
||||
max-width: 120px;
|
||||
max-height: 120px;
|
||||
left: 0;
|
||||
top: -5mm;
|
||||
}
|
||||
|
||||
.debug-test {
|
||||
background: yellow;
|
||||
border: 3px solid red;
|
||||
font-size: 20pt;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="debug-test">
|
||||
ТЕСТ CSS
|
||||
</div>
|
||||
|
||||
<!-- Шапка договора -->
|
||||
<div class="header-block">
|
||||
<div class="contract-main-title">
|
||||
ДОГОВОР ПОСТАВКИ ПРОДУКЦИИ № <t t-esc="o.name or ''"/>
|
||||
</div>
|
||||
<div class="contract-subtitle">
|
||||
(пример шаблона, оптимизированного под WeasyPrint)
|
||||
</div>
|
||||
<table class="meta-table" border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="meta-city">
|
||||
<t t-esc="company.partner_id.city or ''"/>
|
||||
</td>
|
||||
<td class="meta-date">
|
||||
<t t-esc="o.get_date_text(o.date_start) if o.date_start else ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="parties-block">
|
||||
<t t-if="o.partner_type in ['company', 'company_ip']">
|
||||
<t t-esc="company.name or ''"/>,
|
||||
именуемое в дальнейшем «Поставщик», с одной стороны, и
|
||||
<t t-esc="o.partner_id.name or ''"/>,
|
||||
именуемое в дальнейшем «Покупатель», совместно именуемые «Стороны»,
|
||||
заключили настоящий договор о нижеследующем:
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-esc="company.name or ''"/> (далее — «Поставщик») и
|
||||
гражданин(ка) <t t-esc="o.partner_id.name or ''"/> (далее — «Покупатель»),
|
||||
совместно именуемые «Стороны», заключили настоящий договор о нижеследующем:
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Основная часть: пункты в две колонки -->
|
||||
<div class="clauses-root clauses-columns">
|
||||
<t t-foreach="o.lines_ids.sorted(key=lambda r: r.sequence, reverse=False)" t-as="line">
|
||||
<div class="clause">
|
||||
<div class="clause-title">
|
||||
<t t-esc="line.name or ''"/>
|
||||
</div>
|
||||
<div class="clause-body">
|
||||
<span>
|
||||
<t t-raw="line.punct or ''"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<!-- Реквизиты и подписи с использованием CSS Grid -->
|
||||
<div class="requisites-title">
|
||||
Реквизиты и подписи сторон
|
||||
</div>
|
||||
|
||||
<div class="requisites-grid">
|
||||
<!-- Поставщик -->
|
||||
<div class="requisites-card">
|
||||
<div class="requisites-card-title">Поставщик</div>
|
||||
<div class="requisites-line">
|
||||
<strong>Наименование:</strong>
|
||||
<t t-esc="company.name or ''"/>
|
||||
</div>
|
||||
<div class="requisites-line">
|
||||
<strong>ИНН:</strong>
|
||||
<t t-esc="company.inn or ''"/>
|
||||
</div>
|
||||
<div class="requisites-line">
|
||||
<strong>ОГРН:</strong>
|
||||
<t t-esc="company.company_registry or ''"/>
|
||||
</div>
|
||||
<div class="requisites-line">
|
||||
<strong>КПП:</strong>
|
||||
<t t-esc="company.kpp or ''"/>
|
||||
</div>
|
||||
<div class="requisites-line">
|
||||
<strong>Юр. адрес:</strong><br/>
|
||||
<t t-esc="o.address(o.company_id) or ''"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Покупатель -->
|
||||
<div class="requisites-card">
|
||||
<div class="requisites-card-title">Покупатель</div>
|
||||
<div class="requisites-line">
|
||||
<strong>Наименование / ФИО:</strong>
|
||||
<t t-esc="o.partner_id.name or ''"/>
|
||||
</div>
|
||||
<div class="requisites-line">
|
||||
<strong>ИНН:</strong>
|
||||
<t t-esc="o.partner_id.inn or ''"/>
|
||||
</div>
|
||||
<t t-if="o.partner_type in ['company', 'company_ip']">
|
||||
<div class="requisites-line">
|
||||
<strong>ОГРН:</strong>
|
||||
<t t-esc="o.partner_id.ogrn or ''"/>
|
||||
</div>
|
||||
<t t-if="o.partner_type == 'company'">
|
||||
<div class="requisites-line">
|
||||
<strong>КПП:</strong>
|
||||
<t t-esc="o.partner_id.kpp or ''"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="requisites-line">
|
||||
<strong>Паспорт:</strong>
|
||||
<t t-esc="o.partner_id.passport or ''"/>
|
||||
</div>
|
||||
</t>
|
||||
<div class="requisites-line">
|
||||
<strong>Юр. адрес / адрес регистрации:</strong><br/>
|
||||
<t t-esc="o.address(o.partner_id) or ''"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="signature-block">
|
||||
<div>
|
||||
<div class="signature-side-title">Поставщик</div>
|
||||
<div class="signature-line"></div>
|
||||
<div class="signature-name">
|
||||
<t t-esc="o.director_name_company or ''"/>
|
||||
</div>
|
||||
<div class="signature-stamp">
|
||||
<t t-if="o.stamp">
|
||||
<t t-raw="o.img(o.company_id.stamp) or ''"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="signature-side-title">Покупатель</div>
|
||||
<div class="signature-line"></div>
|
||||
<div class="signature-name">
|
||||
<!-- тут можно вывести ФИО либо оставить пустым -->
|
||||
<t t-esc="''"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
6
l10n_ru_contract/security/ir.model.access.csv
Normal file
6
l10n_ru_contract/security/ir.model.access.csv
Normal file
@ -0,0 +1,6 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_partner_contract_customer,access_partner_contract_customer,model_partner_contract_customer,,1,1,1,1
|
||||
access_contract_day,access_contract_day,model_contract_day,,1,1,1,1
|
||||
access_contract_allowed_profiles,access_contract_allowed_profiles,model_contract_allowed_profiles,,1,1,1,1
|
||||
access_partner_contract_customerline,access_partner_contract_customerline,model_contract_line,,1,1,1,1
|
||||
access_contract_profile,access_contract_profile,model_contract_profile,base.group_user,1,1,1,1
|
||||
|
1
l10n_ru_contract/tests/__init__.py
Normal file
1
l10n_ru_contract/tests/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import test_contract
|
||||
215
l10n_ru_contract/tests/test_contract.py
Normal file
215
l10n_ru_contract/tests/test_contract.py
Normal file
@ -0,0 +1,215 @@
|
||||
"""
|
||||
Tests for Contract_Model (l10n_ru_contract).
|
||||
|
||||
Validates: Requirements 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 4.10
|
||||
"""
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _get_or_create_profile(env):
|
||||
"""Return an existing contract.profile or create a minimal one."""
|
||||
profile = env['contract.profile'].search([], limit=1)
|
||||
if not profile:
|
||||
profile = env['contract.profile'].create({'name': 'Test Profile'})
|
||||
return profile
|
||||
|
||||
|
||||
def _get_or_create_partner(env):
|
||||
"""Return an existing external partner or create one."""
|
||||
partner = env['res.partner'].search(
|
||||
[('is_company', '=', True), ('id', 'not in', env.companies.ids)], limit=1
|
||||
)
|
||||
if not partner:
|
||||
partner = env['res.partner'].create({'name': 'Test Partner', 'is_company': True})
|
||||
return partner
|
||||
|
||||
|
||||
def _make_contract(env, **kwargs):
|
||||
"""Create a minimal PartnerContractCustomer record."""
|
||||
profile = _get_or_create_profile(env)
|
||||
partner = _get_or_create_partner(env)
|
||||
vals = {
|
||||
'date_start': '2024-01-01',
|
||||
'date_end': '2024-12-31',
|
||||
'type': 'customer',
|
||||
'profile_id': profile.id,
|
||||
'partner_id': partner.id,
|
||||
'partner_type': 'company',
|
||||
'company_id': env.company.id,
|
||||
}
|
||||
vals.update(kwargs)
|
||||
return env['partner.contract.customer'].create(vals)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestContractCreate
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestContractCreate(TransactionCase):
|
||||
"""Validates: Requirement 4.1 — unique contract number assigned via sequence on create."""
|
||||
|
||||
def test_create_assigns_unique_name_via_sequence(self):
|
||||
"""Req 4.1 — creating a customer contract assigns a non-empty unique name."""
|
||||
contract1 = _make_contract(self.env)
|
||||
contract2 = _make_contract(self.env)
|
||||
|
||||
self.assertTrue(contract1.name, "Contract 1 should have a name assigned")
|
||||
self.assertTrue(contract2.name, "Contract 2 should have a name assigned")
|
||||
self.assertNotEqual(
|
||||
contract1.name, contract2.name,
|
||||
"Each contract should receive a unique number from the sequence"
|
||||
)
|
||||
|
||||
def test_create_supplier_contract_assigns_name(self):
|
||||
"""Req 4.1 — creating a supplier contract also assigns a name via sequence."""
|
||||
contract = _make_contract(self.env, type='supplier')
|
||||
self.assertTrue(contract.name, "Supplier contract should have a name assigned")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestContractStates
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestContractStates(TransactionCase):
|
||||
"""Validates: Requirements 4.2, 4.3 — state transitions."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.contract = _make_contract(self.env)
|
||||
|
||||
def test_contract_action_confirm_draft_to_progress(self):
|
||||
"""Req 4.2 — contract_action_confirm moves draft → progress."""
|
||||
self.assertEqual(self.contract.state, 'draft')
|
||||
self.contract.contract_action_confirm()
|
||||
self.assertEqual(
|
||||
self.contract.state, 'progress',
|
||||
"contract_action_confirm should move draft contract to progress"
|
||||
)
|
||||
|
||||
def test_contract_action_confirm_progress_to_signed(self):
|
||||
"""Req 4.2 — second call to contract_action_confirm moves progress → signed."""
|
||||
self.contract.contract_action_confirm() # draft → progress
|
||||
self.contract.contract_action_confirm() # progress → signed
|
||||
self.assertEqual(
|
||||
self.contract.state, 'signed',
|
||||
"Second contract_action_confirm should move progress contract to signed"
|
||||
)
|
||||
|
||||
def test_contract_in_draft_returns_to_draft(self):
|
||||
"""Req 4.3 — contract_in_draft moves any state back to draft."""
|
||||
self.contract.contract_action_confirm() # draft → progress
|
||||
self.assertEqual(self.contract.state, 'progress')
|
||||
|
||||
self.contract.contract_in_draft()
|
||||
self.assertEqual(
|
||||
self.contract.state, 'draft',
|
||||
"contract_in_draft should return contract to draft state"
|
||||
)
|
||||
|
||||
def test_contract_in_draft_from_signed(self):
|
||||
"""Req 4.3 — contract_in_draft from signed state returns to draft."""
|
||||
self.contract.contract_action_confirm() # draft → progress
|
||||
self.contract.contract_action_confirm() # progress → signed
|
||||
self.assertEqual(self.contract.state, 'signed')
|
||||
|
||||
self.contract.contract_in_draft()
|
||||
self.assertEqual(
|
||||
self.contract.state, 'draft',
|
||||
"contract_in_draft should return signed contract to draft"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestContractHelpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestContractHelpers(TransactionCase):
|
||||
"""Validates: Requirements 4.4–4.10 — helper methods on the contract model."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.contract = _make_contract(self.env, date_start='2024-01-15')
|
||||
|
||||
def test_get_dateend_returns_date_11_months_later(self):
|
||||
"""Req 4.4 — get_dateend returns a date 11 months after date_start."""
|
||||
result = self.contract.get_dateend()
|
||||
self.assertTrue(result, "get_dateend should return a non-empty string")
|
||||
# date_start = 2024-01-15, +11 months = 2024-12-15
|
||||
self.assertIn('2024-12-15', result,
|
||||
"get_dateend should return date 11 months after date_start")
|
||||
|
||||
def test_initials_full_name(self):
|
||||
"""Req 4.5 — initials('Иванов Иван Иванович') returns 'Иванов И.И.'"""
|
||||
result = self.contract.initials('Иванов Иван Иванович')
|
||||
self.assertEqual(result, 'Иванов И.И.',
|
||||
"initials should format full name as 'Фамилия И.О.'")
|
||||
|
||||
def test_initials_empty_string(self):
|
||||
"""Req 4.5 — initials('') returns empty string."""
|
||||
result = self.contract.initials('')
|
||||
self.assertFalse(result, "initials with empty string should return falsy value")
|
||||
|
||||
def test_ru_date_returns_russian_format(self):
|
||||
"""Req 4.6 — ru_date returns date in Russian format with quoted day."""
|
||||
result = self.contract.ru_date('2024-03-15')
|
||||
self.assertTrue(result, "ru_date should return a non-empty string")
|
||||
self.assertIn('2024', result, "ru_date result should contain the year")
|
||||
self.assertIn('15', result, "ru_date result should contain the day")
|
||||
|
||||
def test_rubles_returns_string_with_rubles_word(self):
|
||||
"""Req 4.7 — rubles(1500.50) returns string containing a rubles word."""
|
||||
result = self.contract.rubles(1500.50)
|
||||
self.assertTrue(result, "rubles should return a non-empty string")
|
||||
has_rubles_word = any(
|
||||
word in result for word in ('рублей', 'рубля', 'рубль')
|
||||
)
|
||||
self.assertTrue(has_rubles_word,
|
||||
"rubles result should contain 'рублей', 'рубля', or 'рубль'")
|
||||
|
||||
def test_rubles_1500_50_exact(self):
|
||||
"""Req 4.7 — rubles(1500.50) returns expected Russian text."""
|
||||
result = self.contract.rubles(1500.50)
|
||||
self.assertIn('тысяча', result, "1500 should produce 'тысяча' in result")
|
||||
self.assertIn('пятьсот', result, "1500 should produce 'пятьсот' in result")
|
||||
self.assertIn('копеек', result, "0.50 kopecks should appear in result")
|
||||
|
||||
def test_numeral_choose_plural_1_returns_rubly(self):
|
||||
"""Req 4.8 — numeral_choose_plural(1, ...) returns 'рубль'."""
|
||||
result = self.contract.numeral_choose_plural(1, ('рубль', 'рубля', 'рублей'))
|
||||
self.assertEqual(result, 'рубль',
|
||||
"numeral_choose_plural(1) should return first variant 'рубль'")
|
||||
|
||||
def test_numeral_choose_plural_11_returns_rubley(self):
|
||||
"""Req 4.9 — numeral_choose_plural(11, ...) returns 'рублей'."""
|
||||
result = self.contract.numeral_choose_plural(11, ('рубль', 'рубля', 'рублей'))
|
||||
self.assertEqual(result, 'рублей',
|
||||
"numeral_choose_plural(11) should return third variant 'рублей'")
|
||||
|
||||
def test_numeral_choose_plural_2_returns_rublya(self):
|
||||
"""numeral_choose_plural(2) returns second variant 'рубля'."""
|
||||
result = self.contract.numeral_choose_plural(2, ('рубль', 'рубля', 'рублей'))
|
||||
self.assertEqual(result, 'рубля',
|
||||
"numeral_choose_plural(2) should return second variant 'рубля'")
|
||||
|
||||
def test_check_positive_negative_raises_value_error(self):
|
||||
"""Req 4.10 — check_positive with negative number raises ValueError."""
|
||||
with self.assertRaises(ValueError):
|
||||
self.contract.check_positive(-1)
|
||||
|
||||
def test_check_positive_zero_does_not_raise(self):
|
||||
"""check_positive(0) should not raise (non-strict mode)."""
|
||||
try:
|
||||
self.contract.check_positive(0)
|
||||
except ValueError:
|
||||
self.fail("check_positive(0) should not raise ValueError in non-strict mode")
|
||||
|
||||
def test_check_positive_positive_does_not_raise(self):
|
||||
"""check_positive with positive number should not raise."""
|
||||
try:
|
||||
self.contract.check_positive(100)
|
||||
except ValueError:
|
||||
self.fail("check_positive(100) should not raise ValueError")
|
||||
@ -0,0 +1,46 @@
|
||||
<odoo>
|
||||
<menuitem id="menu_report_templates" name="Шаблоны отчетов" parent="l10n_ru_contract.separator" sequence="10"/>
|
||||
|
||||
<record id="view_report_template_tree" model="ir.ui.view">
|
||||
<field name="name">contract.report.template.tree</field>
|
||||
<field name="model">contract.report.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Шаблоны отчетов">
|
||||
<field name="name"/>
|
||||
<field name="typeformat"/>
|
||||
<field name="attachment_filename"/>
|
||||
<field name="create_date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_report_template_form" model="ir.ui.view">
|
||||
<field name="name">contract.report.template.form</field>
|
||||
<field name="model">contract.report.template</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Шаблон отчета">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="attachment" filename="attachment_filename"/>
|
||||
<field name="typeformat" readonly="1"/>
|
||||
<field name="create_date" readonly="1"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_templates" model="ir.actions.act_window">
|
||||
<field name="name">Шаблоны отчетов</field>
|
||||
<field name="res_model">contract.report.template</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Создайте новый шаблон отчета.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_report_templates_action" parent="menu_report_templates" action="action_report_templates"/>
|
||||
</odoo>
|
||||
149
l10n_ru_contract/views/contract_customer_view.xml
Normal file
149
l10n_ru_contract/views/contract_customer_view.xml
Normal file
@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="contract_customer_form" model="ir.ui.view">
|
||||
<field name="name">Договор</field>
|
||||
<field name="model">partner.contract.customer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Договор">
|
||||
<header>
|
||||
<field name="state" widget="statusbar" nolabel="1" clickable="True"/>
|
||||
<button name="action_set_on_approval"
|
||||
string="На согласовании"
|
||||
type="object"
|
||||
invisible="state!='draft'"/>
|
||||
|
||||
<button name="action_confirm"
|
||||
class="btn-primary"
|
||||
string="Подтвердить"
|
||||
type="object"
|
||||
invisible="state!='progress'"/>
|
||||
|
||||
<button name="action_reset_to_draft"
|
||||
string="В черновик"
|
||||
type="object"
|
||||
invisible="state not in ('signed','progress')"/>
|
||||
|
||||
</header>
|
||||
<sheet>
|
||||
<h1 class="o_row"
|
||||
style="align-items:center; gap:8px; font-weight:400;">
|
||||
<field name="name"
|
||||
nolabel="1"
|
||||
placeholder="Номер договора"
|
||||
readonly="state!='draft'"
|
||||
style="max-width:220px; font-weight:400;"/>
|
||||
|
||||
<span style="font-weight:400; position:relative; top:-3px;"> от </span>
|
||||
|
||||
<field name="date_start"
|
||||
nolabel="1"
|
||||
readonly="state!='draft'"
|
||||
style="max-width:150px; font-weight:400;"/>
|
||||
</h1>
|
||||
<group string="Сведения о контрагенте">
|
||||
<group>
|
||||
<field name="partner_type" readonly="state!='draft'"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="partner_id" readonly="state!='draft'"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group string="Сведения о договоре">
|
||||
<group>
|
||||
<field name="type" readonly="state!='draft'"/>
|
||||
<field name="date_end" readonly="state!='draft'"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="profile_id" readonly="state!='draft'"/>
|
||||
<field name="company_id" readonly="state!='draft'"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<chatter/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="contract_customer_tree" model="ir.ui.view">
|
||||
<field name="name">Договор</field>
|
||||
<field name="model">partner.contract.customer</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Договоры">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date_start"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="contract_customer_search" model="ir.ui.view">
|
||||
<field name="name">Поиск в договорах</field>
|
||||
<field name="model">partner.contract.customer</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="partner_id" string="Номер или клиент"
|
||||
filter_domain="['|',('partner_id','ilike',self),('name','ilike',self)]"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="company_id"/>
|
||||
<field name="date_start"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="contract_customer_action" model="ir.actions.act_window">
|
||||
<field name="name">Договоры</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">partner.contract.customer</field>
|
||||
<field name="view_mode">kanban,list,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Нет созданных контрактов
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="contract_customer_kanban" model="ir.ui.view">
|
||||
<field name="name">Договор</field>
|
||||
<field name="model">partner.contract.customer</field>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<kanban default_group_by="state">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="profile_id"/>
|
||||
<field name="company_id"/>
|
||||
<field name="type"/>
|
||||
<templates>
|
||||
<!-- Odoo 19 OWL требует шаблон card -->
|
||||
<t t-name="card">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="oe_kanban_content">
|
||||
<div>
|
||||
<strong class="o_kanban_record_title">Номер: <b><field name="name"/></b></strong>
|
||||
</div>
|
||||
<div>
|
||||
Контрагент: <b><field name="partner_id"/></b> <br/>
|
||||
Тип: <b><field name="type"/></b> <br/>
|
||||
Вид договора: <b><field name="profile_id"/></b>
|
||||
</div>
|
||||
<div class="o_kanban_record_bottom">
|
||||
Наша компания: <b><field name="company_id"/></b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
|
||||
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
51
l10n_ru_contract/views/contract_header_templates.xml
Normal file
51
l10n_ru_contract/views/contract_header_templates.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<odoo>
|
||||
<template id="contract_header_entity">
|
||||
<span>
|
||||
<b><t t-esc="object.company_id.partner_id.name"/></b>,
|
||||
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
|
||||
<t t-esc="(object.company_id.chief_id.partner_id.function or '').lower()"/>
|
||||
<t t-esc="(object.director_name_company or '').title()"/>,
|
||||
действующего на основании ОГРНИП №
|
||||
<t t-esc="object.company_id.company_registry or ''"/>,
|
||||
с одной стороны, и <b><t t-esc="object.partner_id.name or ''"/></b>,
|
||||
именуемое в дальнейшем <b>«Покупатель»</b>, в лице
|
||||
<t t-esc="(object.director_name_partner or '').title()"/>,
|
||||
действующего на основании устава общества,
|
||||
с другой стороны, вместе именуемые в дальнейшем <b>«Стороны»</b>
|
||||
заключили настоящий Договор о нижеследующем:
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template id="contract_header_ip">
|
||||
<span>
|
||||
<b><t t-esc="object.company_id.partner_id.name"/></b>,
|
||||
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
|
||||
<t t-esc="(object.company_id.chief_id.partner_id.function or '').lower()"/>
|
||||
<t t-esc="(object.director_name_company or '').title()"/>,
|
||||
действующего на основании ОГРНИП №
|
||||
<t t-esc="object.company_id.company_registry or ''"/>,
|
||||
с одной стороны, и <b><t t-esc="object.partner_id.name or ''"/></b>,
|
||||
именуемое в дальнейшем <b>«Покупатель»</b>, в лице
|
||||
<t t-esc="(object.director_name_partner or '').title()"/>,
|
||||
действующего на основании ОГРНИП №
|
||||
<t t-esc="object.partner_id.ogrn or ''"/>,
|
||||
с другой стороны, вместе именуемые в дальнейшем <b>«Стороны»</b>
|
||||
заключили настоящий Договор о нижеследующем:
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template id="contract_header_individual">
|
||||
<span>
|
||||
<b><t t-esc="object.company_id.partner_id.name"/></b>,
|
||||
именуемое в дальнейшем <b>«Поставщик»</b>, в лице
|
||||
<t t-esc="(object.company_id.chief_id.partner_id.function or '').lower()"/>
|
||||
<t t-esc="(object.director_name_company or '').title()"/>,
|
||||
действующего на основании ОГРНИП №
|
||||
<t t-esc="object.company_id.company_registry or ''"/>,
|
||||
с одной стороны, и <b><t t-esc="object.partner_id.name or ''"/></b>,
|
||||
именуемое в дальнейшем <b>«Покупатель»</b>,
|
||||
вместе именуемые в дальнейшем <b>«Стороны»</b>
|
||||
заключили настоящий Договор о нижеследующем:
|
||||
</span>
|
||||
</template>
|
||||
</odoo>
|
||||
29
l10n_ru_contract/views/contract_profile_views.xml
Normal file
29
l10n_ru_contract/views/contract_profile_views.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<odoo>
|
||||
<record id="contract_profile_view" model="ir.ui.view">
|
||||
<field name="name">contract.profile.form</field>
|
||||
<field name="model">contract.profile</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Профиль договора">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="contract_profile_action" model="ir.actions.act_window">
|
||||
<field name="name">Виды договоров</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">contract.profile</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<record id="contract_allowed_profiles_action" model="ir.actions.act_window">
|
||||
<field name="name">Настройка одновременно включенных договоров</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">contract.allowed.profiles</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
</odoo>
|
||||
30
l10n_ru_contract/views/mail_template.xml
Normal file
30
l10n_ru_contract/views/mail_template.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<odoo>
|
||||
<!-- <data noupdate="1">-->
|
||||
<!-- <record id="email_template_order_special" model="mail.template">-->
|
||||
<!-- <field name="name">Шаблон почты</field>-->
|
||||
<!-- <field name="model_id" ref="sale.model_sale_order" />-->
|
||||
<!-- <field name="email_from">${(object.company_id.email |safe}</field>-->
|
||||
<!-- <field name="email_to" >${object.partner_id.email}</field>-->
|
||||
<!-- <field name="subject">Заказ ${object.name or 'n/a' }</field>-->
|
||||
<!-- <field name="auto_delete" eval="True"/>-->
|
||||
<!-- <field name="body_html"><![CDATA[-->
|
||||
<!-- <p></p>-->
|
||||
<!-- ]]>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
|
||||
<!-- <record id="email_template_contract2" model="mail.template">-->
|
||||
<!-- <field name="name">Договор</field>-->
|
||||
<!-- <field name="model_id" ref="l10n_ru_contract.model_partner_contract_customer" />-->
|
||||
<!-- <field name="email_from">${(object.company_id.email |safe}</field>-->
|
||||
<!-- <field name="email_to" >${object.partner_id.email}</field>-->
|
||||
<!-- <field name="subject">Договор №${(object.name or 'n/a')} ${(object.company_id.name or 'n/a')} - ${(object.partner_id.parent_id.name or object.partner_id.name or 'n/a')} от ${(object.date_start or 'n/a')}</field>-->
|
||||
<!-- <field name="auto_delete" eval="True"/>-->
|
||||
<!-- <field name="lang">${object.partner_id.lang}</field>-->
|
||||
<!-- <field name="body_html"><![CDATA[-->
|
||||
<!-- <p></p>-->
|
||||
<!-- ]]>-->
|
||||
<!-- </field>-->
|
||||
<!-- </record>-->
|
||||
<!-- </data>-->
|
||||
</odoo>
|
||||
12
l10n_ru_contract/views/res_company_views.xml
Normal file
12
l10n_ru_contract/views/res_company_views.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<odoo>
|
||||
<record id="view_company_form_inherit_chief" model="ir.ui.view">
|
||||
<field name="name">res.company.form.chief.inherit</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vat']" position="after">
|
||||
<field name="chief_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
18
l10n_ru_contract/views/res_partner_views.xml
Normal file
18
l10n_ru_contract/views/res_partner_views.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<odoo>
|
||||
<record id="view_partner_form" model="ir.ui.view">
|
||||
<field name="name">view_partner_form</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 name="action_view_contract" type="object" class="oe_stat_button" icon="fa-pencil-square-o"
|
||||
context="{'view':'contract_customer_action', 'search_default_open': 1}">
|
||||
<field name="contract_count" widget="statinfo" string="Договоры"/>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='category_id']" position="after">
|
||||
<field name="passport" invisible="is_company==True"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
3
l10n_ru_contract/wizard/__init__.py
Normal file
3
l10n_ru_contract/wizard/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import report_template_wizard
|
||||
63
l10n_ru_contract/wizard/report_template_wizard.py
Normal file
63
l10n_ru_contract/wizard/report_template_wizard.py
Normal file
@ -0,0 +1,63 @@
|
||||
import base64
|
||||
from odoo import models, fields, api
|
||||
from io import BytesIO
|
||||
|
||||
try:
|
||||
from docxtpl import DocxTemplate
|
||||
except ImportError:
|
||||
DocxTemplate = None
|
||||
|
||||
class ReportTemplateWizard(models.TransientModel):
|
||||
_name = 'report.template.wizard'
|
||||
_description = 'Визард выбора шаблона для печати договора'
|
||||
|
||||
contract_id = fields.Many2one('model.contract', string='Договор', required=True)
|
||||
typeformat = fields.Selection([('docx','DOCX'),
|
||||
('md','MD')
|
||||
], string='Формат', required=True)
|
||||
template_id = fields.Many2one('contract.report.template', string='Шаблон отчета', required=True)
|
||||
generated_report = fields.Binary(string='Сформированный отчет')
|
||||
|
||||
def print_report(self):
|
||||
self.ensure_one()
|
||||
template = self.template_id
|
||||
contract = self.contract_id
|
||||
|
||||
if template.format == 'docx':
|
||||
template_data = base64.b64decode(template.attachment)
|
||||
docx_io = BytesIO(template_data)
|
||||
doc = DocxTemplate(docx_io)
|
||||
|
||||
context = {
|
||||
'o': contract,
|
||||
'user': self.env.user,
|
||||
'name': contract.name or '',
|
||||
}
|
||||
doc.render(context)
|
||||
|
||||
output_io = BytesIO()
|
||||
doc.save(output_io)
|
||||
output_io.seek(0)
|
||||
|
||||
self.generated_report = base64.b64encode(output_io.read())
|
||||
self.report_filename = f'{contract.name or "report"}.docx'
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': f'/web/content/?model={self._name}&id={self.id}&field=generated_report&filename={self.report_filename}&download=true',
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
elif template.format == 'md':
|
||||
md_content = f"# Отчет по договору\n\nНазвание договора: {contract.name or ''}\n"
|
||||
self.generated_report = base64.b64encode(md_content.encode('utf-8'))
|
||||
self.report_filename = f'{contract.name or "report"}.md'
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': f'/web/content/?model={self._name}&id={self.id}&field=generated_report&filename={self.report_filename}&download=true',
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
else:
|
||||
raise UserError('Неподдерживаемый формат шаблона')
|
||||
19
l10n_ru_contract/wizard/report_template_wizard.xml
Normal file
19
l10n_ru_contract/wizard/report_template_wizard.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<odoo>
|
||||
<record id="view_print_report_wizard_form" model="ir.ui.view">
|
||||
<field name="name">report.template.wizard.form</field>
|
||||
<field name="model">report.template.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Печать по шаблону">
|
||||
<group>
|
||||
<field name="contract_id" invisible="1"/>
|
||||
<field name="typeformat"/>
|
||||
<field name="template_id" required="1" options="{'no_create': True}" domain="[('typeformat','=',typeformat)]"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Печать" type="object" name="print_report" class="btn-primary"/>
|
||||
<button string="Отмена" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user