add more tests for bemade_vendor_pricelist to get coverage up
This commit is contained in:
parent
5f6fb6002a
commit
626d4c5dc2
4 changed files with 377 additions and 213 deletions
|
|
@ -12,20 +12,6 @@ class SaleOrderLine(models.Model):
|
|||
digits='Product Price'
|
||||
)
|
||||
|
||||
gross_profit_percent_vendor = fields.Float(
|
||||
string='GP (%) on Vendor Price',
|
||||
groups='base.group_user',
|
||||
group_operator='avg',
|
||||
compute='_compute_gp_vendor'
|
||||
)
|
||||
|
||||
gross_profit_vendor = fields.Float(
|
||||
string='GP on Vendor Price',
|
||||
groups='base.group_user',
|
||||
digits='Product Price',
|
||||
compute='_compute_gp_vendor'
|
||||
)
|
||||
|
||||
purchase_price_actual = fields.Float(
|
||||
compute="_compute_actual_gp",
|
||||
digits='Product Price',
|
||||
|
|
@ -102,31 +88,13 @@ class SaleOrderLine(models.Model):
|
|||
if is_order and self.qty_to_deliver <= 0:
|
||||
return 0
|
||||
elif is_order and self.qty_to_deliver > 0:
|
||||
reserved = sum([q.reserved_quantity for q in self.move_ids.mapped('move_line_ids').mapped('quant_id')])
|
||||
missing = self.qty_to_deliver - reserved
|
||||
if float_compare(missing, 0.0,
|
||||
precision_rounding=self.product_uom.rounding) == 1:
|
||||
# Not enough reserved, check stock
|
||||
missing = missing - qty_available
|
||||
if float_compare(missing, 0.0,
|
||||
precision_rounding=self.product_uom.rounding) == 1:
|
||||
# Missing some stock to meet demand, return the quantity
|
||||
return missing
|
||||
else:
|
||||
# Enough stock available to meet this line's demand
|
||||
return 0
|
||||
else:
|
||||
# Already have stock reserved
|
||||
return 0
|
||||
reserved = sum([q.reserved_quantity for q in
|
||||
self.move_ids.mapped('move_line_ids').mapped('quant_id')])
|
||||
return max(0, self.qty_to_deliver - reserved - qty_available)
|
||||
else:
|
||||
# This is a quotation, don't bother with stock reservations
|
||||
# Also, if available is negative for some reason,
|
||||
missing = self.product_uom_qty - qty_available
|
||||
if float_compare(missing, 0.0,
|
||||
precision_rounding=self.product_uom.rounding) == 1:
|
||||
return missing
|
||||
else:
|
||||
return 0
|
||||
return max(0, self.product_uom_qty - qty_available)
|
||||
|
||||
@api.depends('product_id', 'product_id.seller_ids',
|
||||
'product_id.seller_ids.price')
|
||||
|
|
@ -147,15 +115,3 @@ class SaleOrderLine(models.Model):
|
|||
date=line.order_id.date_order or fields.Date.today(),
|
||||
round=False,
|
||||
) if to_cur and suppinfo.price else suppinfo.price
|
||||
|
||||
@api.depends('purchase_price_vendor')
|
||||
def _compute_gp_vendor(self):
|
||||
for line in self:
|
||||
if not line.price_unit or float_is_zero(line.price_unit):
|
||||
line.gross_profit_vendor = 0
|
||||
line.gross_profit_percent_vendor = 0
|
||||
continue
|
||||
unit_gp = line.price_unit - line.purchase_price_vendor
|
||||
line.gross_profit_percent_vendor = unit_gp / line.price_unit
|
||||
|
||||
line.gross_profit_vendor = unit_gp * line.product_uom_qty
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
from . import test_sale_order_line
|
||||
from . import test_margins
|
||||
372
bemade_margin_vendor_pricelist/tests/test_margins.py
Normal file
372
bemade_margin_vendor_pricelist/tests/test_margins.py
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
from odoo.tests import TransactionCase, tagged
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestSaleOrderLine(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
# Create a test vendor
|
||||
cls.vendor = cls.env['res.partner'].create({
|
||||
'name': 'Test Vendor',
|
||||
'supplier_rank': 1,
|
||||
})
|
||||
|
||||
# Create a test product
|
||||
cls.product = cls.env['product.product'].create({
|
||||
'name': 'Test Product',
|
||||
'type': 'product',
|
||||
'list_price': 100.0,
|
||||
'standard_price': 80.0,
|
||||
})
|
||||
|
||||
# Link supplier info to the product
|
||||
cls.supplierinfo = cls.env['product.supplierinfo'].create({
|
||||
'partner_id': cls.vendor.id,
|
||||
'product_tmpl_id': cls.product.product_tmpl_id.id,
|
||||
'min_qty': 1,
|
||||
'price': 75.0, # Vendor price
|
||||
'currency_id': cls.env.ref('base.USD').id, # Assuming USD for simplicity
|
||||
})
|
||||
|
||||
# Create a test sale order
|
||||
cls.sale_order = cls.env['sale.order'].create({
|
||||
'partner_id': cls.env.ref('base.res_partner_1').id,
|
||||
# Assuming a default partner
|
||||
})
|
||||
|
||||
# Create a test sale order line
|
||||
cls.sale_order_line = cls.env['sale.order.line'].create({
|
||||
'order_id': cls.sale_order.id,
|
||||
'product_id': cls.product.id,
|
||||
'product_uom_qty': 10,
|
||||
'price_unit': 120.0,
|
||||
})
|
||||
|
||||
def test_purchase_price_all_from_stock(self):
|
||||
"""Test when all quantity is fulfilled from stock."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 10 # All available
|
||||
|
||||
# Recompute the fields
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
# Assert purchase_price_actual equals purchase_price (from stock)
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price,
|
||||
"Purchase price should be from stock when all quantity is available."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from stock purchase price."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def assertSaleOrderComputesLikeSaleOrderLine(self):
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
self.sale_order.gross_profit,
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit_percent,
|
||||
self.sale_order.gross_profit_percent,
|
||||
)
|
||||
|
||||
def test_purchase_price_all_from_vendor(self):
|
||||
"""Test when all quantity is fulfilled from vendor."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 0
|
||||
|
||||
# Assert purchase_price_actual equals purchase_price_vendor (from vendor)
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price_vendor,
|
||||
"Purchase price should be from vendor when all quantity is missing from stock."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from vendor purchase price."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_purchase_price_mixed(self):
|
||||
"""Test when quantity is partially fulfilled from stock and partially from vendor."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 5
|
||||
# Calculate expected blended purchase price
|
||||
qty_from_vendor = 5
|
||||
qty_from_stock = 5
|
||||
expected_purchase_price = (
|
||||
(
|
||||
qty_from_vendor * self.sale_order_line.purchase_price_vendor) +
|
||||
(
|
||||
qty_from_stock * self.sale_order_line.purchase_price)
|
||||
) / self.sale_order_line.product_uom_qty
|
||||
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
expected_purchase_price,
|
||||
msg="Purchase price should be a blend of stock and vendor prices."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
expected_purchase_price * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from blended purchase price."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_negative_available_stock_zero_qty_ordered(self):
|
||||
"""We had a division by zero error with a zero product_uom_qty field
|
||||
and a negative available stock. This test aims to root out this and
|
||||
other possible causes of a div by zero error."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = -1 # Negative available stock
|
||||
|
||||
# Set product_uom_qty to zero
|
||||
self.sale_order_line.product_uom_qty = 0
|
||||
# Recompute the fields
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
# Assert gross_profit is 0
|
||||
self.assertEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
0.0,
|
||||
"Gross profit should be 0 when product_uom_qty is zero."
|
||||
)
|
||||
|
||||
# Assert gross_profit_percent is 0
|
||||
self.assertEqual(
|
||||
self.sale_order_line.gross_profit_percent,
|
||||
0.0,
|
||||
"Gross profit percent should be 0 when product_uom_qty is zero."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_purchase_price_all_from_stock_confirmed(self):
|
||||
"""Test when all quantity is fulfilled from stock."""
|
||||
self.sale_order.action_confirm()
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 10 # All available
|
||||
|
||||
# Recompute the fields
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
# Assert purchase_price_actual equals purchase_price (from stock)
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price,
|
||||
"Purchase price should be from stock when all quantity is available."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from stock purchase price."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_purchase_price_all_from_vendor_confirmed(self):
|
||||
"""Test when all quantity is fulfilled from vendor."""
|
||||
self.sale_order.action_confirm()
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 0
|
||||
|
||||
# Assert purchase_price_actual equals purchase_price_vendor (from vendor)
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price_vendor,
|
||||
"Purchase price should be from vendor when all quantity is missing from stock."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from vendor purchase price."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_purchase_price_mixed_confirmed(self):
|
||||
"""Test when quantity is partially fulfilled from stock and partially from vendor."""
|
||||
self.sale_order.action_confirm()
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 5
|
||||
# Calculate expected blended purchase price
|
||||
qty_from_vendor = 5
|
||||
qty_from_stock = 5
|
||||
expected_purchase_price = (
|
||||
(
|
||||
qty_from_vendor * self.sale_order_line.purchase_price_vendor) +
|
||||
(
|
||||
qty_from_stock * self.sale_order_line.purchase_price)
|
||||
) / self.sale_order_line.product_uom_qty
|
||||
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
expected_purchase_price,
|
||||
msg="Purchase price should be a blend of stock and vendor prices."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
expected_purchase_price * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from blended purchase price."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_negative_available_stock_zero_qty_ordered_confirmed(self):
|
||||
"""We had a division by zero error with a zero product_uom_qty field
|
||||
and a negative available stock. This test aims to root out this and
|
||||
other possible causes of a div by zero error."""
|
||||
self.sale_order.action_confirm()
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = -1 # Negative available stock
|
||||
|
||||
# Set product_uom_qty to zero
|
||||
self.sale_order_line.product_uom_qty = 0
|
||||
# Recompute the fields
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
# Assert gross_profit is 0
|
||||
self.assertEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
0.0,
|
||||
"Gross profit should be 0 when product_uom_qty is zero."
|
||||
)
|
||||
|
||||
# Assert gross_profit_percent is 0
|
||||
self.assertEqual(
|
||||
self.sale_order_line.gross_profit_percent,
|
||||
0.0,
|
||||
"Gross profit percent should be 0 when product_uom_qty is zero."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
||||
def test_determine_missing_stock_all_reserved(self):
|
||||
"""Test the actual _determine_missing_stock method when all stock is reserved."""
|
||||
# Set initial stock for the product
|
||||
location = self.env["stock.warehouse"].search([])[0].lot_stock_id
|
||||
quant = self.env["stock.quant"].create({
|
||||
"product_id": self.product.id,
|
||||
"location_id": location.id,
|
||||
"inventory_quantity": 10,
|
||||
"product_uom_id": self.product.uom_id.id,
|
||||
})
|
||||
quant.action_apply_inventory()
|
||||
|
||||
self.sale_order.action_confirm()
|
||||
# Force the move to be reserved
|
||||
self.sale_order.picking_ids.action_assign()
|
||||
|
||||
# Now, call _determine_missing_stock without mocking
|
||||
missing_stock = self.sale_order_line._determine_missing_stock()
|
||||
|
||||
# Assert that missing_stock is 0
|
||||
self.assertEqual(
|
||||
missing_stock,
|
||||
0,
|
||||
"_determine_missing_stock should return 0 when all stock is reserved."
|
||||
)
|
||||
|
||||
# Additionally, verify that purchase_price_actual is from stock
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price,
|
||||
"Purchase price should be from stock when all stock is reserved."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated when all stock is reserved."
|
||||
)
|
||||
|
||||
# Assert gross_profit_percent is calculated correctly
|
||||
if self.sale_order_line.price_subtotal:
|
||||
expected_gross_profit_percent = self.sale_order_line.gross_profit / self.sale_order_line.price_subtotal
|
||||
else:
|
||||
expected_gross_profit_percent = 0.0
|
||||
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit_percent,
|
||||
expected_gross_profit_percent,
|
||||
msg="Gross profit percent should be correctly calculated when all stock is reserved."
|
||||
)
|
||||
# Ensure the SO is correct as well
|
||||
self.assertSaleOrderComputesLikeSaleOrderLine()
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
from odoo.tests import TransactionCase, tagged
|
||||
from unittest.mock import patch, PropertyMock
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestSaleOrderLine(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
# Create a test vendor
|
||||
cls.vendor = cls.env['res.partner'].create({
|
||||
'name': 'Test Vendor',
|
||||
'supplier_rank': 1,
|
||||
})
|
||||
|
||||
# Create a test product
|
||||
cls.product = cls.env['product.product'].create({
|
||||
'name': 'Test Product',
|
||||
'type': 'product',
|
||||
'list_price': 100.0,
|
||||
'standard_price': 80.0,
|
||||
})
|
||||
|
||||
# Link supplier info to the product
|
||||
cls.supplierinfo = cls.env['product.supplierinfo'].create({
|
||||
'partner_id': cls.vendor.id,
|
||||
'product_tmpl_id': cls.product.product_tmpl_id.id,
|
||||
'min_qty': 1,
|
||||
'price': 75.0, # Vendor price
|
||||
'currency_id': cls.env.ref('base.USD').id, # Assuming USD for simplicity
|
||||
})
|
||||
|
||||
# Create a test sale order
|
||||
cls.sale_order = cls.env['sale.order'].create({
|
||||
'partner_id': cls.env.ref('base.res_partner_1').id,
|
||||
# Assuming a default partner
|
||||
})
|
||||
|
||||
# Create a test sale order line
|
||||
cls.sale_order_line = cls.env['sale.order.line'].create({
|
||||
'order_id': cls.sale_order.id,
|
||||
'product_id': cls.product.id,
|
||||
'product_uom_qty': 10,
|
||||
'price_unit': 120.0,
|
||||
})
|
||||
|
||||
def test_purchase_price_all_from_stock(self):
|
||||
"""Test when all quantity is fulfilled from stock."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 10 # All available
|
||||
|
||||
# Recompute the fields
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
# Assert purchase_price_actual equals purchase_price (from stock)
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price,
|
||||
"Purchase price should be from stock when all quantity is available."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from stock purchase price."
|
||||
)
|
||||
|
||||
def test_purchase_price_all_from_vendor(self):
|
||||
"""Test when all quantity is fulfilled from vendor."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 0
|
||||
|
||||
# Assert purchase_price_actual equals purchase_price_vendor (from vendor)
|
||||
self.assertEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
self.sale_order_line.purchase_price_vendor,
|
||||
"Purchase price should be from vendor when all quantity is missing from stock."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
self.sale_order_line.purchase_price_actual * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from vendor purchase price."
|
||||
)
|
||||
|
||||
def test_purchase_price_mixed(self):
|
||||
"""Test when quantity is partially fulfilled from stock and partially from vendor."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = 5
|
||||
# Calculate expected blended purchase price
|
||||
qty_from_vendor = 5
|
||||
qty_from_stock = 5
|
||||
expected_purchase_price = (
|
||||
(
|
||||
qty_from_vendor * self.sale_order_line.purchase_price_vendor) +
|
||||
(
|
||||
qty_from_stock * self.sale_order_line.purchase_price)
|
||||
) / self.sale_order_line.product_uom_qty
|
||||
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.purchase_price_actual,
|
||||
expected_purchase_price,
|
||||
msg="Purchase price should be a blend of stock and vendor prices."
|
||||
)
|
||||
|
||||
# Assert gross_profit is calculated correctly
|
||||
expected_gross_profit = self.sale_order_line.price_subtotal - (
|
||||
expected_purchase_price * self.sale_order_line.product_uom_qty
|
||||
)
|
||||
self.assertAlmostEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
expected_gross_profit,
|
||||
msg="Gross profit should be correctly calculated from blended purchase price."
|
||||
)
|
||||
|
||||
def test_negative_available_stock_zero_qty_ordered(self):
|
||||
"""We had a division by zero error with a zero product_uom_qty field
|
||||
and a negative available stock. This test aims to root out this and
|
||||
other possible causes of a div by zero error."""
|
||||
with patch.object(
|
||||
self.sale_order_line.product_id.__class__,
|
||||
"qty_available",
|
||||
new_callable=PropertyMock
|
||||
) as mock_qty_available:
|
||||
mock_qty_available.return_value = -1 # Negative available stock
|
||||
|
||||
# Set product_uom_qty to zero
|
||||
self.sale_order_line.product_uom_qty = 0
|
||||
# Recompute the fields
|
||||
self.sale_order_line._compute_actual_gp()
|
||||
|
||||
# Assert gross_profit is 0
|
||||
self.assertEqual(
|
||||
self.sale_order_line.gross_profit,
|
||||
0.0,
|
||||
"Gross profit should be 0 when product_uom_qty is zero."
|
||||
)
|
||||
|
||||
# Assert gross_profit_percent is 0
|
||||
self.assertEqual(
|
||||
self.sale_order_line.gross_profit_percent,
|
||||
0.0,
|
||||
"Gross profit percent should be 0 when product_uom_qty is zero."
|
||||
)
|
||||
Loading…
Reference in a new issue