mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-13 17:20:36 +00:00
Merge pull request #57352 from mihir-kandoi/company-restriction-transaction-enforcement
fix: enforce company restrictions at transaction level
This commit is contained in:
@@ -20,7 +20,6 @@ from erpnext.controllers.website_list_for_contact import (
|
|||||||
add_role_for_portal_user,
|
add_role_for_portal_user,
|
||||||
link_portal_users_to_contacts,
|
link_portal_users_to_contacts,
|
||||||
)
|
)
|
||||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
|
|
||||||
@@ -154,7 +153,6 @@ class Supplier(TransactionBase):
|
|||||||
self.validate_internal_supplier()
|
self.validate_internal_supplier()
|
||||||
self.add_role_for_user()
|
self.add_role_for_user()
|
||||||
self.validate_currency_for_receivable_payable_and_advance_account()
|
self.validate_currency_for_receivable_payable_and_advance_account()
|
||||||
validate_allowed_companies(self)
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_supplier_group_details(self):
|
def get_supplier_group_details(self):
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from pypika import Order
|
|||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
from erpnext.accounts.utils import build_qb_match_conditions
|
from erpnext.accounts.utils import build_qb_match_conditions
|
||||||
|
from erpnext.stock.doctype.company_restriction.company_restriction import get_restriction_criterion
|
||||||
from erpnext.stock.get_item_details import _get_item_tax_template
|
from erpnext.stock.get_item_details import _get_item_tax_template
|
||||||
from erpnext.stock.utils import get_combine_datetime
|
from erpnext.stock.utils import get_combine_datetime
|
||||||
from erpnext.utilities.query import get_filter_conditions_qb
|
from erpnext.utilities.query import get_filter_conditions_qb
|
||||||
@@ -214,6 +215,7 @@ def item_query(
|
|||||||
doctype = "Item"
|
doctype = "Item"
|
||||||
|
|
||||||
filters = frappe.parse_json(filters)
|
filters = frappe.parse_json(filters)
|
||||||
|
company = filters.pop("company", None) if isinstance(filters, dict) else None
|
||||||
|
|
||||||
if filters and isinstance(filters, dict):
|
if filters and isinstance(filters, dict):
|
||||||
if filters.get("customer") or filters.get("supplier"):
|
if filters.get("customer") or filters.get("supplier"):
|
||||||
@@ -361,6 +363,9 @@ def item_query(
|
|||||||
.offset(start)
|
.offset(start)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if company:
|
||||||
|
query = query.where(get_restriction_criterion("Item", [company]))
|
||||||
|
|
||||||
return query.run(as_dict=as_dict)
|
return query.run(as_dict=as_dict)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ doc_events = {
|
|||||||
"validate": [
|
"validate": [
|
||||||
"erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
|
"erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
|
||||||
"erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.check_for_running_deletion_job",
|
"erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.check_for_running_deletion_job",
|
||||||
|
"erpnext.stock.doctype.company_restriction.company_restriction.validate_transaction_company",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
tuple(period_closing_doctypes): {
|
tuple(period_closing_doctypes): {
|
||||||
@@ -377,6 +378,9 @@ doc_events = {
|
|||||||
tuple(pre_submit_validation_doctypes): {
|
tuple(pre_submit_validation_doctypes): {
|
||||||
"validate": "erpnext.accounts.utils.pre_submit_validation",
|
"validate": "erpnext.accounts.utils.pre_submit_validation",
|
||||||
},
|
},
|
||||||
|
("Item", "Customer", "Supplier"): {
|
||||||
|
"validate": "erpnext.stock.doctype.company_restriction.company_restriction.validate_allowed_companies",
|
||||||
|
},
|
||||||
"Stock Entry": {
|
"Stock Entry": {
|
||||||
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
||||||
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ erpnext.buying = {
|
|||||||
|
|
||||||
this.frm.set_query("item_code", "items", function () {
|
this.frm.set_query("item_code", "items", function () {
|
||||||
if (me.frm.doc.is_subcontracted) {
|
if (me.frm.doc.is_subcontracted) {
|
||||||
var filters = { supplier: me.frm.doc.supplier };
|
var filters = { supplier: me.frm.doc.supplier, company: me.frm.doc.company };
|
||||||
filters["is_stock_item"] = 0;
|
filters["is_stock_item"] = 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -101,7 +101,12 @@ erpnext.buying = {
|
|||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.item_query",
|
query: "erpnext.controllers.queries.item_query",
|
||||||
filters: { supplier: me.frm.doc.supplier, is_purchase_item: 1, has_variants: 0 },
|
filters: {
|
||||||
|
supplier: me.frm.doc.supplier,
|
||||||
|
is_purchase_item: 1,
|
||||||
|
has_variants: 0,
|
||||||
|
company: me.frm.doc.company,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -81,7 +81,12 @@ erpnext.sales_common = {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.item_query",
|
query: "erpnext.controllers.queries.item_query",
|
||||||
filters: { is_sales_item: 1, customer: customer, has_variants: 0 },
|
filters: {
|
||||||
|
is_sales_item: 1,
|
||||||
|
customer: customer,
|
||||||
|
has_variants: 0,
|
||||||
|
company: me.frm.doc.company,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ from erpnext.controllers.website_list_for_contact import (
|
|||||||
add_role_for_portal_user,
|
add_role_for_portal_user,
|
||||||
link_portal_users_to_contacts,
|
link_portal_users_to_contacts,
|
||||||
)
|
)
|
||||||
from erpnext.stock.doctype.company_restriction.company_restriction import validate_allowed_companies
|
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
from .mapper import (
|
from .mapper import (
|
||||||
@@ -193,7 +192,6 @@ class Customer(TransactionBase):
|
|||||||
self.validate_internal_customer()
|
self.validate_internal_customer()
|
||||||
self.add_role_for_user()
|
self.add_role_for_user()
|
||||||
self.validate_currency_for_receivable_payable_and_advance_account()
|
self.validate_currency_for_receivable_payable_and_advance_account()
|
||||||
validate_allowed_companies(self)
|
|
||||||
|
|
||||||
# set loyalty program tier
|
# set loyalty program tier
|
||||||
if not self.is_new() and (customer := self.get_doc_before_save()):
|
if not self.is_new() and (customer := self.get_doc_before_save()):
|
||||||
|
|||||||
@@ -1,11 +1,39 @@
|
|||||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import comma_and
|
||||||
from pypika.terms import Bracket, ExistsCriterion
|
from pypika.terms import Bracket, ExistsCriterion
|
||||||
|
|
||||||
|
RESTRICTABLE_MASTER_DOCTYPES = ("Item", "Customer", "Supplier")
|
||||||
|
|
||||||
|
COMPANY_RESTRICTION_EXEMPT_DOCTYPES = frozenset(
|
||||||
|
{
|
||||||
|
"Asset",
|
||||||
|
"Bank Transaction",
|
||||||
|
"Exchange Rate Revaluation",
|
||||||
|
"Landed Cost Voucher",
|
||||||
|
"POS Closing Entry",
|
||||||
|
"POS Invoice Merge Log",
|
||||||
|
"Payment Reconciliation",
|
||||||
|
"Process Payment Reconciliation",
|
||||||
|
"Repost Accounting Ledger",
|
||||||
|
"Repost Item Valuation",
|
||||||
|
"Repost Payment Ledger",
|
||||||
|
"Serial No",
|
||||||
|
"Serial and Batch Bundle",
|
||||||
|
"Unreconcile Payment",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyRestrictionError(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CompanyRestriction(Document):
|
class CompanyRestriction(Document):
|
||||||
# begin: auto-generated types
|
# begin: auto-generated types
|
||||||
@@ -40,6 +68,10 @@ def get_permission_query_conditions(user, doctype=None):
|
|||||||
if not allowed_companies:
|
if not allowed_companies:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
return get_restriction_criterion(doctype, allowed_companies)
|
||||||
|
|
||||||
|
|
||||||
|
def get_restriction_criterion(doctype, companies):
|
||||||
parent = frappe.qb.DocType(doctype)
|
parent = frappe.qb.DocType(doctype)
|
||||||
restriction = frappe.qb.DocType("Company Restriction")
|
restriction = frappe.qb.DocType("Company Restriction")
|
||||||
allowed_rows = (
|
allowed_rows = (
|
||||||
@@ -49,7 +81,7 @@ def get_permission_query_conditions(user, doctype=None):
|
|||||||
(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))
|
& (restriction.company.isin(companies))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return Bracket((parent.restrict_to_companies == 0) | ExistsCriterion(allowed_rows))
|
return Bracket((parent.restrict_to_companies == 0) | ExistsCriterion(allowed_rows))
|
||||||
@@ -66,7 +98,7 @@ def has_permission(doc, ptype=None, user=None):
|
|||||||
return any(row.company in allowed_companies for row in doc.get("allowed_companies") or [])
|
return any(row.company in allowed_companies for row in doc.get("allowed_companies") or [])
|
||||||
|
|
||||||
|
|
||||||
def validate_allowed_companies(doc):
|
def validate_allowed_companies(doc, method=None):
|
||||||
if not doc.get("restrict_to_companies"):
|
if not doc.get("restrict_to_companies"):
|
||||||
doc.set("allowed_companies", [])
|
doc.set("allowed_companies", [])
|
||||||
elif not doc.get("allowed_companies") and not doc.flags.ignore_mandatory:
|
elif not doc.get("allowed_companies") and not doc.flags.ignore_mandatory:
|
||||||
@@ -95,6 +127,75 @@ def validate_allowed_companies(doc):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_transaction_company(doc, method=None):
|
||||||
|
if doc.doctype in COMPANY_RESTRICTION_EXEMPT_DOCTYPES or doc.meta.in_create:
|
||||||
|
return
|
||||||
|
|
||||||
|
company_field = doc.meta.get_field("company")
|
||||||
|
if not company_field or company_field.fieldtype != "Link" or company_field.options != "Company":
|
||||||
|
return
|
||||||
|
|
||||||
|
company = doc.get("company")
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
for doctype, names in get_master_references(doc).items():
|
||||||
|
if blocked := get_blocked_masters(doctype, names, company):
|
||||||
|
frappe.throw(
|
||||||
|
_("{0} {1} cannot be used with Company {2} because of Company Restrictions").format(
|
||||||
|
_(doctype),
|
||||||
|
comma_and([frappe.bold(name) for name in blocked], add_quotes=False),
|
||||||
|
frappe.bold(company),
|
||||||
|
),
|
||||||
|
CompanyRestrictionError,
|
||||||
|
title=_("Restricted to Other Companies"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_master_references(doc):
|
||||||
|
references = defaultdict(set)
|
||||||
|
collect_master_references(doc, references)
|
||||||
|
for table_field in doc.meta.get_table_fields():
|
||||||
|
for row in doc.get(table_field.fieldname) or []:
|
||||||
|
collect_master_references(row, references)
|
||||||
|
|
||||||
|
return references
|
||||||
|
|
||||||
|
|
||||||
|
def collect_master_references(row, references):
|
||||||
|
meta = frappe.get_meta(row.doctype)
|
||||||
|
for field in meta.get_link_fields():
|
||||||
|
if field.options in RESTRICTABLE_MASTER_DOCTYPES and (value := row.get(field.fieldname)):
|
||||||
|
references[field.options].add(value)
|
||||||
|
|
||||||
|
for field in meta.get_dynamic_link_fields():
|
||||||
|
doctype = row.get(field.options)
|
||||||
|
if doctype in RESTRICTABLE_MASTER_DOCTYPES and (value := row.get(field.fieldname)):
|
||||||
|
references[doctype].add(value)
|
||||||
|
|
||||||
|
|
||||||
|
def get_blocked_masters(doctype, names, company):
|
||||||
|
restricted = frappe.get_all(
|
||||||
|
doctype,
|
||||||
|
filters={"name": ("in", sorted(names)), "restrict_to_companies": 1},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
if not restricted:
|
||||||
|
return []
|
||||||
|
|
||||||
|
allowed = frappe.get_all(
|
||||||
|
"Company Restriction",
|
||||||
|
filters={
|
||||||
|
"parenttype": doctype,
|
||||||
|
"parentfield": "allowed_companies",
|
||||||
|
"parent": ("in", restricted),
|
||||||
|
"company": company,
|
||||||
|
},
|
||||||
|
pluck="parent",
|
||||||
|
)
|
||||||
|
return sorted(set(restricted) - set(allowed))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def company_query(
|
def company_query(
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||||
|
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||||
|
from erpnext.selling.doctype.customer.test_customer import make_customer
|
||||||
|
from erpnext.selling.doctype.quotation.test_quotation import make_quotation
|
||||||
|
from erpnext.stock.doctype.company_restriction.company_restriction import CompanyRestrictionError
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
|
||||||
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
|
|
||||||
|
class TestCompanyRestriction(ERPNextTestSuite):
|
||||||
|
def restrict_to_companies(self, doctype, name, companies):
|
||||||
|
doc = frappe.get_doc(doctype, name)
|
||||||
|
doc.restrict_to_companies = 1
|
||||||
|
doc.set("allowed_companies", [])
|
||||||
|
for company in companies:
|
||||||
|
doc.append("allowed_companies", {"company": company})
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
def test_restricted_item_blocks_transaction_in_other_company(self):
|
||||||
|
item = make_item()
|
||||||
|
self.restrict_to_companies("Item", item.name, ["_Test Company 1"])
|
||||||
|
|
||||||
|
self.assertRaises(CompanyRestrictionError, make_material_request, item_code=item.name)
|
||||||
|
|
||||||
|
self.restrict_to_companies("Item", item.name, ["_Test Company 1", "_Test Company"])
|
||||||
|
make_material_request(item_code=item.name)
|
||||||
|
|
||||||
|
def test_restricted_customer_blocks_transaction_in_other_company(self):
|
||||||
|
customer = make_customer("_Test Company Restricted Customer")
|
||||||
|
self.restrict_to_companies("Customer", customer, ["_Test Company 1"])
|
||||||
|
|
||||||
|
self.assertRaises(CompanyRestrictionError, make_quotation, party_name=customer, do_not_submit=1)
|
||||||
|
|
||||||
|
self.restrict_to_companies("Customer", customer, ["_Test Company"])
|
||||||
|
make_quotation(party_name=customer, do_not_submit=1)
|
||||||
|
|
||||||
|
def test_restricted_supplier_blocks_transaction_in_other_company(self):
|
||||||
|
supplier = create_supplier(supplier_name="_Test Company Restricted Supplier")
|
||||||
|
self.restrict_to_companies("Supplier", supplier.name, ["_Test Company 1"])
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
CompanyRestrictionError, create_purchase_order, supplier=supplier.name, do_not_submit=1
|
||||||
|
)
|
||||||
|
|
||||||
|
self.restrict_to_companies("Supplier", supplier.name, ["_Test Company"])
|
||||||
|
create_purchase_order(supplier=supplier.name, do_not_submit=1)
|
||||||
|
|
||||||
|
def test_unrestricted_item_is_not_blocked(self):
|
||||||
|
item = make_item()
|
||||||
|
make_material_request(item_code=item.name)
|
||||||
|
|
||||||
|
def test_allowed_companies_is_mandatory_when_restricted(self):
|
||||||
|
item = make_item()
|
||||||
|
item.restrict_to_companies = 1
|
||||||
|
self.assertRaises(frappe.MandatoryError, item.save)
|
||||||
|
|
||||||
|
def test_exempt_doctypes_exist(self):
|
||||||
|
from erpnext.stock.doctype.company_restriction.company_restriction import (
|
||||||
|
COMPANY_RESTRICTION_EXEMPT_DOCTYPES,
|
||||||
|
)
|
||||||
|
|
||||||
|
for doctype in COMPANY_RESTRICTION_EXEMPT_DOCTYPES:
|
||||||
|
self.assertTrue(frappe.db.exists("DocType", doctype), f"{doctype} is not a DocType")
|
||||||
|
|
||||||
|
def test_cancel_works_after_restriction_change(self):
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
|
||||||
|
item = make_item()
|
||||||
|
stock_entry = make_stock_entry(
|
||||||
|
item_code=item.name, qty=5, to_warehouse="_Test Warehouse - _TC", rate=100
|
||||||
|
)
|
||||||
|
|
||||||
|
self.restrict_to_companies("Item", item.name, ["_Test Company 1"])
|
||||||
|
stock_entry.reload()
|
||||||
|
stock_entry.cancel()
|
||||||
@@ -30,7 +30,6 @@ from erpnext.controllers.item_variant import (
|
|||||||
make_variant_item_code,
|
make_variant_item_code,
|
||||||
validate_item_variant_attributes,
|
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.doctype.item_default.item_default import ItemDefault
|
||||||
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
|
||||||
from erpnext.stock.utils import get_valuation_method
|
from erpnext.stock.utils import get_valuation_method
|
||||||
@@ -246,7 +245,6 @@ class Item(Document):
|
|||||||
self.validate_serialized_change_with_bundle()
|
self.validate_serialized_change_with_bundle()
|
||||||
self.validate_standard_cost_change()
|
self.validate_standard_cost_change()
|
||||||
self.validate_item_tax_net_rate_range()
|
self.validate_item_tax_net_rate_range()
|
||||||
validate_allowed_companies(self)
|
|
||||||
|
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ frappe.ui.form.on("Material Request", {
|
|||||||
return doc.stock_qty <= doc.ordered_qty ? "green" : "orange";
|
return doc.stock_qty <= doc.ordered_qty ? "green" : "orange";
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("item_code", "items", function () {
|
frm.set_query("item_code", "items", function (doc) {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.item_query",
|
query: "erpnext.controllers.queries.item_query",
|
||||||
|
filters: { company: doc.company },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -604,7 +605,7 @@ erpnext.buying.MaterialRequestController = class MaterialRequestController exten
|
|||||||
|
|
||||||
onload() {
|
onload() {
|
||||||
this.frm.set_query("item_code", "items", function (doc, cdt, cdn) {
|
this.frm.set_query("item_code", "items", function (doc, cdt, cdn) {
|
||||||
let filters = { is_stock_item: 1 };
|
let filters = { is_stock_item: 1, company: doc.company };
|
||||||
|
|
||||||
if (doc.material_request_type == "Customer Provided") {
|
if (doc.material_request_type == "Customer Provided") {
|
||||||
filters.customer = doc.customer;
|
filters.customer = doc.customer;
|
||||||
|
|||||||
@@ -1238,7 +1238,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.frm.fields_dict.items.grid.get_field("item_code").get_query = function () {
|
this.frm.fields_dict.items.grid.get_field("item_code").get_query = function () {
|
||||||
return erpnext.queries.item({ is_stock_item: 1 });
|
return erpnext.queries.item({ is_stock_item: 1, company: me.frm.doc.company });
|
||||||
};
|
};
|
||||||
|
|
||||||
this.frm.set_query("subcontracting_order", function () {
|
this.frm.set_query("subcontracting_order", function () {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ frappe.ui.form.on("Stock Reconciliation", {
|
|||||||
query: "erpnext.controllers.queries.item_query",
|
query: "erpnext.controllers.queries.item_query",
|
||||||
filters: {
|
filters: {
|
||||||
is_stock_item: 1,
|
is_stock_item: 1,
|
||||||
|
company: doc.company,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user