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

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

View File

@ -0,0 +1,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',
)