From f82ea857a0e3129457549318bc2d402a70a0d01a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 26 Sep 2019 12:27:56 +0530 Subject: [PATCH 1/5] feat(job-applicant): allow rename/merge with existing --- erpnext/hr/doctype/job_applicant/job_applicant.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json index e9de393bc4..b0cddc257e 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.json +++ b/erpnext/hr/doctype/job_applicant/job_applicant.json @@ -2,7 +2,7 @@ "allow_copy": 0, "allow_guest_to_view": 0, "allow_import": 0, - "allow_rename": 0, + "allow_rename": 1, "autoname": "HR-APP-.YYYY.-.#####", "beta": 0, "creation": "2013-01-29 19:25:37", @@ -346,7 +346,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-06-21 16:15:43.552049", + "modified": "2019-07-21 16:15:43.552049", "modified_by": "Administrator", "module": "HR", "name": "Job Applicant", From 8781dc8ca32e02dce9e6900320bc02638be4f7e7 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 26 Sep 2019 12:53:52 +0530 Subject: [PATCH 2/5] fix: footer link redirect to landing pages (#19181) --- .../includes/footer/footer_powered.html | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index d4deaaef68..cf7661ee3f 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -1,3 +1,18 @@ {% set domains = frappe.get_doc("Domain Settings").active_domains %} +{% set links = { + 'Manufacturing': '/manufacturing', + 'Services': '/services', + 'Retail': '/retail', + 'Distribution': '/distribution', + 'Non Profit': '/non-profit', + 'Education': '/education', + 'Healthcare': '/healthcare', + 'Agriculture': '/agriculture', + 'Hospitality': '' +} %} +{% set link = '' %} +{% if domains %} + {% set link = links[domains[0].domain] %} +{% endif %} -Powered by ERPNext - ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }} +Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }} From 001edb4464e83ae8ce21e171bcf11b3730a118af Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 26 Sep 2019 16:44:35 +0530 Subject: [PATCH 3/5] refactor: Reposting utility of Stock ledger (#19155) --- .../purchase_invoice/purchase_invoice.py | 2 +- .../doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/stock/stock_balance.py | 78 +++++++------------ 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index bc2ddffbcf..bc9c1783ef 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -364,7 +364,7 @@ class PurchaseInvoice(BuyingController): update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes" make_gl_entries(gl_entries, cancel=(self.docstatus == 2), - update_outstanding=update_outstanding, merge_entries=False) + update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost) if update_outstanding == "No": update_outstanding_amt(self.credit_to, "Supplier", self.supplier, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index abbac77783..e1256a78d9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -698,7 +698,7 @@ class SalesInvoice(SellingController): cint(self.redeem_loyalty_points)) else "Yes" make_gl_entries(gl_entries, cancel=(self.docstatus == 2), - update_outstanding=update_outstanding, merge_entries=False) + update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost) if update_outstanding == "No": from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index d68f97917f..e5dc6b12df 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -3,10 +3,10 @@ from __future__ import print_function, unicode_literals import frappe - from frappe.utils import flt, cstr, nowdate, nowtime from erpnext.stock.utils import update_bin from erpnext.stock.stock_ledger import update_entries_after +from erpnext.controllers.stock_controller import update_gl_entries_after def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False): """ @@ -18,23 +18,29 @@ def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) - for d in frappe.db.sql("""select distinct item_code, warehouse from - (select item_code, warehouse from tabBin - union - select item_code, warehouse from `tabStock Ledger Entry`) a"""): - try: - repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin) - frappe.db.commit() - except: - frappe.db.rollback() + item_warehouses = frappe.db.sql(""" + select distinct item_code, warehouse + from + (select item_code, warehouse from tabBin + union + select item_code, warehouse from `tabStock Ledger Entry`) a + """) + for d in item_warehouses: + try: + repost_stock(d[0], d[1], allow_zero_rate, only_actual, only_bin, allow_negative_stock) + frappe.db.commit() + except: + frappe.db.rollback() if allow_negative_stock: frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) frappe.db.auto_commit_on_many_writes = 0 -def repost_stock(item_code, warehouse, allow_zero_rate=False, only_actual=False, only_bin=False): +def repost_stock(item_code, warehouse, allow_zero_rate=False, + only_actual=False, only_bin=False, allow_negative_stock=False): + if not only_bin: - repost_actual_qty(item_code, warehouse, allow_zero_rate) + repost_actual_qty(item_code, warehouse, allow_zero_rate, allow_negative_stock) if item_code and warehouse and not only_actual: qty_dict = { @@ -50,11 +56,8 @@ def repost_stock(item_code, warehouse, allow_zero_rate=False, only_actual=False, update_bin_qty(item_code, warehouse, qty_dict) -def repost_actual_qty(item_code, warehouse, allow_zero_rate=False): - try: - update_entries_after({ "item_code": item_code, "warehouse": warehouse }, allow_zero_rate) - except: - pass +def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False): update_entries_after({ "item_code": item_code, "warehouse": warehouse }, + allow_zero_rate=allow_zero_rate, allow_negative_stock=allow_negative_stock) def get_balance_qty_from_sle(item_code, warehouse): balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry` @@ -227,39 +230,14 @@ def reset_serial_no_status_and_warehouse(serial_nos=None): except: pass -def repost_all_stock_vouchers(): - warehouses_with_account = frappe.db.sql_list("""select warehouse from tabAccount - where ifnull(account_type, '') = 'Stock' and (warehouse is not null and warehouse != '') - and is_group=0""") +def repost_gle_for_stock_transactions(posting_date=None, posting_time=None, for_warehouses=None): + frappe.db.auto_commit_on_many_writes = 1 - vouchers = frappe.db.sql("""select distinct voucher_type, voucher_no - from `tabStock Ledger Entry` sle - where voucher_type != "Serial No" and sle.warehouse in (%s) - order by posting_date, posting_time, creation""" % - ', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account)) + if not posting_date: + posting_date = "1900-01-01" + if not posting_time: + posting_time = "00:00" - rejected = [] - i = 0 - for voucher_type, voucher_no in vouchers: - i+=1 - print(i, "/", len(vouchers), voucher_type, voucher_no) - try: - for dt in ["Stock Ledger Entry", "GL Entry"]: - frappe.db.sql("""delete from `tab%s` where voucher_type=%s and voucher_no=%s"""% - (dt, '%s', '%s'), (voucher_type, voucher_no)) + update_gl_entries_after(posting_date, posting_time, for_warehouses=for_warehouses) - doc = frappe.get_doc(voucher_type, voucher_no) - if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]: - doc.calculate_rate_and_amount(force=1) - elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes": - doc.validate() - - doc.update_stock_ledger() - doc.make_gl_entries(repost_future_gle=False) - frappe.db.commit() - except Exception: - print(frappe.get_traceback()) - rejected.append([voucher_type, voucher_no]) - frappe.db.rollback() - - print(rejected) + frappe.db.auto_commit_on_many_writes = 0 From 83b0b2adecd1130c55a5f7c2da266a0b9c345534 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Fri, 27 Sep 2019 00:59:48 +0530 Subject: [PATCH 4/5] feat(Global Search): Add fixtures for global search (#19049) --- erpnext/hooks.py | 50 +++++++++++++++++++ .../operations/install_fixtures.py | 3 ++ 2 files changed, 53 insertions(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 7e33a14d51..b165b136ba 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -352,3 +352,53 @@ user_privacy_documents = [ 'personal_fields': ['contact_mobile', 'contact_display', 'customer_name'], } ] + +global_search_doctypes = [ + {"doctype": "Customer", "index": 0}, + {"doctype": "Supplier", "index": 1}, + {"doctype": "Item", "index": 2}, + {"doctype": "Warehouse", "index": 3}, + {"doctype": "Account", "index": 4}, + {"doctype": "Employee", "index": 5}, + {"doctype": "BOM", "index": 6}, + {"doctype": "Sales Invoice", "index": 7}, + {"doctype": "Sales Order", "index": 8}, + {"doctype": "Quotation", "index": 9}, + {"doctype": "Work Order", "index": 10}, + {"doctype": "Purchase Receipt", "index": 11}, + {"doctype": "Purchase Invoice", "index": 12}, + {"doctype": "Delivery Note", "index": 13}, + {"doctype": "Stock Entry", "index": 14}, + {"doctype": "Material Request", "index": 15}, + {"doctype": "Delivery Trip", "index": 16}, + {"doctype": "Pick List", "index": 17}, + {"doctype": "Salary Slip", "index": 18}, + {"doctype": "Leave Application", "index": 19}, + {"doctype": "Expense Claim", "index": 20}, + {"doctype": "Payment Entry", "index": 21}, + {"doctype": "Lead", "index": 22}, + {"doctype": "Opportunity", "index": 23}, + {"doctype": "Item Price", "index": 24}, + {"doctype": "Purchase Taxes and Charges Template", "index": 25}, + {"doctype": "Sales Taxes and Charges", "index": 26}, + {"doctype": "Asset", "index": 27}, + {"doctype": "Project", "index": 28}, + {"doctype": "Task", "index": 29}, + {"doctype": "Timesheet", "index": 30}, + {"doctype": "Issue", "index": 31}, + {"doctype": "Serial No", "index": 32}, + {"doctype": "Batch", "index": 33}, + {"doctype": "Branch", "index": 34}, + {"doctype": "Department", "index": 35}, + {"doctype": "Employee Grade", "index": 36}, + {"doctype": "Designation", "index": 37}, + {"doctype": "Job Opening", "index": 38}, + {"doctype": "Job Applicant", "index": 39}, + {"doctype": "Job Offer", "index": 40}, + {"doctype": "Salary Structure Assignment", "index": 41}, + {"doctype": "Appraisal", "index": 42}, + {"doctype": "Loan", "index": 43}, + {"doctype": "Maintenance Schedule", "index": 44}, + {"doctype": "Maintenance Visit", "index": 45}, + {"doctype": "Warranty Claim", "index": 46}, +] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index b65716536d..66598f40de 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -9,6 +9,7 @@ from frappe import _ from frappe.desk.page.setup_wizard.setup_wizard import make_records from frappe.utils import cstr, getdate from erpnext.accounts.doctype.account.account import RootNotEditable +from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes default_lead_sources = ["Existing Customer", "Reference", "Advertisement", "Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing", @@ -274,6 +275,8 @@ def install(country=None): set_more_defaults() + update_global_search_doctypes() + # path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country)) # if os.path.exists(path.encode("utf-8")): # frappe.get_attr("erpnext.regional.{0}.setup.setup_company_independent_fixtures".format(frappe.scrub(country)))() From 25ab1e41df59e276dbc8ec345f8eb9b6d8f188f3 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 30 Sep 2019 10:08:15 +0530 Subject: [PATCH 5/5] fix(Contact): mobile_no re-introduced and travis fixes (#19009) * fix: mobile_no re-introduced * fix: test cases * fix: set email as primary * fix: add primary email and phone * fix: utils for contact creation * chore: remove = from dict --- erpnext/accounts/doctype/sales_invoice/pos.py | 2 +- erpnext/accounts/page/pos/pos.js | 5 +- .../doctype/opportunity/test_opportunity.py | 2 +- erpnext/hub_node/legacy.py | 2 +- erpnext/selling/doctype/customer/customer.py | 6 ++- .../selling/doctype/sms_center/sms_center.py | 2 +- erpnext/tests/utils.py | 51 ++++++++++--------- 7 files changed, 40 insertions(+), 30 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 261363c493..7d4fc63955 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -238,7 +238,7 @@ def get_contacts(customers): customers = [frappe._dict({'name': customers})] for data in customers: - contact = frappe.db.sql(""" select email_id, phone from `tabContact` + contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact` where is_primary_contact=1 and name in (select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s and parenttype = 'Contact')""", data.name, as_dict=1) diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index ff0c265a42..8dc00f3f21 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -817,6 +817,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ if(reg.test(data.name.toLowerCase()) || reg.test(data.customer_name.toLowerCase()) || (contact && reg.test(contact["phone"])) + || (contact && reg.test(contact["mobile_no"])) || (data.customer_group && reg.test(data.customer_group.toLowerCase()))){ return data; } @@ -833,6 +834,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ if(contact && !c['phone']) { c["phone"] = contact["phone"]; c["email_id"] = contact["email_id"]; + c["mobile_no"] = contact["mobile_no"]; } me.customers_mapper.push({ @@ -842,9 +844,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ customer_group: c.customer_group, territory: c.territory, phone: contact ? contact["phone"] : '', + mobile_no: contact ? contact["mobile_no"] : '', email_id: contact ? contact["email_id"] : '', searchtext: ['customer_name', 'customer_group', 'name', 'value', - 'label', 'email_id', 'phone'] + 'label', 'email_id', 'phone', 'mobile_no'] .map(key => c[key]).join(' ') .toLowerCase() }); diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 8f61edf00e..33d90076c4 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -53,7 +53,7 @@ class TestOpportunity(unittest.TestCase): "link_name": customer.name }] }) - contact.add_email(new_lead_email_id) + contact.add_email(new_lead_email_id, is_primary=True) contact.insert(ignore_permissions=True) opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py index 85eb1b2bb0..b61b88bf07 100644 --- a/erpnext/hub_node/legacy.py +++ b/erpnext/hub_node/legacy.py @@ -73,7 +73,7 @@ def make_contact(supplier): {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} ] }) - contact.add_email(supplier.supplier_email) + contact.add_email(supplier.supplier_email, is_primary=True) contact.insert() else: contact = frappe.get_doc('Contact', contact_name) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d49b9a9062..a8e3ce4eae 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -350,8 +350,10 @@ def make_contact(args, is_primary_contact=1): 'link_name': args.get('name') }] }) - contact.add_email(args.get('email_id')) - contact.add_phone(args.get('mobile_no')) + if args.get('email_id'): + contact.add_email(args.get('email_id'), is_primary=True) + if args.get('mobile_no'): + contact.add_phone(args.get('mobile_no'), is_primary_mobile_no=True) contact.insert() return contact diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py index 289b045e1c..bb6ba1ffce 100644 --- a/erpnext/selling/doctype/sms_center/sms_center.py +++ b/erpnext/selling/doctype/sms_center/sms_center.py @@ -31,7 +31,7 @@ class SMSCenter(Document): self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''" if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']: rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')), - c.phone from `tabContact` c, `tabDynamic Link` dl where ifnull(c.phone,'')!='' and + c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and c.docstatus != 2 and dl.parent = c.name%s""" % where_clause) elif self.send_to == 'All Lead (Open)': diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index 7024b0db92..dfd3ed76bc 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -10,27 +10,32 @@ def create_test_contact_and_address(): frappe.db.sql('delete from tabAddress') frappe.db.sql('delete from `tabDynamic Link`') - frappe.get_doc(dict( - doctype='Address', - address_title='_Test Address for Customer', - address_type='Office', - address_line1='Station Road', - city='_Test City', - state='Test State', - country='India', - links = [dict( - link_doctype='Customer', - link_name='_Test Customer' - )] - )).insert() + frappe.get_doc({ + "doctype": "Address", + "address_title": "_Test Address for Customer", + "address_type": "Office", + "address_line1": "Station Road", + "city": "_Test City", + "state": "Test State", + "country": "India", + "links": [ + { + "link_doctype": "Customer", + "link_name": "_Test Customer" + } + ] + }).insert() - frappe.get_doc(dict( - doctype='Contact', - email_id='test_contact_customer@example.com', - phone='+91 0000000000', - first_name='_Test Contact for _Test Customer', - links = [dict( - link_doctype='Customer', - link_name='_Test Customer' - )] - )).insert() + contact = frappe.get_doc({ + "doctype": 'Contact', + "first_name": "_Test Contact for _Test Customer", + "links": [ + { + "link_doctype": "Customer", + "link_name": "_Test Customer" + } + ] + }) + contact.add_email("test_contact_customer@example.com", is_primary=True) + contact.add_phone("+91 0000000000", is_primary_phone=True) + contact.insert()