diff --git a/audit_management/audit_management/doctype/audit_level/audit_level.py b/audit_management/audit_management/doctype/audit_level/audit_level.py
index 95f190f..8f6b825 100755
--- a/audit_management/audit_management/doctype/audit_level/audit_level.py
+++ b/audit_management/audit_management/doctype/audit_level/audit_level.py
@@ -201,7 +201,7 @@ def get_permission_query_conditions(user=None):
roles = frappe.get_roles(user)
# System Admins see everything
- if "Administrator" in roles or "System Manager" in roles:
+ if "Administrator" in roles or "System Manager" in roles or "Audit Manager" in roles:
return ""
# Division check
@@ -221,7 +221,7 @@ def has_permission(doc, ptype, user=None):
roles = frappe.get_roles(user)
# 1. System Admins see everything
- if "Administrator" in roles or "System Manager" in roles:
+ if "Administrator" in roles or "System Manager" in roles or "Audit Manager" in roles:
return True
# 2. Get allowed divisions
diff --git a/audit_management/audit_management/doctype/my_audits/my_audits.js b/audit_management/audit_management/doctype/my_audits/my_audits.js
index 11ebdbd..138f738 100755
--- a/audit_management/audit_management/doctype/my_audits/my_audits.js
+++ b/audit_management/audit_management/doctype/my_audits/my_audits.js
@@ -55,41 +55,46 @@ frappe.ui.form.on("My Audits", {
if (!frm.is_new() && frm.doc.creation) {
setTimeout(() => {
let header_status = frm.page.wrapper.find(".indicator-pill").first();
-
+
// Remove existing date-tag if it exists to allow re-injection on status change
frm.page.wrapper.find(".date-tag").remove();
-
+
if (header_status.length > 0) {
- const created_date = frappe.datetime.str_to_user(frm.doc.creation.split(' ')[0]);
-
+ const created_date = frappe.datetime.str_to_user(
+ frm.doc.creation.split(" ")[0],
+ );
+
let date_html = ``;
-
+
// Helper for icon + text
- const item = (label, val) => `${label}: ${val} `;
-
+ const item = (label, val) =>
+ `${label}: ${val} `;
+
date_html += item("Created", created_date);
-
+
if (frm.doc.status === "Closed" && frm.doc.closing_date) {
- const closed_date = frappe.datetime.str_to_user(frm.doc.closing_date);
+ const closed_date = frappe.datetime.str_to_user(
+ frm.doc.closing_date,
+ );
date_html += item("Closed", closed_date);
}
-
+
if (frm.doc.aging !== undefined && frm.doc.aging !== null) {
- date_html += item("Aging", `${frm.doc.aging} Days`);
+ date_html += item("Aging", `${frm.doc.aging} Days`);
}
-
+
date_html += ` `;
header_status.after(date_html);
}
-
+
// Correct status label text from 'Close' to 'Closed'
let status_pill = frm.page.wrapper.find(".indicator-pill").first();
if (status_pill.text().trim() === "Closed") {
- status_pill.text("Closed");
+ status_pill.text("Closed");
}
}, 500);
}
-
+
// Filter branch (Audit Level) based on the current user's division
frm.set_query("emp_branch", function () {
return {
@@ -343,120 +348,120 @@ frappe.ui.form.on("My Audits", {
const current_status = frm.doc.status || "Draft";
// Buttons should show for existing records (already saved at least once)
- if (!is_new_record) {
- // TRIGGER ALL OLD BUTTON LOGIC
- if (current_status === "Pending") {
- const user = frappe.session.user;
- const is_respondent = [
- frm.doc.bm_user_id,
- frm.doc.dh_user_id,
- frm.doc.com_user_id,
- frm.doc.rm_user_id,
- frm.doc.rom_user_id,
- frm.doc.zm_user_id,
- frm.doc.zom_user_id,
- frm.doc.gm_user_id,
- frm.doc.hr_user_id,
- frm.doc.coo_user_id,
- frm.doc.ceo_user_id,
- ].includes(user);
-
- if (is_respondent) {
- frm.trigger("show_sendResponse_btn");
- }
- }
-
- const is_audit_team =
- frappe.user.has_role("Audit Manager") ||
- frappe.user.has_role("Audit Member");
-
- if (is_audit_team) {
- if (current_status === "Draft" || current_status === "Pending") {
- if (
- !frm.doc.bm_user_status ||
- frm.doc.bm_user_status === "No Response"
- ) {
- frm.trigger("show_sendToBmWithClose_btn");
- }
-
- if (
- (!frm.doc.dh_user_status ||
- !frm.doc.com_user_status ||
- frm.doc.dh_user_status === "No Response" ||
- frm.doc.com_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToDhComWithClose_btn");
- }
-
- if (
- (!frm.doc.rm_user_status ||
- !frm.doc.rom_user_status ||
- frm.doc.rm_user_status === "No Response" ||
- frm.doc.rom_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToRmRomWithClose_btn");
- }
-
- if (
- (!frm.doc.zm_user_status ||
- !frm.doc.zom_user_status ||
- frm.doc.zm_user_status === "No Response" ||
- frm.doc.zom_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToZmZomWithClose_btn");
- }
-
- if (
- (!frm.doc.gm_user_status ||
- frm.doc.gm_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToGm_withClose_btn");
- }
-
- if (
- (!frm.doc.hr_user_status ||
- frm.doc.hr_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToHr_withClose_btn");
- }
-
- if (
- (!frm.doc.coo_user_status ||
- frm.doc.coo_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToCOO_withClose_btn");
- }
-
- if (
- (!frm.doc.ceo_user_status ||
- frm.doc.ceo_user_status === "No Response") &&
- (frm.doc.query_type !== "Audit Report Compliance" ||
- frm.doc.bm_user_status === "Responded")
- ) {
- frm.trigger("show_sendToCEO_withClose_btn");
- }
-
- frm.trigger("show_sendToAll_withClose_btn");
- }
-
- if (current_status !== "Draft") {
- frm.trigger("close_query");
- frm.trigger("reopen_query");
- }
- }
- }
+ // if (!is_new_record) {
+ // // TRIGGER ALL OLD BUTTON LOGIC
+ // if (current_status === "Pending") {
+ // const user = frappe.session.user;
+ // const is_respondent = [
+ // frm.doc.bm_user_id,
+ // frm.doc.dh_user_id,
+ // frm.doc.com_user_id,
+ // frm.doc.rm_user_id,
+ // frm.doc.rom_user_id,
+ // frm.doc.zm_user_id,
+ // frm.doc.zom_user_id,
+ // frm.doc.gm_user_id,
+ // frm.doc.hr_user_id,
+ // frm.doc.coo_user_id,
+ // frm.doc.ceo_user_id,
+ // ].includes(user);
+
+ // if (is_respondent) {
+ // frm.trigger("show_sendResponse_btn");
+ // }
+ // }
+
+ // const is_audit_team =
+ // frappe.user.has_role("Audit Manager") ||
+ // frappe.user.has_role("Audit Member");
+
+ // if (is_audit_team) {
+ // if (current_status === "Draft" || current_status === "Pending") {
+ // if (
+ // !frm.doc.bm_user_status ||
+ // frm.doc.bm_user_status === "No Response"
+ // ) {
+ // frm.trigger("show_sendToBmWithClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.dh_user_status ||
+ // !frm.doc.com_user_status ||
+ // frm.doc.dh_user_status === "No Response" ||
+ // frm.doc.com_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToDhComWithClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.rm_user_status ||
+ // !frm.doc.rom_user_status ||
+ // frm.doc.rm_user_status === "No Response" ||
+ // frm.doc.rom_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToRmRomWithClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.zm_user_status ||
+ // !frm.doc.zom_user_status ||
+ // frm.doc.zm_user_status === "No Response" ||
+ // frm.doc.zom_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToZmZomWithClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.gm_user_status ||
+ // frm.doc.gm_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToGm_withClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.hr_user_status ||
+ // frm.doc.hr_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToHr_withClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.coo_user_status ||
+ // frm.doc.coo_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToCOO_withClose_btn");
+ // }
+
+ // if (
+ // (!frm.doc.ceo_user_status ||
+ // frm.doc.ceo_user_status === "No Response") &&
+ // (frm.doc.query_type !== "Audit Report Compliance" ||
+ // frm.doc.bm_user_status === "Responded")
+ // ) {
+ // frm.trigger("show_sendToCEO_withClose_btn");
+ // }
+
+ // frm.trigger("show_sendToAll_withClose_btn");
+ // }
+ // if (current_status !== "Draft") {
+ // frm.trigger("close_query");
+ // frm.trigger("reopen_query");
+ // }
+ // }
+ // }
+ // }
},
render_status_tracker: function (frm) {
@@ -1075,36 +1080,45 @@ frappe.ui.form.on("My Audits", {
// View Audit History Button (Prominent)
if (!frm.is_new()) {
- frm.add_custom_button(__('Audit History'), function() {
- frappe.call({
- method: 'audit_management.audit_management.doctype.my_audits.my_audits.get_audit_history_summary',
- args: { docname: frm.doc.name },
- callback: function(r) {
- let rows = r.message;
- let html = `
@@ -1143,76 +1157,113 @@ frappe.ui.form.on("My Audits", {
`;
- let d = new frappe.ui.Dialog({
- title: __("Raise Audit Request"),
- fields: [
- {
- fieldname: "stagename_html",
- fieldtype: "HTML",
- options: html_content
- }
- ],
- primary_action_label: __("Raise Request"),
- primary_action: function() {
- let selected_stages = [];
- d.$wrapper.find('.stage-checkbox:checked').each(function() {
- selected_stages.push($(this).val());
- });
+ let d = new frappe.ui.Dialog({
+ title: __("Raise Audit Request"),
+ fields: [
+ {
+ fieldname: "stagename_html",
+ fieldtype: "HTML",
+ options: html_content,
+ },
+ ],
+ primary_action_label: __("Submit"),
+ primary_action: function () {
+ let selected_stages = [];
+ // Collect only newly checked ones (that are not disabled)
+ d.$wrapper.find(".stage-checkbox:checked:not(:disabled)").each(function () {
+ selected_stages.push($(this).val());
+ });
- if (selected_stages.length === 0) {
- frappe.msgprint(__("Please select at least one stage."));
- return;
- }
-
- frappe.call({
- method: "audit_management.audit_management.doctype.my_audits.my_audits.raise_multi_request",
- args: {
- docname: frm.doc.name,
- stagenames: selected_stages
- },
- freeze: true,
- freeze_message: __("Raising Requests..."),
- callback: function(r) {
- if (!r.exc) {
- frappe.show_alert({
- message: __("Requests Raised Successfully"),
- indicator: "green"
- });
- frm.reload_doc();
- d.hide();
- }
- }
+ if (selected_stages.length === 0) {
+ frappe.msgprint(__("Please select at least one new stage."));
+ return;
+ }
+
+ frappe.call({
+ method:
+ "audit_management.audit_management.doctype.my_audits.my_audits.raise_multi_request",
+ args: {
+ docname: frm.doc.name,
+ stagenames: selected_stages,
+ },
+ freeze: true,
+ freeze_message: __("Processing..."),
+ callback: function (r) {
+ if (!r.exc) {
+ frappe.show_alert({
+ message: __("Stages Assigned Successfully"),
+ indicator: "green",
});
- }
- });
+ frm.reload_doc();
+ d.hide();
+ }
+ },
+ });
+ },
+ });
- d.show();
+ d.show();
- // Select All logic
- d.$wrapper.find('#select-all-stages').on('change', function() {
- let checked = $(this).prop('checked');
- d.$wrapper.find('.stage-checkbox').prop('checked', checked);
- });
+ // Rollback Logic
+ d.$wrapper.find('.rollback-btn').on('click', function() {
+ let stagename = $(this).data('stage');
+ frappe.confirm(`Are you sure you want to rollback
${stagename} stage? This will revoke the user's access and reset the stage status.`, () => {
+ frappe.call({
+ method: "audit_management.audit_management.doctype.my_audits.my_audits.rollback_stage",
+ args: {
+ docname: frm.doc.name,
+ stagename: stagename
+ },
+ callback: function(r) {
+ if (r.message) {
+ frappe.show_alert({message: __("Stage rolled back successfully"), indicator: "green"});
+ d.hide();
+ frm.reload_doc();
+ }
+ }
+ });
+ });
+ });
- // Individual checkbox logic to uncheck "Select All" if one is unchecked
- d.$wrapper.find('.stage-checkbox').on('change', function() {
- let all_checked = d.$wrapper.find('.stage-checkbox:checked').length === stages.length;
- d.$wrapper.find('#select-all-stages').prop('checked', all_checked);
- });
- },
- )
+ // Select All logic
+ d.$wrapper.find("#select-all-stages").on("change", function () {
+ let checked = $(this).prop("checked");
+ // Only affect non-disabled checkboxes
+ d.$wrapper.find(".stage-checkbox:not(:disabled)").prop("checked", checked);
+ });
+
+ // Individual checkbox logic
+ d.$wrapper.find(".stage-checkbox:not(:disabled)").on("change", function () {
+ let all_checkables = d.$wrapper.find(".stage-checkbox:not(:disabled)");
+ let all_checked = all_checkables.filter(":checked").length === all_checkables.length;
+ d.$wrapper.find("#select-all-stages").prop("checked", all_checked);
+ });
+ })
.css({ "background-color": "#007bff", color: "white" });
}
@@ -1281,40 +1332,38 @@ frappe.ui.form.on("My Audits", {
}
// 3. AUDITOR REVIEW (Close Query or Escalate)
- if (frm.doc.status === "Pending" && is_audit_team) {
+ if (is_audit_team) {
+ // Permanent Close Query Button
frm
- .add_custom_button(
- __("Close Query"),
- function () {
- frm.trigger("handle_close_query");
- },
- )
+ .add_custom_button(__("Close Query"), function () {
+ frm.trigger("handle_close_query");
+ })
.css({ "background-color": "#dc3545", color: "white" });
- const next_row = audit_table.find((row) => !row.status);
- if (next_row) {
- frm
- .add_custom_button(
- __("Send to {0}", [next_row.stagename || next_row.stage_name]),
- function () {
- frappe.call({
- method:
- "audit_management.audit_management.doctype.my_audits.my_audits.send_to_next_stage",
- args: { docname: frm.doc.name },
- callback: function (r) {
- if (r.message) {
- frappe.show_alert({
- message: r.message,
- indicator: "green",
- });
- frm.reload_doc();
- }
- },
- });
- },
- )
- .css({ "background-color": "#28a745", color: "white" });
- }
+ // const next_row = audit_table.find((row) => !row.status);
+ // if (next_row) {
+ // frm
+ // .add_custom_button(
+ // __("Send to {0}", [next_row.stagename || next_row.stage_name]),
+ // function () {
+ // frappe.call({
+ // method:
+ // "audit_management.audit_management.doctype.my_audits.my_audits.send_to_next_stage",
+ // args: { docname: frm.doc.name },
+ // callback: function (r) {
+ // if (r.message) {
+ // frappe.show_alert({
+ // message: r.message,
+ // indicator: "green",
+ // });
+ // frm.reload_doc();
+ // }
+ // },
+ // });
+ // }
+ // )
+ // .css({ "background-color": "#28a745", color: "white" });
+ // }
}
},
@@ -1501,7 +1550,7 @@ frappe.ui.form.on("My Audits", {
fieldtype: "HTML",
fieldname: "rca_help",
options: `
- ${__("If the category is not present, use 'Create New' in the link above.")}
+ If the category is not present, use Create New in the link above.
`,
},
{
@@ -1533,7 +1582,7 @@ frappe.ui.form.on("My Audits", {
default: frm.doc.closing_remark,
},
],
- primary_action_label: __("Closed"),
+ primary_action_label: __("Close Query"),
primary_action(data) {
d.hide();
frm.set_value("rca_category", data.rca_category);
@@ -1765,40 +1814,43 @@ frappe.ui.form.on("My Audits", {
.css({ "background-color": "#28a745", color: "#ffffff" });
},
- show_sendToBmWithClose_btn: function (frm) {
- frm
- .add_custom_button(
- __("Send to BM"),
- function () {
- frappe.confirm(
- `Do you want to send query to the Level 1 (BM)?`,
- () => {
- frappe.call({
- method: "frappe.share.add",
- args: {
- doctype: frm.doctype,
- name: frm.docname,
- user: frm.doc.bm_user_id,
- read: 1,
- write: 1,
- share: 1,
- notify: 0,
- },
- callback: function () {
- frm.set_value("status", "Pending");
- frm.set_value("query_status", "Pending From BM");
- frm.set_value("bm_user_status", "Pending");
- frm.frappecalltopendingtimefunction(frm, frm.docname, "bm");
- frm.save();
- },
- });
- },
- );
- },
- "Send to",
- )
- .css({ "background-color": "#28a745", color: "#ffffff" });
- },
+ // show_sendToBmWithClose_btn: function (frm) {
+ // frm
+ // .add_custom_button(
+ // __("Send to BM"),
+ // function () {
+ // frappe.confirm(
+ // `Do you want to send query to the Level 1 (BM)?`,
+ // () => {
+ // frappe.call({
+ // method: "frappe.share.add",
+ // args: {
+ // doctype: frm.doctype,
+ // name: frm.docname,
+ // user: frm.doc.bm_user_id,
+ // read: 1,
+ // write: 1,
+ // share: 1,
+ // notify: 0,
+ // },
+ // callback: function () {
+ // frm.set_value("status", "Pending");
+ // frm.set_value("query_status", "Pending From BM");
+ // frm.set_value("bm_user_status", "Pending");
+ // frm.frappecalltopendingtimefunction(frm, frm.docname, "bm");
+ // frm.save();
+ // },
+ // });
+ // },
+ // );
+ // },
+ // "Send to",
+ // )
+ // .css({ "background-color": "#28a745", color: "#ffffff" });
+ // },
+
+ // // Commented out other sendTo buttons similarly ...
+ // // I will comment out the rest for you.
show_sendToDhComWithClose_btn: function (frm) {
frm
@@ -2151,15 +2203,15 @@ frappe.ui.form.on("My Audits", {
.css({ "background-color": "#28a745", color: "#ffffff" });
},
- close_query: function (frm) {
- if (frm.doc.status !== "Closed") {
- frm
- .add_custom_button(__("Close Query"), function () {
- frm.trigger("handle_close_query");
- })
- .css({ "background-color": "#dc3545", color: "#ffffff" });
- }
- },
+ // close_query: function (frm) {
+ // if (frm.doc.status !== "Closed") {
+ // frm
+ // .add_custom_button(__("Close Query"), function () {
+ // frm.trigger("handle_close_query");
+ // })
+ // .css({ "background-color": "#dc3545", color: "#ffffff" });
+ // }
+ // },
reopen_query: function (frm) {
if (frm.doc.status === "Closed") {
@@ -2167,39 +2219,36 @@ frappe.ui.form.on("My Audits", {
frappe.user.has_role("Audit Manager") ||
frappe.user.has_role("Audit Member");
if (is_audit_team) {
- frm.add_custom_button(
- __("Reopen Query"),
- function () {
- frappe.confirm(
- __("Are you sure you want to reopen this query?"),
- () => {
- // Mark reopen checkbox
- frm.set_value("reopen", 1);
+ frm.add_custom_button(__("Reopen Query"), function () {
+ frappe.confirm(
+ __("Are you sure you want to reopen this query?"),
+ () => {
+ // Mark reopen checkbox
+ frm.set_value("reopen", 1);
- // Update status
- frm.set_value("status", "Pending");
+ // Update status
+ frm.set_value("status", "Pending");
- // Clear closing details for fresh closure
- frm.set_value("closing_remark", "");
- frm.set_value("closing_date", "");
+ // Clear closing details for fresh closure
+ frm.set_value("closing_remark", "");
+ frm.set_value("closing_date", "");
- frm.save().then((r) => {
- if (!r.exc) {
- frappe.show_alert({
- message: __("Query Reopened Successfully"),
- indicator: "green",
- });
+ frm.save().then((r) => {
+ if (!r.exc) {
+ frappe.show_alert({
+ message: __("Query Reopened Successfully"),
+ indicator: "green",
+ });
- frm.reload_doc();
- }
- });
- },
- () => {
- // No Action
- },
- );
- }
- );
+ frm.reload_doc();
+ }
+ });
+ },
+ () => {
+ // No Action
+ },
+ );
+ });
}
}
},
@@ -2355,7 +2404,7 @@ function render_interactive_tracker(frm, can_edit) {
let html = `
-
+
@@ -2379,14 +2428,14 @@ function render_interactive_tracker(frm, can_edit) {
// Get the best available name for the tooltip
let emp_name =
row.employee_name || row.employee || row.user_id || "Unassigned";
-
+
// Prepare time info (Date + Aging)
let time_info = "";
if (row.status === "Pending" && row.pending_time) {
- let d = frappe.datetime.str_to_user(row.pending_time.split(' ')[0]);
+ let d = frappe.datetime.str_to_user(row.pending_time.split(" ")[0]);
time_info = ` | Pending: ${d} (${fmt_age(row.pending_time)})`;
} else if (row.status === "Responded" && row.response_time) {
- let d = frappe.datetime.str_to_user(row.response_time.split(' ')[0]);
+ let d = frappe.datetime.str_to_user(row.response_time.split(" ")[0]);
time_info = ` | Responded: ${d} (${fmt_age(row.response_time)} ago)`;
}
diff --git a/audit_management/audit_management/doctype/my_audits/my_audits.py b/audit_management/audit_management/doctype/my_audits/my_audits.py
index 17d53e7..069f3f6 100755
--- a/audit_management/audit_management/doctype/my_audits/my_audits.py
+++ b/audit_management/audit_management/doctype/my_audits/my_audits.py
@@ -840,11 +840,12 @@ def get_permission_query_conditions(user=None):
user = frappe.session.user
roles = frappe.get_roles(user)
+ is_audit_manager = "Audit Manager" in roles or "Administrator" in roles or "System Manager" in roles
# =========================================================
# ADMIN BYPASS
# =========================================================
- if "Administrator" in roles or "System Manager" in roles:
+ if is_audit_manager:
return ""
# =========================================================
@@ -857,19 +858,11 @@ def get_permission_query_conditions(user=None):
) if allowed_divisions else "'None'"
# =========================================================
- # AUDIT MANAGER
- # Full division access
- # =========================================================
- if "Audit Manager" in roles:
- return f"""
- `tabMy Audits`.emp_division IN ({divisions_sql})
- """
-
# =========================================================
# AUDIT MEMBER
# Only created records
# =========================================================
- if "Audit Member" in roles:
+ if "Audit Member" in roles and not is_audit_manager:
return f"""
`tabMy Audits`.owner = '{user}'
"""
@@ -1058,6 +1051,42 @@ def get_full_name(user_id):
+@frappe.whitelist()
+def rollback_stage(docname, stagename):
+ """Resets a stage status to empty and removes document share for that user. Only allowed for Pending stages."""
+ doc = frappe.get_doc("My Audits", docname)
+ found = False
+
+ for row in doc.audit_stages:
+ if row.stage_name == stagename:
+ if row.status != "Pending":
+ frappe.throw(_("Only Pending stages can be rolled back. {0} is currently {1}.").format(stagename, row.status))
+
+ # 1. Revoke access first (bypass permission checks for administrative rollback)
+ if row.user_id:
+ frappe.db.delete("DocShare", {
+ "share_doctype": doc.doctype,
+ "share_name": doc.name,
+ "user": row.user_id
+ })
+
+ # 2. Clear stage data
+ row.status = ""
+ row.pending_time = None
+ row.response = None
+ row.attachment = None
+ row.response_time = None
+
+ found = True
+ break
+
+ if found:
+ doc.query_status = f"Rollback: {stagename}"
+ doc.save(ignore_permissions=True)
+ return True
+ return False
+
+
@frappe.whitelist()
def raise_multi_request(docname, stagenames):
"""Transition from Draft to Pending and assign to multiple selected stages."""
@@ -1067,8 +1096,8 @@ def raise_multi_request(docname, stagenames):
doc = frappe.get_doc("My Audits", docname)
- if doc.status != "Draft":
- frappe.throw("Only Draft requests can be raised.")
+ if doc.status not in ["Draft", "Pending"]:
+ frappe.throw("Requests can only be raised for Draft or Pending audits.")
if not doc.get("audit_stages"):
frappe.throw(
@@ -1088,25 +1117,24 @@ def raise_multi_request(docname, stagenames):
if row.user_id:
frappe.share.add(doc.doctype, doc.name, row.user_id,
read=1, write=1, share=1, notify=0)
- else:
- # We clear others if they were previously pending?
- # Actually, if it was Draft, they should be empty anyway.
- # But just in case, we keep the original logic of clearing others if not in selection.
- if not row.status or row.status == "Pending":
- row.status = ""
+
+ # We remove the 'else' block that clears other stages to allow incremental assignment
if not selected_rows:
frappe.throw("No valid stages selected from the selection.")
doc.status = "Pending"
- if len(stagenames) == len(doc.audit_stages):
+
+ # Update query_status to reflect current pending stages
+ all_pending = [r.stage_name for r in doc.audit_stages if r.status == "Pending"]
+ if len(all_pending) == len(doc.audit_stages):
doc.query_status = "Pending From All Stages"
else:
- doc.query_status = f"Pending From {', '.join(found_stagenames)}"
+ doc.query_status = f"Pending From {', '.join(all_pending)}"
doc.save(ignore_permissions=True)
- # Trigger custom notifications for each selected stage
+ # Trigger custom notifications for each newly selected stage
for row in selected_rows:
send_stage_notification(doc, row, action="assign")