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_template
from . import account_move_template_mixin
from . import account_move_template_wizard

View File

@ -0,0 +1,94 @@
from odoo import api, fields, models
from odoo.exceptions import ValidationError
class AccountMoveTemplateTag(models.Model):
_name = 'account.move.template.tag'
_description = 'Признак шаблона'
name = fields.Char(string='Название', required=True, translate=True)
color = fields.Integer(string='Цвет')
class AccountMoveTemplate(models.Model):
_name = 'account.move.template'
_description = 'Шаблон типовой операции'
name = fields.Char(string='Название', required=True)
description = fields.Text(string='Описание')
tag_ids = fields.Many2many(
comodel_name='account.move.template.tag',
string='Признаки',
)
line_ids = fields.One2many(
comodel_name='account.move.template.line',
inverse_name='template_id',
string='Строки шаблона',
)
@api.constrains('line_ids')
def _check_balance(self):
for template in self:
if not template.line_ids:
raise ValidationError(
'Шаблон должен содержать хотя бы одну строку.'
)
debit_sum = sum(
line.percent for line in template.line_ids if line.move_type == 'debit'
)
credit_sum = sum(
line.percent for line in template.line_ids if line.move_type == 'credit'
)
if round(debit_sum, 2) != round(credit_sum, 2):
diff = round(abs(debit_sum - credit_sum), 2)
raise ValidationError(
f'Сумма дебета ({debit_sum:.2f}%) не равна сумме кредита '
f'({credit_sum:.2f}%), разница: {diff:.2f}%'
)
class AccountMoveTemplateLine(models.Model):
_name = 'account.move.template.line'
_description = 'Строка шаблона типовой операции'
template_id = fields.Many2one(
comodel_name='account.move.template',
string='Шаблон',
required=True,
ondelete='cascade',
)
account_id = fields.Many2one(
comodel_name='account.account',
string='Счёт',
required=True,
ondelete='restrict',
)
move_type = fields.Selection(
selection=[('debit', 'Дебет'), ('credit', 'Кредит')],
string='Сторона',
required=True,
)
percent = fields.Float(
string='Процент',
required=True,
digits=(5, 2),
)
line_type = fields.Selection(
selection=[
('product', 'Продуктовая строка (заменяет счёт)'),
('payment', 'Строка оплаты (receivable/payable)')
],
string='Тип строки',
required=True,
default='product',
help='Продуктовая строка заменяет счёт в существующих product-строках инвойса. '
'Строка оплаты создаёт новую payment_term строку.'
)
@api.constrains('percent')
def _check_percent(self):
for line in self:
if line.percent < 0.01 or line.percent > 100.0:
raise ValidationError(
'Процент должен быть от 0.01 до 100.00.'
)

View File

@ -0,0 +1,57 @@
from odoo import api, fields, models
class AccountMoveTemplateMixin(models.AbstractModel):
_name = 'account.move.template.mixin'
_description = 'Миксин для создания проводок через шаблоны'
move_ids = fields.Many2many(
comodel_name='account.move',
string='Проводки',
)
move_count = fields.Integer(
compute='_compute_move_count',
string='Количество проводок',
)
@api.depends('move_ids')
def _compute_move_count(self):
for record in self:
record.move_count = len(record.move_ids)
def action_open_journal_wizard(self):
"""If move_ids is empty, open wizard. Otherwise delegate to action_view_moves."""
self.ensure_one()
if self.move_ids:
return self.action_view_moves()
amounts = self.get_move_line_amounts()
default_amount = amounts[0]['amount'] if amounts else 0.0
return {
'type': 'ir.actions.act_window',
'name': 'Создать проводку',
'res_model': 'account.move.template.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_res_model': self._name,
'default_res_id': self.id,
'default_amount': default_amount,
},
}
def action_view_moves(self):
"""Return action to view related account.move records."""
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': 'Проводки',
'res_model': 'account.move',
'view_mode': 'list,form',
'domain': [('id', 'in', self.move_ids.ids)],
}
def get_move_line_amounts(self):
"""Override in inheriting models to provide line amounts.
Returns list of dicts: [{'name': str, 'amount': float}, ...]
"""
return []

View File

@ -0,0 +1,103 @@
from odoo import api, fields, models
from odoo.exceptions import UserError
class AccountMoveTemplateWizardLine(models.TransientModel):
_name = 'account.move.template.wizard.line'
_description = 'Черновая строка визарда шаблона'
wizard_id = fields.Many2one(
comodel_name='account.move.template.wizard',
required=True,
ondelete='cascade',
)
account_id = fields.Many2one(
comodel_name='account.account',
string='Счёт',
required=True,
)
move_type = fields.Selection(
selection=[('debit', 'Дебет'), ('credit', 'Кредит')],
string='Сторона',
required=True,
)
amount = fields.Float(
string='Сумма',
digits=(16, 2),
)
class AccountMoveTemplateWizard(models.TransientModel):
_name = 'account.move.template.wizard'
_description = 'Визард создания проводки по шаблону'
res_model = fields.Char(string='Модель документа', required=True)
res_id = fields.Integer(string='ID документа', required=True)
template_id = fields.Many2one(
comodel_name='account.move.template',
string='Шаблон',
)
amount = fields.Float(
string='Базовая сумма',
digits=(16, 2),
)
draft_line_ids = fields.One2many(
comodel_name='account.move.template.wizard.line',
inverse_name='wizard_id',
string='Черновые строки',
)
@api.onchange('template_id', 'amount')
def _onchange_compute_draft_lines(self):
"""Recompute draft lines when template or amount changes."""
self.draft_line_ids = [(5, 0, 0)] # clear existing
if not self.template_id or not self.amount:
return
lines = []
for tpl_line in self.template_id.line_ids:
line_amount = self.amount * tpl_line.percent / 100.0
lines.append((0, 0, {
'account_id': tpl_line.account_id.id,
'move_type': tpl_line.move_type,
'amount': line_amount,
}))
self.draft_line_ids = lines
def action_post(self):
"""Create account.move from draft lines and link to source document."""
self.ensure_one()
if not self.template_id or not self.amount:
raise UserError('Необходимо выбрать шаблон и указать базовую сумму.')
try:
# Build move lines
move_line_vals = []
for draft_line in self.draft_line_ids:
if draft_line.move_type == 'debit':
debit = draft_line.amount
credit = 0.0
else:
debit = 0.0
credit = draft_line.amount
move_line_vals.append((0, 0, {
'account_id': draft_line.account_id.id,
'debit': debit,
'credit': credit,
'name': self.template_id.name,
}))
# Create the move
move = self.env['account.move'].create({
'move_type': 'entry',
'state': 'draft',
'line_ids': move_line_vals,
})
# Link move to source document via move_ids
source_record = self.env[self.res_model].browse(self.res_id)
source_record.move_ids = [(4, move.id)]
except Exception as e:
raise UserError(f'Ошибка при создании проводки: {str(e)}') from e
return {'type': 'ir.actions.act_window_close'}