mirror of
https://github.com/bringout/oca-ocb-sale.git
synced 2026-04-25 11:52:05 +02:00
19.0 vanilla
This commit is contained in:
parent
79f83631d5
commit
73afc09215
6267 changed files with 1534193 additions and 1130106 deletions
|
|
@ -1,5 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from unittest.mock import patch
|
||||
from odoo import Command, fields
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
|
@ -7,43 +10,58 @@ from odoo.tests import tagged
|
|||
@tagged('post_install', '-at_install')
|
||||
class TestProductMargin(AccountTestInvoicingCommon):
|
||||
|
||||
def test_product_margin(self):
|
||||
''' In order to test the product_margin module '''
|
||||
|
||||
supplier = self.env['res.partner'].create({'name': 'Supplier'})
|
||||
customer = self.env['res.partner'].create({'name': 'Customer'})
|
||||
ipad = self.env['product.product'].create({
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.supplier = cls.env['res.partner'].create({'name': 'Supplier'})
|
||||
cls.customer = cls.env['res.partner'].create({'name': 'Customer'})
|
||||
cls.ipad = cls.env['product.product'].create({
|
||||
'name': 'Ipad',
|
||||
'standard_price': 500.0,
|
||||
'list_price': 750.0,
|
||||
})
|
||||
|
||||
invoices = self.env['account.move'].create([
|
||||
cls.invoices = cls.env['account.move'].create([
|
||||
{
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': supplier.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': ipad.id, 'quantity': 10.0, 'price_unit': 300.0})],
|
||||
'partner_id': cls.supplier.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': cls.ipad.id, 'quantity': 10.0, 'price_unit': 300.0})],
|
||||
},
|
||||
{
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': supplier.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': ipad.id, 'quantity': 4.0, 'price_unit': 450.0})],
|
||||
'partner_id': cls.supplier.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': cls.ipad.id, 'quantity': 4.0, 'price_unit': 450.0})],
|
||||
},
|
||||
{
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': customer.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': ipad.id, 'quantity': 20.0, 'price_unit': 750.0})],
|
||||
'partner_id': cls.customer.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': cls.ipad.id, 'quantity': 20.0, 'price_unit': 750.0})],
|
||||
},
|
||||
{
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': customer.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': ipad.id, 'quantity': 10.0, 'price_unit': 550.0})],
|
||||
'partner_id': cls.customer.id,
|
||||
'invoice_line_ids': [(0, 0, {'product_id': cls.ipad.id, 'quantity': 10.0, 'price_unit': 550.0})],
|
||||
},
|
||||
])
|
||||
invoices.invoice_date = invoices[0].date
|
||||
invoices.action_post()
|
||||
cls.invoices.invoice_date = cls.invoices[0].date
|
||||
|
||||
result = ipad._compute_product_margin_fields_values()
|
||||
def test_aggregates(self):
|
||||
model = self.env['product.product']
|
||||
field_names = [
|
||||
'turnover', 'sale_avg_price', 'sale_num_invoiced', 'purchase_num_invoiced',
|
||||
'sales_gap', 'purchase_gap', 'total_cost', 'sale_expected', 'normal_cost',
|
||||
'total_margin', 'expected_margin', 'total_margin_rate', 'expected_margin_rate',
|
||||
]
|
||||
self.assertEqual(
|
||||
model.fields_get(field_names, ['aggregator']),
|
||||
{field_name: {'aggregator': 'sum'} for field_name in field_names},
|
||||
f"Fields {', '.join(map(repr, field_names))} must be flagged as aggregatable.",
|
||||
)
|
||||
|
||||
def test_product_margin(self):
|
||||
''' In order to test the product_margin module '''
|
||||
|
||||
self.invoices.action_post()
|
||||
|
||||
# Sale turnover ( Quantity * Price Subtotal / Quantity)
|
||||
sale_turnover = ((20.0 * 750.00) + (10.0 * 550.00))
|
||||
|
|
@ -61,7 +79,195 @@ class TestProductMargin(AccountTestInvoicingCommon):
|
|||
expected_margin = sale_expected - purchase_normal_cost
|
||||
|
||||
# Check total margin
|
||||
self.assertEqual(result[ipad.id]['total_margin'], total_margin, "Wrong Total Margin.")
|
||||
self.assertEqual(self.ipad.total_margin, total_margin, "Wrong Total Margin.")
|
||||
|
||||
# Check expected margin
|
||||
self.assertEqual(result[ipad.id]['expected_margin'], expected_margin, "Wrong Expected Margin.")
|
||||
self.assertEqual(self.ipad.expected_margin, expected_margin, "Wrong Expected Margin.")
|
||||
|
||||
# Check that read_group doesn't generate an UPDATE and returns the right answer
|
||||
self.ipad.invalidate_recordset()
|
||||
with patch.object(self.registry['product.product'], 'write') as write_method:
|
||||
total_margin_sum, expected_margin_sum = self.env['product.product']._read_group(
|
||||
[('id', '=', self.ipad.id)],
|
||||
aggregates=['total_margin:sum', 'expected_margin:sum'],
|
||||
)[0]
|
||||
self.assertEqual(total_margin_sum, total_margin)
|
||||
self.assertEqual(expected_margin_sum, expected_margin)
|
||||
write_method.assert_not_called()
|
||||
|
||||
def test_product_margin_negative_price_in_move_lines(self):
|
||||
"""
|
||||
Test that product margins are calculated correctly when move lines
|
||||
include negative quantities or prices.
|
||||
"""
|
||||
self.ipad.write({
|
||||
'standard_price': 1000.0,
|
||||
'list_price': 1000.0,
|
||||
})
|
||||
|
||||
customer_invoice = self.env['account.move'].create([{
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': self.customer.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': self.ipad.id,
|
||||
'price_unit': 1000,
|
||||
'quantity': 2,
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': self.ipad.id,
|
||||
'price_unit': 1000,
|
||||
'quantity': -1,
|
||||
}),
|
||||
],
|
||||
}])
|
||||
|
||||
customer_invoice.action_post()
|
||||
|
||||
results = self.ipad._compute_product_margin_fields_values()
|
||||
self.assertEqual(results[self.ipad.id]['turnover'], 1000)
|
||||
self.assertEqual(results[self.ipad.id]['total_margin'], 1000)
|
||||
|
||||
vendor_bill = self.env['account.move'].create([{
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': self.supplier.id,
|
||||
'invoice_date': fields.Date.today(),
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': self.ipad.id,
|
||||
'price_unit': 250,
|
||||
'quantity': 2,
|
||||
}),
|
||||
Command.create({
|
||||
'product_id': self.ipad.id,
|
||||
'price_unit': 250,
|
||||
'quantity': -1,
|
||||
}),
|
||||
],
|
||||
}])
|
||||
vendor_bill.action_post()
|
||||
|
||||
results = self.ipad._compute_product_margin_fields_values()
|
||||
self.assertEqual(results[self.ipad.id]['total_cost'], 250)
|
||||
self.assertEqual(results[self.ipad.id]['total_margin'], 750)
|
||||
|
||||
def test_product_margin_read_grouping_sets(self):
|
||||
"""
|
||||
Test that product margins are aggregated properly when using _read_grouping_sets.
|
||||
"""
|
||||
self.invoices.action_post()
|
||||
|
||||
# Create a category and two other products.
|
||||
categ_portable = self.env['product.category'].create({'name': 'Portable'})
|
||||
iphone, imac = self.env['product.product'].create([
|
||||
{
|
||||
'name': 'iPhone',
|
||||
'standard_price': 1000,
|
||||
'list_price': 1200,
|
||||
'uom_id': self.uom_dozen.id,
|
||||
'categ_id': categ_portable.id,
|
||||
},
|
||||
{
|
||||
'name': 'iMac',
|
||||
'standard_price': 1500,
|
||||
'list_price': 1800,
|
||||
},
|
||||
])
|
||||
|
||||
# Update products
|
||||
self.ipad.write({'categ_id': categ_portable.id})
|
||||
|
||||
# Create invoices for iPhone and iMac
|
||||
invoices = self.env['account.move'].create([
|
||||
{
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': self.supplier.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({'product_id': iphone.id, 'quantity': 10.0, 'price_unit': 600.0}),
|
||||
Command.create({'product_id': imac.id, 'quantity': 10.0, 'price_unit': 900.0}),
|
||||
],
|
||||
},
|
||||
{
|
||||
'move_type': 'in_invoice',
|
||||
'partner_id': self.supplier.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({'product_id': iphone.id, 'quantity': 4.0, 'price_unit': 900.0}),
|
||||
Command.create({'product_id': imac.id, 'quantity': 4.0, 'price_unit': 1350.0}),
|
||||
],
|
||||
},
|
||||
{
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': self.customer.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({'product_id': iphone.id, 'quantity': 20.0, 'price_unit': 1500.0}),
|
||||
Command.create({'product_id': imac.id, 'quantity': 20.0, 'price_unit': 2250.0}),
|
||||
],
|
||||
},
|
||||
{
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': self.customer.id,
|
||||
'invoice_line_ids': [
|
||||
Command.create({'product_id': iphone.id, 'quantity': 10.0, 'price_unit': 1100.0}),
|
||||
Command.create({'product_id': imac.id, 'quantity': 10.0, 'price_unit': 1650.0}),
|
||||
],
|
||||
},
|
||||
])
|
||||
invoices.invoice_date = fields.Date.today()
|
||||
invoices.action_post()
|
||||
|
||||
# Expected Values
|
||||
|
||||
# ipad (Portable, Units):
|
||||
# sale_turnover = (20 * 750) + (10 * 550) = 20500
|
||||
# purchase_total_cost = (10 * 300) + (4 * 450) = 4800
|
||||
# sale_expected = 750 * 30 = 22500
|
||||
# purchase_expected = 14 * 500 = 7000
|
||||
# total_margin = 20500 - 4800 = 15700
|
||||
# expected_margin = 22500 - 7000 = 15500
|
||||
|
||||
# iphone (Portable, Dozens):
|
||||
# sale_turnover = (20 * 1500) + (10 * 1100) = 41000
|
||||
# purchase_total_cost = (10 * 600) + (4 * 900) = 9600
|
||||
# sale_expected = 1200 * 30 = 36000
|
||||
# purchase_expected = 14 * 1000 = 14000
|
||||
# total_margin = 41000 - 9600 = 31400
|
||||
# expected_margin = 36000 - 14000 = 22000
|
||||
|
||||
# imac (All, Units):
|
||||
# sale_turnover = (20 * 2250) + (10 * 1650) = 61500
|
||||
# purchase_total_cost = (10 * 900) + (4 * 1350) = 14400
|
||||
# sale_expected = 1800 * 30 = 54000
|
||||
# purchase_expected = 14 * 1500 = 21000
|
||||
# total_margin = 61500 - 14400 = 47100
|
||||
# expected_margin = 54000 - 21000 = 33000
|
||||
|
||||
result = self.env['product.product']._read_grouping_sets(
|
||||
[('id', 'in', [iphone.id, imac.id, self.ipad.id])],
|
||||
grouping_sets=[['categ_id', 'uom_id'], ['categ_id'], ['uom_id'], []],
|
||||
aggregates=['total_margin:sum', 'expected_margin:sum'],
|
||||
)
|
||||
|
||||
# 1. ['categ_id', 'uom_id']
|
||||
res_cat_uom = result[0]
|
||||
self.assertEqual(len(res_cat_uom), 3)
|
||||
map_cat_uom = {(r[0], r[1]): (r[2], r[3]) for r in res_cat_uom}
|
||||
self.assertEqual(map_cat_uom[categ_portable, self.uom_unit], (15700, 15500))
|
||||
self.assertEqual(map_cat_uom[categ_portable, self.uom_dozen], (31400, 22000))
|
||||
no_categ = self.env['product.category']
|
||||
self.assertEqual(map_cat_uom[no_categ, self.uom_unit], (47100, 33000))
|
||||
|
||||
# 2. ['categ_id']
|
||||
res_cat = result[1]
|
||||
map_cat = {r[0]: (r[1], r[2]) for r in res_cat}
|
||||
self.assertEqual(map_cat[categ_portable], (47100, 37500))
|
||||
self.assertEqual(map_cat[no_categ], (47100, 33000))
|
||||
|
||||
# 3. ['uom_id']
|
||||
res_uom = result[2]
|
||||
map_uom = {r[0]: (r[1], r[2]) for r in res_uom}
|
||||
self.assertEqual(map_uom[self.uom_unit], (62800, 48500))
|
||||
self.assertEqual(map_uom[self.uom_dozen], (31400, 22000))
|
||||
|
||||
# 4. []
|
||||
res_all = result[3]
|
||||
self.assertEqual(res_all[0], (94200, 70500))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue