Merge pull request #55744 from frappe/mergify/bp/version-16-hotfix/pr-55737

fix: allow specific methods to run (backport #55737)
This commit is contained in:
rohitwaghchaure
2026-06-09 09:27:46 +05:30
committed by GitHub
5 changed files with 69 additions and 5 deletions

View File

@@ -49,7 +49,7 @@ class ChequePrintTemplate(Document):
@frappe.whitelist()
def create_or_update_cheque_print_format(template_name):
frappe.only_for("System Manager")
if not frappe.db.exists("Print Format", template_name):
cheque_print = frappe.new_doc("Print Format")
cheque_print.update(

View File

@@ -577,6 +577,10 @@ frappe.ui.form.on("Job Card", {
const wrapper = $(frm.fields_dict["job_card_dashboard"].wrapper);
wrapper.empty();
if (frm.doc.docstatus !== 0) {
return;
}
const { doc } = frm;
const { time_logs, status } = doc;

View File

@@ -857,9 +857,6 @@ class JobCard(Document):
)
def validate_job_card(self):
if self.track_semi_finished_goods:
return
if self.work_order and frappe.get_cached_value("Work Order", self.work_order, "status") == "Stopped":
frappe.throw(
_("Transaction not allowed against stopped Work Order {0}").format(
@@ -1259,6 +1256,10 @@ class JobCard(Document):
@frappe.whitelist()
def pause_job(self, **kwargs):
frappe.has_permission("Job Card", "write", doc=self, throw=True)
self.validate_docstatus()
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
@@ -1267,6 +1268,10 @@ class JobCard(Document):
@frappe.whitelist()
def resume_job(self, **kwargs):
frappe.has_permission("Job Card", "write", doc=self, throw=True)
self.validate_docstatus()
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
@@ -1439,6 +1444,9 @@ class JobCard(Document):
@frappe.whitelist()
def start_timer(self, **kwargs):
frappe.has_permission("Job Card", "write", doc=self, throw=True)
self.validate_docstatus()
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
@@ -1453,6 +1461,22 @@ class JobCard(Document):
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
frappe.has_permission("Job Card", "write", doc=self, throw=True)
self.validate_docstatus()
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
self.validate_complete_job_card_qty(kwargs)
def validate_docstatus(self):
if self.docstatus == 2:
frappe.throw(_("Cancelled Job Card cannot be processed."))
if self.docstatus == 1:
frappe.throw(_("Submitted Job Card cannot be processed."))
def validate_complete_job_card_qty(self, kwargs):
if flt(kwargs.pending_qty) and flt(kwargs.pending_qty) < 0:
frappe.throw(_("Pending quantity cannot be negative."))

View File

@@ -9,11 +9,23 @@ from erpnext.manufacturing.doctype.workstation.workstation import (
NotInWorkingHoursError,
WorkstationHolidayError,
check_if_within_operating_hours,
update_job_card,
)
from erpnext.tests.utils import ERPNextTestSuite
class TestWorkstation(ERPNextTestSuite):
def test_update_job_card_rejects_disallowed_method(self):
# The whitelisted update_job_card endpoint must only run an allowlisted set of Job Card
# methods. An arbitrary method name must be rejected (PermissionError) before the document
# is even loaded, so this needs no Job Card to exist.
self.assertRaises(
frappe.PermissionError,
update_job_card,
"NON-EXISTENT-JOB-CARD",
"delete",
)
def test_validate_timings(self):
check_if_within_operating_hours(
"_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00"

View File

@@ -516,8 +516,33 @@ def get_color_map():
}
ALLOWED_JOB_CARD_METHODS = frozenset(
{
"start_timer",
"pause_job",
"resume_job",
"complete_job_card",
}
)
@frappe.whitelist()
def update_job_card(job_card: str, method: str, **kwargs):
if method not in ALLOWED_JOB_CARD_METHODS:
frappe.throw(
_("Method {0} is not allowed to be run on a Job Card.").format(bold(method)),
frappe.PermissionError,
title=_("Not Allowed"),
)
frappe.has_permission("Job Card", "read", throw=True)
doc = frappe.get_doc("Job Card", job_card)
# These methods mutate the Job Card, but frappe.get_doc does not enforce permissions —
# require write access before running anything.
frappe.has_permission("Job Card", "write", doc=doc, throw=True)
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
@@ -527,7 +552,6 @@ def update_job_card(job_card: str, method: str, **kwargs):
if kwargs.qty and isinstance(kwargs.qty, str):
kwargs.qty = flt(kwargs.qty)
doc = frappe.get_doc("Job Card", job_card)
doc.run_method(method, **kwargs)