from odoo import fields, models, _ from odoo.exceptions import UserError from odoo.tools.safe_eval import safe_eval # Подсказка для поля формулы: доступные переменные и обязательный результат. CODE_HELP = ( "Python-код расчёта комиссии. Результат присвоить переменной `result`.\n" "Доступные переменные:\n" " line — строка заказа (sale.order.line)\n" " order — заказ (sale.order)\n" " product — товар строки (product.product)\n" " qty — количество (product_uom_qty)\n" " price_unit — цена за единицу\n" " price_subtotal — сумма строки без налогов\n" " partner — контрагент заказа (res.partner)\n" " commission — текущий вид комиссии (commission.commission)\n" "Пример: result = price_subtotal * 0.05" ) DEFAULT_CODE = "result = price_subtotal * 0.0" class CommissionCommission(models.Model): _name = 'commission.commission' _description = 'Вид комиссии агенту' _order = 'name' name = fields.Char(string='Наименование', required=True, translate=True) active = fields.Boolean(string='Активно', default=True) company_id = fields.Many2one( 'res.company', string='Компания', required=True, default=lambda self: self.env.company, ) calc_type = fields.Selection( selection=[ ('percentage', 'Процент'), ('code', 'Формула'), ], string='Способ расчёта', required=True, default='percentage', ) percentage = fields.Float( string='Процент, %', digits=(16, 4), help='Процент от суммы строки без налогов (для способа «Процент»).', ) amount_python_compute = fields.Text( string='Формула (Python)', default=DEFAULT_CODE, help=CODE_HELP, ) # --- Настройки будущих проводок вендор-билла --- journal_id = fields.Many2one( 'account.journal', string='Журнал', required=True, domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]", help='Журнал для счетов поставщика (Vendor Bill) по комиссии.', ) account_id = fields.Many2one( 'account.account', string='Счёт расходов', required=True, domain="[('account_type', 'in', ('expense', 'expense_depreciation', 'expense_direct_cost'))]", help='Счёт расходов (тип «Расходы»), на котором копятся комиссии.', ) partner_id = fields.Many2one( 'res.partner', string='Получатель', required=True, help='Партнёр-агент, на кого выставляется вендор-билл по комиссии.', ) analytic_account_id = fields.Many2one( 'account.analytic.account', string='Аналитический счёт', help='Опциональный аналитический счёт для строки вендор-билла.', ) # ------------------------------------------------------------------ # Расчёт суммы комиссии # ------------------------------------------------------------------ def _get_eval_context(self, line): """Ограниченный контекст выполнения формулы комиссии.""" self.ensure_one() return { 'line': line, 'order': line.order_id, 'product': line.product_id, 'qty': line.product_uom_qty, 'price_unit': line.price_unit, 'price_subtotal': line.price_subtotal, 'partner': line.order_id.partner_id, 'commission': self, 'result': 0.0, } def _compute_amount(self, line): """Сумма комиссии для конкретной строки заказа. :param line: sale.order.line :return: float — сумма комиссии в валюте заказа """ self.ensure_one() if self.calc_type == 'percentage': return line.price_subtotal * (self.percentage or 0.0) / 100.0 # calc_type == 'code' — формула result = ... localdict = self._get_eval_context(line) try: safe_eval( self.amount_python_compute or '', localdict, mode='exec', ) except Exception as ex: raise UserError(_( "Ошибка в формуле комиссии «%(name)s»:\n%(err)s", name=self.name, err=ex, )) from ex try: return float(localdict.get('result', 0.0)) except (TypeError, ValueError): raise UserError(_( "Формула комиссии «%(name)s» вернула нечисловой результат.", name=self.name, )) from None def _prepare_bill_line_vals(self, line, amount): """Значения строки вендор-билла для этой комиссии по строке заказа. :param line: sale.order.line — исходная строка заказа :param amount: float — рассчитанная сумма комиссии :return: dict значений для account.move.line (invoice_line_ids) """ self.ensure_one() vals = { 'name': _( "Комиссия «%(commission)s» по %(order)s / %(product)s", commission=self.name, order=line.order_id.name, product=line.product_id.display_name or line.name, ), 'account_id': self.account_id.id, 'quantity': 1.0, 'price_unit': amount, } if self.analytic_account_id: # analytic_distribution — JSON {id_счёта: процент}, сумма = 100%. vals['analytic_distribution'] = {str(self.analytic_account_id.id): 100.0} return vals