Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC

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

View File

@ -0,0 +1,103 @@
====================================================
Шаблоны типовых операций - Интеграция с инвойсами
====================================================
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
|badge1| |badge2|
Модуль расширяет функциональность шаблонов типовых операций для работы
с инвойсами клиентов и счетами поставщиков, автоматически применяя шаблоны
при проведении документов.
**Содержание**
.. contents::
:local:
Возможности
===========
* Автоматическое применение шаблонов при проведении инвойсов
* Замена стандартного распределения по счетам на распределение из шаблона
* Поддержка инвойсов клиентов и счетов поставщиков
* Настройка через параметры системы
* Сохранение строк инвойса с обновлением счетов в проводках
* Автоматическое создание строк оплаты с датами погашения
Настройка
=========
Для настройки модуля необходимо:
#. Перейти в *Бухгалтерия > Настройка > Параметры*
#. В разделе "Шаблоны типовых операций" включить опцию "Использовать шаблоны для инвойсов"
#. Сохранить настройки
Настройка шаблонов:
~~~~~~~~~~~~~~~~~~~
При создании шаблонов для инвойсов настройте типы строк:
* **Продуктовая строка**: Заменяет счёт в существующих продуктовых строках (счета доходов/расходов)
* **Строка оплаты**: Создаёт строки дебиторки/кредиторки с датами погашения
Использование
=============
После настройки модуль работает автоматически:
#. Создайте инвойс клиента или счёт поставщика
#. Добавьте строки инвойса как обычно
#. Выберите шаблон типовой операции в поле "Шаблон типовой операции"
#. Нажмите "Провести"
Модуль выполнит следующие действия:
* Удалит стандартные строки условий оплаты
* Обновит счета в продуктовых строках согласно шаблону (line_type='product')
* Создаст новые строки оплаты согласно шаблону (line_type='payment')
* Установит корректные даты погашения для счетов дебиторки/кредиторки
* Проведёт инвойс с распределением по счетам из шаблона
Известные проблемы / Планы развития
====================================
* В настоящее время поддерживаются только типы документов out_invoice и in_invoice
* Шаблон должен быть выбран до проведения документа
* В планах: Автоматический выбор шаблона на основе свойств инвойса
Отслеживание ошибок
===================
Ошибки отслеживаются на `GitHub Issues <https://github.com/OCA/account-financial-tools/issues>`_.
В случае проблем, пожалуйста, проверьте, не была ли ваша проблема уже зарегистрирована.
Авторы
======
Авторы
~~~~~~
* MK.Lab, RuOdoo
Участники
~~~~~~~~~
* MK.Lab, RuOdoo
Сопровождающие
~~~~~~~~~~~~~~
Модуль сопровождается MK.Lab, RuOdoo.
.. image:: https://ruodoo.ru/logo.png
:alt: MK.Lab, RuOdoo
:target: https://ruodoo.ru
Модуль является частью проекта расширений для бухгалтерии.

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,42 @@
{
'name': 'Accounting Journal Templates - Invoice',
'version': '19.0.1.0.0',
'summary': 'Интеграция шаблонов типовых операций с Invoice и Vendor Bill',
'description': """
Шаблоны типовых операций - Интеграция с инвойсами
==================================================
Автоматическое применение шаблонов типовых операций к инвойсам клиентов и счетам
поставщиков при проведении, заменяя стандартное распределение по счетам на
распределение из шаблона.
Возможности:
------------
* Автоматическое применение шаблонов при проведении инвойсов
* Замена счетов в продуктовых строках на счета из шаблона
* Создание строк условий оплаты с корректными датами погашения
* Поддержка инвойсов клиентов и счетов поставщиков
* Настройка через параметры системы
* Сохранение итоговых сумм инвойса
Технические детали:
-------------------
* Расширяет модель account.move
* Переопределяет метод action_post()
* Обновляет существующие продуктовые строки вместо создания дубликатов
* Создаёт строки payment_term для счетов дебиторки/кредиторки
* Поддерживает баланс и валидацию инвойса
""",
'author': 'MK.Lab, RuOdoo',
'website': 'https://ruodoo.ru',
'category': 'Accounting/Accounting',
'depends': ['account', 'account_move_templates'],
'data': [
'views/account_move_views.xml',
'views/res_config_settings_views.xml',
],
'installable': True,
'application': False,
'license': 'LGPL-3',
'maintainers': ['mklab', 'ruodoo'],
}

