diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 30ca22aedc5..2f7398e019e 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -14,7 +14,7 @@ concurrency: jobs: test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 timeout-minutes: 60 name: Patch Test diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index 5bf42fb7ffe..d5c1e30f1eb 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -18,7 +18,7 @@ concurrency: jobs: test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 timeout-minutes: 60 strategy: diff --git a/.github/workflows/translation_linter.yml b/.github/workflows/translation_linter.yml index 4becaebd6b4..af7e0e77528 100644 --- a/.github/workflows/translation_linter.yml +++ b/.github/workflows/translation_linter.yml @@ -8,7 +8,7 @@ on: jobs: check_translation: name: Translation Syntax Check - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Setup python3 diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 010284c2ea5..2100f26c1ec 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -58,9 +58,8 @@ frappe.query_reports["General Ledger"] = { { "fieldname":"party_type", "label": __("Party Type"), - "fieldtype": "Link", - "options": "Party Type", - "default": "", + "fieldtype": "Autocomplete", + options: Object.keys(frappe.boot.party_account_types), on_change: function() { frappe.query_report.set_filter_value('party', ""); } diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index f0899b06b57..1ae6f036474 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -64,7 +64,7 @@ frappe.ui.form.on("Supplier", { // custom buttons frm.add_custom_button(__('Accounting Ledger'), function () { frappe.set_route('query-report', 'General Ledger', - { party_type: 'Supplier', party: frm.doc.name }); + { party_type: 'Supplier', party: frm.doc.name, party_name: frm.doc.supplier_name }); }, __("View")); frm.add_custom_button(__('Accounts Payable'), function () { diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index 6fdeaaa4c1e..3cfb796c591 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -128,18 +128,9 @@ class Supplier(TransactionBase): def on_trash(self): if self.supplier_primary_contact: - frappe.db.sql( - """ - UPDATE `tabSupplier` - SET - supplier_primary_contact=null, - supplier_primary_address=null, - mobile_no=null, - email_id=null, - primary_address=null - WHERE name=%(name)s""", - {"name": self.name}, - ) + self.db_set("supplier_primary_contact", None) + if self.supplier_primary_address: + self.db_set("supplier_primary_address", None) delete_contact_and_address("Supplier", self.name) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index b3cb0e82bdd..926083a2d75 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -3,6 +3,7 @@ import frappe +from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.test_runner import make_test_records from frappe.tests.utils import FrappeTestCase @@ -151,6 +152,44 @@ class TestSupplier(FrappeTestCase): # Rollback address.delete() + def test_serach_fields_for_supplier(self): + from erpnext.controllers.queries import supplier_query + + frappe.db.set_value("Buying Settings", None, "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_value("Buying Settings", None, "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 53a17422004..e39ef9d8e40 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -78,18 +78,16 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def customer_query(doctype, txt, searchfield, start, page_len, filters): +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") - if cust_master_name == "Customer Name": - fields = ["name", "customer_group", "territory"] - else: - fields = ["name", "customer_name", "customer_group", "territory"] + 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) @@ -112,20 +110,20 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): } ), {"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): +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") - if supp_master_name == "Supplier Name": - fields = ["name", "supplier_group"] - else: - fields = ["name", "supplier_name", "supplier_group"] + fields = ["name"] + if supp_master_name != "Supplier Name": + fields.append("supplier_name") fields = get_fields(doctype, fields) @@ -145,6 +143,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): **{"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, ) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 23dad95fa0d..30874f8160d 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -450,7 +450,7 @@ class StatusUpdater(Document): ifnull((select ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0) / sum(abs(%(target_ref_field)s)) * 100 - from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6) + from `tab%(target_dt)s` where parent='%(name)s' and parenttype='%(target_parent_dt)s' having sum(abs(%(target_ref_field)s)) > 0), 0), 6) %(update_modified)s where name='%(name)s'""" % args diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 2b8d45b070f..5a75cb222ed 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -123,7 +123,7 @@ frappe.ui.form.on("Customer", { frm.add_custom_button(__('Accounting Ledger'), function () { frappe.set_route('query-report', 'General Ledger', - {party_type: 'Customer', party: frm.doc.name}); + {party_type: 'Customer', party: frm.doc.name, party_name: frm.doc.customer_name}); }, __('View')); frm.add_custom_button(__('Pricing Rule'), function () { diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 35e0b0de407..483c79bbdbb 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -275,18 +275,9 @@ class Customer(TransactionBase): def on_trash(self): if self.customer_primary_contact: - frappe.db.sql( - """ - UPDATE `tabCustomer` - SET - customer_primary_contact=null, - customer_primary_address=null, - mobile_no=null, - email_id=null, - primary_address=null - WHERE name=%(name)s""", - {"name": self.name}, - ) + self.db_set("customer_primary_contact", None) + if self.customer_primary_address: + self.db_set("customer_primary_address", None) delete_contact_and_address("Customer", self.name) if self.lead_name: diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 5c65cb68ef5..1bc3b72d9b7 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -3,6 +3,7 @@ import frappe +from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.test_runner import make_test_records from frappe.tests.utils import FrappeTestCase from frappe.utils import flt @@ -343,6 +344,37 @@ class TestCustomer(FrappeTestCase): 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_value("Selling Settings", None, "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_value("Selling Settings", None, "cust_master_name", "Customer Name") + def get_customer_dict(customer_name): return { diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 261383d4a20..3e1471fbb7a 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -6,6 +6,7 @@ from operator import itemgetter import frappe from frappe import _ +from frappe.query_builder.functions import Coalesce from frappe.utils import cint, date_diff, flt, getdate from six import iteritems @@ -276,11 +277,39 @@ def get_stock_ledger_entries(filters, items): ) +def get_opening_vouchers(to_date): + opening_vouchers = {"Stock Entry": [], "Stock Reconciliation": []} + + se = frappe.qb.DocType("Stock Entry") + sr = frappe.qb.DocType("Stock Reconciliation") + + vouchers_data = ( + frappe.qb.from_( + ( + frappe.qb.from_(se) + .select(se.name, Coalesce("Stock Entry").as_("voucher_type")) + .where((se.docstatus == 1) & (se.posting_date <= to_date) & (se.is_opening == "Yes")) + ) + + ( + frappe.qb.from_(sr) + .select(sr.name, Coalesce("Stock Reconciliation").as_("voucher_type")) + .where((sr.docstatus == 1) & (sr.posting_date <= to_date) & (sr.purpose == "Opening Stock")) + ) + ).select("voucher_type", "name") + ).run(as_dict=True) + + if vouchers_data: + for d in vouchers_data: + opening_vouchers[d.voucher_type].append(d.name) + + return opening_vouchers + + def get_item_warehouse_map(filters, sle): iwb_map = {} from_date = getdate(filters.get("from_date")) to_date = getdate(filters.get("to_date")) - + opening_vouchers = get_opening_vouchers(to_date) float_precision = cint(frappe.db.get_default("float_precision")) or 3 for d in sle: @@ -309,11 +338,7 @@ def get_item_warehouse_map(filters, sle): value_diff = flt(d.stock_value_difference) - if d.posting_date < from_date or ( - d.posting_date == from_date - and d.voucher_type == "Stock Reconciliation" - and frappe.db.get_value("Stock Reconciliation", d.voucher_no, "purpose") == "Opening Stock" - ): + if d.posting_date < from_date or d.voucher_no in opening_vouchers.get(d.voucher_type, []): qty_dict.opening_qty += qty_diff qty_dict.opening_val += value_diff