diff --git a/one_compliance/hooks.py b/one_compliance/hooks.py index 538ec15a..fdd1e440 100644 --- a/one_compliance/hooks.py +++ b/one_compliance/hooks.py @@ -132,7 +132,8 @@ 'one_compliance.one_compliance.doc_events.task.make_sales_invoice', 'one_compliance.one_compliance.doc_events.task.subtask_on_update', 'one_compliance.one_compliance.doc_events.task.on_task_update', - 'one_compliance.one_compliance.doc_events.task.enable_customer_on_task_completion' + 'one_compliance.one_compliance.doc_events.task.enable_customer_on_task_completion', + 'one_compliance.one_compliance.doc_events.task.on_update' ], 'validate':[ 'one_compliance.one_compliance.doc_events.task.append_users_to_project', diff --git a/one_compliance/one_compliance/doc_events/task.py b/one_compliance/one_compliance/doc_events/task.py index e1ea8edb..a207e3d4 100644 --- a/one_compliance/one_compliance/doc_events/task.py +++ b/one_compliance/one_compliance/doc_events/task.py @@ -191,6 +191,21 @@ def on_update(self): self.unassign_todo() self.populate_depends_on() + def before_validate(self): + self.update_project_eed() + + def update_project_eed(self): + ''' + Update the project's expected end date if the task's completion date is later. + ''' + if not self.project or not self.completed_on: + return + + project = frappe.get_doc("Project", self.project) + + if project.expected_end_date < self.completed_on: + project.expected_end_date = self.completed_on + project.save() def unassign_todo(self): if self.status == "Completed": close_all_assignments(self.doctype, self.name) @@ -344,13 +359,14 @@ def task_on_update(doc, method): if frappe.db.exists('Project', doc.project): project = frappe.get_doc ('Project', doc.project) if project.status == 'Completed': - if not frappe.db.get_value("Sales Order", project.sales_order, "custom_is_rework"): - create_project_completion_todos(project.sales_order, project.project_name) - # send_project_completion_mail = frappe.db.get_value('Customer', project.customer, 'send_project_completion_mail') - # if send_project_completion_mail: - # email_id = frappe.db.get_value('Customer', project.customer, 'email_id') - # if email_id: - # project_complete_notification_for_customer(project, email_id) + sales_order = project.sales_order + if not sales_order or not frappe.db.exists('Sales Order', sales_order): + sales_order = frappe.db.exists("Sales Order", {"project":project.name, 'docstatus':1}) + if not sales_order: + frappe.throw(_(f"Sales Order not found for {project.name}")) + if not frappe.db.get_value("Sales Order", sales_order, "custom_is_rework"): + create_project_completion_todos(sales_order, project.name) + # Check if this task is a dependency for other tasks dependent_tasks = frappe.get_all('Task Depends On', filters={'task': doc.name}, fields=['parent']) for dependent_task in dependent_tasks: @@ -501,12 +517,31 @@ def create_sales_order(project, rate, sub_category_doc, payment_terms=None, subm frappe.msgprint("Sales Order {0} Created against {1}".format(new_sales_order.name, project.name), alert=True) @frappe.whitelist() -def update_task_status(task_id, status, completed_by, completed_on): +def update_task_status(task_id, status, completed_by, completed_on, comment=None): # Load the task document from the database task_doc = frappe.get_doc("Task", task_id) task_doc.completed_on = frappe.utils.getdate(completed_on) task_doc.status = status task_doc.completed_by = completed_by + if comment: + # Task comment + task_prefix = frappe.bold(_("Reason for updating task status:")) + task_comment = f"{task_prefix}
{frappe.utils.escape_html(comment)}" + task_doc.add_comment("Comment", task_comment) + + # Project comment + if task_doc.project: + project_prefix = frappe.bold(_("Reason for updating expected end:")) + project_comment = ( + f"{project_prefix}
" + f"Task {task_doc.name} updated to status {status} " + f"on {completed_on}.
" + f"Comment: {frappe.utils.escape_html(comment)}" + ) + project_doc = frappe.get_doc("Project", task_doc.project) + project_doc.add_comment("Comment", project_comment) + + task_doc.save() frappe.db.commit() frappe.msgprint("Task Status has been set to {0}".format(status), alert=True) @@ -694,3 +729,17 @@ def enable_customer_on_task_completion(doc, method): customer.aml_compliance_checked = 1 customer.save(ignore_permissions=True) frappe.msgprint(f"Customer {customer.name} has been enabled after AML compliance task completion.") +def on_update(doc, method): + ''' + Update the project's expected end date if the task's completion date is later. + ''' + if not doc.project or not doc.completed_on: + return + + project = frappe.get_doc("Project", doc.project) + + if project.expected_end_date < doc.completed_on: + project.db_set('expected_end_date', doc.completed_on) + comment_content = '

Reason for updating project completion date:

' + comment_content += f"Expected End Date updated to {doc.completed_on} based on Task {doc.name} completion." + project.add_comment(text=_(comment_content)) diff --git a/one_compliance/one_compliance/page/task_management_tool/task_management_tool.js b/one_compliance/one_compliance/page/task_management_tool/task_management_tool.js index a0668e1f..28e48a0b 100644 --- a/one_compliance/one_compliance/page/task_management_tool/task_management_tool.js +++ b/one_compliance/one_compliance/page/task_management_tool/task_management_tool.js @@ -925,7 +925,12 @@ function update_status(page, taskName, projectId, taskId) { fieldname: 'completed_on', fieldtype: 'Date', default: 'Today' - } + }, + { + label: 'Comment', + fieldname: 'comment', + fieldtype: 'Small Text' + } ]; let d = new frappe.ui.Dialog({ @@ -933,36 +938,61 @@ function update_status(page, taskName, projectId, taskId) { fields: fields, primary_action_label: 'Update', primary_action(values) { + // Block if validation requires comment but it's missing + if (d.fields_dict.comment.df.reqd && !values.comment) { + frappe.msgprint(__('Please enter a comment before updating.')); + return; + } + frappe.call({ - method: 'one_compliance.one_compliance.doc_events.task.update_task_status', - args: { - 'task_id': taskId, - 'status': values.status, - 'completed_by': values.completed_by, - 'completed_on': values.completed_on - }, - callback: function(r){ - if (r.message){ - d.hide(); - const selectedStatus = page.fields_dict.status.get_value(); - const taskName = page.fields_dict.task.get_value(); - const projectName = page.fields_dict.project.get_value(); - const customerName = page.fields_dict.customer.get_value(); - const department = page.fields_dict.department.get_value(); - const subCategory = page.fields_dict.compliance_sub_category.get_value(); - const employee = page.fields_dict.employee.get_value(); - const employeeGroup = page.fields_dict.employee_group.get_value(); - const from_date = page.fields_dict.from_date.get_value(); - const to_date = page.fields_dict.to_date.get_value(); - refresh_tasks_manually(page, selectedStatus, taskName, projectName, customerName, department, subCategory, employee, employeeGroup, from_date, to_date) - // location.reload(); - } - } - }); + method: 'one_compliance.one_compliance.doc_events.task.update_task_status', + args: { + 'task_id': taskId, + 'status': values.status, + 'completed_by': values.completed_by, + 'completed_on': values.completed_on, + 'comment': values.comment + }, + callback: function(r){ + if (r.message){ + d.hide(); + const selectedStatus = page.fields_dict.status.get_value(); + const taskName = page.fields_dict.task.get_value(); + const projectName = page.fields_dict.project.get_value(); + const customerName = page.fields_dict.customer.get_value(); + const department = page.fields_dict.department.get_value(); + const subCategory = page.fields_dict.compliance_sub_category.get_value(); + const employee = page.fields_dict.employee.get_value(); + const employeeGroup = page.fields_dict.employee_group.get_value(); + const from_date = page.fields_dict.from_date.get_value(); + const to_date = page.fields_dict.to_date.get_value(); + refresh_tasks_manually(page, selectedStatus, taskName, projectName, customerName, department, subCategory, employee, employeeGroup, from_date, to_date) + } + } + }); }, }); d.set_value('completed_by', frappe.session.user); -d.show(); + + //Hide comment initially + d.fields_dict.comment.$wrapper.hide(); + d.set_df_property('comment', 'reqd', 0); + + //Fetch project and validate dates + frappe.db.get_doc("Project", projectId).then(project => { + if (project.expected_end_date) { + let expectedDate = new Date(project.expected_end_date); + let completedDate = new Date(frappe.datetime.get_today()); + + if (completedDate > expectedDate) { + // force comment if task is completed after project EED + d.fields_dict.comment.$wrapper.show(); + d.set_df_property('comment', 'reqd', 1); + } + } + }); + + d.show(); } // Function to update status to working when clicking start time diff --git a/one_compliance/one_compliance/utils.py b/one_compliance/one_compliance/utils.py index 36f32a6e..00433c40 100644 --- a/one_compliance/one_compliance/utils.py +++ b/one_compliance/one_compliance/utils.py @@ -48,7 +48,7 @@ def create_todo(doctype, name, assign_to, owner, description): add_custom( { - "assign_to": assign_to, + "assign_to": [assign_to], "doctype": doctype, "name": name, "description": description,