mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-14 01:20:41 +00:00
Merge pull request #57258 from mihir-kandoi/per-master-company-restriction
This commit is contained in:
@@ -2,6 +2,12 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Supplier", {
|
frappe.ui.form.on("Supplier", {
|
||||||
|
restrict_to_companies(frm) {
|
||||||
|
if (!frm.doc.restrict_to_companies) {
|
||||||
|
frm.set_value("allowed_companies", []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setup: function (frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("allowed_companies", () => ({
|
frm.set_query("allowed_companies", () => ({
|
||||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
"tax_withholding_group",
|
"tax_withholding_group",
|
||||||
"settings_tab",
|
"settings_tab",
|
||||||
"company_restrictions_section",
|
"company_restrictions_section",
|
||||||
|
"restrict_to_companies",
|
||||||
"allowed_companies",
|
"allowed_companies",
|
||||||
"invoice_settings_section",
|
"invoice_settings_section",
|
||||||
"is_transporter",
|
"is_transporter",
|
||||||
@@ -430,16 +431,22 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company_restrictions_section",
|
"fieldname": "company_restrictions_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Company Restrictions",
|
"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)"
|
{
|
||||||
|
"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",
|
"fieldname": "allowed_companies",
|
||||||
"fieldtype": "Table MultiSelect",
|
"fieldtype": "Table MultiSelect",
|
||||||
"label": "Allowed Companies",
|
"label": "Allowed Companies",
|
||||||
"options": "Company Restriction",
|
"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",
|
"fieldname": "contact_and_address_tab",
|
||||||
@@ -578,7 +585,7 @@
|
|||||||
"link_fieldname": "party"
|
"link_fieldname": "party"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2026-07-14 21:00:00.000000",
|
"modified": "2026-07-14 23:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier",
|
"name": "Supplier",
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class Supplier(TransactionBase):
|
|||||||
primary_address: DF.TextEditor | None
|
primary_address: DF.TextEditor | None
|
||||||
release_date: DF.Date | None
|
release_date: DF.Date | None
|
||||||
represents_company: DF.Link | None
|
represents_company: DF.Link | None
|
||||||
|
restrict_to_companies: DF.Check
|
||||||
supplier_details: DF.Text | None
|
supplier_details: DF.Text | None
|
||||||
supplier_group: DF.Link | None
|
supplier_group: DF.Link | None
|
||||||
supplier_name: DF.Data
|
supplier_name: DF.Data
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.ui.form.on("Customer", {
|
frappe.ui.form.on("Customer", {
|
||||||
|
restrict_to_companies(frm) {
|
||||||
|
if (!frm.doc.restrict_to_companies) {
|
||||||
|
frm.set_value("allowed_companies", []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setup: function (frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("allowed_companies", () => ({
|
frm.set_query("allowed_companies", () => ({
|
||||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
"tax_withholding_category",
|
"tax_withholding_category",
|
||||||
"settings_tab",
|
"settings_tab",
|
||||||
"company_restrictions_section",
|
"company_restrictions_section",
|
||||||
|
"restrict_to_companies",
|
||||||
"allowed_companies",
|
"allowed_companies",
|
||||||
"section_break_ario",
|
"section_break_ario",
|
||||||
"so_required",
|
"so_required",
|
||||||
@@ -516,18 +517,24 @@
|
|||||||
"label": "Settings"
|
"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",
|
"fieldname": "company_restrictions_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Company Restrictions",
|
"label": "Company Restrictions"
|
||||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
},
|
||||||
|
{
|
||||||
|
"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",
|
"fieldname": "allowed_companies",
|
||||||
"fieldtype": "Table MultiSelect",
|
"fieldtype": "Table MultiSelect",
|
||||||
"label": "Allowed Companies",
|
"label": "Allowed Companies",
|
||||||
"options": "Company Restriction",
|
"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,
|
"collapsible": 1,
|
||||||
@@ -717,7 +724,7 @@
|
|||||||
"link_fieldname": "party"
|
"link_fieldname": "party"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2026-07-14 21:00:00.000000",
|
"modified": "2026-07-14 23:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Customer",
|
"name": "Customer",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class Customer(TransactionBase):
|
|||||||
primary_address: DF.TextEditor | None
|
primary_address: DF.TextEditor | None
|
||||||
prospect_name: DF.Link | None
|
prospect_name: DF.Link | None
|
||||||
represents_company: DF.Link | None
|
represents_company: DF.Link | None
|
||||||
|
restrict_to_companies: DF.Check
|
||||||
sales_team: DF.Table[SalesTeam]
|
sales_team: DF.Table[SalesTeam]
|
||||||
so_required: DF.Check
|
so_required: DF.Check
|
||||||
supplier_numbers: DF.Table[SupplierNumberAtCustomer]
|
supplier_numbers: DF.Table[SupplierNumberAtCustomer]
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
"disable_in_words",
|
"disable_in_words",
|
||||||
"column_break_hnew",
|
"column_break_hnew",
|
||||||
"use_posting_datetime_for_naming_documents",
|
"use_posting_datetime_for_naming_documents"
|
||||||
"enable_company_wise_masters"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -93,13 +92,6 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Use Posting Datetime for Naming Documents"
|
"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",
|
"fieldname": "defaults_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@@ -121,7 +113,7 @@
|
|||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-07-14 18:30:00.000000",
|
"modified": "2026-07-14 23:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Global Defaults",
|
"name": "Global Defaults",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ keydict = {
|
|||||||
"account_url": "account_url",
|
"account_url": "account_url",
|
||||||
"disable_rounded_total": "disable_rounded_total",
|
"disable_rounded_total": "disable_rounded_total",
|
||||||
"disable_in_words": "disable_in_words",
|
"disable_in_words": "disable_in_words",
|
||||||
"enable_company_wise_masters": "enable_company_wise_masters",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ROUNDED_TOTAL_DOCTYPES = (
|
ROUNDED_TOTAL_DOCTYPES = (
|
||||||
@@ -52,7 +51,6 @@ class GlobalDefaults(Document):
|
|||||||
demo_company: DF.Link | None
|
demo_company: DF.Link | None
|
||||||
disable_in_words: DF.Check
|
disable_in_words: DF.Check
|
||||||
disable_rounded_total: DF.Check
|
disable_rounded_total: DF.Check
|
||||||
enable_company_wise_masters: DF.Check
|
|
||||||
hide_currency_symbol: DF.Check
|
hide_currency_symbol: DF.Check
|
||||||
use_posting_datetime_for_naming_documents: DF.Check
|
use_posting_datetime_for_naming_documents: DF.Check
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ class CompanyRestriction(Document):
|
|||||||
def get_allowed_companies(user, doctype):
|
def get_allowed_companies(user, doctype):
|
||||||
from frappe.permissions import get_allowed_docs_for_doctype, get_user_permissions
|
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)
|
user_permissions = get_user_permissions(user or frappe.session.user)
|
||||||
if "Company" not in user_permissions:
|
if "Company" not in user_permissions:
|
||||||
return None
|
return None
|
||||||
@@ -45,31 +42,39 @@ def get_permission_query_conditions(user, doctype=None):
|
|||||||
|
|
||||||
parent = frappe.qb.DocType(doctype)
|
parent = frappe.qb.DocType(doctype)
|
||||||
restriction = frappe.qb.DocType("Company Restriction")
|
restriction = frappe.qb.DocType("Company Restriction")
|
||||||
restriction_rows = (
|
allowed_rows = (
|
||||||
frappe.qb.from_(restriction)
|
frappe.qb.from_(restriction)
|
||||||
.select(restriction.name)
|
.select(restriction.name)
|
||||||
.where(
|
.where(
|
||||||
(restriction.parenttype == doctype)
|
(restriction.parenttype == doctype)
|
||||||
& (restriction.parentfield == "allowed_companies")
|
& (restriction.parentfield == "allowed_companies")
|
||||||
& (restriction.parent == parent.name)
|
& (restriction.parent == parent.name)
|
||||||
|
& (restriction.company.isin(allowed_companies))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
allowed_rows = restriction_rows.where(restriction.company.isin(allowed_companies))
|
return Bracket((parent.restrict_to_companies == 0) | ExistsCriterion(allowed_rows))
|
||||||
return Bracket(ExistsCriterion(allowed_rows) | ExistsCriterion(restriction_rows).negate())
|
|
||||||
|
|
||||||
|
|
||||||
def has_permission(doc, ptype=None, user=None):
|
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)
|
allowed_companies = get_allowed_companies(user, doc.doctype)
|
||||||
if not allowed_companies:
|
if not allowed_companies:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
companies = [row.company for row in doc.get("allowed_companies") or []]
|
return any(row.company in allowed_companies for row in doc.get("allowed_companies") or [])
|
||||||
if not companies:
|
|
||||||
return True
|
|
||||||
return any(company in allowed_companies for company in companies)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_allowed_companies(doc):
|
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:
|
if doc.flags.ignore_permissions:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ frappe.ui.form.on("Item", {
|
|||||||
erpnext.utils.confirm_negative_stock(frm);
|
erpnext.utils.confirm_negative_stock(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
restrict_to_companies(frm) {
|
||||||
|
if (!frm.doc.restrict_to_companies) {
|
||||||
|
frm.set_value("allowed_companies", []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setup: function (frm) {
|
setup: function (frm) {
|
||||||
frm.set_query("allowed_companies", () => ({
|
frm.set_query("allowed_companies", () => ({
|
||||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
"column_break_wugd",
|
"column_break_wugd",
|
||||||
"over_billing_allowance",
|
"over_billing_allowance",
|
||||||
"company_restrictions_section",
|
"company_restrictions_section",
|
||||||
|
"restrict_to_companies",
|
||||||
"allowed_companies",
|
"allowed_companies",
|
||||||
"section_break_11",
|
"section_break_11",
|
||||||
"brand",
|
"brand",
|
||||||
@@ -1089,16 +1090,22 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company_restrictions_section",
|
"fieldname": "company_restrictions_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Company Restrictions",
|
"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)"
|
{
|
||||||
|
"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",
|
"fieldname": "allowed_companies",
|
||||||
"fieldtype": "Table MultiSelect",
|
"fieldtype": "Table MultiSelect",
|
||||||
"label": "Allowed Companies",
|
"label": "Allowed Companies",
|
||||||
"options": "Company Restriction",
|
"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",
|
"icon": "fa fa-tag",
|
||||||
@@ -1106,7 +1113,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2026-07-14 21:00:00.000000",
|
"modified": "2026-07-14 23:00:00.000000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ class Item(Document):
|
|||||||
quality_inspection_template: DF.Link | None
|
quality_inspection_template: DF.Link | None
|
||||||
reorder_levels: DF.Table[ItemReorder]
|
reorder_levels: DF.Table[ItemReorder]
|
||||||
retain_sample: DF.Check
|
retain_sample: DF.Check
|
||||||
|
restrict_to_companies: DF.Check
|
||||||
safety_stock: DF.Float
|
safety_stock: DF.Float
|
||||||
sales_tax_withholding_category: DF.Link | None
|
sales_tax_withholding_category: DF.Link | None
|
||||||
sales_uom: DF.Link | None
|
sales_uom: DF.Link | None
|
||||||
|
|||||||
Reference in New Issue
Block a user