Merge pull request #40502 from ruthra-kumar/toggle_http_and_https_on_currency_exchange_settings
refactor: toggle between 'http' and 'https' on exchange rate API
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 30e564c..6728fea 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -149,6 +149,9 @@
import_file = ImportFile("Bank Transaction", file=file, import_type="Insert New Records")
data = parse_data_from_template(import_file.raw_data)
+ # Importer expects 'Data Import' class, which has 'payload_count' attribute
+ if not data_import.get("payload_count"):
+ data_import.payload_count = len(data) - 1
if import_file_path:
add_bank_account(data, bank_account)
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index 5e17881..4246ba5 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -56,17 +56,17 @@
Bank Transaction should be on the same currency as the Bank Account.
"""
if self.currency and self.bank_account:
- account = frappe.get_cached_value("Bank Account", self.bank_account, "account")
- account_currency = frappe.get_cached_value("Account", account, "account_currency")
+ if account := frappe.get_cached_value("Bank Account", self.bank_account, "account"):
+ account_currency = frappe.get_cached_value("Account", account, "account_currency")
- if self.currency != account_currency:
- frappe.throw(
- _(
- "Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
- ).format(
- frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency)
+ if self.currency != account_currency:
+ frappe.throw(
+ _(
+ "Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
+ ).format(
+ frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency)
+ )
)
- )
def set_status(self):
if self.docstatus == 2:
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
index 8be09db..29732ef 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py
@@ -628,21 +628,21 @@
if account_balance and (
account_balance[0].balance or account_balance[0].balance_in_account_currency
):
- account_with_new_balance = ExchangeRateRevaluation.calculate_new_account_balance(
+ if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance(
company, posting_date, account_balance
- )
- row = account_with_new_balance[0]
- account_details.update(
- {
- "balance_in_base_currency": row["balance_in_base_currency"],
- "balance_in_account_currency": row["balance_in_account_currency"],
- "current_exchange_rate": row["current_exchange_rate"],
- "new_exchange_rate": row["new_exchange_rate"],
- "new_balance_in_base_currency": row["new_balance_in_base_currency"],
- "new_balance_in_account_currency": row["new_balance_in_account_currency"],
- "zero_balance": row["zero_balance"],
- "gain_loss": row["gain_loss"],
- }
- )
+ ):
+ row = account_with_new_balance[0]
+ account_details.update(
+ {
+ "balance_in_base_currency": row["balance_in_base_currency"],
+ "balance_in_account_currency": row["balance_in_account_currency"],
+ "current_exchange_rate": row["current_exchange_rate"],
+ "new_exchange_rate": row["new_exchange_rate"],
+ "new_balance_in_base_currency": row["new_balance_in_base_currency"],
+ "new_balance_in_account_currency": row["new_balance_in_account_currency"],
+ "zero_balance": row["zero_balance"],
+ "gain_loss": row["gain_loss"],
+ }
+ )
return account_details
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index ab50c38..961ee20 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -395,10 +395,6 @@
return {
query: "erpnext.controllers.queries.employee_query",
};
- } else if (frm.doc.party_type == "Customer") {
- return {
- query: "erpnext.controllers.queries.customer_query",
- };
}
});
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 8a5d2c6..b4d1d39 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -489,7 +489,9 @@
ref_details = get_reference_details(
d.reference_doctype, d.reference_name, self.party_account_currency
)
- if ref_exchange_rate:
+
+ # Only update exchange rate when the reference is Journal Entry
+ if ref_exchange_rate and d.reference_doctype == "Journal Entry":
ref_details.update({"exchange_rate": ref_exchange_rate})
for field, value in ref_details.items():
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index 301e6ef..1d20a5b 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -1130,6 +1130,17 @@
self.assertEqual(pr.allocation[0].allocated_amount, 85)
self.assertEqual(pr.allocation[0].difference_amount, 0)
+ pr.reconcile()
+ si.reload()
+ self.assertEqual(si.outstanding_amount, 0)
+ # No Exchange Gain/Loss journal should be generated
+ exc_gain_loss_journals = frappe.db.get_all(
+ "Journal Entry Account",
+ filters={"reference_type": si.doctype, "reference_name": si.name, "docstatus": 1},
+ fields=["parent"],
+ )
+ self.assertEqual(exc_gain_loss_journals, [])
+
def test_reconciliation_purchase_invoice_against_return(self):
self.supplier = "_Test Supplier USD"
pi = self.create_purchase_invoice(qty=5, rate=50, do_not_submit=True)
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 272d077..6b10df8 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -518,16 +518,15 @@
callback: load_suppliers,
});
} else if (args.supplier_group) {
- return frappe.call({
- method: "frappe.client.get_list",
- args: {
- doctype: "Supplier",
+ frappe.db
+ .get_list("Supplier", {
+ filters: { supplier_group: args.supplier_group },
+ limit: 100,
order_by: "name",
- fields: ["name"],
- filters: [["Supplier", "supplier_group", "=", args.supplier_group]],
- },
- callback: load_suppliers,
- });
+ })
+ .then((r) => {
+ load_suppliers({ message: r });
+ });
}
},
});
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 60dd54c..3dae044 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -485,7 +485,7 @@
"link_fieldname": "party"
}
],
- "modified": "2023-10-19 16:55:15.148325",
+ "modified": "2024-03-13 11:14:06.516519",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
@@ -544,7 +544,7 @@
}
],
"quick_entry": 1,
- "search_fields": "supplier_name, supplier_group",
+ "search_fields": "supplier_group",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "ASC",
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 350a25f..55974ea 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -154,44 +154,6 @@
# Rollback
address.delete()
- def test_serach_fields_for_supplier(self):
- from erpnext.controllers.queries import supplier_query
-
- frappe.db.set_single_value("Buying Settings", "supp_master_name", "Naming Series")
-
- supplier_name = create_supplier(supplier_name="Test Supplier 1").name
-
- make_property_setter(
- "Supplier", None, "search_fields", "supplier_group", "Data", for_doctype="Doctype"
- )
-
- data = supplier_query(
- "Supplier", supplier_name, "name", 0, 20, filters={"name": supplier_name}, as_dict=True
- )
-
- self.assertEqual(data[0].name, supplier_name)
- self.assertEqual(data[0].supplier_group, "Services")
- self.assertTrue("supplier_type" not in data[0])
-
- make_property_setter(
- "Supplier",
- None,
- "search_fields",
- "supplier_group, supplier_type",
- "Data",
- for_doctype="Doctype",
- )
- data = supplier_query(
- "Supplier", supplier_name, "name", 0, 20, filters={"name": supplier_name}, as_dict=True
- )
-
- self.assertEqual(data[0].name, supplier_name)
- self.assertEqual(data[0].supplier_group, "Services")
- self.assertEqual(data[0].supplier_type, "Company")
- self.assertTrue("supplier_type" in data[0])
-
- frappe.db.set_single_value("Buying Settings", "supp_master_name", "Supplier Name")
-
def create_supplier(**args):
args = frappe._dict(args)
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index bb1ed35..0de75d4 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -85,79 +85,6 @@
{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
)
- # searches for customer
-
-
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def customer_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
- doctype = "Customer"
- conditions = []
- cust_master_name = frappe.defaults.get_user_default("cust_master_name")
-
- fields = ["name"]
- if cust_master_name != "Customer Name":
- fields.append("customer_name")
-
- fields = get_fields(doctype, fields)
- searchfields = frappe.get_meta(doctype).get_search_fields()
- searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
-
- return frappe.db.sql(
- """select {fields} from `tabCustomer`
- where docstatus < 2
- and ({scond}) and disabled=0
- {fcond} {mcond}
- order by
- (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
- (case when locate(%(_txt)s, customer_name) > 0 then locate(%(_txt)s, customer_name) else 99999 end),
- idx desc,
- name, customer_name
- limit %(page_len)s offset %(start)s""".format(
- **{
- "fields": ", ".join(fields),
- "scond": searchfields,
- "mcond": get_match_cond(doctype),
- "fcond": get_filters_cond(doctype, filters, conditions).replace("%", "%%"),
- }
- ),
- {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
- as_dict=as_dict,
- )
-
-
-# searches for supplier
-@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def supplier_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
- doctype = "Supplier"
- supp_master_name = frappe.defaults.get_user_default("supp_master_name")
-
- fields = ["name"]
- if supp_master_name != "Supplier Name":
- fields.append("supplier_name")
-
- fields = get_fields(doctype, fields)
-
- return frappe.db.sql(
- """select {field} from `tabSupplier`
- where docstatus < 2
- and ({key} like %(txt)s
- or supplier_name like %(txt)s) and disabled=0
- and (on_hold = 0 or (on_hold = 1 and CURRENT_DATE > release_date))
- {mcond}
- order by
- (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
- (case when locate(%(_txt)s, supplier_name) > 0 then locate(%(_txt)s, supplier_name) else 99999 end),
- idx desc,
- name, supplier_name
- limit %(page_len)s offset %(start)s""".format(
- **{"field": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)}
- ),
- {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
- as_dict=as_dict,
- )
-
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py
index 3a3bc1c..c536d1c 100644
--- a/erpnext/controllers/tests/test_queries.py
+++ b/erpnext/controllers/tests/test_queries.py
@@ -31,18 +31,6 @@
self.assertGreaterEqual(len(query(txt="_Test Lead")), 4)
self.assertEqual(len(query(txt="_Test Lead 4")), 1)
- def test_customer_query(self):
- query = add_default_params(queries.customer_query, "Customer")
-
- self.assertGreaterEqual(len(query(txt="_Test Customer")), 7)
- self.assertGreaterEqual(len(query(txt="_Test Customer USD")), 1)
-
- def test_supplier_query(self):
- query = add_default_params(queries.supplier_query, "Supplier")
-
- self.assertGreaterEqual(len(query(txt="_Test Supplier")), 7)
- self.assertGreaterEqual(len(query(txt="_Test Supplier USD")), 1)
-
def test_item_query(self):
query = add_default_params(queries.item_query, "Item")
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 0b6cdf2..609eab7 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -17,10 +17,6 @@
}
onload() {
- this.frm.set_query("customer", function (doc, cdt, cdn) {
- return { query: "erpnext.controllers.queries.customer_query" };
- });
-
this.frm.set_query("lead_owner", function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" };
});
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 308e6ca..9b996fe 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -282,9 +282,6 @@
before_tests = "erpnext.setup.utils.before_tests"
-standard_queries = {
- "Customer": "erpnext.controllers.queries.customer_query",
-}
period_closing_doctypes = [
"Sales Invoice",
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 4cd0530..2debf91 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -28,28 +28,6 @@
class TestBOM(FrappeTestCase):
@timeout
- def test_bom_qty(self):
- from erpnext.stock.doctype.item.test_item import make_item
-
- # No error.
- bom = frappe.new_doc("BOM")
- item = make_item(properties={"is_stock_item": 1})
- bom.item = fg_item.item_code
- bom.quantity = 1
- bom.append(
- "items",
- {
- "item_code": bom_item.item_code,
- "qty": 0,
- "uom": bom_item.stock_uom,
- "stock_uom": bom_item.stock_uom,
- "rate": 100.0,
- },
- )
- bom.save()
- self.assertEqual(bom.items[0].qty, 0)
-
- @timeout
def test_get_items(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 42f6943..70e803d 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -9,6 +9,8 @@
"Job Card": "Create Job Card",
};
+ frm.ignore_doctypes_on_cancel_all = ["Serial and Batch Bundle"];
+
// Set query for warehouses
frm.set_query("wip_warehouse", function () {
return {
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 16ac8db..49e8d84 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -27,8 +27,6 @@
};
};
- frm.set_query("customer", "erpnext.controllers.queries.customer_query");
-
frm.set_query("user", "users", function () {
return {
query: "erpnext.projects.doctype.project.project.get_users_for_project",
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 1d0d47e..934becf 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -74,11 +74,6 @@
me.frm.set_query('billing_address', erpnext.queries.company_address_query);
erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
- if(this.frm.fields_dict.supplier) {
- this.frm.set_query("supplier", function() {
- return{ query: "erpnext.controllers.queries.supplier_query" }});
- }
-
this.frm.set_query("item_code", "items", function() {
if (me.frm.doc.is_subcontracted) {
var filters = {'supplier': me.frm.doc.supplier};
diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js
index b7e685c..d7edac3 100644
--- a/erpnext/public/js/queries.js
+++ b/erpnext/public/js/queries.js
@@ -12,14 +12,6 @@
return { query: "erpnext.controllers.queries.lead_query" };
},
- customer: function () {
- return { query: "erpnext.controllers.queries.customer_query" };
- },
-
- supplier: function () {
- return { query: "erpnext.controllers.queries.supplier_query" };
- },
-
item: function (filters) {
var args = { query: "erpnext.controllers.queries.item_query" };
if (filters) args["filters"] = filters;
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index db712d9..41c6311 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -583,7 +583,7 @@
"link_fieldname": "party"
}
],
- "modified": "2023-12-28 13:15:36.298369",
+ "modified": "2024-03-16 19:41:47.971815",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
@@ -661,7 +661,7 @@
}
],
"quick_entry": 1,
- "search_fields": "customer_name,customer_group,territory, mobile_no,primary_address",
+ "search_fields": "customer_group,territory, mobile_no,primary_address",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index a8ebccd..7e6d6de 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -370,37 +370,6 @@
due_date = get_due_date("2017-01-22", "Customer", "_Test Customer")
self.assertEqual(due_date, "2017-01-22")
- def test_serach_fields_for_customer(self):
- from erpnext.controllers.queries import customer_query
-
- frappe.db.set_single_value("Selling Settings", "cust_master_name", "Naming Series")
-
- make_property_setter(
- "Customer", None, "search_fields", "customer_group", "Data", for_doctype="Doctype"
- )
-
- data = customer_query(
- "Customer", "_Test Customer", "", 0, 20, filters={"name": "_Test Customer"}, as_dict=True
- )
-
- self.assertEqual(data[0].name, "_Test Customer")
- self.assertEqual(data[0].customer_group, "_Test Customer Group")
- self.assertTrue("territory" not in data[0])
-
- make_property_setter(
- "Customer", None, "search_fields", "customer_group, territory", "Data", for_doctype="Doctype"
- )
- data = customer_query(
- "Customer", "_Test Customer", "", 0, 20, filters={"name": "_Test Customer"}, as_dict=True
- )
-
- self.assertEqual(data[0].name, "_Test Customer")
- self.assertEqual(data[0].customer_group, "_Test Customer Group")
- self.assertEqual(data[0].territory, "_Test Territory")
- self.assertTrue("territory" in data[0])
-
- frappe.db.set_single_value("Selling Settings", "cust_master_name", "Customer Name")
-
def test_parse_full_name(self):
first, middle, last = parse_full_name("John")
self.assertEqual(first, "John")
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 9fcda0d..f7e65e0 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -2206,13 +2206,14 @@
return so
-def create_dn_against_so(so, delivered_qty=0):
+def create_dn_against_so(so, delivered_qty=0, do_not_submit=False):
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
dn = make_delivery_note(so)
dn.get("items")[0].qty = delivered_qty or 5
dn.insert()
- dn.submit()
+ if not do_not_submit:
+ dn.submit()
return dn
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index d95ef58..fbee9c1 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -295,10 +295,10 @@
<div class="customer-field"></div>
`);
const me = this;
- const query = { query: "erpnext.controllers.queries.customer_query" };
const allowed_customer_group = this.allowed_customer_groups || [];
+ let filters = {};
if (allowed_customer_group.length) {
- query.filters = {
+ filters = {
customer_group: ["in", allowed_customer_group],
};
}
@@ -308,7 +308,11 @@
fieldtype: "Link",
options: "Customer",
placeholder: __("Search by customer name, phone, email."),
- get_query: () => query,
+ get_query: function () {
+ return {
+ filters: filters,
+ };
+ },
onchange: function () {
if (this.value) {
const frm = me.events.get_frm();
diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.py b/erpnext/setup/doctype/sales_partner/sales_partner.py
index 1047360..a8a965d 100644
--- a/erpnext/setup/doctype/sales_partner/sales_partner.py
+++ b/erpnext/setup/doctype/sales_partner/sales_partner.py
@@ -54,25 +54,30 @@
self.partner_website = "http://" + self.partner_website
def get_context(self, context):
- address = frappe.db.get_value(
- "Address", {"sales_partner": self.name, "is_primary_address": 1}, "*", as_dict=True
+ address_names = frappe.db.get_all(
+ "Dynamic Link",
+ filters={"link_doctype": "Sales Partner", "link_name": self.name, "parenttype": "Address"},
+ pluck=["parent"],
)
- if address:
- city_state = ", ".join(filter(None, [address.city, address.state]))
- address_rows = [
- address.address_line1,
- address.address_line2,
- city_state,
- address.pincode,
- address.country,
- ]
- context.update(
+ addresses = []
+ for address_name in address_names:
+ address_doc = frappe.get_doc("Address", address_name)
+ city_state = ", ".join([item for item in [address_doc.city, address_doc.state] if item])
+ address_rows = [
+ address_doc.address_line1,
+ address_doc.address_line2,
+ city_state,
+ address_doc.pincode,
+ address_doc.country,
+ ]
+ addresses.append(
{
- "email": address.email_id,
+ "email": address_doc.email_id,
"partner_address": filter_strip_join(address_rows, "\n<br>"),
- "phone": filter_strip_join(cstr(address.phone).split(","), "\n<br>"),
+ "phone": filter_strip_join(cstr(address_doc.phone).split(","), "\n<br>"),
}
)
+ context["addresses"] = addresses
return context
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 0d2d207..2f52f21 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -251,6 +251,7 @@
def validate(self):
self.validate_posting_time()
super(DeliveryNote, self).validate()
+ self.validate_references()
self.set_status()
self.so_required()
self.validate_proj_cust()
@@ -341,6 +342,58 @@
item.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
+ def validate_references(self):
+ self.validate_sales_order_references()
+ self.validate_sales_invoice_references()
+
+ def validate_sales_order_references(self):
+ err_msg = ""
+ for item in self.items:
+ if (item.against_sales_order and not item.so_detail) or (
+ not item.against_sales_order and item.so_detail
+ ):
+ if not item.against_sales_order:
+ err_msg += (
+ _("'Sales Order' reference ({1}) is missing in row {0}").format(
+ frappe.bold(item.idx), frappe.bold("against_sales_order")
+ )
+ + "<br>"
+ )
+ else:
+ err_msg += (
+ _("'Sales Order Item' reference ({1}) is missing in row {0}").format(
+ frappe.bold(item.idx), frappe.bold("so_detail")
+ )
+ + "<br>"
+ )
+
+ if err_msg:
+ frappe.throw(err_msg, title=_("References to Sales Orders are Incomplete"))
+
+ def validate_sales_invoice_references(self):
+ err_msg = ""
+ for item in self.items:
+ if (item.against_sales_invoice and not item.si_detail) or (
+ not item.against_sales_invoice and item.si_detail
+ ):
+ if not item.against_sales_invoice:
+ err_msg += (
+ _("'Sales Invoice' reference ({1}) is missing in row {0}").format(
+ frappe.bold(item.idx), frappe.bold("against_sales_invoice")
+ )
+ + "<br>"
+ )
+ else:
+ err_msg += (
+ _("'Sales Invoice Item' reference ({1}) is missing in row {0}").format(
+ frappe.bold(item.idx), frappe.bold("si_detail")
+ )
+ + "<br>"
+ )
+
+ if err_msg:
+ frappe.throw(err_msg, title=_("References to Sales Invoices are Incomplete"))
+
def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""
if self.project and self.customer:
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 293ef9f..905287d 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -824,6 +824,15 @@
dn.cancel()
self.assertEqual(dn.status, "Cancelled")
+ def test_sales_order_reference_validation(self):
+ so = make_sales_order(po_no="12345")
+ dn = create_dn_against_so(so.name, delivered_qty=2, do_not_submit=True)
+ dn.items[0].against_sales_order = None
+ self.assertRaises(frappe.ValidationError, dn.save)
+ dn.reload()
+ dn.items[0].so_detail = None
+ self.assertRaises(frappe.ValidationError, dn.save)
+
def test_dn_billing_status_case1(self):
# SO -> DN -> SI
so = make_sales_order(po_no="12345")
@@ -1099,9 +1108,30 @@
dn.load_from_db()
batch_no = get_batch_from_bundle(dn.packed_items[0].serial_and_batch_bundle)
+ packed_name = dn.packed_items[0].name
self.assertTrue(batch_no)
+ dn.cancel()
+
+ # Cancel the reposting entry
+ reposting_entries = frappe.get_all("Repost Item Valuation", filters={"docstatus": 1})
+ for entry in reposting_entries:
+ doc = frappe.get_doc("Repost Item Valuation", entry.name)
+ doc.cancel()
+ doc.delete()
+
+ frappe.db.set_single_value("Accounts Settings", "delete_linked_ledger_entries", 1)
+
+ dn.reload()
+ dn.delete()
+
+ bundle = frappe.db.get_value(
+ "Serial and Batch Bundle", {"voucher_detail_no": packed_name}, "name"
+ )
+ self.assertFalse(bundle)
+
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
+ frappe.db.set_single_value("Accounts Settings", "delete_linked_ledger_entries", 0)
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 7a38024..5310a0f 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -406,14 +406,6 @@
};
};
- frm.fields_dict.customer_items.grid.get_field("customer_name").get_query = function (doc, cdt, cdn) {
- return { query: "erpnext.controllers.queries.customer_query" };
- };
-
- frm.fields_dict.supplier_items.grid.get_field("supplier").get_query = function (doc, cdt, cdn) {
- return { query: "erpnext.controllers.queries.supplier_query" };
- };
-
frm.fields_dict["item_defaults"].grid.get_field("default_warehouse").get_query = function (
doc,
cdt,
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 8a1f79d..627520c 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -184,7 +184,11 @@
def delink_serial_and_batch_bundle(self):
for row in self.locations:
- if row.serial_and_batch_bundle:
+ if (
+ row.serial_and_batch_bundle
+ and frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "docstatus")
+ == 1
+ ):
frappe.db.set_value(
"Serial and Batch Bundle",
row.serial_and_batch_bundle,
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 9a7395f..1fb4969 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -778,6 +778,10 @@
or_filters=or_filters,
)
+ if not vouchers and self.voucher_type == "Delivery Note":
+ frappe.db.set_value("Packed Item", self.voucher_detail_no, "serial_and_batch_bundle", None)
+ return
+
for voucher in vouchers:
if voucher.get("current_serial_and_batch_bundle"):
frappe.db.set_value(self.child_table, voucher.name, "current_serial_and_batch_bundle", None)
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 2e4b08c..e98351a 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -60,6 +60,7 @@
if filters.get("batch_no") or inventory_dimension_filters_applied:
actual_qty += flt(sle.actual_qty, precision)
stock_value += sle.stock_value_difference
+ batch_balance_dict[sle.batch_no] += sle.actual_qty
if sle.voucher_type == "Stock Reconciliation" and not sle.actual_qty:
actual_qty = sle.qty_after_transaction
diff --git a/erpnext/templates/generators/sales_partner.html b/erpnext/templates/generators/sales_partner.html
index 39138d3..9dd2db9 100644
--- a/erpnext/templates/generators/sales_partner.html
+++ b/erpnext/templates/generators/sales_partner.html
@@ -8,18 +8,20 @@
<div class="partner-content" itemscope itemtype="http://schema.org/Organization">
<div class="row">
<div class="col-md-4">
- {% if logo -%}
+ {% if logo %}
<img itemprop="brand" src="{{ logo }}" class="partner-logo"
alt="{{ partner_name }}" title="{{ partner_name }}" />
<br><br>
- {%- endif %}
- <address>
- {% if partner_website -%}<p><a href="{{ partner_website }}"
- target="_blank">{{ partner_website }}</a></p>{%- endif %}
- {% if partner_address -%}<p itemprop="address">{{ partner_address }}</p>{%- endif %}
- {% if phone -%}<p itemprop="telephone">{{ phone }}</p>{%- endif %}
- {% if email -%}<p itemprop="email"><span class="fa fa-envelope"></span> {{ email }}</p>{%- endif %}
- </address>
+ {% endif %}
+ {% if addresses %}
+ {% for address in addresses %}
+ <address>
+ {% if address.partner_address %}<p itemprop="address">{{ address.partner_address }}</p>{% endif %}
+ {% if address.phone %}<p itemprop="telephone">{{ address.phone }}</p>{% endif %}
+ {% if address.email %}<p itemprop="email"><span class="fa fa-envelope"></span> {{ address.email }}</p>{% endif %}
+ </address>
+ {% endfor %}
+ {% endif %}
</div>
<div class="col-md-8">
<p>{{ description }}</p>