mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-14 23:18:40 +00:00
feat: company-wise restriction for Item, Customer and Supplier masters (#57124)
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
|
||||
frappe.ui.form.on("Supplier", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("allowed_companies", () => ({
|
||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||
}));
|
||||
frm.set_query("default_price_list", { buying: 1 });
|
||||
if (frm.doc.__islocal == 1) {
|
||||
frm.set_value("represents_company", "");
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
"tax_withholding_category",
|
||||
"tax_withholding_group",
|
||||
"settings_tab",
|
||||
"company_restrictions_section",
|
||||
"allowed_companies",
|
||||
"invoice_settings_section",
|
||||
"is_transporter",
|
||||
"allow_purchase_invoice_creation_without_purchase_order",
|
||||
@@ -425,6 +427,20 @@
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Settings"
|
||||
},
|
||||
{
|
||||
"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)"
|
||||
},
|
||||
{
|
||||
"fieldname": "allowed_companies",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Allowed Companies",
|
||||
"options": "Company Restriction",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"fieldname": "contact_and_address_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
@@ -562,7 +578,7 @@
|
||||
"link_fieldname": "party"
|
||||
}
|
||||
],
|
||||
"modified": "2026-06-27 16:12:33.190257",
|
||||
"modified": "2026-07-14 21:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier",
|
||||
|
||||
@@ -17,6 +17,7 @@ from erpnext.accounts.party import (
|
||||
validate_party_currency_before_merging,
|
||||
)
|
||||
from erpnext.controllers.website_list_for_contact import add_role_for_portal_user
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
|
||||
@@ -36,12 +37,14 @@ class Supplier(TransactionBase):
|
||||
from erpnext.buying.doctype.customer_number_at_supplier.customer_number_at_supplier import (
|
||||
CustomerNumberAtSupplier,
|
||||
)
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestriction
|
||||
from erpnext.utilities.doctype.portal_user.portal_user import PortalUser
|
||||
|
||||
accounts: DF.Table[PartyAccount]
|
||||
alias: DF.Data | None
|
||||
allow_purchase_invoice_creation_without_purchase_order: DF.Check
|
||||
allow_purchase_invoice_creation_without_purchase_receipt: DF.Check
|
||||
allowed_companies: DF.TableMultiSelect[CompanyRestriction]
|
||||
companies: DF.Table[AllowedToTransactWith]
|
||||
country: DF.Link | None
|
||||
customer_numbers: DF.Table[CustomerNumberAtSupplier]
|
||||
@@ -146,6 +149,7 @@ class Supplier(TransactionBase):
|
||||
self.validate_internal_supplier()
|
||||
self.add_role_for_user()
|
||||
self.validate_currency_for_receivable_payable_and_advance_account()
|
||||
validate_allowed_companies(self)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_supplier_group_details(self):
|
||||
|
||||
@@ -307,6 +307,18 @@ sounds = [
|
||||
|
||||
has_upload_permission = {"Employee": "erpnext.setup.doctype.employee.employee.has_upload_permission"}
|
||||
|
||||
permission_query_conditions = {
|
||||
"Item": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Customer": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
"Supplier": "erpnext.stock.doctype.company_restriction.company_restriction.get_permission_query_conditions",
|
||||
}
|
||||
|
||||
has_permission = {
|
||||
"Item": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Customer": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
"Supplier": "erpnext.stock.doctype.company_restriction.company_restriction.has_permission",
|
||||
}
|
||||
|
||||
has_website_permission = {
|
||||
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
frappe.ui.form.on("Customer", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("allowed_companies", () => ({
|
||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||
}));
|
||||
frm.custom_make_buttons = {
|
||||
Opportunity: "Opportunity",
|
||||
Quotation: "Quotation",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-06-11 14:26:44",
|
||||
"creation": "2026-07-14 12:46:50.256889",
|
||||
"description": "Buyer of Goods and Services.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
@@ -65,6 +65,9 @@
|
||||
"tax_withholding_group",
|
||||
"tax_withholding_category",
|
||||
"settings_tab",
|
||||
"company_restrictions_section",
|
||||
"allowed_companies",
|
||||
"section_break_ario",
|
||||
"so_required",
|
||||
"dn_required",
|
||||
"column_break_53",
|
||||
@@ -512,6 +515,20 @@
|
||||
"fieldtype": "Tab Break",
|
||||
"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)"
|
||||
},
|
||||
{
|
||||
"fieldname": "allowed_companies",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Allowed Companies",
|
||||
"options": "Company Restriction",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "default_sales_partner",
|
||||
@@ -683,6 +700,10 @@
|
||||
"label": "Alias",
|
||||
"no_copy": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_ario",
|
||||
"fieldtype": "Section Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-user",
|
||||
@@ -696,7 +717,7 @@
|
||||
"link_fieldname": "party"
|
||||
}
|
||||
],
|
||||
"modified": "2026-06-27 16:12:10.457900",
|
||||
"modified": "2026-07-14 21:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Customer",
|
||||
|
||||
@@ -25,6 +25,7 @@ from erpnext.accounts.party import (
|
||||
validate_party_currency_before_merging,
|
||||
)
|
||||
from erpnext.controllers.website_list_for_contact import add_role_for_portal_user
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
from .mapper import (
|
||||
@@ -51,11 +52,13 @@ class Customer(TransactionBase):
|
||||
from erpnext.selling.doctype.supplier_number_at_customer.supplier_number_at_customer import (
|
||||
SupplierNumberAtCustomer,
|
||||
)
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestriction
|
||||
from erpnext.utilities.doctype.portal_user.portal_user import PortalUser
|
||||
|
||||
account_manager: DF.Link | None
|
||||
accounts: DF.Table[PartyAccount]
|
||||
alias: DF.Data | None
|
||||
allowed_companies: DF.TableMultiSelect[CompanyRestriction]
|
||||
companies: DF.Table[AllowedToTransactWith]
|
||||
credit_limits: DF.Table[CustomerCreditLimit]
|
||||
customer_details: DF.Text | None
|
||||
@@ -186,6 +189,7 @@ class Customer(TransactionBase):
|
||||
self.validate_internal_customer()
|
||||
self.add_role_for_user()
|
||||
self.validate_currency_for_receivable_payable_and_advance_account()
|
||||
validate_allowed_companies(self)
|
||||
|
||||
# set loyalty program tier
|
||||
if not self.is_new() and (customer := self.get_doc_before_save()):
|
||||
|
||||
@@ -5,16 +5,20 @@
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"defaults_section",
|
||||
"default_company",
|
||||
"country",
|
||||
"default_distance_unit",
|
||||
"column_break_8",
|
||||
"default_currency",
|
||||
"default_distance_unit",
|
||||
"demo_company",
|
||||
"general_settings_section",
|
||||
"hide_currency_symbol",
|
||||
"disable_rounded_total",
|
||||
"disable_in_words",
|
||||
"column_break_hnew",
|
||||
"use_posting_datetime_for_naming_documents",
|
||||
"demo_company"
|
||||
"enable_company_wise_masters"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -27,7 +31,7 @@
|
||||
{
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Link",
|
||||
"label": "Country",
|
||||
"label": "Default Country",
|
||||
"options": "Country"
|
||||
},
|
||||
{
|
||||
@@ -88,6 +92,27 @@
|
||||
"fieldname": "use_posting_datetime_for_naming_documents",
|
||||
"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",
|
||||
"label": "Defaults"
|
||||
},
|
||||
{
|
||||
"fieldname": "general_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "General Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_hnew",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
@@ -96,7 +121,7 @@
|
||||
"in_create": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2026-07-14 13:37:46.177444",
|
||||
"modified": "2026-07-14 15:18:25.829886",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Global Defaults",
|
||||
|
||||
@@ -18,6 +18,7 @@ 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 = (
|
||||
@@ -51,6 +52,7 @@ 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.Literal["", "No", "Yes"]
|
||||
use_posting_datetime_for_naming_documents: DF.Check
|
||||
# end: auto-generated types
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_bulk_edit": 1,
|
||||
"allow_rename": 1,
|
||||
"creation": "2026-07-13 21:39:49.805859",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"company"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-07-14 00:15:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Company Restriction",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"rows_threshold_for_grid_search": 20,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
116
erpnext/stock/doctype/company_restriction/company_restriction.py
Normal file
116
erpnext/stock/doctype/company_restriction/company_restriction.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from pypika.terms import Bracket, ExistsCriterion
|
||||
|
||||
|
||||
class CompanyRestriction(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
company: DF.Link
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
# end: auto-generated types
|
||||
|
||||
|
||||
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
|
||||
return get_allowed_docs_for_doctype(user_permissions["Company"], doctype) or None
|
||||
|
||||
|
||||
def get_permission_query_conditions(user, doctype=None):
|
||||
if not doctype:
|
||||
return None
|
||||
|
||||
allowed_companies = get_allowed_companies(user, doctype)
|
||||
if not allowed_companies:
|
||||
return None
|
||||
|
||||
parent = frappe.qb.DocType(doctype)
|
||||
restriction = frappe.qb.DocType("Company Restriction")
|
||||
restriction_rows = (
|
||||
frappe.qb.from_(restriction)
|
||||
.select(restriction.name)
|
||||
.where(
|
||||
(restriction.parenttype == doctype)
|
||||
& (restriction.parentfield == "allowed_companies")
|
||||
& (restriction.parent == parent.name)
|
||||
)
|
||||
)
|
||||
allowed_rows = restriction_rows.where(restriction.company.isin(allowed_companies))
|
||||
return Bracket(ExistsCriterion(allowed_rows) | ExistsCriterion(restriction_rows).negate())
|
||||
|
||||
|
||||
def has_permission(doc, ptype=None, user=None):
|
||||
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)
|
||||
|
||||
|
||||
def validate_allowed_companies(doc):
|
||||
if doc.flags.ignore_permissions:
|
||||
return
|
||||
|
||||
allowed_companies = get_allowed_companies(frappe.session.user, doc.doctype)
|
||||
if not allowed_companies:
|
||||
return
|
||||
|
||||
previous_companies = set()
|
||||
if previous_doc := doc.get_doc_before_save():
|
||||
previous_companies = {row.company for row in previous_doc.get("allowed_companies") or []}
|
||||
|
||||
current_companies = {row.company for row in doc.get("allowed_companies") or []}
|
||||
for company in current_companies.symmetric_difference(previous_companies):
|
||||
if company not in allowed_companies:
|
||||
frappe.throw(
|
||||
_("You are not permitted to add or remove Company {0} in Allowed Companies").format(company),
|
||||
frappe.PermissionError,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def company_query(
|
||||
doctype: str,
|
||||
txt: str,
|
||||
searchfield: str,
|
||||
start: int,
|
||||
page_len: int,
|
||||
filters: dict | str | None = None,
|
||||
):
|
||||
filters = frappe.parse_json(filters) if filters else {}
|
||||
if isinstance(filters, list):
|
||||
filters.append(["Company", "name", "like", f"%{txt}%"])
|
||||
else:
|
||||
filters["name"] = ("like", f"%{txt}%")
|
||||
|
||||
return frappe.get_list(
|
||||
"Company",
|
||||
filters=filters,
|
||||
limit_start=start,
|
||||
limit_page_length=page_len,
|
||||
order_by="name",
|
||||
as_list=True,
|
||||
)
|
||||
@@ -59,6 +59,9 @@ frappe.ui.form.on("Item", {
|
||||
},
|
||||
|
||||
setup: function (frm) {
|
||||
frm.set_query("allowed_companies", () => ({
|
||||
query: "erpnext.stock.doctype.company_restriction.company_restriction.company_query",
|
||||
}));
|
||||
frm.add_fetch("attribute", "numeric_values", "numeric_values");
|
||||
frm.add_fetch("attribute", "from_range", "from_range");
|
||||
frm.add_fetch("attribute", "to_range", "to_range");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:item_code",
|
||||
"creation": "2026-02-02 14:41:23.105228",
|
||||
"creation": "2026-07-13 23:00:47.512490",
|
||||
"description": "A Product or a Service that is bought, sold or kept in stock.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
@@ -40,6 +40,8 @@
|
||||
"over_delivery_receipt_allowance",
|
||||
"column_break_wugd",
|
||||
"over_billing_allowance",
|
||||
"company_restrictions_section",
|
||||
"allowed_companies",
|
||||
"section_break_11",
|
||||
"brand",
|
||||
"description",
|
||||
@@ -240,7 +242,6 @@
|
||||
"description": "ERPNext will make a stock ledger entry for each transaction of this item. Keep unchecked for non-stock or service items.",
|
||||
"fieldname": "is_stock_item",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 0,
|
||||
"label": "Maintain Stock",
|
||||
"oldfieldname": "is_stock_item",
|
||||
"oldfieldtype": "Select",
|
||||
@@ -281,9 +282,9 @@
|
||||
"description": "Enable if this item is a company asset like machinery or furniture.",
|
||||
"fieldname": "is_fixed_asset",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Is Fixed Asset",
|
||||
"read_only_depends_on": "eval:doc.is_stock_item",
|
||||
"in_list_view": 1
|
||||
"read_only_depends_on": "eval:doc.is_stock_item"
|
||||
},
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
@@ -596,7 +597,7 @@
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"description": "Minimum stock level to maintain as a buffer. Used to calculate recommended reorder level: Reorder Level = Safety Stock + (Average Daily Consumption × Lead Time).",
|
||||
"description": "Minimum stock level to maintain as a buffer. Used to calculate recommended reorder level: Reorder Level = Safety Stock + (Average Daily Consumption \u00d7 Lead Time).",
|
||||
"fieldname": "safety_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Safety Stock",
|
||||
@@ -699,9 +700,9 @@
|
||||
"description": "Allow this item to be used in sales transactions.",
|
||||
"fieldname": "is_sales_item",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Allow Sales",
|
||||
"show_description_on_click": 1,
|
||||
"in_list_view": 1
|
||||
"show_description_on_click": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break3",
|
||||
@@ -1084,6 +1085,20 @@
|
||||
"fieldname": "item_prices_column",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Item Prices"
|
||||
},
|
||||
{
|
||||
"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)"
|
||||
},
|
||||
{
|
||||
"fieldname": "allowed_companies",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Allowed Companies",
|
||||
"options": "Company Restriction",
|
||||
"depends_on": "eval:cint(frappe.sys_defaults.enable_company_wise_masters)"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-tag",
|
||||
@@ -1091,7 +1106,7 @@
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2026-07-05 23:24:45.734144",
|
||||
"modified": "2026-07-14 21:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
||||
@@ -30,6 +30,7 @@ from erpnext.controllers.item_variant import (
|
||||
make_variant_item_code,
|
||||
validate_item_variant_attributes,
|
||||
)
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
||||
from erpnext.stock.doctype.item_default.item_default import ItemDefault
|
||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
@@ -60,6 +61,7 @@ class Item(Document):
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestriction
|
||||
from erpnext.stock.doctype.item_barcode.item_barcode import ItemBarcode
|
||||
from erpnext.stock.doctype.item_customer_detail.item_customer_detail import ItemCustomerDetail
|
||||
from erpnext.stock.doctype.item_default.item_default import ItemDefault
|
||||
@@ -71,6 +73,7 @@ class Item(Document):
|
||||
|
||||
allow_alternative_item: DF.Check
|
||||
allow_negative_stock: DF.Check
|
||||
allowed_companies: DF.TableMultiSelect[CompanyRestriction]
|
||||
asset_category: DF.Link | None
|
||||
asset_naming_series: DF.Literal[None]
|
||||
attributes: DF.Table[ItemVariantAttribute]
|
||||
@@ -242,6 +245,7 @@ class Item(Document):
|
||||
self.validate_serialized_change_with_bundle()
|
||||
self.validate_standard_cost_change()
|
||||
self.validate_item_tax_net_rate_range()
|
||||
validate_allowed_companies(self)
|
||||
|
||||
if not self.is_new():
|
||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||
|
||||
Reference in New Issue
Block a user