Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC

This commit is contained in:
CI Publish Bot
2026-11-01 22:54:30 +00:00
commit 26250aa580
2492 changed files with 664277 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
from odoo.tests import tagged
from odoo.exceptions import UserError
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
@tagged('post_install', '-at_install')
class TestCommission(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
# AccountTestInvoicingCommon работает под пользователем бухгалтерии
# в контексте тестовой компании, но без прав на продажи и комиссии.
# Добавляем нужные группы этому пользователю (компания сохраняется).
cls.env.user.group_ids |= (
cls.env.ref('sales_team.group_sale_salesman_all_leads')
| cls.env.ref('ruodoo_commission.group_commission_manager')
)
cls.company = cls.company_data['company']
cls.purchase_journal = cls.company_data['default_journal_purchase']
cls.expense_account = cls.company_data['default_account_expense']
cls.agent_a = cls.env['res.partner'].create({'name': 'Агент А'})
cls.agent_b = cls.env['res.partner'].create({'name': 'Агент Б'})
cls.customer = cls.env['res.partner'].create({'name': 'Покупатель ООО'})
cls.product = cls.env['product.product'].create({
'name': 'Товар А', 'type': 'consu', 'lst_price': 100.0,
})
cls.commission_pct = cls.env['commission.commission'].create({
'name': 'Комиссия 10%',
'calc_type': 'percentage',
'percentage': 10.0,
'partner_id': cls.agent_a.id,
'journal_id': cls.purchase_journal.id,
'account_id': cls.expense_account.id,
})
cls.commission_code = cls.env['commission.commission'].create({
'name': 'Комиссия по формуле',
'calc_type': 'code',
'amount_python_compute': 'result = price_subtotal * 0.05',
'partner_id': cls.agent_b.id,
'journal_id': cls.purchase_journal.id,
'account_id': cls.expense_account.id,
})
def _make_order(self, commissions=None, price_unit=1000.0, qty=1):
line_vals = {
'product_id': self.product.id,
'product_uom_qty': qty,
'price_unit': price_unit,
}
if commissions:
line_vals['commission_ids'] = [(6, 0, [c.id for c in commissions])]
return self.env['sale.order'].create({
'partner_id': self.customer.id,
'order_line': [(0, 0, line_vals)],
})
# ---------------- Расчёт ----------------
def test_percentage_amount(self):
order = self._make_order([self.commission_pct], price_unit=1000.0)
line = order.order_line
# 10% от price_subtotal (1000) = 100
self.assertAlmostEqual(line.commission_amount, 100.0)
def test_code_formula_amount(self):
order = self._make_order([self.commission_code], price_unit=1000.0)
# 5% от 1000 = 50
self.assertAlmostEqual(order.order_line.commission_amount, 50.0)
def test_multiple_commissions_on_line(self):
order = self._make_order(
[self.commission_pct, self.commission_code], price_unit=1000.0,
)
# 100 + 50 = 150
self.assertAlmostEqual(order.order_line.commission_amount, 150.0)
self.assertAlmostEqual(order.amount_commission, 150.0)
def test_commission_not_in_amount_total(self):
order = self._make_order([self.commission_pct], price_unit=1000.0)
# amount_total не должен включать комиссию
self.assertNotEqual(order.amount_total, 0.0)
self.assertAlmostEqual(order.amount_total, order.amount_untaxed + order.amount_tax)
def test_bad_formula_raises(self):
bad = self.env['commission.commission'].create({
'name': 'Плохая формула',
'calc_type': 'code',
'amount_python_compute': 'result = undefined_var + 1',
'partner_id': self.agent_a.id,
'journal_id': self.purchase_journal.id,
'account_id': self.expense_account.id,
})
# Сумма комиссии — хранимое вычисляемое поле, ошибка формулы
# проявляется при вычислении (создании строки заказа).
with self.assertRaises(UserError):
order = self._make_order([bad], price_unit=1000.0)
order.order_line._compute_commission_amount()
# ---------------- Начисление ----------------
def test_accrue_creates_posted_bills_by_partner(self):
order = self._make_order(
[self.commission_pct, self.commission_code], price_unit=1000.0,
)
order.action_confirm()
self.assertEqual(order.state, 'sale')
order.action_accrue_commission()
bills = order.commission_bill_ids
# Два разных получателя -> два билла
self.assertEqual(len(bills), 2)
self.assertEqual(set(bills.mapped('move_type')), {'in_invoice'})
self.assertEqual(set(bills.mapped('state')), {'posted'})
self.assertEqual(bills.mapped('commission_sale_order_id'), order)
bill_a = bills.filtered(lambda m: m.partner_id == self.agent_a)
bill_b = bills.filtered(lambda m: m.partner_id == self.agent_b)
self.assertAlmostEqual(bill_a.amount_total, 100.0)
self.assertAlmostEqual(bill_b.amount_total, 50.0)
# Счёт расходов проставлен на строках
self.assertEqual(
set(bill_a.invoice_line_ids.mapped('account_id')),
{self.expense_account},
)
def test_accrue_requires_confirmed(self):
order = self._make_order([self.commission_pct])
with self.assertRaises(UserError):
order.action_accrue_commission()
def test_accrue_dedup(self):
order = self._make_order([self.commission_pct])
order.action_confirm()
order.action_accrue_commission()
with self.assertRaises(UserError):
order.action_accrue_commission()
def test_accrue_skips_zero(self):
zero = self.env['commission.commission'].create({
'name': 'Комиссия 0%',
'calc_type': 'percentage', 'percentage': 0.0,
'partner_id': self.agent_a.id,
'journal_id': self.purchase_journal.id,
'account_id': self.expense_account.id,
})
order = self._make_order([zero])
order.action_confirm()
with self.assertRaises(UserError):
order.action_accrue_commission()
self.assertFalse(order.commission_bill_ids)
# ---------------- Отмена заказа ----------------
def test_cancel_order_removes_bills(self):
order = self._make_order([self.commission_pct])
order.action_confirm()
order.action_accrue_commission()
self.assertTrue(order.commission_bill_ids)
bill_ids = order.commission_bill_ids.ids
order.action_cancel()
self.assertEqual(order.state, 'cancel')
# Биллы удалены
remaining = self.env['account.move'].search([('id', 'in', bill_ids)])
self.assertFalse(remaining)
def test_cancel_order_no_bills_ok(self):
order = self._make_order([self.commission_pct])
order.action_confirm()
# Без начисления отмена не должна падать
order.action_cancel()
self.assertEqual(order.state, 'cancel')
# ---------------- Смарт-кнопка ----------------
def test_bill_count_and_action(self):
order = self._make_order(
[self.commission_pct, self.commission_code],
)
order.action_confirm()
order.action_accrue_commission()
self.assertEqual(order.commission_bill_count, 2)
action = order.action_view_commission_bills()
self.assertEqual(action['res_model'], 'account.move')
self.assertIn('commission_sale_order_id', str(action['domain']))