5113a417a1
* fix: create and add Portal Users child table in Supplier/Customer Issue #35772 * fix: modify the original permission check hook * fix: auto-add role for portal users * fix: added patch for auto-populating portal users * fix: modify patch to fetch users correctly * fix: remove unnecessary code for updating naming_series * fix(UX): show portal user in list view Also split columns to reduce whitespace. * refactor: simpler role checking * fix: consider parenttype while fetching portal user * refactor: simpler code, rename variable * test: supplier portal user can access their docs * refactor: only add role if not added * refactor: rename and move patch to supplier * refactor: dont add role if no perm or existing doc * fix: add role before save * refactor: run query directly * refactor: split patch and apply roles - if role isn't present dont add portal user - ignore failure as it's not critical * test: fix permission creation for webform test --------- Co-authored-by: Ankush Menat <ankush@frappe.io>
307 lines
7.8 KiB
Python
307 lines
7.8 KiB
Python
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
# License: GNU General Public License v3. See license.txt
|
|
|
|
|
|
import json
|
|
|
|
import frappe
|
|
from frappe import _
|
|
from frappe.modules.utils import get_module_app
|
|
from frappe.utils import flt, has_common
|
|
from frappe.utils.user import is_website_user
|
|
|
|
|
|
def get_list_context(context=None):
|
|
return {
|
|
"global_number_format": frappe.db.get_default("number_format") or "#,###.##",
|
|
"currency": frappe.db.get_default("currency"),
|
|
"currency_symbols": json.dumps(
|
|
dict(
|
|
frappe.db.sql(
|
|
"""select name, symbol
|
|
from tabCurrency where enabled=1"""
|
|
)
|
|
)
|
|
),
|
|
"row_template": "templates/includes/transaction_row.html",
|
|
"get_list": get_transaction_list,
|
|
}
|
|
|
|
|
|
def get_webform_list_context(module):
|
|
if get_module_app(module) != "erpnext":
|
|
return
|
|
return {"get_list": get_webform_transaction_list}
|
|
|
|
|
|
def get_webform_transaction_list(
|
|
doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"
|
|
):
|
|
"""Get List of transactions for custom doctypes"""
|
|
from frappe.www.list import get_list
|
|
|
|
if not filters:
|
|
filters = []
|
|
|
|
meta = frappe.get_meta(doctype)
|
|
|
|
for d in meta.fields:
|
|
if d.fieldtype == "Link" and d.fieldname != "amended_from":
|
|
allowed_docs = [d.name for d in get_transaction_list(doctype=d.options, custom=True)]
|
|
allowed_docs.append("")
|
|
filters.append((d.fieldname, "in", allowed_docs))
|
|
|
|
return get_list(
|
|
doctype,
|
|
txt,
|
|
filters,
|
|
limit_start,
|
|
limit_page_length,
|
|
ignore_permissions=False,
|
|
fields=None,
|
|
order_by="modified",
|
|
)
|
|
|
|
|
|
def get_transaction_list(
|
|
doctype,
|
|
txt=None,
|
|
filters=None,
|
|
limit_start=0,
|
|
limit_page_length=20,
|
|
order_by="modified",
|
|
custom=False,
|
|
):
|
|
user = frappe.session.user
|
|
ignore_permissions = False
|
|
|
|
if not filters:
|
|
filters = {}
|
|
|
|
filters["docstatus"] = ["<", "2"] if doctype in ["Supplier Quotation", "Purchase Invoice"] else 1
|
|
|
|
if (user != "Guest" and is_website_user()) or doctype == "Request for Quotation":
|
|
parties_doctype = (
|
|
"Request for Quotation Supplier" if doctype == "Request for Quotation" else doctype
|
|
)
|
|
# find party for this contact
|
|
customers, suppliers = get_customers_suppliers(parties_doctype, user)
|
|
|
|
if customers:
|
|
if doctype == "Quotation":
|
|
filters["quotation_to"] = "Customer"
|
|
filters["party_name"] = ["in", customers]
|
|
else:
|
|
filters["customer"] = ["in", customers]
|
|
elif suppliers:
|
|
filters["supplier"] = ["in", suppliers]
|
|
elif not custom:
|
|
return []
|
|
|
|
if doctype == "Request for Quotation":
|
|
parties = customers or suppliers
|
|
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
|
|
|
|
# Since customers and supplier do not have direct access to internal doctypes
|
|
ignore_permissions = True
|
|
|
|
if not customers and not suppliers and custom:
|
|
ignore_permissions = False
|
|
filters = {}
|
|
|
|
transactions = get_list_for_transactions(
|
|
doctype,
|
|
txt,
|
|
filters,
|
|
limit_start,
|
|
limit_page_length,
|
|
fields="name",
|
|
ignore_permissions=ignore_permissions,
|
|
order_by="modified desc",
|
|
)
|
|
|
|
if custom:
|
|
return transactions
|
|
|
|
return post_process(doctype, transactions)
|
|
|
|
|
|
def get_list_for_transactions(
|
|
doctype,
|
|
txt,
|
|
filters,
|
|
limit_start,
|
|
limit_page_length=20,
|
|
ignore_permissions=False,
|
|
fields=None,
|
|
order_by=None,
|
|
):
|
|
"""Get List of transactions like Invoices, Orders"""
|
|
from frappe.www.list import get_list
|
|
|
|
meta = frappe.get_meta(doctype)
|
|
data = []
|
|
or_filters = []
|
|
|
|
for d in get_list(
|
|
doctype,
|
|
txt,
|
|
filters=filters,
|
|
fields="name",
|
|
limit_start=limit_start,
|
|
limit_page_length=limit_page_length,
|
|
ignore_permissions=ignore_permissions,
|
|
order_by="modified desc",
|
|
):
|
|
data.append(d)
|
|
|
|
if txt:
|
|
if meta.get_field("items"):
|
|
if meta.get_field("items").options:
|
|
child_doctype = meta.get_field("items").options
|
|
for item in frappe.get_all(child_doctype, {"item_name": ["like", "%" + txt + "%"]}):
|
|
child = frappe.get_doc(child_doctype, item.name)
|
|
or_filters.append([doctype, "name", "=", child.parent])
|
|
|
|
if or_filters:
|
|
for r in frappe.get_list(
|
|
doctype,
|
|
fields=fields,
|
|
filters=filters,
|
|
or_filters=or_filters,
|
|
limit_start=limit_start,
|
|
limit_page_length=limit_page_length,
|
|
ignore_permissions=ignore_permissions,
|
|
order_by=order_by,
|
|
):
|
|
data.append(r)
|
|
|
|
return data
|
|
|
|
|
|
def rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length):
|
|
data = frappe.db.sql(
|
|
"""select distinct parent as name, supplier from `tab{doctype}`
|
|
where supplier = '{supplier}' and docstatus=1 order by modified desc limit {start}, {len}""".format(
|
|
doctype=parties_doctype, supplier=parties[0], start=limit_start, len=limit_page_length
|
|
),
|
|
as_dict=1,
|
|
)
|
|
|
|
return post_process(doctype, data)
|
|
|
|
|
|
def post_process(doctype, data):
|
|
result = []
|
|
for d in data:
|
|
doc = frappe.get_doc(doctype, d.name)
|
|
|
|
doc.status_percent = 0
|
|
doc.status_display = []
|
|
|
|
if doc.get("per_billed"):
|
|
doc.status_percent += flt(doc.per_billed)
|
|
doc.status_display.append(
|
|
_("Billed") if doc.per_billed == 100 else _("{0}% Billed").format(doc.per_billed)
|
|
)
|
|
|
|
if doc.get("per_delivered"):
|
|
doc.status_percent += flt(doc.per_delivered)
|
|
doc.status_display.append(
|
|
_("Delivered") if doc.per_delivered == 100 else _("{0}% Delivered").format(doc.per_delivered)
|
|
)
|
|
|
|
if hasattr(doc, "set_indicator"):
|
|
doc.set_indicator()
|
|
|
|
doc.status_display = ", ".join(doc.status_display)
|
|
doc.items_preview = ", ".join(d.item_name for d in doc.items if d.item_name)
|
|
result.append(doc)
|
|
|
|
return result
|
|
|
|
|
|
def get_customers_suppliers(doctype, user):
|
|
customers = []
|
|
suppliers = []
|
|
meta = frappe.get_meta(doctype)
|
|
|
|
customer_field_name = get_customer_field_name(doctype)
|
|
|
|
has_customer_field = meta.has_field(customer_field_name)
|
|
has_supplier_field = meta.has_field("supplier")
|
|
|
|
if has_common(["Supplier", "Customer"], frappe.get_roles(user)):
|
|
suppliers = get_parents_for_user("Supplier")
|
|
customers = get_parents_for_user("Customer")
|
|
elif frappe.has_permission(doctype, "read", user=user):
|
|
customer_list = frappe.get_list("Customer")
|
|
customers = suppliers = [customer.name for customer in customer_list]
|
|
|
|
return customers if has_customer_field else None, suppliers if has_supplier_field else None
|
|
|
|
|
|
def get_parents_for_user(parenttype: str) -> list[str]:
|
|
portal_user = frappe.qb.DocType("Portal User")
|
|
|
|
return (
|
|
frappe.qb.from_(portal_user)
|
|
.select(portal_user.parent)
|
|
.where(portal_user.user == frappe.session.user)
|
|
.where(portal_user.parenttype == parenttype)
|
|
).run(pluck="name")
|
|
|
|
|
|
def has_website_permission(doc, ptype, user, verbose=False):
|
|
doctype = doc.doctype
|
|
customers, suppliers = get_customers_suppliers(doctype, user)
|
|
if customers:
|
|
return frappe.db.exists(doctype, get_customer_filter(doc, customers))
|
|
elif suppliers:
|
|
fieldname = "suppliers" if doctype == "Request for Quotation" else "supplier"
|
|
return frappe.db.exists(doctype, {"name": doc.name, fieldname: ["in", suppliers]})
|
|
else:
|
|
return False
|
|
|
|
|
|
def get_customer_filter(doc, customers):
|
|
doctype = doc.doctype
|
|
filters = frappe._dict()
|
|
filters.name = doc.name
|
|
filters[get_customer_field_name(doctype)] = ["in", customers]
|
|
if doctype == "Quotation":
|
|
filters.quotation_to = "Customer"
|
|
return filters
|
|
|
|
|
|
def get_customer_field_name(doctype):
|
|
if doctype == "Quotation":
|
|
return "party_name"
|
|
else:
|
|
return "customer"
|
|
|
|
|
|
def add_role_for_portal_user(portal_user, role):
|
|
"""When a new portal user is added, give appropriate roles to user if
|
|
posssible, else warn user to add roles."""
|
|
if not portal_user.is_new():
|
|
return
|
|
|
|
user_doc = frappe.get_doc("User", portal_user.user)
|
|
roles = {r.role for r in user_doc.roles}
|
|
|
|
if role in roles:
|
|
return
|
|
|
|
if "System Manager" not in frappe.get_roles():
|
|
frappe.msgprint(
|
|
_("Please add {1} role to user {0}.").format(portal_user.user, role),
|
|
alert=True,
|
|
)
|
|
return
|
|
|
|
user_doc.add_roles(role)
|
|
frappe.msgprint(
|
|
_("Added {1} Role to User {0}.").format(frappe.bold(user_doc.name), role), alert=True
|
|
)
|