diff --git a/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.js b/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.js index 7c6d89af..bcd2ee0a 100644 --- a/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.js +++ b/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.js @@ -2,55 +2,85 @@ // For license information, please see license.txt frappe.query_reports["Detailed Project Summary"] = { - "filters": [ - { - label: __("From Date"), - fieldname: "from_date", - fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), - reqd: 1 - }, - { - label: __("To Date"), - fieldname: "to_date", - fieldtype: "Date", - default: frappe.datetime.get_today(), - reqd: 1 - }, - { - label: __("Department"), - fieldname: "department", - fieldtype: "Link", - options: "Department", - get_query: function(){ - return { - filters: { - 'is_compliance': 1 - } - } - } - }, - // { - // label: __("Employee"), - // fieldname: "employee", - // fieldtype: "Link", - // options: "Employee", - // get_query: function(){ - // return { - // filters: { - // 'department': frappe.query_report.get_filter_value('department') - // } - // } - // } - // }, - ], - formatter: function (value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); - if (data.department_row) { - value = $(`${value}`); - var $value = $(value).css("font-weight", "bold"); - value = $value.wrap("

").parent().html(); - } - return value; - } + filters: [ + { + label: __("From Date"), + fieldname: "from_date", + fieldtype: "Date", + default: "Today", + reqd: 1, + }, + { + label: __("To Date"), + fieldname: "to_date", + fieldtype: "Date", + default: "Today", + reqd: 1, + }, + { + label: __("Department"), + fieldname: "department", + fieldtype: "Link", + options: "Department", + get_query: function () { + return { + filters: { + is_compliance: 1, + }, + }; + }, + }, + { + label: __("Customer"), + fieldname: "customer", + fieldtype: "Link", + options: "Customer", + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: "\nOpen\nInvoiced\nPaid\nHold\nOverdue\nCompleted\nCancelled", + }, + { + label: __("Reference Type"), + fieldname: "reference_type", + fieldtype: "Select", + options: "\nProject\nEvent", + }, + { + label: __("Project"), + fieldname: "project", + fieldtype: "Link", + options: "Project", + }, + { + label: __("Invoiced"), + fieldname: "invoiced", + fieldtype: "Select", + options: "\nYes\nNo", + }, + // { + // label: __("Employee"), + // fieldname: "employee", + // fieldtype: "Link", + // options: "Employee", + // get_query: function(){ + // return { + // filters: { + // 'department': frappe.query_report.get_filter_value('department') + // } + // } + // } + // }, + ], + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + if (data.department_row) { + value = $(`${value}`); + var $value = $(value).css("font-weight", "bold"); + value = $value.wrap("

