mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-09 15:29:30 +00:00
* fix: check write permission in whitelisted document methods * test: permission coverage for production plan status roll-ups * fix: add type hints to whitelisted arguments and submit MR in test * test: clear request cache before reading non-completed production plans
This commit is contained in:
@@ -233,7 +233,9 @@ class Lead(SellingController, CRMNote):
|
|||||||
return frappe.db.get_value("Quotation", {"party_name": self.name, "docstatus": 1, "status": "Lost"})
|
return frappe.db.get_value("Quotation", {"party_name": self.name, "docstatus": 1, "status": "Lost"})
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_prospect_and_contact(self, data):
|
def create_prospect_and_contact(self, data: dict):
|
||||||
|
self.check_permission("write")
|
||||||
|
|
||||||
data = frappe._dict(data)
|
data = frappe._dict(data)
|
||||||
if data.create_contact:
|
if data.create_contact:
|
||||||
self.create_contact()
|
self.create_contact()
|
||||||
|
|||||||
@@ -636,7 +636,9 @@ class ProductionPlan(Document):
|
|||||||
frappe.delete_doc("Work Order", d.name)
|
frappe.delete_doc("Work Order", d.name)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_status(self, close=None, update_bin=False):
|
def set_status(self, close: bool | None = None, update_bin: bool = False):
|
||||||
|
self.check_permission("write")
|
||||||
|
|
||||||
self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus)
|
self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus)
|
||||||
|
|
||||||
if close:
|
if close:
|
||||||
|
|||||||
@@ -1405,6 +1405,7 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
# Plan submission cached this list before the Work Orders updated ordered quantities.
|
# Plan submission cached this list before the Work Orders updated ordered quantities.
|
||||||
frappe.local.request_cache.clear()
|
frappe.local.request_cache.clear()
|
||||||
non_completed_plans = get_non_completed_production_plans()
|
non_completed_plans = get_non_completed_production_plans()
|
||||||
|
|
||||||
for plan in plans:
|
for plan in plans:
|
||||||
self.assertNotIn(plan, non_completed_plans)
|
self.assertNotIn(plan, non_completed_plans)
|
||||||
|
|
||||||
@@ -2199,6 +2200,46 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
for row in plan.sub_assembly_items:
|
for row in plan.sub_assembly_items:
|
||||||
self.assertEqual(row.ordered_qty, 10.0)
|
self.assertEqual(row.ordered_qty, 10.0)
|
||||||
|
|
||||||
|
def test_set_status_requires_write_permission(self):
|
||||||
|
pln = create_production_plan(item_code="Test Production Item 1")
|
||||||
|
|
||||||
|
with self.set_user(create_user_without_production_plan_access()):
|
||||||
|
doc = frappe.get_doc("Production Plan", pln.name)
|
||||||
|
self.assertRaises(frappe.PermissionError, doc.set_status)
|
||||||
|
|
||||||
|
def test_work_order_status_rollup_without_production_plan_permission(self):
|
||||||
|
pln = create_production_plan(item_code="Test Production Item 1")
|
||||||
|
pln.make_work_order()
|
||||||
|
|
||||||
|
wo_name = frappe.db.get_value("Work Order", {"production_plan": pln.name}, "name")
|
||||||
|
frappe.db.set_value("Production Plan Item", pln.po_items[0].name, "ordered_qty", 99)
|
||||||
|
|
||||||
|
with self.set_user(create_user_without_production_plan_access()):
|
||||||
|
frappe.get_doc("Work Order", wo_name).update_ordered_qty()
|
||||||
|
|
||||||
|
pln.reload()
|
||||||
|
self.assertEqual(pln.po_items[0].ordered_qty, 0.0)
|
||||||
|
self.assertEqual(pln.status, "Submitted")
|
||||||
|
|
||||||
|
def test_material_request_status_rollup_without_production_plan_permission(self):
|
||||||
|
pln = create_production_plan(item_code="Test Production Item 1")
|
||||||
|
pln.make_material_request()
|
||||||
|
|
||||||
|
plan_item = pln.mr_items[0].name
|
||||||
|
mr_name = frappe.db.get_value(
|
||||||
|
"Material Request Item", {"material_request_plan_item": plan_item}, "parent"
|
||||||
|
)
|
||||||
|
frappe.get_doc("Material Request", mr_name).submit()
|
||||||
|
frappe.db.set_value("Material Request Plan Item", plan_item, "requested_qty", 0)
|
||||||
|
|
||||||
|
with self.set_user(create_user_without_production_plan_access()):
|
||||||
|
frappe.get_doc("Material Request", mr_name).update_requested_qty_in_production_plan()
|
||||||
|
|
||||||
|
pln.reload()
|
||||||
|
requested_qty = frappe.db.get_value("Material Request Plan Item", plan_item, "requested_qty")
|
||||||
|
self.assertGreater(requested_qty, 0)
|
||||||
|
self.assertEqual(pln.status, "Material Requested")
|
||||||
|
|
||||||
|
|
||||||
def create_production_plan(**args):
|
def create_production_plan(**args):
|
||||||
"""
|
"""
|
||||||
@@ -2304,3 +2345,19 @@ def make_bom(**args):
|
|||||||
bom.submit()
|
bom.submit()
|
||||||
|
|
||||||
return bom
|
return bom
|
||||||
|
|
||||||
|
|
||||||
|
def create_user_without_production_plan_access():
|
||||||
|
user = "test_production_plan_no_access@example.com"
|
||||||
|
if not frappe.db.exists("User", user):
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "User",
|
||||||
|
"email": user,
|
||||||
|
"first_name": "Production Plan No Access",
|
||||||
|
"send_welcome_email": 0,
|
||||||
|
"roles": [{"doctype": "Has Role", "role": "Stock User"}],
|
||||||
|
}
|
||||||
|
).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
return user
|
||||||
|
|||||||
@@ -558,6 +558,7 @@ class WorkOrder(Document):
|
|||||||
|
|
||||||
def update_production_plan_status(self):
|
def update_production_plan_status(self):
|
||||||
production_plan = frappe.get_doc("Production Plan", self.production_plan)
|
production_plan = frappe.get_doc("Production Plan", self.production_plan)
|
||||||
|
production_plan.flags.ignore_permissions = True
|
||||||
produced_qty = 0
|
produced_qty = 0
|
||||||
if self.production_plan_item:
|
if self.production_plan_item:
|
||||||
total_qty = frappe.get_all(
|
total_qty = frappe.get_all(
|
||||||
@@ -900,6 +901,7 @@ class WorkOrder(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
doc = frappe.get_doc("Production Plan", self.production_plan)
|
doc = frappe.get_doc("Production Plan", self.production_plan)
|
||||||
|
doc.flags.ignore_permissions = True
|
||||||
doc.set_status()
|
doc.set_status()
|
||||||
doc.db_set("status", doc.status)
|
doc.db_set("status", doc.status)
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ class ImportSupplierInvoice(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def process_file_data(self):
|
def process_file_data(self):
|
||||||
|
self.check_permission("write")
|
||||||
|
|
||||||
self.db_set("status", "Processing File Data", notify=True, commit=True)
|
self.db_set("status", "Processing File Data", notify=True, commit=True)
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "import_xml_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "import_xml_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,8 @@ class Batch(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def recalculate_batch_qty(self):
|
def recalculate_batch_qty(self):
|
||||||
|
self.check_permission("write")
|
||||||
|
|
||||||
batches = get_batch_qty(
|
batches = get_batch_qty(
|
||||||
batch_no=self.name,
|
batch_no=self.name,
|
||||||
item_code=self.item,
|
item_code=self.item,
|
||||||
|
|||||||
@@ -428,6 +428,7 @@ class MaterialRequest(BuyingController):
|
|||||||
|
|
||||||
for production_plan in production_plans:
|
for production_plan in production_plans:
|
||||||
doc = frappe.get_doc("Production Plan", production_plan)
|
doc = frappe.get_doc("Production Plan", production_plan)
|
||||||
|
doc.flags.ignore_permissions = True
|
||||||
doc.set_status()
|
doc.set_status()
|
||||||
doc.db_set("status", doc.status)
|
doc.db_set("status", doc.status)
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,8 @@ class RepostItemValuation(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def restart_reposting(self):
|
def restart_reposting(self):
|
||||||
|
self.check_permission("write")
|
||||||
|
|
||||||
self.set_status("Queued", write=False)
|
self.set_status("Queued", write=False)
|
||||||
self.current_index = 0
|
self.current_index = 0
|
||||||
self.distinct_item_and_warehouse = None
|
self.distinct_item_and_warehouse = None
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ class StockRepostingSettings(Document):
|
|||||||
def convert_to_item_wh_reposting(self):
|
def convert_to_item_wh_reposting(self):
|
||||||
"""Convert Transaction reposting to Item Warehouse based reposting if Item Based Reposting has enabled."""
|
"""Convert Transaction reposting to Item Warehouse based reposting if Item Based Reposting has enabled."""
|
||||||
|
|
||||||
|
self.check_permission("write")
|
||||||
|
|
||||||
reposting_data = get_reposting_entries()
|
reposting_data = get_reposting_entries()
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in reposting_data]
|
vouchers = [d.voucher_no for d in reposting_data]
|
||||||
|
|||||||
Reference in New Issue
Block a user