mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 11:09:17 +00:00
test: add coverage for taxes_and_totals and transaction_base
Agent-Logs-Url: https://github.com/frappe/erpnext/sessions/a54bcd34-9afc-47ca-b06d-a00df73a80ea Co-authored-by: mihir-kandoi <8833206+mihir-kandoi@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
032a282f84
commit
252eef710c
@@ -3,6 +3,7 @@ from unittest.mock import patch
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
||||||
|
from erpnext.selling.doctype.quotation.test_quotation import make_quotation
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.tests.utils import ERPNextTestSuite
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
@@ -59,3 +60,82 @@ class TestTaxesAndTotals(ERPNextTestSuite):
|
|||||||
self.assertEqual(so.rounding_adjustment, 0)
|
self.assertEqual(so.rounding_adjustment, 0)
|
||||||
self.assertEqual(so.base_rounded_total, 0)
|
self.assertEqual(so.base_rounded_total, 0)
|
||||||
self.assertEqual(so.base_rounding_adjustment, 0)
|
self.assertEqual(so.base_rounding_adjustment, 0)
|
||||||
|
|
||||||
|
def test_calculate_margin_amount_type(self):
|
||||||
|
"""When rate exceeds price_list_rate and no pricing rules, margin type is set to 'Amount'."""
|
||||||
|
so = make_sales_order(do_not_save=True)
|
||||||
|
item = so.items[0]
|
||||||
|
item.qty = 2
|
||||||
|
item.price_list_rate = 100.0
|
||||||
|
item.rate = 120.0 # rate > price_list_rate → implicit Amount margin
|
||||||
|
item.pricing_rules = ""
|
||||||
|
item.margin_type = None
|
||||||
|
item.margin_rate_or_amount = 0
|
||||||
|
|
||||||
|
calculate_taxes_and_totals(so)
|
||||||
|
|
||||||
|
self.assertEqual(item.margin_type, "Amount")
|
||||||
|
self.assertEqual(item.margin_rate_or_amount, 20.0)
|
||||||
|
# rate_with_margin should equal the explicit rate
|
||||||
|
self.assertEqual(item.rate_with_margin, 120.0)
|
||||||
|
|
||||||
|
def test_calculate_margin_percentage_type(self):
|
||||||
|
"""Percentage margin should add a fraction of price_list_rate to derive rate_with_margin."""
|
||||||
|
so = make_sales_order(do_not_save=True)
|
||||||
|
item = so.items[0]
|
||||||
|
item.qty = 1
|
||||||
|
item.price_list_rate = 200.0
|
||||||
|
item.rate = 200.0
|
||||||
|
item.pricing_rules = ""
|
||||||
|
item.margin_type = "Percentage"
|
||||||
|
item.margin_rate_or_amount = 10 # 10% margin
|
||||||
|
|
||||||
|
calculate_taxes_and_totals(so)
|
||||||
|
|
||||||
|
# rate_with_margin = price_list_rate * (1 + margin_rate / 100)
|
||||||
|
expected_rate_with_margin = 200.0 * 1.10
|
||||||
|
self.assertAlmostEqual(item.rate_with_margin, expected_rate_with_margin, places=2)
|
||||||
|
|
||||||
|
def test_filter_rows_excludes_alternative_items(self):
|
||||||
|
"""Quotation totals must not include rows marked as is_alternative."""
|
||||||
|
qo = make_quotation(qty=5, rate=100, do_not_save=True)
|
||||||
|
# Append an alternative item that should be excluded from the net total
|
||||||
|
qo.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"qty": 10,
|
||||||
|
"rate": 500,
|
||||||
|
"is_alternative": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
calculate_taxes_and_totals(qo)
|
||||||
|
|
||||||
|
# Only the first (non-alternative) item should contribute: 5 × 100 = 500
|
||||||
|
self.assertEqual(qo.net_total, 500.0)
|
||||||
|
self.assertEqual(qo.grand_total, 500.0)
|
||||||
|
|
||||||
|
def test_calculate_total_net_weight(self):
|
||||||
|
"""total_net_weight must equal the sum of total_weight across all item rows."""
|
||||||
|
so = make_sales_order(do_not_save=True)
|
||||||
|
so.items[0].qty = 3
|
||||||
|
so.items[0].rate = 50
|
||||||
|
so.items[0].total_weight = 6.0 # set directly so no item master lookup needed
|
||||||
|
|
||||||
|
calculate_taxes_and_totals(so)
|
||||||
|
|
||||||
|
self.assertEqual(so.total_net_weight, 6.0)
|
||||||
|
|
||||||
|
def test_set_discount_amount_exceeds_grand_total_throws(self):
|
||||||
|
"""Discount amount larger than grand total must raise a ValidationError."""
|
||||||
|
so = make_sales_order(do_not_save=True)
|
||||||
|
so.items[0].qty = 1
|
||||||
|
so.items[0].rate = 100
|
||||||
|
so.apply_discount_on = "Grand Total"
|
||||||
|
so.discount_amount = 200 # more than the 100 grand total
|
||||||
|
# _action must be set to trigger the validation path
|
||||||
|
so._action = "save"
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, calculate_taxes_and_totals, so)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.tests.utils import ERPNextTestSuite
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
from erpnext.utilities.transaction_base import validate_uom_is_integer
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(ERPNextTestSuite):
|
class TestUtils(ERPNextTestSuite):
|
||||||
@@ -92,3 +93,73 @@ class TestUtils(ERPNextTestSuite):
|
|||||||
doc.reset_default_field_value("to_warehouse", "items", "t_warehouse")
|
doc.reset_default_field_value("to_warehouse", "items", "t_warehouse")
|
||||||
self.assertEqual(doc.from_warehouse, None)
|
self.assertEqual(doc.from_warehouse, None)
|
||||||
self.assertEqual(doc.to_warehouse, "Warehouse 2")
|
self.assertEqual(doc.to_warehouse, "Warehouse 2")
|
||||||
|
|
||||||
|
def test_validate_posting_time_invalid(self):
|
||||||
|
"""An invalid posting_time string must raise a ValidationError."""
|
||||||
|
doc = frappe.get_doc({"doctype": "Stock Entry"})
|
||||||
|
doc.set_posting_time = 1
|
||||||
|
doc.posting_time = "not-a-time"
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, doc.validate_posting_time)
|
||||||
|
|
||||||
|
def test_validate_posting_time_auto_set(self):
|
||||||
|
"""When set_posting_time is falsy, posting_date and posting_time are replaced with now."""
|
||||||
|
from frappe.utils import getdate, nowdate
|
||||||
|
|
||||||
|
doc = frappe.get_doc({"doctype": "Stock Entry"})
|
||||||
|
doc.set_posting_time = 0
|
||||||
|
doc.posting_date = "2000-01-01"
|
||||||
|
doc.posting_time = "00:00:00"
|
||||||
|
|
||||||
|
doc.validate_posting_time()
|
||||||
|
|
||||||
|
# Both fields must have been refreshed to the current date/time
|
||||||
|
self.assertEqual(doc.posting_date, nowdate())
|
||||||
|
# posting_time should look like HH:MM:SS (not the old midnight value)
|
||||||
|
self.assertNotEqual(doc.posting_time, "00:00:00")
|
||||||
|
|
||||||
|
def test_validate_uom_is_integer_raises_for_fraction(self):
|
||||||
|
"""Fractional qty in a whole-number UOM must raise UOMMustBeIntegerError."""
|
||||||
|
from erpnext.utilities.transaction_base import UOMMustBeIntegerError
|
||||||
|
|
||||||
|
# Nos is seeded as a whole-number UOM in test fixtures
|
||||||
|
se = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Stock Entry",
|
||||||
|
"purpose": "Material Receipt",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"uom": "Nos",
|
||||||
|
"qty": 1.5,
|
||||||
|
"t_warehouse": "_Test Warehouse - _TC",
|
||||||
|
"basic_rate": 100,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(UOMMustBeIntegerError, validate_uom_is_integer, se, "uom", "qty")
|
||||||
|
|
||||||
|
def test_validate_uom_is_integer_passes_for_whole_number(self):
|
||||||
|
"""Integer qty in a whole-number UOM must NOT raise any error."""
|
||||||
|
se = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Stock Entry",
|
||||||
|
"purpose": "Material Receipt",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"uom": "Nos",
|
||||||
|
"qty": 3,
|
||||||
|
"t_warehouse": "_Test Warehouse - _TC",
|
||||||
|
"basic_rate": 100,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should complete without raising
|
||||||
|
validate_uom_is_integer(se, "uom", "qty")
|
||||||
|
|||||||
Reference in New Issue
Block a user