").parent().html(); + } + return value; + }, }; diff --git a/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.py b/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.py index 79a43adc..60365911 100644 --- a/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.py +++ b/one_compliance/one_compliance/report/detailed_project_summary/detailed_project_summary.py @@ -5,6 +5,7 @@ from frappe import _ from frappe.utils import getdate + def execute(filters=None): columns = get_columns(filters) data = get_data(filters) @@ -28,7 +29,7 @@ def get_columns(filters=None): "width": 200 }, { - "label": _("Project"), + "label": _("Project/Event"), "fieldname": "project", "fieldtype": "Link", "options": 'Project', @@ -110,50 +111,219 @@ def get_data(filters): return data def prepare_data(departments, filters): - data=[] - for department in departments: - department_row = [] - projects = frappe.db.get_all('Project', { 'department': department, 'expected_start_date':['between', [ getdate(filters.get('from_date')), getdate(filters.get('to_date'))]] }, pluck='name') - department_row.append({ - 'department_row': 1, - 'department': department - }) - total_pending_tasks, total_completed_tasks, total_overdue_tasks = 0, 0, 0 - total_invoiced_amount, total_payment_recieved, total_outstanding_amount = 0, 0, 0 - for project in projects: - project_doc = frappe.get_doc('Project', project) - invoice_details = get_invoiced_and_outstanding_amount_from_project(project) - row = { - 'department': department, - 'customer': project_doc.customer, - 'project': project_doc.name, - 'project_name': project_doc.project_name, - 'status': project_doc.status, - 'progress': project_doc.percent_complete, - 'pending_task': get_project_count_based_on_status(project, 'Pending'), - 'completed_task': get_project_count_based_on_status(project, 'Completed'), - 'overdue_task': get_project_count_based_on_status(project, 'Overdue'), - 'invoiced': is_invoiced_or_not(project), - 'invoiced_amount': invoice_details.get('invoiced_amount') or 0, - 'payment_recieved': invoice_details.get('payment_recieved') or 0, - 'outstanding_amount': invoice_details.get('outstanding_amount') or 0 - } - total_pending_tasks += get_project_count_based_on_status(project, 'Pending') - total_completed_tasks += get_project_count_based_on_status(project, 'Completed') - total_overdue_tasks += get_project_count_based_on_status(project, 'Overdue') - total_invoiced_amount += row.get('invoiced_amount') - total_payment_recieved += row.get('payment_recieved') - total_outstanding_amount += row.get('outstanding_amount') - department_row.append(row) - department_row[0]['pending_task'] = total_pending_tasks - department_row[0]['completed_task'] = total_completed_tasks - department_row[0]['overdue_task'] = total_overdue_tasks - department_row[0]['invoiced_amount'] = total_invoiced_amount - department_row[0]['payment_recieved'] = total_payment_recieved - department_row[0]['outstanding_amount'] = total_outstanding_amount - data.extend(department_row) + data = [] + if filters.get("reference_type") in [None, "Project"]: + for department in departments: + department_row = [] + + if filters.get("reference_type") == "Project" or not filters.get( + "reference_type" + ): + projects = [] + if filters.get("project"): + projects.append(filters.get("project")) + else: + projects = frappe.db.get_all( + "Project", + { + "department": department, + "expected_start_date": [ + "between", + [ + getdate(filters.get("from_date")), + getdate(filters.get("to_date")), + ], + ], + }, + pluck="name", + ) + total_pending_tasks, total_completed_tasks, total_overdue_tasks = 0, 0, 0 + total_invoiced_amount, total_payment_recieved, total_outstanding_amount = ( + 0, + 0, + 0, + ) + for project in projects: + project_doc = frappe.get_doc("Project", project) + invoice_details = get_invoiced_and_outstanding_amount_from_project( + project + ) + if filters.get("customer") and ( + project_doc.customer != filters.get("customer") + or not project_doc.customer + ): + continue + if filters.get("status") and ( + project_doc.status != filters.get("status") + or not project_doc.status + ): + continue + if filters.get("invoiced") and filters.get( + "invoiced" + ) != is_invoiced_or_not(project): + continue + row = { + "department": department, + "customer": project_doc.customer, + "project": project_doc.name, + "project_name": project_doc.project_name, + "status": project_doc.status, + "progress": project_doc.percent_complete, + "pending_task": get_project_count_based_on_status( + project, "Pending" + ), + "completed_task": get_project_count_based_on_status( + project, "Completed" + ), + "overdue_task": get_project_count_based_on_status( + project, "Overdue" + ), + "invoiced": is_invoiced_or_not(project), + "invoiced_amount": invoice_details.get("invoiced_amount") or 0, + "payment_recieved": invoice_details.get("payment_recieved") or 0, + "outstanding_amount": invoice_details.get("outstanding_amount") + or 0, + } + total_pending_tasks += get_project_count_based_on_status( + project, "Pending" + ) + total_completed_tasks += get_project_count_based_on_status( + project, "Completed" + ) + total_overdue_tasks += get_project_count_based_on_status( + project, "Overdue" + ) + total_invoiced_amount += row.get("invoiced_amount") + total_payment_recieved += row.get("payment_recieved") + total_outstanding_amount += row.get("outstanding_amount") + department_row.append(row) + data.extend(department_row) + if filters.get("reference_type") in [None, "Event"] and not filters.get("project") and not filters.get("invoiced"): + work = frappe.db.exists( + "Compliance Sub Category", {"department": department} + ) + if work: + event_filters = {"custom_is_billable":1, "custom_service": work} + if filters.get("customer"): + event_filters["custom_customer"] = filters.get("customer") + events = frappe.db.get_all( + "Event", filters=event_filters, fields=["*"] + ) + for event in events: + event_billing_details = handle_event_billing( + {"client": event["custom_customer"], "sub_category": work} + ) + row = { + "department": department, + "project": event["name"], + "customer": event["custom_customer"], + "status": event["status"], + "invoiced": "Yes", + "invoiced_amount": event_billing_details["invoice_amount"], + "payment_received": event_billing_details["payment_received"], + "outstanding_amount": event_billing_details[ + "outstanding_amount" + ], + } + department_row.append(row) + + #Fetching billed events that do not have a department + if filters.get("reference_type") in [None, "Event"] and not filters.get("department") and not filters.get("project") and not filters.get("invoiced"): + no_department_events = get_events_without_department(filters) + data += no_department_events return data +def get_events_without_department(filters): + event_rows = [] + valid_works = frappe.db.get_all("Compliance Sub Category", {"department":""}, pluck="name") + event_filters = {"custom_is_billable":1, "custom_service":["in", valid_works]} + if filters.get("customer"): + event_filters["custom_customer"] = filters.get("customer") + if filters.get("status"): + event_filters["status"] = filters.get("status") + events = frappe.db.get_all("Event", event_filters, ["*"]) + for event in events: + event_billing_details = handle_event_billing( + {"client": event["custom_customer"], "sub_category": event["custom_service"]} + ) + row = { + "invoiced_amount": 0, + "payment_received": 0, + "outstanding_amount": 0, + } + if event_billing_details: + if "invoiced_amount" in event_billing_details: + row["invoiced_amount"] = event_billing_details["invoiced_amount"] + if "payment_received" in event_billing_details: + row["payment_received"] = event_billing_details["payment_received"] + if "outstanding_amount" in event_billing_details: + row["outstanding_amount"] = event_billing_details["outstanding_amount"] + row = { + "project": event["name"], + "department": "", + "customer": event["custom_customer"], + "status": event["status"], + "invoiced": "Yes", + } + if row["status"] == "Completed": + row["progress"] = 100 + event_rows.append(row) + return event_rows + +# This is the same code from detailed task summary refactor, once that is merged import that method instead of re-defining it +def handle_event_billing(record): + """ + Handles fetching billing details for event records. + Args: + record (dict): The event record to be updated. + Returns: + None: Updates the record in place with billing details. + """ + record["invoiced"] = "Not Billlable" + item_code = frappe.db.get_value( + "Compliance Sub Category", record.get("sub_category"), "item_code" + ) + if item_code: + sales_order = frappe.db.sql( + f""" + SELECT DISTINCT so.name + FROM `tabSales Order` as so, `tabSales Order Item` as soi + WHERE soi.parent = so.name AND soi.item_code = '{item_code}' AND so.customer = '{record['client']}' + """, + as_dict=True, + ) + + if sales_order: + record["billing_date"] = frappe.db.get_value( + "Sales Order", sales_order, "custom_billing_date" + ) + record["invoiced"] = "Yes" + si = frappe.db.get_value( + "Sales Invoice Item", {"sales_order": sales_order[0]["name"]}, "parent" + ) + record["invoice_amount"] = frappe.utils.fmt_money( + frappe.db.get_value("Sales Order", sales_order, "grand_total"), + currency="INR", + ) + + if si: + record["payment_received"] = ( + frappe.db.get_value("Sales Invoice", si, "grand_total") or 0 + ) + grand_total = ( + frappe.db.get_value( + "Sales Order", sales_order[0]["name"], "grand_total" + ) + or 0 + ) + record["outstanding_amount"] = frappe.utils.fmt_money( + grand_total - record["payment_received"], currency="INR" + ) + record["payment_received"] = frappe.utils.fmt_money( + record["payment_received"], currency="INR" + ) + + def get_project_count_based_on_status(project, status): ''' Method to get count of Tasks with respect to Project and status