Merge pull request #34116 from prateekkaramchandani/develop
fix: use max function to get default company address
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
index 3920d4c..b9680df 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
@@ -15,7 +15,7 @@
</div>
<h2 class="text-center">{{ _("STATEMENTS OF ACCOUNTS") }}</h2>
<div>
- <h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party[0] }}</b></h5>
+ <h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party_name[0] }}</b></h5>
<h5 style="float: right;">
{{ _("Date: ") }}
<b>{{ frappe.format(filters.from_date, 'Date')}}
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index a48c027..a482931 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -24,7 +24,7 @@
class ProcessStatementOfAccounts(Document):
def validate(self):
if not self.subject:
- self.subject = "Statement Of Accounts for {{ customer.name }}"
+ self.subject = "Statement Of Accounts for {{ customer.customer_name }}"
if not self.body:
self.body = "Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}."
@@ -87,6 +87,7 @@
"account": [doc.account] if doc.account else None,
"party_type": "Customer",
"party": [entry.customer],
+ "party_name": [entry.customer_name] if entry.customer_name else None,
"presentation_currency": presentation_currency,
"group_by": doc.group_by,
"currency": doc.currency,
@@ -156,7 +157,7 @@
]
return frappe.get_list(
"Customer",
- fields=["name", "email_id"],
+ fields=["name", "customer_name", "email_id"],
filters=[[fields_dict[customer_collection], "IN", selected]],
)
@@ -179,7 +180,7 @@
if sales_person_records.get("Customer"):
return frappe.get_list(
"Customer",
- fields=["name", "email_id"],
+ fields=["name", "customer_name", "email_id"],
filters=[["name", "in", list(sales_person_records["Customer"])]],
)
else:
@@ -228,7 +229,7 @@
if customer_collection == "Sales Partner":
customers = frappe.get_list(
"Customer",
- fields=["name", "email_id"],
+ fields=["name", "customer_name", "email_id"],
filters=[["default_sales_partner", "=", collection_name]],
)
else:
@@ -245,7 +246,12 @@
continue
customer_list.append(
- {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email}
+ {
+ "name": customer.name,
+ "customer_name": customer.customer_name,
+ "primary_email": primary_email,
+ "billing_email": billing_email,
+ }
)
return customer_list
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
index dd04dc1..8bffd6a 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
+++ b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json
@@ -1,12 +1,12 @@
{
"actions": [],
- "allow_workflow": 1,
"creation": "2020-08-03 16:35:21.852178",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"customer",
+ "customer_name",
"billing_email",
"primary_email"
],
@@ -30,11 +30,18 @@
"fieldtype": "Read Only",
"in_list_view": 1,
"label": "Billing Email"
+ },
+ {
+ "fetch_from": "customer.customer_name",
+ "fieldname": "customer_name",
+ "fieldtype": "Data",
+ "label": "Customer Name",
+ "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2020-08-03 22:55:38.875601",
+ "modified": "2023-03-13 00:12:34.508086",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts Customer",
@@ -43,5 +50,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 2f4e45e..2a8ff40 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -32,9 +32,6 @@
"cost_center",
"dimension_col_break",
"project",
- "column_break_27",
- "campaign",
- "source",
"currency_and_price_list",
"currency",
"conversion_rate",
@@ -203,7 +200,9 @@
"more_information",
"status",
"inter_company_invoice_reference",
+ "campaign",
"represents_company",
+ "source",
"customer_group",
"col_break23",
"is_internal_customer",
@@ -2084,10 +2083,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "column_break_27",
- "fieldtype": "Column Break"
- },
- {
"fieldname": "column_break_52",
"fieldtype": "Column Break"
},
@@ -2143,11 +2138,10 @@
"link_fieldname": "consolidated_invoice"
}
],
- "modified": "2023-01-28 19:45:47.538163",
+ "modified": "2023-03-13 11:43:15.883055",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
- "name_case": "Title Case",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 0ffd946..6051c99 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -266,16 +266,16 @@
"_Test Account Education Cess - _TC": [3, 1618, 0.06, 32.36],
"_Test Account S&H Education Cess - _TC": [1.5, 1619.5, 0.03, 32.39],
"_Test Account CST - _TC": [32.5, 1652, 0.65, 33.04],
- "_Test Account VAT - _TC": [156.5, 1808.5, 3.13, 36.17],
- "_Test Account Discount - _TC": [-181.0, 1627.5, -3.62, 32.55],
+ "_Test Account VAT - _TC": [156.0, 1808.0, 3.12, 36.16],
+ "_Test Account Discount - _TC": [-181.0, 1627.0, -3.62, 32.54],
}
for d in si.get("taxes"):
for i, k in enumerate(expected_values["keys"]):
self.assertEqual(d.get(k), expected_values[d.account_head][i])
- self.assertEqual(si.base_grand_total, 1627.5)
- self.assertEqual(si.grand_total, 32.55)
+ self.assertEqual(si.base_grand_total, 1627.0)
+ self.assertEqual(si.grand_total, 32.54)
def test_sales_invoice_with_discount_and_inclusive_tax(self):
si = create_sales_invoice(qty=100, rate=50, do_not_save=True)
@@ -401,10 +401,10 @@
"_Test Account S&H Education Cess - _TC": [1.4, 1.30, 1297.67],
"_Test Account CST - _TC": [27.88, 25.95, 1323.62],
"_Test Account VAT - _TC": [156.25, 145.43, 1469.05],
- "_Test Account Customs Duty - _TC": [125, 116.35, 1585.40],
- "_Test Account Shipping Charges - _TC": [100, 100, 1685.40],
- "_Test Account Discount - _TC": [-180.33, -168.54, 1516.86],
- "_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.01],
+ "_Test Account Customs Duty - _TC": [125, 116.34, 1585.39],
+ "_Test Account Shipping Charges - _TC": [100, 100, 1685.39],
+ "_Test Account Discount - _TC": [-180.33, -168.54, 1516.85],
+ "_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.00],
}
for d in si.get("taxes"):
@@ -413,7 +413,7 @@
self.assertEqual(si.base_grand_total, 1500)
self.assertEqual(si.grand_total, 1500)
- self.assertEqual(si.rounding_adjustment, -0.01)
+ self.assertEqual(si.rounding_adjustment, 0.0)
def test_discount_amount_gl_entry(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
@@ -454,7 +454,7 @@
[test_records[3]["taxes"][2]["account_head"], 0.0, 1.30],
[test_records[3]["taxes"][3]["account_head"], 0.0, 25.95],
[test_records[3]["taxes"][4]["account_head"], 0.0, 145.43],
- [test_records[3]["taxes"][5]["account_head"], 0.0, 116.35],
+ [test_records[3]["taxes"][5]["account_head"], 0.0, 116.34],
[test_records[3]["taxes"][6]["account_head"], 0.0, 100],
[test_records[3]["taxes"][7]["account_head"], 168.54, 0.0],
["_Test Account Service Tax - _TC", 16.85, 0.0],
@@ -1614,7 +1614,7 @@
"_Test Account Education Cess - _TC": [1.4, 1.4, 1.4],
"_Test Account S&H Education Cess - _TC": [0.7, 0.7, 0.7],
"_Test Account CST - _TC": [17.19, 17.19, 17.19],
- "_Test Account VAT - _TC": [78.13, 78.13, 78.13],
+ "_Test Account VAT - _TC": [78.12, 78.12, 78.12],
"_Test Account Discount - _TC": [-95.49, -95.49, -95.49],
}
@@ -1623,9 +1623,9 @@
if expected_values.get(d.account_head):
self.assertEqual(d.get(k), expected_values[d.account_head][i])
- self.assertEqual(si.total_taxes_and_charges, 234.43)
- self.assertEqual(si.base_grand_total, 859.43)
- self.assertEqual(si.grand_total, 859.43)
+ self.assertEqual(si.total_taxes_and_charges, 234.42)
+ self.assertEqual(si.base_grand_total, 859.42)
+ self.assertEqual(si.grand_total, 859.42)
def test_multi_currency_gle(self):
si = create_sales_invoice(
@@ -1985,17 +1985,17 @@
)
si.save()
si.submit()
- self.assertEqual(si.net_total, 19453.13)
+ self.assertEqual(si.net_total, 19453.12)
self.assertEqual(si.grand_total, 24900)
self.assertEqual(si.total_taxes_and_charges, 5446.88)
- self.assertEqual(si.rounding_adjustment, -0.01)
+ self.assertEqual(si.rounding_adjustment, 0.0)
expected_values = dict(
(d[0], d)
for d in [
[si.debit_to, 24900, 0.0],
["_Test Account Service Tax - _TC", 0.0, 5446.88],
- ["Sales - _TC", 0.0, 19453.13],
+ ["Sales - _TC", 0.0, 19453.12],
["Round Off - _TC", 0.01, 0.0],
]
)
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 01cfb58..b217f00 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -32,6 +32,16 @@
from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
+PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"}
+SALES_TRANSACTION_TYPES = {
+ "Quotation",
+ "Sales Order",
+ "Delivery Note",
+ "Sales Invoice",
+ "POS Invoice",
+}
+TRANSACTION_TYPES = PURCHASE_TRANSACTION_TYPES | SALES_TRANSACTION_TYPES
+
class DuplicatePartyAccountError(frappe.ValidationError):
pass
@@ -124,12 +134,6 @@
set_other_values(party_details, party, party_type)
set_price_list(party_details, party, party_type, price_list, pos_profile)
- party_details["tax_category"] = get_address_tax_category(
- party.get("tax_category"),
- party_address,
- shipping_address if party_type != "Supplier" else party_address,
- )
-
tax_template = set_taxes(
party.name,
party_type,
@@ -211,20 +215,10 @@
else:
party_details.update(get_company_address(company))
- if doctype and doctype in [
- "Delivery Note",
- "Sales Invoice",
- "Sales Order",
- "Quotation",
- "POS Invoice",
- ]:
- if party_details.company_address:
- party_details.update(
- get_fetch_values(doctype, "company_address", party_details.company_address)
- )
- get_regional_address_details(party_details, doctype, company)
+ if doctype in SALES_TRANSACTION_TYPES and party_details.company_address:
+ party_details.update(get_fetch_values(doctype, "company_address", party_details.company_address))
- elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
+ if doctype in PURCHASE_TRANSACTION_TYPES:
if shipping_address:
party_details.update(
shipping_address=shipping_address,
@@ -250,9 +244,21 @@
**get_fetch_values(doctype, "shipping_address", party_details.billing_address)
)
+ party_address, shipping_address = (
+ party_details.get(billing_address_field),
+ party_details.shipping_address_name,
+ )
+
+ party_details["tax_category"] = get_address_tax_category(
+ party.get("tax_category"),
+ party_address,
+ shipping_address if party_type != "Supplier" else party_address,
+ )
+
+ if doctype in TRANSACTION_TYPES:
get_regional_address_details(party_details, doctype, company)
- return party_details.get(billing_address_field), party_details.shipping_address_name
+ return party_address, shipping_address
@erpnext.allow_regional
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 3af01fd..bc334c7 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -78,7 +78,6 @@
def get_data(filters):
-
accounts = frappe.db.sql(
"""select name, account_number, parent_account, account_name, root_type, report_type, lft, rgt
@@ -118,12 +117,10 @@
ignore_closing_entries=not flt(filters.with_period_closing_entry),
)
- total_row = calculate_values(
- accounts, gl_entries_by_account, opening_balances, filters, company_currency
- )
+ calculate_values(accounts, gl_entries_by_account, opening_balances)
accumulate_values_into_parents(accounts, accounts_by_name)
- data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
+ data = prepare_data(accounts, filters, parent_children_map, company_currency)
data = filter_out_zero_value_rows(
data, parent_children_map, show_zero_values=filters.get("show_zero_values")
)
@@ -218,7 +215,7 @@
return opening
-def calculate_values(accounts, gl_entries_by_account, opening_balances, filters, company_currency):
+def calculate_values(accounts, gl_entries_by_account, opening_balances):
init = {
"opening_debit": 0.0,
"opening_credit": 0.0,
@@ -228,22 +225,6 @@
"closing_credit": 0.0,
}
- total_row = {
- "account": "'" + _("Total") + "'",
- "account_name": "'" + _("Total") + "'",
- "warn_if_negative": True,
- "opening_debit": 0.0,
- "opening_credit": 0.0,
- "debit": 0.0,
- "credit": 0.0,
- "closing_debit": 0.0,
- "closing_credit": 0.0,
- "parent_account": None,
- "indent": 0,
- "has_value": True,
- "currency": company_currency,
- }
-
for d in accounts:
d.update(init.copy())
@@ -261,8 +242,28 @@
prepare_opening_closing(d)
- for field in value_fields:
- total_row[field] += d[field]
+
+def calculate_total_row(accounts, company_currency):
+ total_row = {
+ "account": "'" + _("Total") + "'",
+ "account_name": "'" + _("Total") + "'",
+ "warn_if_negative": True,
+ "opening_debit": 0.0,
+ "opening_credit": 0.0,
+ "debit": 0.0,
+ "credit": 0.0,
+ "closing_debit": 0.0,
+ "closing_credit": 0.0,
+ "parent_account": None,
+ "indent": 0,
+ "has_value": True,
+ "currency": company_currency,
+ }
+
+ for d in accounts:
+ if not d.parent_account:
+ for field in value_fields:
+ total_row[field] += d[field]
return total_row
@@ -274,7 +275,7 @@
accounts_by_name[d.parent_account][key] += d[key]
-def prepare_data(accounts, filters, total_row, parent_children_map, company_currency):
+def prepare_data(accounts, filters, parent_children_map, company_currency):
data = []
for d in accounts:
@@ -305,6 +306,7 @@
row["has_value"] = has_value
data.append(row)
+ total_row = calculate_total_row(accounts, company_currency)
data.extend([{}, total_row])
return data
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 8ab79e6..619a415 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -31,7 +31,7 @@
# 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 @@
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)
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index d60feb2..01bf2e4 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -6,7 +6,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
):
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
# 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 @@
)
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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}
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
index 5dd557f..2026f62 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/test_bom_update_tool.py
@@ -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 @@
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 @@
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)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index ae9e9c6..66b871c 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -682,7 +682,7 @@
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))
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e4995f8..c1d4b82 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -323,5 +323,6 @@
erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers
erpnext.patches.v14_0.set_pick_list_status
erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
+erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
# below migration patches should always run last
-erpnext.patches.v14_0.migrate_gl_to_payment_ledger
+erpnext.patches.v14_0.migrate_gl_to_payment_ledger
\ No newline at end of file
diff --git a/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py b/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py
new file mode 100644
index 0000000..afb59e0
--- /dev/null
+++ b/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py
@@ -0,0 +1,20 @@
+import frappe
+
+
+def execute():
+ # not using frappe.qb because https://github.com/frappe/frappe/issues/20292
+ frappe.db.sql(
+ """UPDATE `tabAsset Depreciation Schedule`
+ JOIN `tabAsset`
+ ON `tabAsset Depreciation Schedule`.`asset`=`tabAsset`.`name`
+ SET
+ `tabAsset Depreciation Schedule`.`gross_purchase_amount`=`tabAsset`.`gross_purchase_amount`,
+ `tabAsset Depreciation Schedule`.`number_of_depreciations_booked`=`tabAsset`.`number_of_depreciations_booked`
+ WHERE
+ (
+ `tabAsset Depreciation Schedule`.`gross_purchase_amount`<>`tabAsset`.`gross_purchase_amount`
+ OR
+ `tabAsset Depreciation Schedule`.`number_of_depreciations_booked`<>`tabAsset`.`number_of_depreciations_booked`
+ )
+ AND `tabAsset Depreciation Schedule`.`docstatus`<2"""
+ )
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index a376bf4..d1d07a7 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -5,6 +5,8 @@
setup: function(frm) {
frappe.require("/assets/erpnext/js/projects/timer.js");
+ frm.ignore_doctypes_on_cancel_all = ['Sales Invoice'];
+
frm.fields_dict.employee.get_query = function() {
return {
filters:{
diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
index 17e3155..766e40e 100644
--- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
+++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.py
@@ -46,6 +46,9 @@
# task has no end date, hence no delay
task.delay = 0
+ task.status = _(task.status)
+ task.priority = _(task.priority)
+
# Sort by descending order of delay
tasks.sort(key=lambda x: x["delay"], reverse=True)
return tasks
@@ -73,7 +76,7 @@
on_track = on_track + 1
charts = {
"data": {
- "labels": ["On Track", "Delayed"],
+ "labels": [_("On Track"), _("Delayed")],
"datasets": [{"name": "Delayed", "values": [on_track, delay]}],
},
"type": "percentage",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index c06700a..05a37ee 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -377,7 +377,9 @@
"" 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 @@
)
-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):
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 67ed90d..0c6dc77 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -581,8 +581,9 @@
},
{"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"},
diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py
index e60c1ca..05f153b 100644
--- a/erpnext/stock/tests/test_valuation.py
+++ b/erpnext/stock/tests/test_valuation.py
@@ -132,7 +132,7 @@
total_qty = 0
for qty, rate in stock_queue:
- if qty == 0:
+ if round_off_if_near_zero(qty) == 0:
continue
if qty > 0:
self.queue.add_stock(qty, rate)
@@ -154,7 +154,7 @@
for qty, rate in stock_queue:
# don't allow negative stock
- if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
+ if round_off_if_near_zero(qty) == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
continue
if qty > 0:
self.queue.add_stock(qty, rate)
@@ -179,7 +179,7 @@
for qty, rate in stock_queue:
# don't allow negative stock
- if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
+ if round_off_if_near_zero(qty) == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
continue
if qty > 0:
self.queue.add_stock(qty, rate)
@@ -282,7 +282,7 @@
total_qty = 0
for qty, rate in stock_stack:
- if qty == 0:
+ if round_off_if_near_zero(qty) == 0:
continue
if qty > 0:
self.stack.add_stock(qty, rate)
@@ -304,7 +304,7 @@
for qty, rate in stock_stack:
# don't allow negative stock
- if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
+ if round_off_if_near_zero(qty) == 0 or total_qty + qty < 0 or abs(qty) < 0.1:
continue
if qty > 0:
self.stack.add_stock(qty, rate)
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index 8367afd..bace129 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -2801,7 +2801,7 @@
Stock Levels,Niveaux du Stocks,
Stock Liabilities,Passif du Stock,
Stock Options,Options du Stock,
-Stock Qty,Qté en Stock,
+Stock Qty,Qté en unité de stock,
Stock Received But Not Billed,Stock Reçus Mais Non Facturés,
Stock Reports,Rapports de stock,
Stock Summary,Résumé du Stock,