From bf72ec0598de4688af03d80c34c0241c5b5fb194 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 27 Aug 2021 14:57:42 +0530 Subject: [PATCH 1/3] feat: (consistency) Add Primary Address and Contact section in Supplier - The same is present in customer and is inconsistent with supplier - Helps quickly create primary address and contact via quick entry --- erpnext/buying/doctype/supplier/supplier.js | 43 +++++++++++ erpnext/buying/doctype/supplier/supplier.json | 51 ++++++++++++- erpnext/buying/doctype/supplier/supplier.py | 45 +++++++++++ erpnext/public/build.json | 1 + .../public/js/utils/supplier_quick_entry.js | 75 +++++++++++++++++++ 5 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 erpnext/public/js/utils/supplier_quick_entry.js diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 1766c2c80c..7ee91961ca 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -24,7 +24,26 @@ frappe.ui.form.on("Supplier", { } } }); + + frm.set_query("supplier_primary_contact", function(doc) { + return { + query: "erpnext.buying.doctype.supplier.supplier.get_supplier_primary_contact", + filters: { + "supplier": doc.name + } + }; + }); + + frm.set_query("supplier_primary_address", function(doc) { + return { + filters: { + "link_doctype": "Supplier", + "link_name": doc.name + } + }; + }); }, + refresh: function (frm) { frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' } @@ -78,6 +97,30 @@ frappe.ui.form.on("Supplier", { }); }, + supplier_primary_address: function(frm) { + if (frm.doc.supplier_primary_address) { + frappe.call({ + method: 'frappe.contacts.doctype.address.address.get_address_display', + args: { + "address_dict": frm.doc.supplier_primary_address + }, + callback: function(r) { + frm.set_value("primary_address", r.message); + } + }); + } + if (!frm.doc.supplier_primary_address) { + frm.set_value("primary_address", ""); + } + }, + + supplier_primary_contact: function(frm) { + if (!frm.doc.supplier_primary_contact) { + frm.set_value("mobile_no", ""); + frm.set_value("email_id", ""); + } + }, + is_internal_supplier: function(frm) { if (frm.doc.is_internal_supplier == 1) { frm.toggle_reqd("represents_company", true); diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 38b8dfdf48..22e689c101 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -49,6 +49,13 @@ "address_html", "column_break1", "contact_html", + "primary_address_and_contact_detail_section", + "supplier_primary_contact", + "mobile_no", + "email_id", + "column_break_44", + "supplier_primary_address", + "primary_address", "default_payable_accounts", "accounts", "default_tax_withholding_config", @@ -378,6 +385,48 @@ "fieldname": "allow_purchase_invoice_creation_without_purchase_receipt", "fieldtype": "Check", "label": "Allow Purchase Invoice Creation Without Purchase Receipt" + }, + { + "fieldname": "primary_address_and_contact_detail_section", + "fieldtype": "Section Break", + "label": "Primary Address and Contact Detail" + }, + { + "description": "Reselect, if the chosen contact is edited after save", + "fieldname": "supplier_primary_contact", + "fieldtype": "Link", + "label": "Supplier Primary Contact", + "options": "Contact" + }, + { + "depends_on": "mobile_no", + "fetch_from": "supplier_primary_contact.mobile_no", + "fieldname": "mobile_no", + "fieldtype": "Read Only", + "label": "Mobile No" + }, + { + "fetch_from": "supplier_primary_contact.email_id", + "fieldname": "email_id", + "fieldtype": "Read Only", + "label": "Email Id" + }, + { + "fieldname": "column_break_44", + "fieldtype": "Column Break" + }, + { + "fieldname": "primary_address", + "fieldtype": "Text", + "label": "Primary Address", + "read_only": 1 + }, + { + "description": "Reselect, if the chosen address is edited after save", + "fieldname": "supplier_primary_address", + "fieldtype": "Link", + "label": "Supplier Primary Address", + "options": "Address" } ], "icon": "fa fa-user", @@ -390,7 +439,7 @@ "link_fieldname": "supplier" } ], - "modified": "2021-05-18 15:10:11.087191", + "modified": "2021-08-27 13:46:18.212802", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index fd16b23c22..8252b40566 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -42,6 +42,8 @@ class Supplier(TransactionBase): if not self.naming_series: self.naming_series = '' + self.create_primary_contact() + def validate(self): # validation for Naming Series mandatory field... if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series': @@ -76,7 +78,32 @@ class Supplier(TransactionBase): frappe.throw(_("Internal Supplier for company {0} already exists").format( frappe.bold(self.represents_company))) + def create_primary_contact(self): + from erpnext.selling.doctype.customer.customer import make_contact + + if not self.supplier_primary_contact: + if self.mobile_no or self.email_id: + contact = make_contact(self) + self.db_set('supplier_primary_contact', contact.name) + self.db_set('mobile_no', self.mobile_no) + self.db_set('email_id', self.email_id) + + def create_primary_address(self): + from erpnext.selling.doctype.customer.customer import make_address + + if self.flags.is_new_doc and self.get('address_line1'): + make_address(self) + def on_trash(self): + if self.supplier_primary_contact: + frappe.db.sql(f""" + UPDATE `tabSupplier` + SET + supplier_primary_contact=null, + mobile_no=null, + email_id=null + WHERE name='{self.name}'""") + delete_contact_and_address('Supplier', self.name) def after_rename(self, olddn, newdn, merge=False): @@ -104,3 +131,21 @@ class Supplier(TransactionBase): doc.name, args.get('supplier_email_' + str(i))) except frappe.NameError: pass + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_supplier_primary_contact(doctype, txt, searchfield, start, page_len, filters): + supplier = filters.get("supplier") + return frappe.db.sql(""" + SELECT + `tabContact`.name from `tabContact`, + `tabDynamic Link` + WHERE + `tabContact`.name = `tabDynamic Link`.parent + and `tabDynamic Link`.link_name = %(supplier)s + and `tabDynamic Link`.link_doctype = 'Supplier' + and `tabContact`.name like %(txt)s + """, { + 'supplier': supplier, + 'txt': '%%%s%%' % txt + }) diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 3c60e3ee50..6b70dab803 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -38,6 +38,7 @@ "public/js/templates/item_quick_entry.html", "public/js/utils/item_quick_entry.js", "public/js/utils/customer_quick_entry.js", + "public/js/utils/supplier_quick_entry.js", "public/js/education/student_button.html", "public/js/education/assessment_result_tool.html", "public/js/hub/hub_factory.js", diff --git a/erpnext/public/js/utils/supplier_quick_entry.js b/erpnext/public/js/utils/supplier_quick_entry.js new file mode 100644 index 0000000000..f650d1f101 --- /dev/null +++ b/erpnext/public/js/utils/supplier_quick_entry.js @@ -0,0 +1,75 @@ +frappe.provide('frappe.ui.form'); + +frappe.ui.form.SupplierQuickEntryForm = class SupplierQuickEntryForm extends frappe.ui.form.QuickEntryForm { + constructor(doctype, after_insert, init_callback, doc, force) { + super(doctype, after_insert, init_callback, doc, force); + this.skip_redirect_on_error = true; + } + + render_dialog() { + this.mandatory = this.mandatory.concat(this.get_variant_fields()); + super.render_dialog(); + } + + get_variant_fields() { + var variant_fields = [{ + fieldtype: "Section Break", + label: __("Primary Contact Details"), + collapsible: 1 + }, + { + label: __("Email Id"), + fieldname: "email_id", + fieldtype: "Data" + }, + { + fieldtype: "Column Break" + }, + { + label: __("Mobile Number"), + fieldname: "mobile_no", + fieldtype: "Data" + }, + { + fieldtype: "Section Break", + label: __("Primary Address Details"), + collapsible: 1 + }, + { + label: __("Address Line 1"), + fieldname: "address_line1", + fieldtype: "Data" + }, + { + label: __("Address Line 2"), + fieldname: "address_line2", + fieldtype: "Data" + }, + { + label: __("ZIP Code"), + fieldname: "pincode", + fieldtype: "Data" + }, + { + fieldtype: "Column Break" + }, + { + label: __("City"), + fieldname: "city", + fieldtype: "Data" + }, + { + label: __("State"), + fieldname: "state", + fieldtype: "Data" + }, + { + label: __("Country"), + fieldname: "country", + fieldtype: "Link", + options: "Country" + }]; + + return variant_fields; + } +}; From 2a3ef03388c4581550a247e2dbe99d8ca496883c Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 27 Aug 2021 18:06:51 +0530 Subject: [PATCH 2/3] fix: Popup stale build and data consistency - Include `supplier_quick_entry.js` in erpnext.bundle.js - Create primary supplier address on update - Set newly created address (quick entry) in Supplier and Customer - Clear address set in supplier and customer on delete (dependency) --- erpnext/buying/doctype/supplier/supplier.json | 3 +-- erpnext/buying/doctype/supplier/supplier.py | 14 +++++++++++-- erpnext/public/js/erpnext.bundle.js | 1 + .../public/js/utils/supplier_quick_entry.js | 3 ++- erpnext/selling/doctype/customer/customer.py | 20 +++++++++++++++---- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index 22e689c101..c7a5db5994 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -399,7 +399,6 @@ "options": "Contact" }, { - "depends_on": "mobile_no", "fetch_from": "supplier_primary_contact.mobile_no", "fieldname": "mobile_no", "fieldtype": "Read Only", @@ -439,7 +438,7 @@ "link_fieldname": "supplier" } ], - "modified": "2021-08-27 13:46:18.212802", + "modified": "2021-08-27 18:02:44.314077", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index 8252b40566..207485e1bc 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -43,8 +43,11 @@ class Supplier(TransactionBase): self.naming_series = '' self.create_primary_contact() + self.create_primary_address() def validate(self): + self.flags.is_new_doc = self.is_new() + # validation for Naming Series mandatory field... if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series': if not self.naming_series: @@ -90,9 +93,14 @@ class Supplier(TransactionBase): def create_primary_address(self): from erpnext.selling.doctype.customer.customer import make_address + from frappe.contacts.doctype.address.address import get_address_display if self.flags.is_new_doc and self.get('address_line1'): - make_address(self) + address = make_address(self) + address_display = get_address_display(address.name) + + self.db_set("supplier_primary_address", address.name) + self.db_set("primary_address", address_display) def on_trash(self): if self.supplier_primary_contact: @@ -100,8 +108,10 @@ class Supplier(TransactionBase): UPDATE `tabSupplier` SET supplier_primary_contact=null, + supplier_primary_address=null, mobile_no=null, - email_id=null + email_id=null, + primary_address=null WHERE name='{self.name}'""") delete_contact_and_address('Supplier', self.name) diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js index 9f7f29ad72..febdb24da3 100644 --- a/erpnext/public/js/erpnext.bundle.js +++ b/erpnext/public/js/erpnext.bundle.js @@ -15,6 +15,7 @@ import "./agriculture/ternary_plot"; import "./templates/item_quick_entry.html"; import "./utils/item_quick_entry"; import "./utils/customer_quick_entry"; +import "./utils/supplier_quick_entry"; import "./education/student_button.html"; import "./education/assessment_result_tool.html"; import "./hub/hub_factory"; diff --git a/erpnext/public/js/utils/supplier_quick_entry.js b/erpnext/public/js/utils/supplier_quick_entry.js index f650d1f101..e4a3812cf4 100644 --- a/erpnext/public/js/utils/supplier_quick_entry.js +++ b/erpnext/public/js/utils/supplier_quick_entry.js @@ -12,7 +12,8 @@ frappe.ui.form.SupplierQuickEntryForm = class SupplierQuickEntryForm extends fra } get_variant_fields() { - var variant_fields = [{ + var variant_fields = [ + { fieldtype: "Section Break", label: __("Primary Contact Details"), collapsible: 1 diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index abf146c43f..05cabcbbe2 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -150,8 +150,14 @@ class Customer(TransactionBase): self.db_set('email_id', self.email_id) def create_primary_address(self): + from frappe.contacts.doctype.address.address import get_address_display + if self.flags.is_new_doc and self.get('address_line1'): - make_address(self) + address = make_address(self) + address_display = get_address_display(address.name) + + self.db_set("customer_primary_address", address.name) + self.db_set("primary_address", address_display) def update_lead_status(self): '''If Customer created from Lead, update lead status to "Converted" @@ -246,9 +252,15 @@ class Customer(TransactionBase): def on_trash(self): if self.customer_primary_contact: - frappe.db.sql("""update `tabCustomer` - set customer_primary_contact=null, mobile_no=null, email_id=null - where name=%s""", self.name) + frappe.db.sql(f""" + UPDATE `tabCustomer` + SET + customer_primary_contact=null, + customer_primary_address=null, + mobile_no=null, + email_id=null, + primary_address=null + WHERE name='{self.name}'""") delete_contact_and_address('Customer', self.name) if self.lead_name: From 7b78473da35fa66476d93c5212f30fd07b63336d Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 30 Aug 2021 12:50:24 +0530 Subject: [PATCH 3/3] fix: Indentation and removed f-strings - Sider: fixed indentation in js - Dont use f-strings in queries --- erpnext/buying/doctype/supplier/supplier.py | 4 +- .../public/js/utils/supplier_quick_entry.js | 115 +++++++++--------- erpnext/selling/doctype/customer/customer.py | 4 +- 3 files changed, 62 insertions(+), 61 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index 207485e1bc..c9750caa65 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -104,7 +104,7 @@ class Supplier(TransactionBase): def on_trash(self): if self.supplier_primary_contact: - frappe.db.sql(f""" + frappe.db.sql(""" UPDATE `tabSupplier` SET supplier_primary_contact=null, @@ -112,7 +112,7 @@ class Supplier(TransactionBase): mobile_no=null, email_id=null, primary_address=null - WHERE name='{self.name}'""") + WHERE name=%(name)s""", {"name": self.name}) delete_contact_and_address('Supplier', self.name) diff --git a/erpnext/public/js/utils/supplier_quick_entry.js b/erpnext/public/js/utils/supplier_quick_entry.js index e4a3812cf4..8d591a9651 100644 --- a/erpnext/public/js/utils/supplier_quick_entry.js +++ b/erpnext/public/js/utils/supplier_quick_entry.js @@ -13,63 +13,64 @@ frappe.ui.form.SupplierQuickEntryForm = class SupplierQuickEntryForm extends fra get_variant_fields() { var variant_fields = [ - { - fieldtype: "Section Break", - label: __("Primary Contact Details"), - collapsible: 1 - }, - { - label: __("Email Id"), - fieldname: "email_id", - fieldtype: "Data" - }, - { - fieldtype: "Column Break" - }, - { - label: __("Mobile Number"), - fieldname: "mobile_no", - fieldtype: "Data" - }, - { - fieldtype: "Section Break", - label: __("Primary Address Details"), - collapsible: 1 - }, - { - label: __("Address Line 1"), - fieldname: "address_line1", - fieldtype: "Data" - }, - { - label: __("Address Line 2"), - fieldname: "address_line2", - fieldtype: "Data" - }, - { - label: __("ZIP Code"), - fieldname: "pincode", - fieldtype: "Data" - }, - { - fieldtype: "Column Break" - }, - { - label: __("City"), - fieldname: "city", - fieldtype: "Data" - }, - { - label: __("State"), - fieldname: "state", - fieldtype: "Data" - }, - { - label: __("Country"), - fieldname: "country", - fieldtype: "Link", - options: "Country" - }]; + { + fieldtype: "Section Break", + label: __("Primary Contact Details"), + collapsible: 1 + }, + { + label: __("Email Id"), + fieldname: "email_id", + fieldtype: "Data" + }, + { + fieldtype: "Column Break" + }, + { + label: __("Mobile Number"), + fieldname: "mobile_no", + fieldtype: "Data" + }, + { + fieldtype: "Section Break", + label: __("Primary Address Details"), + collapsible: 1 + }, + { + label: __("Address Line 1"), + fieldname: "address_line1", + fieldtype: "Data" + }, + { + label: __("Address Line 2"), + fieldname: "address_line2", + fieldtype: "Data" + }, + { + label: __("ZIP Code"), + fieldname: "pincode", + fieldtype: "Data" + }, + { + fieldtype: "Column Break" + }, + { + label: __("City"), + fieldname: "city", + fieldtype: "Data" + }, + { + label: __("State"), + fieldname: "state", + fieldtype: "Data" + }, + { + label: __("Country"), + fieldname: "country", + fieldtype: "Link", + options: "Country" + } + ]; return variant_fields; } diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 05cabcbbe2..1164f40237 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -252,7 +252,7 @@ class Customer(TransactionBase): def on_trash(self): if self.customer_primary_contact: - frappe.db.sql(f""" + frappe.db.sql(""" UPDATE `tabCustomer` SET customer_primary_contact=null, @@ -260,7 +260,7 @@ class Customer(TransactionBase): mobile_no=null, email_id=null, primary_address=null - WHERE name='{self.name}'""") + WHERE name=%(name)s""", {"name": self.name}) delete_contact_and_address('Customer', self.name) if self.lead_name: