Browse Source

[minor] fixes for Is Your Company Address validations (#2606)

* [WIP] Address & Contacts fixes

* [minor] included sales partner in Addresses and Contacts report

* [minor] filters in address and contact query

* [minor] address and contact utils
version-14
Makarand Bauskar 8 years ago
committed by Rushabh Mehta
parent
commit
dc59e978f7
11 changed files with 357 additions and 14 deletions
  1. +10
    -0
      frappe/email/doctype/contact/contact.py
  2. +140
    -0
      frappe/geo/address_and_contact.py
  3. +30
    -13
      frappe/geo/doctype/address/address.py
  4. +0
    -0
      frappe/geo/report/__init__.py
  5. +0
    -0
      frappe/geo/report/addresses_and_contacts/__init__.py
  6. +34
    -0
      frappe/geo/report/addresses_and_contacts/addresses_and_contacts.js
  7. +18
    -0
      frappe/geo/report/addresses_and_contacts/addresses_and_contacts.json
  8. +90
    -0
      frappe/geo/report/addresses_and_contacts/addresses_and_contacts.py
  9. +5
    -1
      frappe/hooks.py
  10. +1
    -0
      frappe/public/build.json
  11. +29
    -0
      frappe/public/js/frappe/misc/address_and_contact.js

+ 10
- 0
frappe/email/doctype/contact/contact.py View File

@@ -114,6 +114,16 @@ def update_contact(doc, method):
def contact_query(doctype, txt, searchfield, start, page_len, filters): def contact_query(doctype, txt, searchfield, start, page_len, filters):
from frappe.desk.reportview import get_match_cond from frappe.desk.reportview import get_match_cond


link_doctype = filters.pop('link_doctype')
link_name = filters.pop('link_name')

condition = ""
for fieldname, value in filters.iteritems():
condition += " and {field}={value}".format(
field=fieldname,
value=value
)

return frappe.db.sql("""select return frappe.db.sql("""select
contact.name, contact.first_name, contact.last_name contact.name, contact.first_name, contact.last_name
from from


+ 140
- 0
frappe/geo/address_and_contact.py View File

@@ -0,0 +1,140 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt

from __future__ import unicode_literals
import frappe

def load_address_and_contact(doc, key):
"""Loads address list and contact list in `__onload`"""
from frappe.geo.doctype.address.address import get_address_display

filters = [
["Dynamic Link", "link_doctype", "=", doc.doctype],
["Dynamic Link", "link_name", "=", doc.name],
["Dynamic Link", "parenttype", "=", "Address"],
]
address_list = frappe.get_all("Address", filters=filters, fields=["*"])

address_list = [a.update({"display": get_address_display(a)})
for a in address_list]

address_list = sorted(address_list,
lambda a, b:
(int(a.is_primary_address - b.is_primary_address)) or
(1 if a.modified - b.modified else 0))

doc.set_onload('addr_list', address_list)

if doc.doctype != "Lead":
filters = [
["Dynamic Link", "link_doctype", "=", doc.doctype],
["Dynamic Link", "link_name", "=", doc.name],
["Dynamic Link", "parenttype", "=", "Contact"],
]
contact_list = frappe.get_all("Contact", filters=filters, fields=["*"])

contact_list = sorted(contact_list,
lambda a, b:
(int(a.is_primary_contact - b.is_primary_contact)) or
(1 if a.modified - b.modified else 0))

doc.set_onload('contact_list', contact_list)

def set_default_role(doc, method):
'''Set customer, supplier, student based on email'''
if frappe.flags.setting_role:
return
contact_name = frappe.get_value('Contact', dict(email_id=doc.email))
if contact_name:
contact = frappe.get_doc('Contact', contact_name)
for link in contact.links:
frappe.flags.setting_role = True
if link.link_doctype=='Customer':
doc.add_roles('Customer')
elif link.link_doctype=='Supplier':
doc.add_roles('Supplier')
elif frappe.get_value('Student', dict(student_email_id=doc.email)):
doc.add_roles('Student')

def has_permission(doc, ptype, user):
links = get_permitted_and_not_permitted_links(doc.doctype)
if not links.get("not_permitted_links"):
# optimization: don't determine permissions based on link fields
return True

# True if any one is True or all are empty
names = []
for df in (links.get("permitted_links") + links.get("not_permitted_links")):
doctype = df.options
name = doc.get(df.fieldname)
names.append(name)

if name and frappe.has_permission(doctype, ptype, doc=name):
return True

if not any(names):
return True
return False

def get_permission_query_conditions_for_contact(user):
return get_permission_query_conditions("Contact")

def get_permission_query_conditions_for_address(user):
return get_permission_query_conditions("Address")

def get_permission_query_conditions(doctype):
links = get_permitted_and_not_permitted_links(doctype)

if not links.get("not_permitted_links"):
# when everything is permitted, don't add additional condition
return ""

elif not links.get("permitted_links"):
conditions = []

# when everything is not permitted
for df in links.get("not_permitted_links"):
# like ifnull(customer, '')='' and ifnull(supplier, '')=''
conditions.append("ifnull(`tab{doctype}`.`{fieldname}`, '')=''".format(doctype=doctype, fieldname=df.fieldname))

return "( " + " and ".join(conditions) + " )"

else:
conditions = []

for df in links.get("permitted_links"):
# like ifnull(customer, '')!='' or ifnull(supplier, '')!=''
conditions.append("ifnull(`tab{doctype}`.`{fieldname}`, '')!=''".format(doctype=doctype, fieldname=df.fieldname))

return "( " + " or ".join(conditions) + " )"

def get_permitted_and_not_permitted_links(doctype):
permitted_links = []
not_permitted_links = []

meta = frappe.get_meta(doctype)

for df in meta.get_link_fields():
if df.options not in ("Customer", "Supplier", "Company", "Sales Partner"):
continue

if frappe.has_permission(df.options):
permitted_links.append(df)
else:
not_permitted_links.append(df)

return {
"permitted_links": permitted_links,
"not_permitted_links": not_permitted_links
}

def delete_contact_and_address(doctype, docname):
for parenttype in ('Contact', 'Address'):
items = frappe.db.sql_list("""select parent from `tabDynamic Link`
where parenttype=%s and link_doctype=%s and link_name=%s""",
(parenttype, doctype, docname))

for name in items:
doc = frappe.get_doc(parenttype, name)
if len(doc.links)==1:
doc.delete()

+ 30
- 13
frappe/geo/doctype/address/address.py View File

@@ -50,10 +50,12 @@ class Address(Document):


def validate_reference(self): def validate_reference(self):
if self.is_your_company_address: if self.is_your_company_address:
if not self.company:
if not [row for row in self.links if row.link_doctype == "Company"]:
frappe.throw(_("Company is mandatory, as it is your company address")) frappe.throw(_("Company is mandatory, as it is your company address"))
if self.links:
self.links = []

# removing other links
to_remove = [row for row in self.links if row.link_doctype != "Company"]
[ self.remove(row) for row in to_remove ]


def get_display(self): def get_display(self):
return get_address_display(self.as_dict()) return get_address_display(self.as_dict())
@@ -169,18 +171,32 @@ def get_address_templates(address):


@frappe.whitelist() @frappe.whitelist()
def get_shipping_address(company): def get_shipping_address(company):
filters = {"company": company, "is_your_company_address":1}
fieldname = ["name", "address_line1", "address_line2", "city", "state", "country"]

address_as_dict = frappe.db.get_value("Address", filters=filters, fieldname=fieldname, as_dict=True)

if address_as_dict:
filters = [
["Dynamic Link", "link_doctype", "=", "Company"],
["Dynamic Link", "link_name", "=", company],
["Address", "is_your_company_address", "=", 1]
]
fields = ["name", "address_line1", "address_line2", "city", "state", "country"]
address = frappe.get_all("Address", filters=filters, fields=fields) or {}

if address:
address_as_dict = address[0]
name, address_template = get_address_templates(address_as_dict) name, address_template = get_address_templates(address_as_dict)
return address_as_dict.get("name"), frappe.render_template(address_template, address_as_dict) return address_as_dict.get("name"), frappe.render_template(address_template, address_as_dict)


def address_query(doctype, txt, searchfield, start, page_len, filters): def address_query(doctype, txt, searchfield, start, page_len, filters):
from frappe.desk.reportview import get_match_cond from frappe.desk.reportview import get_match_cond


link_doctype = filters.pop('link_doctype')
link_name = filters.pop('link_name')

condition = ""
for fieldname, value in filters.iteritems():
condition += " and {field}={value}".format(
field=fieldname,
value=value
)

return frappe.db.sql("""select return frappe.db.sql("""select
address.name, address.city, address.country address.name, address.city, address.country
from from
@@ -191,18 +207,19 @@ def address_query(doctype, txt, searchfield, start, page_len, filters):
dl.link_doctype = %(link_doctype)s and dl.link_doctype = %(link_doctype)s and
dl.link_name = %(link_name)s and dl.link_name = %(link_name)s and
address.`{key}` like %(txt)s address.`{key}` like %(txt)s
{mcond}
{mcond} {condition}
order by order by
if(locate(%(_txt)s, address.name), locate(%(_txt)s, address.name), 99999), if(locate(%(_txt)s, address.name), locate(%(_txt)s, address.name), 99999),
address.idx desc, address.name address.idx desc, address.name
limit %(start)s, %(page_len)s """.format( limit %(start)s, %(page_len)s """.format(
mcond=get_match_cond(doctype), mcond=get_match_cond(doctype),
key=frappe.db.escape(searchfield)),
key=frappe.db.escape(searchfield),
condition=condition or ""),
{ {
'txt': "%%%s%%" % frappe.db.escape(txt), 'txt': "%%%s%%" % frappe.db.escape(txt),
'_txt': txt.replace("%", ""), '_txt': txt.replace("%", ""),
'start': start, 'start': start,
'page_len': page_len, 'page_len': page_len,
'link_doctype': filters.get('link_doctype'),
'link_name': filters.get('link_name')
'link_doctype': link_doctype,
'link_name': link_name
}) })

+ 0
- 0
frappe/geo/report/__init__.py View File


+ 0
- 0
frappe/geo/report/addresses_and_contacts/__init__.py View File


+ 34
- 0
frappe/geo/report/addresses_and_contacts/addresses_and_contacts.js View File

@@ -0,0 +1,34 @@
// Copyright (c) 2016, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.query_reports["Addresses And Contacts"] = {
"filters": [
{
"reqd": 1,
"fieldname":"party_type",
"label": __("Party Type"),
"fieldtype": "Link",
"options": "DocType",
"get_query": function() {
return {
"filters": {
"name": ["in","Customer,Supplier,Sales Partner"],
}
}
},
"default": "Customer"
},
{
"fieldname":"party_name",
"label": __("Party Name"),
"fieldtype": "Dynamic Link",
"get_options": function() {
var party_type = frappe.query_report_filters_by_name.party_type.get_value();
if(!party_type) {
frappe.throw(__("Please select Party Type first"));
}
return party_type;
}
}
]
}

+ 18
- 0
frappe/geo/report/addresses_and_contacts/addresses_and_contacts.json View File

@@ -0,0 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-01-19 12:57:22.881566",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-01-19 12:57:39.643565",
"modified_by": "Administrator",
"module": "Geo",
"name": "Addresses And Contacts",
"owner": "Administrator",
"ref_doctype": "Address",
"report_name": "Addresses And Contacts",
"report_type": "Script Report"
}

+ 90
- 0
frappe/geo/report/addresses_and_contacts/addresses_and_contacts.py View File

@@ -0,0 +1,90 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe


field_map = {
"Contact": [ "first_name", "last_name", "phone", "mobile_no", "email_id", "is_primary_contact" ],
"Address": [ "address_line1", "address_line2", "city", "state", "pincode", "country", "is_primary_address" ]
}

def execute(filters=None):
columns, data = get_columns(filters), get_data(filters)
return columns, data

def get_columns(filters):
return [
"{party_type}:Link/{party_type}".format(party_type=filters.get("party_type")),
"Address Line 1",
"Address Line 2",
"City",
"State",
"Postal Code",
"Country",
"Is Primary Address:Check",
"First Name",
"Last Name",
"Phone",
"Mobile No",
"Email Id",
"Is Primary Contact:Check"
]

def get_data(filters):
data = []
party_type = filters.get("party_type")
party = filters.get("party_name")

return get_party_addresses_and_contact(party_type, party)

def get_party_addresses_and_contact(party_type, party):
data = []
filters = None
party_details = []

if not party_type:
return []

if party:
filters = { "name": party }
party_details = frappe.get_list(party_type, filters=filters, fields=["name"], as_list=True)
for party_detail in map(list, party_details):
docname = party_detail[0]

addresses = get_party_details(party_type, docname, doctype="Address")
contacts = get_party_details(party_type, docname, doctype="Contact")

if not any([addresses, contacts]):
party_detail.extend([ "" for field in field_map.get("Address", []) ])
party_detail.extend([ "" for field in field_map.get("Contact", []) ])
data.append(party_detail)
else:
addresses = map(list, addresses)
contacts = map(list, contacts)

max_length = max(len(addresses), len(contacts))
for idx in xrange(0, max_length):
result = list(party_detail)

address = addresses[idx] if idx < len(addresses) else [ "" for field in field_map.get("Address", []) ]
contact = contacts[idx] if idx < len(contacts) else [ "" for field in field_map.get("Contact", []) ]
result.extend(address)
result.extend(contact)

data.append(result)
return data

def get_party_details(party_type, docname, doctype="Address", fields=None):
default_filters = get_default_address_contact_filters(party_type, docname)
if not fields:
fields = field_map.get(doctype, ["name"])
return frappe.get_list(doctype, filters=default_filters, fields=fields, as_list=True)

def get_default_address_contact_filters(party_type, docname):
return [
["Dynamic Link", "link_doctype", "=", party_type],
["Dynamic Link", "link_name", "=", docname]
]

+ 5
- 1
frappe/hooks.py View File

@@ -81,6 +81,8 @@ permission_query_conditions = {
"ToDo": "frappe.desk.doctype.todo.todo.get_permission_query_conditions", "ToDo": "frappe.desk.doctype.todo.todo.get_permission_query_conditions",
"User": "frappe.core.doctype.user.user.get_permission_query_conditions", "User": "frappe.core.doctype.user.user.get_permission_query_conditions",
"Note": "frappe.desk.doctype.note.note.get_permission_query_conditions", "Note": "frappe.desk.doctype.note.note.get_permission_query_conditions",
"Contact": "frappe.geo.address_and_contact.get_permission_query_conditions_for_contact",
"Address": "frappe.geo.address_and_contact.get_permission_query_conditions_for_address"
} }


has_permission = { has_permission = {
@@ -88,7 +90,9 @@ has_permission = {
"ToDo": "frappe.desk.doctype.todo.todo.has_permission", "ToDo": "frappe.desk.doctype.todo.todo.has_permission",
"User": "frappe.core.doctype.user.user.has_permission", "User": "frappe.core.doctype.user.user.has_permission",
"Note": "frappe.desk.doctype.note.note.has_permission", "Note": "frappe.desk.doctype.note.note.has_permission",
"Communication": "frappe.core.doctype.communication.communication.has_permission"
"Contact": "erpnext.utilities.address_and_contact.has_permission",
"Address": "erpnext.utilities.address_and_contact.has_permission",
"Communication": "frappe.core.doctype.communication.communication.has_permission",
} }


has_website_permission = { has_website_permission = {


+ 1
- 0
frappe/public/build.json View File

@@ -125,6 +125,7 @@
"public/js/frappe/misc/number_format.js", "public/js/frappe/misc/number_format.js",
"public/js/frappe/misc/help.js", "public/js/frappe/misc/help.js",
"public/js/frappe/misc/help_links.js", "public/js/frappe/misc/help_links.js",
"public/js/frappe/misc/address_and_contact.js",


"public/js/frappe/ui/upload.html", "public/js/frappe/ui/upload.html",
"public/js/frappe/upload.js", "public/js/frappe/upload.js",


+ 29
- 0
frappe/public/js/frappe/misc/address_and_contact.js View File

@@ -0,0 +1,29 @@
frappe.provide('frappe.geo')

$.extend(frappe.geo, {
clear_address_and_contact: function(frm) {
$(frm.fields_dict['address_html'].wrapper).html("");
frm.fields_dict['contact_html'] && $(frm.fields_dict['contact_html'].wrapper).html("");
},

render_address_and_contact: function(frm) {
// render address
$(frm.fields_dict['address_html'].wrapper)
.html(frappe.render_template("address_list",
cur_frm.doc.__onload))
.find(".btn-address").on("click", function() {
frappe.new_doc("Address");
});

// render contact
if(frm.fields_dict['contact_html']) {
$(frm.fields_dict['contact_html'].wrapper)
.html(frappe.render_template("contact_list",
cur_frm.doc.__onload))
.find(".btn-contact").on("click", function() {
frappe.new_doc("Contact");
}
);
}
}
})

Loading…
Cancel
Save