View File

@ -0,0 +1,3 @@
from . import account_move
from . import account_move_line
from . import res_config_settings

View File

@ -0,0 +1,151 @@
from odoo import api, fields, models
from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
class AccountMove(models.Model):
_inherit = ['account.move', 'account.move.template.mixin']
move_ids = fields.Many2many(
comodel_name='account.move',
relation='account_move_template_invoice_move_rel',
column1='document_id',
column2='move_id',
string='Проводки по шаблону',
)
journal_template_id = fields.Many2one(
comodel_name='account.move.template',
string='Шаблон типовой операции',
default=lambda self: self._default_journal_template(),
)
def _default_journal_template(self):
use = self.env['ir.config_parameter'].sudo().get_param(
'account_move_templates_invoice.use_journal_templates_for_invoices'
)
if use:
return self.env['account.move.template'].search([], limit=1, order='id asc')
return False
def get_move_line_amounts(self):
"""Return invoice total as the base amount for the wizard."""
self.ensure_one()
return [{'name': self.name or '/', 'amount': self.amount_total}]
def _replace_lines_from_template(self, move):
"""
Replace ALL lines (product + payment_term) with template lines
plus one explicit payment_term line with date_maturity.
We pass 'balance' explicitly in vals so _get_protected_vals protects
it from being overwritten by _sync_invoice.
"""
_logger.info("TEMPLATE DEBUG: _replace_lines_from_template START for move %s", move.id)
template = move.journal_template_id
_logger.info("TEMPLATE DEBUG: template = %s", template)
base_amount = move.amount_untaxed or move.amount_total
_logger.info("TEMPLATE DEBUG: base_amount = %s", base_amount)
# Determine receivable/payable account
partner = move.commercial_partner_id.with_company(move.company_id)
if move.is_sale_document(include_receipts=True):
term_account = partner.property_account_receivable_id
else:
term_account = partner.property_account_payable_id
_logger.info("TEMPLATE DEBUG: term_account = %s", term_account)
# due date: use invoice_date_due, invoice_date, or today
due_date = (
move.invoice_date_due
or move.invoice_date
or fields.Date.context_today(move)
)
_logger.info("TEMPLATE DEBUG: due_date = %s", due_date)
# Remove only payment_term lines, keep product lines from invoice_line_ids
_logger.info("TEMPLATE DEBUG: Removing existing payment_term lines")
lines_to_remove = move.line_ids.filtered(lambda l: l.display_type == 'payment_term')
_logger.info("TEMPLATE DEBUG: Lines to remove: %s", [(l.id, l.account_id.code, l.display_type) for l in lines_to_remove])
lines_to_remove.with_context(
dynamic_unlink=True,
check_move_validity=False,
skip_invoice_sync=True,
).unlink()
_logger.info("TEMPLATE DEBUG: payment_term lines removed")
AML = self.env['account.move.line']
ctx = dict(check_move_validity=False, skip_invoice_sync=True)
# 1. Process template lines based on line_type
_logger.info("TEMPLATE DEBUG: Processing template lines")
# Get existing product lines from invoice_line_ids
product_lines = move.line_ids.filtered(lambda l: l.display_type == 'product')
_logger.info("TEMPLATE DEBUG: Found %d product lines", len(product_lines))
# Separate template lines by type
template_product_lines = template.line_ids.filtered(lambda l: l.line_type == 'product')
template_payment_lines = template.line_ids.filtered(lambda l: l.line_type == 'payment')
_logger.info("TEMPLATE DEBUG: Template has %d product lines and %d payment lines",
len(template_product_lines), len(template_payment_lines))
# Update existing product lines with accounts from template (line_type='product')
for idx, product_line in enumerate(product_lines):
if idx < len(template_product_lines):
tpl_line = template_product_lines[idx]
_logger.info("TEMPLATE DEBUG: Updating product line %d: old account=%s, new account=%s",
product_line.id, product_line.account_id.code, tpl_line.account_id.code)
product_line.with_context(**ctx).write({'account_id': tpl_line.account_id.id})
# Create payment lines from template (line_type='payment')
_logger.info("TEMPLATE DEBUG: Creating payment lines from template")
for tpl_line in template_payment_lines:
line_amount = base_amount * tpl_line.percent / 100.0
balance = line_amount if tpl_line.move_type == 'debit' else -line_amount
_logger.info("TEMPLATE DEBUG: Creating payment line: account=%s, balance=%s",
tpl_line.account_id.id, balance)
vals = {
'move_id': move.id,
'account_id': tpl_line.account_id.id,
'name': template.name,
'balance': balance,
'amount_currency': balance,
'date_maturity': due_date,
'display_type': 'payment_term',
}
AML.with_context(**ctx).create(vals)
_logger.info("TEMPLATE DEBUG: Template lines processed")
_logger.info("TEMPLATE DEBUG: _replace_lines_from_template END")
def action_post(self):
_logger.info("TEMPLATE DEBUG: action_post called for moves: %s", self.ids)
use_templates = self.env['ir.config_parameter'].sudo().get_param(
'account_move_templates_invoice.use_journal_templates_for_invoices'
)
_logger.info("TEMPLATE DEBUG: use_templates = %s", use_templates)
if use_templates:
for move in self.filtered(
lambda m: m.move_type in ('out_invoice', 'in_invoice')
):
_logger.info("TEMPLATE DEBUG: Processing move %s, template: %s", move.id, move.journal_template_id)
if not move.journal_template_id:
raise UserError(
'Выберите шаблон типовой операции перед проведением.'
)
try:
_logger.info("TEMPLATE DEBUG: Calling _replace_lines_from_template")
self._replace_lines_from_template(move)
_logger.info("TEMPLATE DEBUG: _replace_lines_from_template completed")
except Exception as e:
_logger.error("TEMPLATE DEBUG: Error in _replace_lines_from_template: %s", e, exc_info=True)
raise
return super().action_post()

View File

@ -0,0 +1,7 @@
from odoo import models
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
# No overrides needed - we handle everything in action_post()

View File

@ -0,0 +1,10 @@
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
use_journal_templates_for_invoices = fields.Boolean(
string='Использовать шаблоны типовых операций для счетов',
config_parameter='account_move_templates_invoice.use_journal_templates_for_invoices',
)

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_account_move_template_invoice_form" model="ir.ui.view">
<field name="name">account.move.template.invoice.form</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<!-- Smart button: visible when move_count > 0 -->
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object"
name="action_view_moves"
class="oe_stat_button"
icon="fa-book"
invisible="move_count == 0">
<field name="move_count" widget="statinfo" string="Проводки"/>
</button>
</xpath>
<!-- Template selector after invoice_date -->
<xpath expr="//field[@name='invoice_date']" position="after">
<field name="journal_template_id"
string="Шаблон типовой операции"
options="{'no_create': True}"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form_template_invoice" model="ir.ui.view">
<field name="name">res.config.settings.view.form.template.invoice</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//app[@name='account']" position="inside">
<block title="Шаблоны типовых операций" name="journal_templates_setting_container">
<setting help="Использовать шаблоны типовых операций вместо стандартных проводок для счетов">
<field name="use_journal_templates_for_invoices"/>
<label for="use_journal_templates_for_invoices"/>
</setting>
</block>
</xpath>
</field>
</record>
</data>
</odoo>