mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-18 08:58:43 +00:00
fix: allow specific methods to run
(cherry picked from commit 8db1eb0d27)
# Conflicts:
# erpnext/manufacturing/doctype/job_card/job_card.py
This commit is contained in:
committed by
Mergify
parent
c5434e39d8
commit
c9a5b0026e
@@ -577,6 +577,10 @@ frappe.ui.form.on("Job Card", {
|
|||||||
const wrapper = $(frm.fields_dict["job_card_dashboard"].wrapper);
|
const wrapper = $(frm.fields_dict["job_card_dashboard"].wrapper);
|
||||||
wrapper.empty();
|
wrapper.empty();
|
||||||
|
|
||||||
|
if (frm.doc.docstatus !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { doc } = frm;
|
const { doc } = frm;
|
||||||
const { time_logs, status } = doc;
|
const { time_logs, status } = doc;
|
||||||
|
|
||||||
|
|||||||
@@ -857,9 +857,6 @@ class JobCard(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_job_card(self):
|
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":
|
if self.work_order and frappe.get_cached_value("Work Order", self.work_order, "status") == "Stopped":
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Transaction not allowed against stopped Work Order {0}").format(
|
_("Transaction not allowed against stopped Work Order {0}").format(
|
||||||
@@ -1259,6 +1256,13 @@ class JobCard(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def pause_job(self, **kwargs):
|
def pause_job(self, **kwargs):
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
frappe.has_permission("Job Card", "write", doc=self, throw=True)
|
||||||
|
|
||||||
|
self.validate_docstatus()
|
||||||
|
|
||||||
|
>>>>>>> 8db1eb0d27 (fix: allow specific methods to run)
|
||||||
if isinstance(kwargs, dict):
|
if isinstance(kwargs, dict):
|
||||||
kwargs = frappe._dict(kwargs)
|
kwargs = frappe._dict(kwargs)
|
||||||
|
|
||||||
@@ -1267,6 +1271,13 @@ class JobCard(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def resume_job(self, **kwargs):
|
def resume_job(self, **kwargs):
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
frappe.has_permission("Job Card", "write", doc=self, throw=True)
|
||||||
|
|
||||||
|
self.validate_docstatus()
|
||||||
|
|
||||||
|
>>>>>>> 8db1eb0d27 (fix: allow specific methods to run)
|
||||||
if isinstance(kwargs, dict):
|
if isinstance(kwargs, dict):
|
||||||
kwargs = frappe._dict(kwargs)
|
kwargs = frappe._dict(kwargs)
|
||||||
|
|
||||||
@@ -1439,6 +1450,13 @@ class JobCard(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def start_timer(self, **kwargs):
|
def start_timer(self, **kwargs):
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
frappe.has_permission("Job Card", "write", doc=self, throw=True)
|
||||||
|
|
||||||
|
self.validate_docstatus()
|
||||||
|
|
||||||
|
>>>>>>> 8db1eb0d27 (fix: allow specific methods to run)
|
||||||
if isinstance(kwargs, dict):
|
if isinstance(kwargs, dict):
|
||||||
kwargs = frappe._dict(kwargs)
|
kwargs = frappe._dict(kwargs)
|
||||||
|
|
||||||
@@ -1450,9 +1468,37 @@ class JobCard(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def complete_job_card(self, **kwargs):
|
def complete_job_card(self, **kwargs):
|
||||||
|
<<<<<<< HEAD
|
||||||
if isinstance(kwargs, dict):
|
if isinstance(kwargs, dict):
|
||||||
kwargs = frappe._dict(kwargs)
|
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)
|
||||||
|
|
||||||
|
self.pending_qty = flt(kwargs.pending_qty)
|
||||||
|
self.process_loss_qty = flt(kwargs.process_loss_qty)
|
||||||
|
|
||||||
|
self.add_completion_time_logs(kwargs)
|
||||||
|
|
||||||
|
if kwargs.auto_submit:
|
||||||
|
self.auto_submit_job_card(kwargs.auto_submit)
|
||||||
|
|
||||||
|
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):
|
||||||
|
>>>>>>> 8db1eb0d27 (fix: allow specific methods to run)
|
||||||
if flt(kwargs.pending_qty) and flt(kwargs.pending_qty) < 0:
|
if flt(kwargs.pending_qty) and flt(kwargs.pending_qty) < 0:
|
||||||
frappe.throw(_("Pending quantity cannot be negative."))
|
frappe.throw(_("Pending quantity cannot be negative."))
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,23 @@ from erpnext.manufacturing.doctype.workstation.workstation import (
|
|||||||
NotInWorkingHoursError,
|
NotInWorkingHoursError,
|
||||||
WorkstationHolidayError,
|
WorkstationHolidayError,
|
||||||
check_if_within_operating_hours,
|
check_if_within_operating_hours,
|
||||||
|
update_job_card,
|
||||||
)
|
)
|
||||||
from erpnext.tests.utils import ERPNextTestSuite
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
|
|
||||||
class TestWorkstation(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):
|
def test_validate_timings(self):
|
||||||
check_if_within_operating_hours(
|
check_if_within_operating_hours(
|
||||||
"_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00"
|
"_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00"
|
||||||
|
|||||||
@@ -516,8 +516,33 @@ def get_color_map():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ALLOWED_JOB_CARD_METHODS = frozenset(
|
||||||
|
{
|
||||||
|
"start_timer",
|
||||||
|
"pause_job",
|
||||||
|
"resume_job",
|
||||||
|
"complete_job_card",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_job_card(job_card: str, method: str, **kwargs):
|
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):
|
if isinstance(kwargs, dict):
|
||||||
kwargs = frappe._dict(kwargs)
|
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):
|
if kwargs.qty and isinstance(kwargs.qty, str):
|
||||||
kwargs.qty = flt(kwargs.qty)
|
kwargs.qty = flt(kwargs.qty)
|
||||||
|
|
||||||
doc = frappe.get_doc("Job Card", job_card)
|
|
||||||
doc.run_method(method, **kwargs)
|
doc.run_method(method, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user