Merge pull request #57258 from mihir-kandoi/per-master-company-restriction

This commit is contained in:
Mihir Kandoi
2026-07-18 20:39:13 +05:30
committed by GitHub
12 changed files with 74 additions and 37 deletions

View File

@@ -2,6 +2,12 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Supplier", {
restrict_to_companies(frm) {
if (!frm.doc.restrict_to_companies) {
frm.set_value("allowed_companies", []);
}
},
setup: function (frm) {
frm.set_query("allowed_companies", () => ({
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",

View File

@@ -55,6 +55,7 @@
"tax_withholding_group",
"settings_tab",
"company_restrictions_section",
"restrict_to_companies",
"allowed_companies",
"invoice_settings_section",
"is_transporter",
@@ -430,16 +431,22 @@
{
"fieldname": "company_restrictions_section",
"fieldtype": "Section Break",
"label": "Company Restrictions",
"description": "If set, this Supplier is only available for transactions in the listed companies. Leave empty for no restriction.",
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
"label": "Company Restrictions"
},
{
"default": "0",
"fieldname": "restrict_to_companies",
"fieldtype": "Check",
"label": "Restrict to Companies",
"description": "If checked, this Supplier is only available for transactions in the companies listed below."
},
{
"fieldname": "allowed_companies",
"fieldtype": "Table MultiSelect",
"label": "Allowed Companies",
"options": "Company Restriction",
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
"depends_on": "eval:doc.restrict_to_companies",
"mandatory_depends_on": "eval:doc.restrict_to_companies"
},
{
"fieldname": "contact_and_address_tab",
@@ -578,7 +585,7 @@
"link_fieldname": "party"
}
],
"modified": "2026-07-14 21:00:00.000000",
"modified": "2026-07-14 23:00:00.000000",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",

View File

@@ -73,6 +73,7 @@ class Supplier(TransactionBase):
primary_address: DF.TextEditor | None
release_date: DF.Date | None
represents_company: DF.Link | None
restrict_to_companies: DF.Check
supplier_details: DF.Text | None
supplier_group: DF.Link | None
supplier_name: DF.Data

View File

@@ -2,6 +2,12 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Customer", {
restrict_to_companies(frm) {
if (!frm.doc.restrict_to_companies) {
frm.set_value("allowed_companies", []);
}
},
setup: function (frm) {
frm.set_query("allowed_companies", () => ({
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",

View File

@@ -66,6 +66,7 @@
"tax_withholding_category",
"settings_tab",
"company_restrictions_section",
"restrict_to_companies",
"allowed_companies",
"section_break_ario",
"so_required",
@@ -516,18 +517,24 @@
"label": "Settings"
},
{
"description": "If set, this Customer is only available for transactions in the listed companies. Leave empty for no restriction.",
"fieldname": "company_restrictions_section",
"fieldtype": "Section Break",
"label": "Company Restrictions",
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
"label": "Company Restrictions"
},
{
"default": "0",
"fieldname": "restrict_to_companies",
"fieldtype": "Check",
"label": "Restrict to Companies",
"description": "If checked, this Customer is only available for transactions in the companies listed below."
},
{
"fieldname": "allowed_companies",
"fieldtype": "Table MultiSelect",
"label": "Allowed Companies",
"options": "Company Restriction",
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
"depends_on": "eval:doc.restrict_to_companies",
"mandatory_depends_on": "eval:doc.restrict_to_companies"
},
{
"collapsible": 1,
@@ -717,7 +724,7 @@
"link_fieldname": "party"
}
],
"modified": "2026-07-14 21:00:00.000000",
"modified": "2026-07-14 23:00:00.000000",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",

View File

@@ -99,6 +99,7 @@ class Customer(TransactionBase):
primary_address: DF.TextEditor | None
prospect_name: DF.Link | None
represents_company: DF.Link | None
restrict_to_companies: DF.Check
sales_team: DF.Table[SalesTeam]
so_required: DF.Check
supplier_numbers: DF.Table[SupplierNumberAtCustomer]

View File

@@ -17,8 +17,7 @@
"disable_rounded_total",
"disable_in_words",
"column_break_hnew",
"use_posting_datetime_for_naming_documents",
"enable_company_wise_masters"
"use_posting_datetime_for_naming_documents"
],
"fields": [
{
@@ -93,13 +92,6 @@
"fieldtype": "Check",
"label": "Use Posting Datetime for Naming Documents"
},
{
"default": "0",
"description": "When enabled, Supplier, Customer, and Item records can be restricted to specific companies via their <b>Allowed Companies</b> table. Transactions will only show masters configured for the selected company.",
"fieldname": "enable_company_wise_masters",
"fieldtype": "Check",
"label": "Enable Company-wise Master Filtering"
},
{
"fieldname": "defaults_section",
"fieldtype": "Section Break",
@@ -121,7 +113,7 @@
"in_create": 1,
"issingle": 1,
"links": [],
"modified": "2026-07-14 18:30:00.000000",
"modified": "2026-07-14 23:00:00.000000",
"modified_by": "Administrator",
"module": "Setup",
"name": "Global Defaults",

View File

@@ -18,7 +18,6 @@ keydict = {
"account_url": "account_url",
"disable_rounded_total": "disable_rounded_total",
"disable_in_words": "disable_in_words",
"enable_company_wise_masters": "enable_company_wise_masters",
}
ROUNDED_TOTAL_DOCTYPES = (
@@ -52,7 +51,6 @@ class GlobalDefaults(Document):
demo_company: DF.Link | None
disable_in_words: DF.Check
disable_rounded_total: DF.Check
enable_company_wise_masters: DF.Check
hide_currency_symbol: DF.Check
use_posting_datetime_for_naming_documents: DF.Check
# end: auto-generated types

View File

@@ -26,9 +26,6 @@ class CompanyRestriction(Document):
def get_allowed_companies(user, doctype):
from frappe.permissions import get_allowed_docs_for_doctype, get_user_permissions
if not frappe.get_single_value("Global Defaults", "enable_company_wise_masters"):
return None
user_permissions = get_user_permissions(user or frappe.session.user)
if "Company" not in user_permissions:
return None
@@ -45,31 +42,39 @@ def get_permission_query_conditions(user, doctype=None):
parent = frappe.qb.DocType(doctype)
restriction = frappe.qb.DocType("Company Restriction")
restriction_rows = (
allowed_rows = (
frappe.qb.from_(restriction)
.select(restriction.name)
.where(
(restriction.parenttype == doctype)
& (restriction.parentfield == "allowed_companies")
& (restriction.parent == parent.name)
& (restriction.company.isin(allowed_companies))
)
)
allowed_rows = restriction_rows.where(restriction.company.isin(allowed_companies))
return Bracket(ExistsCriterion(allowed_rows) | ExistsCriterion(restriction_rows).negate())
return Bracket((parent.restrict_to_companies == 0) | ExistsCriterion(allowed_rows))
def has_permission(doc, ptype=None, user=None):
if not doc.get("restrict_to_companies"):
return True
allowed_companies = get_allowed_companies(user, doc.doctype)
if not allowed_companies:
return True
companies = [row.company for row in doc.get("allowed_companies") or []]
if not companies:
return True
return any(company in allowed_companies for company in companies)
return any(row.company in allowed_companies for row in doc.get("allowed_companies") or [])
def validate_allowed_companies(doc):
if not doc.get("restrict_to_companies"):
doc.set("allowed_companies", [])
elif not doc.get("allowed_companies") and not doc.flags.ignore_mandatory:
frappe.throw(
_("Allowed Companies is required when Restrict to Companies is checked"),
frappe.MandatoryError,
)
if doc.flags.ignore_permissions:
return

View File

@@ -60,6 +60,12 @@ frappe.ui.form.on("Item", {
erpnext.utils.confirm_negative_stock(frm);
},
restrict_to_companies(frm) {
if (!frm.doc.restrict_to_companies) {
frm.set_value("allowed_companies", []);
}
},
setup: function (frm) {
frm.set_query("allowed_companies", () => ({
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",

View File

@@ -41,6 +41,7 @@
"column_break_wugd",
"over_billing_allowance",
"company_restrictions_section",
"restrict_to_companies",
"allowed_companies",
"section_break_11",
"brand",
@@ -1089,16 +1090,22 @@
{
"fieldname": "company_restrictions_section",
"fieldtype": "Section Break",
"label": "Company Restrictions",
"description": "If set, this Item is only available for transactions in the listed companies. Leave empty for no restriction.",
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
"label": "Company Restrictions"
},
{
"default": "0",
"fieldname": "restrict_to_companies",
"fieldtype": "Check",
"label": "Restrict to Companies",
"description": "If checked, this Item is only available for transactions in the companies listed below."
},
{
"fieldname": "allowed_companies",
"fieldtype": "Table MultiSelect",
"label": "Allowed Companies",
"options": "Company Restriction",
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
"depends_on": "eval:doc.restrict_to_companies",
"mandatory_depends_on": "eval:doc.restrict_to_companies"
}
],
"icon": "fa fa-tag",
@@ -1106,7 +1113,7 @@
"image_field": "image",
"links": [],
"make_attachments_public": 1,
"modified": "2026-07-14 21:00:00.000000",
"modified": "2026-07-14 23:00:00.000000",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -134,6 +134,7 @@ class Item(Document):
quality_inspection_template: DF.Link | None
reorder_levels: DF.Table[ItemReorder]
retain_sample: DF.Check
restrict_to_companies: DF.Check
safety_stock: DF.Float
sales_tax_withholding_category: DF.Link | None
sales_uom: DF.Link | None