Merge branch 'develop' into fix-22-23-05686

This commit is contained in:
Sagar Sharma 2023-03-14 20:59:38 +05:30 committed by GitHub
commit 357d4994e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 15 deletions

View File

@ -31,7 +31,7 @@ class BOMTree:
# specifying the attributes to save resources
# ref: https://docs.python.org/3/reference/datamodel.html#slots
__slots__ = ["name", "child_items", "is_bom", "item_code", "exploded_qty", "qty"]
__slots__ = ["name", "child_items", "is_bom", "item_code", "qty", "exploded_qty", "bom_qty"]
def __init__(
self, name: str, is_bom: bool = True, exploded_qty: float = 1.0, qty: float = 1
@ -50,9 +50,10 @@ class BOMTree:
def __create_tree(self):
bom = frappe.get_cached_doc("BOM", self.name)
self.item_code = bom.item
self.bom_qty = bom.quantity
for item in bom.get("items", []):
qty = item.qty / bom.quantity # quantity per unit
qty = item.stock_qty / bom.quantity # quantity per unit
exploded_qty = self.exploded_qty * qty
if item.bom_no:
child = BOMTree(item.bom_no, exploded_qty=exploded_qty, qty=qty)

View File

@ -6,7 +6,7 @@ from collections import deque
from functools import partial
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.tests.utils import FrappeTestCase, timeout
from frappe.utils import cstr, flt
from erpnext.controllers.tests.test_subcontracting_controller import (
@ -27,6 +27,7 @@ test_dependencies = ["Item", "Quality Inspection Template"]
class TestBOM(FrappeTestCase):
@timeout
def test_get_items(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
@ -37,6 +38,7 @@ class TestBOM(FrappeTestCase):
self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict)
self.assertEqual(len(items_dict.values()), 2)
@timeout
def test_get_items_exploded(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
@ -49,11 +51,13 @@ class TestBOM(FrappeTestCase):
self.assertTrue(test_records[0]["items"][1]["item_code"] in items_dict)
self.assertEqual(len(items_dict.values()), 3)
@timeout
def test_get_items_list(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items
self.assertEqual(len(get_bom_items(bom=get_default_bom(), company="_Test Company")), 3)
@timeout
def test_default_bom(self):
def _get_default_bom_in_item():
return cstr(frappe.db.get_value("Item", "_Test FG Item 2", "default_bom"))
@ -71,6 +75,7 @@ class TestBOM(FrappeTestCase):
self.assertTrue(_get_default_bom_in_item(), bom.name)
@timeout
def test_update_bom_cost_in_all_boms(self):
# get current rate for '_Test Item 2'
bom_rates = frappe.db.get_values(
@ -99,6 +104,7 @@ class TestBOM(FrappeTestCase):
):
self.assertEqual(d.base_rate, rm_base_rate + 10)
@timeout
def test_bom_cost(self):
bom = frappe.copy_doc(test_records[2])
bom.insert()
@ -127,6 +133,7 @@ class TestBOM(FrappeTestCase):
self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
@timeout
def test_bom_cost_with_batch_size(self):
bom = frappe.copy_doc(test_records[2])
bom.docstatus = 0
@ -145,6 +152,7 @@ class TestBOM(FrappeTestCase):
self.assertAlmostEqual(bom.operating_cost, op_cost / 2)
bom.delete()
@timeout
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
@ -181,6 +189,7 @@ class TestBOM(FrappeTestCase):
self.assertEqual(bom.base_raw_material_cost, 27000)
self.assertEqual(bom.base_total_cost, 33000)
@timeout
def test_bom_cost_multi_uom_based_on_valuation_rate(self):
bom = frappe.copy_doc(test_records[2])
bom.set_rate_of_sub_assembly_item_based_on_bom = 0
@ -202,6 +211,7 @@ class TestBOM(FrappeTestCase):
self.assertEqual(bom.items[0].rate, 20)
@timeout
def test_bom_cost_with_fg_based_operating_cost(self):
bom = frappe.copy_doc(test_records[4])
bom.insert()
@ -229,6 +239,7 @@ class TestBOM(FrappeTestCase):
self.assertAlmostEqual(bom.base_raw_material_cost, base_raw_material_cost)
self.assertAlmostEqual(bom.base_total_cost, base_raw_material_cost + base_op_cost)
@timeout
def test_subcontractor_sourced_item(self):
item_code = "_Test Subcontracted FG Item 1"
set_backflush_based_on("Material Transferred for Subcontract")
@ -310,6 +321,7 @@ class TestBOM(FrappeTestCase):
supplied_items = sorted([d.rm_item_code for d in sco.supplied_items])
self.assertEqual(bom_items, supplied_items)
@timeout
def test_bom_tree_representation(self):
bom_tree = {
"Assembly": {
@ -335,6 +347,7 @@ class TestBOM(FrappeTestCase):
for reqd_item, created_item in zip(reqd_order, created_order):
self.assertEqual(reqd_item, created_item.item_code)
@timeout
def test_generated_variant_bom(self):
from erpnext.controllers.item_variant import create_variant
@ -375,6 +388,7 @@ class TestBOM(FrappeTestCase):
self.assertEqual(reqd_item.qty, created_item.qty)
self.assertEqual(reqd_item.exploded_qty, created_item.exploded_qty)
@timeout
def test_bom_recursion_1st_level(self):
"""BOM should not allow BOM item again in child"""
item_code = make_item(properties={"is_stock_item": 1}).name
@ -387,6 +401,7 @@ class TestBOM(FrappeTestCase):
bom.items[0].bom_no = bom.name
bom.save()
@timeout
def test_bom_recursion_transitive(self):
item1 = make_item(properties={"is_stock_item": 1}).name
item2 = make_item(properties={"is_stock_item": 1}).name
@ -408,6 +423,7 @@ class TestBOM(FrappeTestCase):
bom1.save()
bom2.save()
@timeout
def test_bom_with_process_loss_item(self):
fg_item_non_whole, fg_item_whole, bom_item = create_process_loss_bom_items()
@ -421,6 +437,7 @@ class TestBOM(FrappeTestCase):
# Items with whole UOMs can't be PL Items
self.assertRaises(frappe.ValidationError, bom_doc.submit)
@timeout
def test_bom_item_query(self):
query = partial(
item_query,
@ -440,6 +457,7 @@ class TestBOM(FrappeTestCase):
)
self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
@timeout
def test_exclude_exploded_items_from_bom(self):
bom_no = get_default_bom()
new_bom = frappe.copy_doc(frappe.get_doc("BOM", bom_no))
@ -458,6 +476,7 @@ class TestBOM(FrappeTestCase):
new_bom.delete()
@timeout
def test_valid_transfer_defaults(self):
bom_with_op = frappe.db.get_value(
"BOM", {"item": "_Test FG Item 2", "with_operations": 1, "is_active": 1}
@ -489,11 +508,13 @@ class TestBOM(FrappeTestCase):
self.assertEqual(bom.transfer_material_against, "Work Order")
bom.delete()
@timeout
def test_bom_name_length(self):
"""test >140 char names"""
bom_tree = {"x" * 140: {" ".join(["abc"] * 35): {}}}
create_nested_bom(bom_tree, prefix="")
@timeout
def test_version_index(self):
bom = frappe.new_doc("BOM")
@ -515,6 +536,7 @@ class TestBOM(FrappeTestCase):
msg=f"Incorrect index for {existing_boms}",
)
@timeout
def test_bom_versioning(self):
bom_tree = {frappe.generate_hash(length=10): {frappe.generate_hash(length=10): {}}}
bom = create_nested_bom(bom_tree, prefix="")
@ -547,6 +569,7 @@ class TestBOM(FrappeTestCase):
self.assertNotEqual(amendment.name, version.name)
self.assertEqual(int(version.name.split("-")[-1]), 2)
@timeout
def test_clear_inpection_quality(self):
bom = frappe.copy_doc(test_records[2], ignore_no_copy=True)
@ -565,6 +588,7 @@ class TestBOM(FrappeTestCase):
self.assertEqual(bom.quality_inspection_template, None)
@timeout
def test_bom_pricing_based_on_lpp(self):
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
@ -585,6 +609,7 @@ class TestBOM(FrappeTestCase):
bom.submit()
self.assertEqual(bom.items[0].rate, 42)
@timeout
def test_set_default_bom_for_item_having_single_bom(self):
from erpnext.stock.doctype.item.test_item import make_item
@ -621,6 +646,7 @@ class TestBOM(FrappeTestCase):
bom.reload()
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
@timeout
def test_exploded_items_rate(self):
rm_item = make_item(
properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
@ -649,6 +675,7 @@ class TestBOM(FrappeTestCase):
bom.submit()
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
@timeout
def test_bom_cost_update_flag(self):
rm_item = make_item(
properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.tests.utils import FrappeTestCase, timeout
from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import (
update_cost_in_all_boms_in_test,
@ -20,6 +20,7 @@ class TestBOMUpdateTool(FrappeTestCase):
def tearDown(self):
frappe.db.rollback()
@timeout
def test_replace_bom(self):
current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
@ -33,6 +34,7 @@ class TestBOMUpdateTool(FrappeTestCase):
self.assertFalse(frappe.db.exists("BOM Item", {"bom_no": current_bom, "docstatus": 1}))
self.assertTrue(frappe.db.exists("BOM Item", {"bom_no": bom_doc.name, "docstatus": 1}))
@timeout
def test_bom_cost(self):
for item in ["BOM Cost Test Item 1", "BOM Cost Test Item 2", "BOM Cost Test Item 3"]:
item_doc = create_item(item, valuation_rate=100)

View File

@ -682,7 +682,7 @@ class WorkOrder(Document):
for node in bom_traversal:
if node.is_bom:
operations.extend(_get_operations(node.name, qty=node.exploded_qty))
operations.extend(_get_operations(node.name, qty=node.exploded_qty / node.bom_qty))
bom_qty = frappe.get_cached_value("BOM", self.bom_no, "quantity")
operations.extend(_get_operations(self.bom_no, qty=1.0 / bom_qty))

View File

@ -808,7 +808,7 @@ def get_default_company_address(name, sort_key="is_primary_address", existing_ad
return existing_address
if out:
return min(out, key=lambda x: x[1])[0] # find min by sort_key
return max(out, key=lambda x: x[1])[0] # find max by sort_key
else:
return None

View File

@ -11,6 +11,7 @@ from frappe.utils import random_string
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
get_charts_for_country,
)
from erpnext.setup.doctype.company.company import get_default_company_address
test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component", "Warehouse"]
test_dependencies = ["Fiscal Year"]
@ -132,6 +133,38 @@ class TestCompany(unittest.TestCase):
self.assertTrue(lft >= min_lft)
self.assertTrue(rgt <= max_rgt)
def test_primary_address(self):
company = "_Test Company"
secondary = frappe.get_doc(
{
"address_title": "Non Primary",
"doctype": "Address",
"address_type": "Billing",
"address_line1": "Something",
"city": "Mumbai",
"state": "Maharashtra",
"country": "India",
"is_primary_address": 1,
"pincode": "400098",
"links": [
{
"link_doctype": "Company",
"link_name": company,
}
],
}
)
secondary.insert()
self.addCleanup(secondary.delete)
primary = frappe.copy_doc(secondary)
primary.is_primary_address = 1
primary.insert()
self.addCleanup(primary.delete)
self.assertEqual(get_default_company_address(company), primary.name)
def get_no_of_children(self, company):
def get_no_of_children(companies, no_of_children):
children = []

View File

@ -377,7 +377,9 @@ class Item(Document):
"" if item_barcode.barcode_type not in options else item_barcode.barcode_type
)
if item_barcode.barcode_type:
barcode_type = convert_erpnext_to_barcodenumber(item_barcode.barcode_type.upper())
barcode_type = convert_erpnext_to_barcodenumber(
item_barcode.barcode_type.upper(), item_barcode.barcode
)
if barcode_type in barcodenumber.barcodes():
if not barcodenumber.check_code(barcode_type, item_barcode.barcode):
frappe.throw(
@ -982,20 +984,29 @@ class Item(Document):
)
def convert_erpnext_to_barcodenumber(erpnext_number):
def convert_erpnext_to_barcodenumber(erpnext_number, barcode):
if erpnext_number == "EAN":
ean_type = {
8: "EAN8",
13: "EAN13",
}
barcode_length = len(barcode)
if barcode_length in ean_type:
return ean_type[barcode_length]
return erpnext_number
convert = {
"UPC-A": "UPCA",
"CODE-39": "CODE39",
"EAN": "EAN13",
"EAN-12": "EAN",
"EAN-8": "EAN8",
"ISBN-10": "ISBN10",
"ISBN-13": "ISBN13",
}
if erpnext_number in convert:
return convert[erpnext_number]
else:
return erpnext_number
return erpnext_number
def make_item_price(item, price_list_name, item_price):

View File

@ -581,8 +581,9 @@ class TestItem(FrappeTestCase):
},
{"barcode": "72527273070", "barcode_type": "UPC-A"},
{"barcode": "123456", "barcode_type": "CODE-39"},
{"barcode": "401268452363", "barcode_type": "EAN-12"},
{"barcode": "90311017", "barcode_type": "EAN-8"},
{"barcode": "401268452363", "barcode_type": "EAN"},
{"barcode": "90311017", "barcode_type": "EAN"},
{"barcode": "73513537", "barcode_type": "EAN"},
{"barcode": "0123456789012", "barcode_type": "GS1"},
{"barcode": "2211564566668", "barcode_type": "GTIN"},
{"barcode": "0256480249", "barcode_type": "ISBN"},