From 36aea71fd76d04641ee18b0a363ad182da7ad89a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 May 2020 10:47:36 +0530 Subject: [PATCH 01/22] fix: new dashboard and report --- erpnext/hr/dashboard_fixtures.py | 154 ++++++++++++++++++ erpnext/hr/doctype/attendance/attendance.py | 6 +- .../leave_application/leave_application.py | 4 +- .../department_analytics.json | 28 ---- .../__init__.py | 0 .../employee_analytics.js} | 12 +- .../employee_analytics.json | 30 ++++ .../employee_analytics.py} | 60 ++++--- .../monthly_attendance_sheet.py | 101 +++++++++--- .../production_analytics.py | 1 - erpnext/patches.txt | 1 + 11 files changed, 314 insertions(+), 83 deletions(-) create mode 100644 erpnext/hr/dashboard_fixtures.py delete mode 100644 erpnext/hr/report/department_analytics/department_analytics.json rename erpnext/hr/report/{department_analytics => employee_analytics}/__init__.py (100%) rename erpnext/hr/report/{department_analytics/department_analytics.js => employee_analytics/employee_analytics.js} (55%) create mode 100644 erpnext/hr/report/employee_analytics/employee_analytics.json rename erpnext/hr/report/{department_analytics/department_analytics.py => employee_analytics/employee_analytics.py} (51%) diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py new file mode 100644 index 0000000000..dafacaada4 --- /dev/null +++ b/erpnext/hr/dashboard_fixtures.py @@ -0,0 +1,154 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import erpnext +import json + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + dashboards = [] + dashboards.append(get_human_resource_dashboard()) + return dashboards + +def get_human_resource_dashboard(): + return { + "name": "Human Resource", + "dashboard_name": "Human Resource", + "is_default": 1, + "charts": [ + { "chart": "Gender Diversity Ratio", "width": "Half"}, + { "chart": "Employee Count", "width": "Half"}, + { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Attendance Count", "width": "Full"} + ], + "cards": [ + {"card": "Total Employees"}, + {"card": "New Joinees"}, + {'card': "Job Applicants"}, + {'card': "Employees Left"} + ] + } + +def get_recruitment_dashboard(): + pass + # return { + # "name": "Human Resource", + # "dashboard_name": "Human Resource", + # "is_default": 1, + # "charts": [ + # ], + # "cards": [ + # ] + # } + + +def get_charts(): + company = erpnext.get_default_company() + + if not company: + company = frappe.db.get_value("Company", {"is_group": 0}, "name") + + dashboard_charts = [ + get_dashboards_chart_doc('Gender Diversity Ratio', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="gender", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ] + + dashboard_charts.append( + get_dashboards_chart_doc('Outgoing salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) + ) + + custom_options = '''{"type": "bar", "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}, "barOptions":{"stacked": 1}}''' + filters_json = json.dumps({"month":"May","year":"2020","company":company}) + + dashboard_charts.append( + get_dashboards_chart_doc('Attendance Count', "Report", "Bar",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + ) + + custom_options = """{"type": "donut", "axisOptions": {"shortenYAxisNumbers": 1}}""" + filters_json = json.dumps({"company":company ,"parameter":"Department"}) + + dashboard_charts.append( + get_dashboards_chart_doc('Employee Count', "Report", "Donut",report_name = "Employee Analytics", is_custom =1, group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + ) + + + + +def get_number_cards(): + number_cards = [] + + number_cards = [ + get_number_cards_doc("Employee", "Total Employees", filters_json = json.dumps([ + ["Employee","status","=","Active"] + ]) + ) + ] + number_cards.append( + get_number_cards_doc("Employee", "New Joinees", filters_json = json.dumps([ + ["Employee","date_of_joining","Previous","6 months"], + ["Employee","status","=","Active"] + ]), + stats_time_interval = "Daily") + ) + number_cards.append( + get_number_cards_doc("Employee", "Employees Left", filters_json = json.dumps([ + ["Employee","status","=","Left"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Job Applicants", filters_json = json.dumps([ + ["Job Applicant","status","!=","Rejected"] + ]) + ) + ) + + return number_cards + + +def get_number_cards_doc(document_type, label, **args): + args = frappe._dict(args) + + return { + "doctype": "Number Card", + "document_type": document_type, + "function": args.func or "Count", + "is_public": args.is_public or 1, + "label": label, + "name": args.name or label, + "show_percentage_stats": args.show_percentage_stats or 1, + "stats_time_interval": args.stats_time_interval or 'Monthly', + "filters_json": args.filters_json or '[]', + } + +def get_dashboards_chart_doc(name, chart_type, graph_type, **args): + + args = frappe._dict(args) + + return { + "name": name, + "chart_name": args.chart_name or name, + "chart_type": chart_type, + "document_type": args.document_type or None, + "report_name": args.report_name or None, + "is_custom": args.is_custom or 0, + "group_by_type": args.group_by_type or None, + "group_by_based_on": args.group_by_based_on or None, + "based_on": args.based_on or None, + "value_based_on": args.value_based_on or None, + "number_of_groups": args.number_of_groups or 0, + "is_public": args.is_public or 1, + "timespan": args.timespan or "Last Year", + "time_interval": args.time_interval or "Yearly", + "timeseries": args.timeseries or 0, + "filters_json": args.filters_json or '[]', + "type": graph_type, + "custom_options": args.custom_options or '', + "doctype": "Dashboard Chart", + } \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index b6c80655c2..ba804ceca5 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -41,7 +41,7 @@ class Attendance(Document): leave_record = frappe.db.sql(""" select leave_type, half_day, half_day_date from `tabLeave Application` - where employee = %s + where employee = %s and %s between from_date and to_date and status = 'Approved' and docstatus = 1 @@ -172,8 +172,8 @@ def get_unmarked_days(employee, month): records = frappe.get_all("Attendance", fields = ['attendance_date', 'employee'] , filters = [ - ["attendance_date", ">", month_start], - ["attendance_date", "<", month_end], + ["attendance_date", ">=", month_start], + ["attendance_date", "<=", month_end], ["employee", "=", employee], ["docstatus", "!=", 2] ]) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 47b1bb7684..d3a08cd3c7 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -131,6 +131,8 @@ class LeaveApplication(Document): for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" + print("-------->>>", status) + # frappe.throw("Hello") attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -596,7 +598,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date): is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 + AND docstatus=1 AND (leaves<0 OR is_expired=1) AND (from_date between %(from_date)s AND %(to_date)s diff --git a/erpnext/hr/report/department_analytics/department_analytics.json b/erpnext/hr/report/department_analytics/department_analytics.json deleted file mode 100644 index 1e26b33c53..0000000000 --- a/erpnext/hr/report/department_analytics/department_analytics.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2018-05-15 15:37:20.883263", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-05-15 17:19:32.934321", - "modified_by": "Administrator", - "module": "HR", - "name": "Department Analytics", - "owner": "Administrator", - "ref_doctype": "Employee", - "report_name": "Department Analytics", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/department_analytics/__init__.py b/erpnext/hr/report/employee_analytics/__init__.py similarity index 100% rename from erpnext/hr/report/department_analytics/__init__.py rename to erpnext/hr/report/employee_analytics/__init__.py diff --git a/erpnext/hr/report/department_analytics/department_analytics.js b/erpnext/hr/report/employee_analytics/employee_analytics.js similarity index 55% rename from erpnext/hr/report/department_analytics/department_analytics.js rename to erpnext/hr/report/employee_analytics/employee_analytics.js index 29fedcd735..8620a65a90 100644 --- a/erpnext/hr/report/department_analytics/department_analytics.js +++ b/erpnext/hr/report/employee_analytics/employee_analytics.js @@ -1,7 +1,8 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +/* eslint-disable */ -frappe.query_reports["Department Analytics"] = { +frappe.query_reports["Employee Analytics"] = { "filters": [ { "fieldname":"company", @@ -11,5 +12,12 @@ frappe.query_reports["Department Analytics"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname":"parameter", + "label": __("Parameter"), + "fieldtype": "Select", + "options": ["Branch","Grade","Department","Designation", "Employment Type"], + "reqd": 1 + } ] -}; \ No newline at end of file +}; diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.json b/erpnext/hr/report/employee_analytics/employee_analytics.json new file mode 100644 index 0000000000..5a7ab9a251 --- /dev/null +++ b/erpnext/hr/report/employee_analytics/employee_analytics.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-12 13:52:50.631086", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-12 13:52:50.631086", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Employee", + "report_name": "Employee Analytics", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/report/department_analytics/department_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py similarity index 51% rename from erpnext/hr/report/department_analytics/department_analytics.py rename to erpnext/hr/report/employee_analytics/employee_analytics.py index b28eac43f8..6f8f161c3d 100644 --- a/erpnext/hr/report/department_analytics/department_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -13,12 +13,13 @@ def execute(filters=None): columns = get_columns() employees = get_employees(filters) - departments_result = get_department(filters) - departments = [] - if departments_result: - for department in departments_result: - departments.append(department) - chart = get_chart_data(departments,employees) + parameters_result = get_parameters(filters) + parameters = [] + if parameters_result: + for department in parameters_result: + parameters.append(department) + + chart = get_chart_data(parameters,employees, filters) return columns, employees, None, chart def get_columns(): @@ -29,10 +30,8 @@ def get_columns(): ] def get_conditions(filters): - conditions = "" - if filters.get("department"): conditions += " and department = '%s'" % \ - filters["department"].replace("'", "\\'") - + conditions = " and "+filters.get("parameter").lower().replace(" ","_")+" IS NOT NULL " + if filters.get("company"): conditions += " and company = '%s'" % \ filters["company"].replace("'", "\\'") return conditions @@ -43,25 +42,38 @@ def get_employees(filters): branch, department, designation, gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1) -def get_department(filters): - return frappe.db.sql("""select name from `tabDepartment` where company = %s""", (filters["company"]), as_list=1) - -def get_chart_data(departments,employees): - if not departments: - departments = [] +def get_parameters(filters): + return frappe.db.sql("""select name from `tab"""+filters.get("parameter")+"""` """, as_list=1) + +def get_chart_data(parameters,employees, filters): + if not parameters: + parameters = [] datasets = [] - for department in departments: - if department: - total_employee = frappe.db.sql("""select count(*) from \ - `tabEmployee` where \ - department = %s""" ,(department[0]), as_list=1) + parameter_field_name = filters.get("parameter").lower().replace(" ","_") + label = [] + for parameter in parameters: + if parameter: + total_employee = frappe.db.sql("""select count(*) from + `tabEmployee` where """+ + parameter_field_name + """ = %s and company = %s""" ,( parameter[0], filters.get("company")), as_list=1) + if total_employee[0][0]: + label.append(parameter) datasets.append(total_employee[0][0]) + + values = [ value for value in datasets if value !=0] + + total_employee = frappe.db.count('Employee', {'status':'Active'}) + others = total_employee - sum(values) + + label.append(["Others"]) + values.append(others) + chart = { "data": { - 'labels': departments, - 'datasets': [{'name': 'Employees','values': datasets}] + 'labels': label, + 'datasets': [{'name': 'Employees','values': values}] } } - chart["type"] = "bar" + chart["type"] = "donut" return chart diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 82ed27715f..a6509766c7 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -31,7 +31,7 @@ def execute(filters=None): if not filters: filters = {} conditions, filters = get_conditions(filters) - columns = get_columns(filters) + columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) if filters.group_by: @@ -60,20 +60,77 @@ def execute(filters=None): columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"]) if filters.group_by: + emp_att_map = {} for parameter in group_by_parameters: data.append([ ""+ parameter + ""]) - record = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + emp_att_map.update(aaa) data += record else: - record = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) data += record - return columns, data + chart_data = get_chart_data(emp_att_map, days) + + return columns, data, None, chart_data + +def get_chart_data(emp_att_map, days): + from pprint import pprint + pprint(emp_att_map) + labels = [] + datasets = [ + {"name": "Absent", "values": []}, + {"name": "Present", "values": []}, + {"name": "Leave", "values": []}, + {"name": "Unmarked", "values": []} + ] + for idx, day in enumerate(days, start=0): + p = day.replace("::65", "") + labels.append(day.replace("::65", "")) + total_absent_on_day = 0 + total_leave_on_day = 0 + total_present_on_day = 0 + total_holiday = 0 + total_unmarked_on_day = 0 + for emp in emp_att_map.keys(): + if emp_att_map[emp][idx]: + if emp_att_map[emp][idx] == "A": + total_absent_on_day += 1 + if emp_att_map[emp][idx] in ["P", "WFH"]: + total_present_on_day += 1 + if emp_att_map[emp][idx] == "HD": + total_present_on_day += 0.5 + total_leave_on_day += 0.5 + if emp_att_map[emp][idx] == "L": + total_leave_on_day += 1 + + total_marked = total_absent_on_day + total_present_on_day + total_leave_on_day + + datasets[0]["values"].append(total_absent_on_day) + datasets[1]["values"].append(total_present_on_day) + datasets[2]["values"].append(total_leave_on_day) + datasets[3]["values"].append(frappe.db.count('Employee', {'status':'Active'}) - total_marked) -def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list=None): + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + } + } + + chart["type"] = "bar" + # chart["spaceRatio"] = 0.1 + + + return chart + + + +def add_data(employee_map, att_map, filters, holiday_map, conditions,leave_list=None): record = [] + emp_att_map = {} for emp in employee_map: emp_det = employee_map.get(emp) if not emp_det or emp not in att_map: @@ -85,6 +142,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list row += [emp, emp_det.employee_name] total_p = total_a = total_l = total_h = total_um= 0.0 + ggg = [] for day in range(filters["total_days_in_month"]): status = None status = att_map.get(emp).get(day + 1) @@ -101,19 +159,12 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list status = "Holiday" total_h += 1 - - # if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list][0]: - # if holiday_map[emp_holiday_list][1]: - # status= "Weekly Off" - # else: - # status = "Holiday" - - # += 1 + ggg.append(status_map.get(status, "")) if not filters.summarized_view: - row.append(status_map.get(status, "")) + row += ggg else: - if status == "Present": + if status == "Present" or status == "Work From Home": total_p += 1 elif status == "Absent": total_a += 1 @@ -159,10 +210,10 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list row.append("0.0") row.extend([time_default_counts[0][0],time_default_counts[0][1]]) + emp_att_map[emp] = ggg record.append(row) - - return record + return record, emp_att_map def get_columns(filters): @@ -174,15 +225,17 @@ def get_columns(filters): columns += [ _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120" ] - + days = [] + for day in range(filters["total_days_in_month"]): + date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1) + day_name = day_abbr[getdate(date).weekday()] + days.append(cstr(day+1)+ " " +day_name +"::65") if not filters.summarized_view: - for day in range(filters["total_days_in_month"]): - date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1) - day_name = day_abbr[getdate(date).weekday()] - columns.append(cstr(day+1)+ " " +day_name +"::65") - else: + columns += days + + if filters.summarized_view: columns += [_("Total Present") + ":Float:120", _("Total Leaves") + ":Float:120", _("Total Absent") + ":Float:120", _("Total Holidays") + ":Float:120", _("Unmarked Days")+ ":Float:120"] - return columns + return columns, days def get_attendance_list(conditions, filters): attendance_list = frappe.db.sql("""select employee, day(attendance_date) as day_of_month, diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 7447a1f670..145e4a79b5 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -61,7 +61,6 @@ def get_periodic_data(filters, entry): elif getdate(d.planned_start_date) < getdate(from_date) : periodic_data = update_periodic_data(periodic_data, "Overdue", period) - else: periodic_data = update_periodic_data(periodic_data, "Not Started", period) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3f90d36916..accfa69743 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") +execute:frappe.delete_doc("Report", "Department Analytics") From 79a8bd1cdff67149d4a2bcc32c06b35f46fbb09b Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 10 May 2020 23:36:33 +0530 Subject: [PATCH 02/22] CRM Onboarding --- erpnext/crm/dashboard_fixtures.py | 203 ++++++++++++++++++ erpnext/crm/desk_page/crm/crm.json | 10 +- erpnext/crm/module_onboarding/crm/crm.json | 45 ++++ erpnext/crm/onboarding/crm/crm.json | 45 ++++ .../create_and_send_quotation.json | 17 ++ .../create_lead/create_lead.json | 17 ++ .../create_opportunity.json | 17 ++ .../introduction_to_crm.json | 17 ++ .../start_campaign/start_campaign.json | 17 ++ 9 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 erpnext/crm/dashboard_fixtures.py create mode 100644 erpnext/crm/module_onboarding/crm/crm.json create mode 100644 erpnext/crm/onboarding/crm/crm.json create mode 100644 erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json create mode 100644 erpnext/crm/onboarding_step/create_lead/create_lead.json create mode 100644 erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json create mode 100644 erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json create mode 100644 erpnext/crm/onboarding_step/start_campaign/start_campaign.json diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py new file mode 100644 index 0000000000..5b1ac9bf5e --- /dev/null +++ b/erpnext/crm/dashboard_fixtures.py @@ -0,0 +1,203 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe, erpnext, json + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards() + }) + +def get_dashboards(): + return [{ + "doctype": "Dashboard", + "name": "CRM", + "dashboard_name": "CRM", + "charts": [ + { "chart": "Lead", "width": "Full" }, + { "chart": "Opportunity", "width": "Full"}, + { "chart": "Campaign", "width": "Half" }, + { "chart": "Opportunities Won", "width": "Half" }, + { "chart": "Territory Wise Opportunity", "width": "Half"}, + { "chart": "Territory Wise Sales", "width": "Half"}, + { "chart": "Qualified For Call", "width": "Full"}, + { "chart": "Lead Source", "width": "Half"} + ], + "cards": [ + { "card": "New Lead" }, + { "card": "New Opportunity" }, + { "card": "Won Opportunity" }, + ] + }] + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_charts(): + company = get_company_for_dashboards() + + return [{ + "name": "Lead", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Lead", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Lead", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Bar" + }, + { + "name": "Opportunity", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Opportunity", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Opportunity", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Bar" + }, + { + "name": "Campaign", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Campaign", + "timespan": "Last Year", + "time_interval": "Monthly", + "document_type": "Campaign", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Line", + "color": "#428b46" + }, + { + "name": "Opportunities Won", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Opportunities Won", + "timespan": "Last Year", + "time_interval": "Monthly", + "document_type": "Opportunity", + "based_on": "modified", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "type": "Bar" + }, + { + "name": "Territory Wise Opportunity", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "territory", + "chart_name": "Territory Wise Opportunity", + "document_type": "Opportunity", + 'is_public': 1, + "owner": "Administrator", + "type": "Line" + }, + { + "name": "Territory Wise Sales", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Sum", + "group_by_based_on": "territory", + "chart_name": "Territory Wise Sales", + "aggregate_function_based_on": "opportunity_amount", + "document_type": "Opportunity", + 'is_public': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "type": "Bar", + "color": "#7575ff" + }, + { + "name": "Qualified For Call", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Qualified For Call", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Opportunity", + "based_on": "modified", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Qualification", False]]), + "type": "Line", + "color": "#fff168" + }, + { + "name": "Lead Source", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "source", + "chart_name": "Lead Source", + "document_type": "Lead", + 'is_public': 1, + "owner": "Administrator", + "type": "Donut" + }] + +def get_number_cards(): + return [{ + "doctype": "Number Card", + "document_type": "Lead", + "name": "New Lead", + "filters_json": json.dumps([["Lead","status","=","Lead",False]]), + "function": "Count", + "is_public": 1, + "label": "New Lead", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "New Opportunity", + "filters_json": json.dumps([["Opportunity","status","=","Open",False]]), + "function": "Count", + "is_public": 1, + "label": "New Opportunity", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Won Opportunity", + "filters_json": json.dumps([["Opportunity","status","=","Converted",False]]), + "function": "Count", + "is_public": 1, + "label": "Won Opportunity", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }] \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index ca13d6abb6..80d3d18de9 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -27,21 +27,25 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Territory Wise Sales" + } + ], "creation": "2020-01-23 14:48:30.183272", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-04-27 22:32:26.682911", + "modified": "2020-05-14 17:20:07.636751", "modified_by": "Administrator", "module": "CRM", "name": "CRM", + "onboarding": "CRM", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json new file mode 100644 index 0000000000..9129c2625a --- /dev/null +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Sales Master Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Sales Manager" + } + ], + "creation": "2020-05-09 23:42:50.901548", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 17:41:11.083917", + "modified_by": "Administrator", + "module": "CRM", + "name": "CRM", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to CRM" + }, + { + "step": "Start Campaign" + }, + { + "step": "Create Lead" + }, + { + "step": "Create Opportunity" + }, + { + "step": "Create and Send Quotation" + } + ], + "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "success_message": "CRM Module is all setup!", + "title": "Let's Setup Your CRM", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/crm/onboarding/crm/crm.json b/erpnext/crm/onboarding/crm/crm.json new file mode 100644 index 0000000000..016a8307dd --- /dev/null +++ b/erpnext/crm/onboarding/crm/crm.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Sales Master Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Sales Manager" + } + ], + "creation": "2020-05-09 23:42:50.901548", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-09 23:42:50.901548", + "modified_by": "Administrator", + "module": "CRM", + "name": "CRM", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to CRM" + }, + { + "step": "Start Campaign" + }, + { + "step": "Create Lead" + }, + { + "step": "Convert Lead to Customer" + }, + { + "step": "Create and Send Quotation" + } + ], + "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "success_message": "CRM Module is all setup!", + "title": "Let's Setup Your CRM", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json new file mode 100644 index 0000000000..0997017933 --- /dev/null +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:42:46.592075", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:30:07.887411", + "modified_by": "Administrator", + "name": "Create and Send Quotation", + "owner": "Administrator", + "reference_document": "Quotation", + "title": "Create and Send Quotation" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json new file mode 100644 index 0000000000..b1076e0bf9 --- /dev/null +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:40:25.192503", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:36.441387", + "modified_by": "Administrator", + "name": "Create Lead", + "owner": "Administrator", + "reference_document": "Lead", + "title": "Create Lead" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json new file mode 100644 index 0000000000..20949a435b --- /dev/null +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:38:27.496696", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:38:27.496696", + "modified_by": "Administrator", + "name": "Create Opportunity", + "owner": "Administrator", + "reference_document": "Opportunity", + "title": "Create Opportunity" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json new file mode 100644 index 0000000000..c923a77e1b --- /dev/null +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -0,0 +1,17 @@ +{ + "action": "Watch Video", + "creation": "2020-05-09 23:37:08.926812", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:16.448676", + "modified_by": "Administrator", + "name": "Introduction to CRM", + "owner": "Administrator", + "title": "Introduction to CRM", + "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json new file mode 100644 index 0000000000..53c3becea3 --- /dev/null +++ b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:38:10.284957", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:54.285958", + "modified_by": "Administrator", + "name": "Start Campaign", + "owner": "Administrator", + "reference_document": "Campaign", + "title": "Start Campaign" +} \ No newline at end of file From 57bfee8ea923455bc591da169a1c6a6df003169a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 May 2020 21:47:52 +0530 Subject: [PATCH 03/22] fix: dashboard changes --- erpnext/hr/dashboard_fixtures.py | 94 ++++++++++++++----- erpnext/hr/desk_page/hr/hr.json | 41 ++++---- .../leave_application/leave_application.json | 11 ++- .../human_resource/human_resource.json | 48 ++++++++++ .../create_department/create_department.json | 16 ++++ .../create_designation.json | 16 ++++ .../create_employee/create_employee.json | 16 ++++ .../create_holiday_list.json | 16 ++++ .../create_leave_allocation.json | 16 ++++ .../create_leave_application.json | 16 ++++ .../hr_settings/hr_settings.json | 16 ++++ .../employee_analytics/employee_analytics.py | 2 +- .../monthly_attendance_sheet.py | 16 ++-- 13 files changed, 265 insertions(+), 59 deletions(-) create mode 100644 erpnext/hr/onboarding/human_resource/human_resource.json create mode 100644 erpnext/hr/onboarding_step/create_department/create_department.json create mode 100644 erpnext/hr/onboarding_step/create_designation/create_designation.json create mode 100644 erpnext/hr/onboarding_step/create_employee/create_employee.json create mode 100644 erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json create mode 100644 erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json create mode 100644 erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json create mode 100644 erpnext/hr/onboarding_step/hr_settings/hr_settings.json diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index dafacaada4..c608e2bb29 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -24,33 +24,36 @@ def get_human_resource_dashboard(): "is_default": 1, "charts": [ { "chart": "Gender Diversity Ratio", "width": "Half"}, - { "chart": "Employee Count", "width": "Half"}, + { "chart": "Job Application Status", "width": "Half"}, + { "chart": 'Designation Wise Employee Count', "width": "Half"}, + { "chart": 'Department Wise Employee Count', "width": "Half"}, + { "chart": 'Designation Wise Openings', "width": "Half"}, + { "chart": 'Department Wise Openings', "width": "Half"}, { "chart": "Outgoing Salary", "width": "Full"}, { "chart": "Attendance Count", "width": "Full"} ], "cards": [ {"card": "Total Employees"}, {"card": "New Joinees"}, - {'card': "Job Applicants"}, - {'card': "Employees Left"} + {'card': "Employees Left"}, + {'card': "Total Job Openings"}, + {'card': "Total Applicants"}, + {'card': "Short Listed Candidates"}, + {'card': "Rejected Candidates"}, + {'card': "Total Job Offered"}, ] } def get_recruitment_dashboard(): pass - # return { - # "name": "Human Resource", - # "dashboard_name": "Human Resource", - # "is_default": 1, - # "charts": [ - # ], - # "cards": [ - # ] - # } def get_charts(): company = erpnext.get_default_company() + date = frappe.utils.get_datetime() + + month_map = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov","Dec"] + if not company: company = frappe.db.get_value("Company", {"is_group": 0}, "name") @@ -60,24 +63,34 @@ def get_charts(): ] dashboard_charts.append( - get_dashboards_chart_doc('Outgoing salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) + get_dashboards_chart_doc('Job Application Status', "Group By", "Donut",document_type = "Job Applicant", group_by_type="Count", group_by_based_on="status", filters_json = json.dumps([["Job Applicant","creation","Previous","1 month"]]), time_interval = "Monthly") ) - custom_options = '''{"type": "bar", "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}, "barOptions":{"stacked": 1}}''' - filters_json = json.dumps({"month":"May","year":"2020","company":company}) - dashboard_charts.append( - get_dashboards_chart_doc('Attendance Count', "Report", "Bar",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) ) - - custom_options = """{"type": "donut", "axisOptions": {"shortenYAxisNumbers": 1}}""" - filters_json = json.dumps({"company":company ,"parameter":"Department"}) + custom_options = '''{"type": "line", "lineOptions": {"dotSize": 6}, "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}''' + filters_json = json.dumps({"month": month_map[date.month - 1], "year": str(date.year), "company":company}) dashboard_charts.append( - get_dashboards_chart_doc('Employee Count', "Report", "Donut",report_name = "Employee Analytics", is_custom =1, group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + get_dashboards_chart_doc('Attendance Count', "Report", "Line",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", filters_json = filters_json, custom_options=custom_options) ) + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Employee Count', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="department", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ) + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Employee Count', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="designation", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ) + + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Openings', "Group By", "Bar",document_type = "Job Opening", group_by_type="Sum", group_by_based_on="designation", time_interval = "Monthly", aggregate_function_based_on = "planned_vacancies") + ) + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Openings', "Group By", "Bar",document_type = "Job Opening", group_by_type="Sum", group_by_based_on="department", time_interval = "Monthly", aggregate_function_based_on = "planned_vacancies") + ) + return dashboard_charts def get_number_cards(): @@ -89,26 +102,53 @@ def get_number_cards(): ]) ) ] + number_cards.append( get_number_cards_doc("Employee", "New Joinees", filters_json = json.dumps([ - ["Employee","date_of_joining","Previous","6 months"], + ["Employee","date_of_joining","Previous","1 year"], ["Employee","status","=","Active"] - ]), - stats_time_interval = "Daily") + ]) ) + ) + number_cards.append( get_number_cards_doc("Employee", "Employees Left", filters_json = json.dumps([ + ["Employee","modified","Previous","1 year"], ["Employee","status","=","Left"] ]) ) ) + number_cards.append( - get_number_cards_doc("Job Applicant", "Job Applicants", filters_json = json.dumps([ - ["Job Applicant","status","!=","Rejected"] + get_number_cards_doc("Job Applicant", "Total Applicants", filters_json = json.dumps([ + ["Job Applicant","creation","Previous","1 month"] ]) ) ) + number_cards.append( + get_number_cards_doc("Job Opening", "Total Job Openings", func = "Sum",aggregate_function_based_on = "planned_vacancies", filters_json = json.dumps([["Job Opening","creation","Previous","1 month"]]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Short Listed Candidates", filters_json = json.dumps([ + ["Job Applicant","status","=","Accepted"], + ["Job Applicant","creation","Previous","1 month"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Rejected Candidates", filters_json = json.dumps([ + ["Job Applicant","status","=","Rejected"], + ["Job Applicant","creation","Previous","1 month"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Offer", "Total Job Offered", filters_json = json.dumps([["Job Offer","creation","Previous","1 month"]]) + ) + ) + return number_cards @@ -125,6 +165,7 @@ def get_number_cards_doc(document_type, label, **args): "show_percentage_stats": args.show_percentage_stats or 1, "stats_time_interval": args.stats_time_interval or 'Monthly', "filters_json": args.filters_json or '[]', + "aggregate_function_based_on": args.aggregate_function_based_on or None } def get_dashboards_chart_doc(name, chart_type, graph_type, **args): @@ -151,4 +192,5 @@ def get_dashboards_chart_doc(name, chart_type, graph_type, **args): "type": graph_type, "custom_options": args.custom_options or '', "doctype": "Dashboard Chart", + "aggregate_function_based_on": args.aggregate_function_based_on or None } \ No newline at end of file diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 22aa170744..570596eb5b 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -77,21 +77,26 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" + } + ], "creation": "2020-03-02 15:48:58.322521", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-04-29 20:29:22.114309", + "modified": "2020-05-14 12:14:21.682026", "modified_by": "Administrator", "module": "HR", "name": "HR", + "onboarding": "Human Resource", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -104,10 +109,20 @@ "type": "DocType" }, { - "format": "{} Unpaid", - "label": "Expense Claim", - "link_to": "Expense Claim", - "stats_filter": "{\"approval_status\":\"Draft\"}", + "label": "Attendance", + "link_to": "Attendance", + "stats_filter": "", + "type": "DocType" + }, + { + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":\"Open\"}", + "type": "DocType" + }, + { + "label": "Salary Structure", + "link_to": "Payroll Entry", "type": "DocType" }, { @@ -117,19 +132,9 @@ "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "DocType" }, - { - "label": "Salary Structure", - "link_to": "Salary Structure", - "type": "DocType" - }, - { - "label": "Leave Application", - "link_to": "Leave Application", - "type": "DocType" - }, { "label": "Salary Register", - "link_to": "Salary Register", + "link_to": "Monthly Attendance Sheet", "type": "Report" } ] diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 74707a24b8..7f50ace766 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -174,7 +174,8 @@ "label": "Status", "no_copy": 1, "options": "Open\nApproved\nRejected\nCancelled", - "permlevel": 1 + "permlevel": 1, + "reqd": 1 }, { "fieldname": "sb10", @@ -189,14 +190,14 @@ "reqd": 1 }, { + "fetch_from": "employee.company", "fieldname": "company", "fieldtype": "Link", "label": "Company", "options": "Company", "read_only": 1, "remember_last_selected_value": 1, - "reqd": 1, - "fetch_from": "employee.company" + "reqd": 1 }, { "allow_on_submit": 1, @@ -249,7 +250,7 @@ "is_submittable": 1, "links": [], "max_attachments": 3, - "modified": "2020-03-10 22:40:43.487721", + "modified": "2020-05-18 13:00:41.577327", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", @@ -334,4 +335,4 @@ "sort_order": "DESC", "timeline_field": "employee", "title_field": "employee_name" -} +} \ No newline at end of file diff --git a/erpnext/hr/onboarding/human_resource/human_resource.json b/erpnext/hr/onboarding/human_resource/human_resource.json new file mode 100644 index 0000000000..45dd50d8bb --- /dev/null +++ b/erpnext/hr/onboarding/human_resource/human_resource.json @@ -0,0 +1,48 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-05-14 11:51:45.050242", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 14:44:09.069921", + "modified_by": "Administrator", + "module": "HR", + "name": "Human Resource", + "owner": "Administrator", + "steps": [ + { + "step": "Create Department" + }, + { + "step": "Create Designation" + }, + { + "step": "Create Holiday list" + }, + { + "step": "Create Employee" + }, + { + "step": "Create Leave Allocation" + }, + { + "step": "Create Leave Application" + }, + { + "step": "HR Settings" + } + ], + "subtitle": "Employee, Recruitment, Payroll and more.", + "success_message": "The HR Module is all set up!", + "title": "Let's Setup the Human Resource Module. ", + "user_can_dismiss": 0 +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_department/create_department.json b/erpnext/hr/onboarding_step/create_department/create_department.json new file mode 100644 index 0000000000..c3e461550a --- /dev/null +++ b/erpnext/hr/onboarding_step/create_department/create_department.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:44:34.682115", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:26.448420", + "modified_by": "Administrator", + "name": "Create Department", + "owner": "Administrator", + "reference_document": "Department", + "title": "Create Department" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_designation/create_designation.json b/erpnext/hr/onboarding_step/create_designation/create_designation.json new file mode 100644 index 0000000000..b58c06c3a3 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_designation/create_designation.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:45:07.514193", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:41.500795", + "modified_by": "Administrator", + "name": "Create Designation", + "owner": "Administrator", + "reference_document": "Designation", + "title": "Create Designation" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_employee/create_employee.json b/erpnext/hr/onboarding_step/create_employee/create_employee.json new file mode 100644 index 0000000000..b4988a5ce5 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:43:25.561152", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 12:26:28.629074", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "title": "Create Employee" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json new file mode 100644 index 0000000000..0db67d6763 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:47:34.700174", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 12:25:38.068582", + "modified_by": "Administrator", + "name": "Create Holiday list", + "owner": "Administrator", + "reference_document": "Holiday List", + "title": "Create Holiday list" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json new file mode 100644 index 0000000000..287dd6c0a6 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:48:56.123718", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 11:48:56.123718", + "modified_by": "Administrator", + "name": "Create Leave Allocation", + "owner": "Administrator", + "reference_document": "Leave Allocation", + "title": "Create Leave Allocation" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json new file mode 100644 index 0000000000..7b70a7f5bd --- /dev/null +++ b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:49:45.400764", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 11:49:45.400764", + "modified_by": "Administrator", + "name": "Create Leave Application", + "owner": "Administrator", + "reference_document": "Leave Application", + "title": "Create Leave Application" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json new file mode 100644 index 0000000000..1f4f1bfe00 --- /dev/null +++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json @@ -0,0 +1,16 @@ +{ + "action": "Update Settings", + "creation": "2020-05-14 13:13:52.427711", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 13:13:52.427711", + "modified_by": "Administrator", + "name": "HR Settings", + "owner": "Administrator", + "reference_document": "HR Settings", + "title": "HR settings" +} \ No newline at end of file diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index 6f8f161c3d..df64006c1b 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -65,7 +65,7 @@ def get_chart_data(parameters,employees, filters): total_employee = frappe.db.count('Employee', {'status':'Active'}) others = total_employee - sum(values) - label.append(["Others"]) + label.append(["Not Set"]) values.append(others) chart = { diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index a6509766c7..096e3c5810 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -30,6 +30,9 @@ day_abbr = [ def execute(filters=None): if not filters: filters = {} + if filters.hide_year_field == 1: + filters.year = 2020 + conditions, filters = get_conditions(filters) columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) @@ -75,14 +78,11 @@ def execute(filters=None): return columns, data, None, chart_data def get_chart_data(emp_att_map, days): - from pprint import pprint - pprint(emp_att_map) labels = [] datasets = [ {"name": "Absent", "values": []}, {"name": "Present", "values": []}, {"name": "Leave", "values": []}, - {"name": "Unmarked", "values": []} ] for idx, day in enumerate(days, start=0): p = day.replace("::65", "") @@ -91,7 +91,6 @@ def get_chart_data(emp_att_map, days): total_leave_on_day = 0 total_present_on_day = 0 total_holiday = 0 - total_unmarked_on_day = 0 for emp in emp_att_map.keys(): if emp_att_map[emp][idx]: if emp_att_map[emp][idx] == "A": @@ -104,12 +103,10 @@ def get_chart_data(emp_att_map, days): if emp_att_map[emp][idx] == "L": total_leave_on_day += 1 - total_marked = total_absent_on_day + total_present_on_day + total_leave_on_day datasets[0]["values"].append(total_absent_on_day) datasets[1]["values"].append(total_present_on_day) datasets[2]["values"].append(total_leave_on_day) - datasets[3]["values"].append(frappe.db.count('Employee', {'status':'Active'}) - total_marked) chart = { @@ -119,10 +116,11 @@ def get_chart_data(emp_att_map, days): } } - chart["type"] = "bar" - # chart["spaceRatio"] = 0.1 - + chart["type"] = "line" + chart['lineOptions'] = { + "dotSize": 6 + } return chart From e6147ed9bc89639db843417704718ce98db5169c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 12 May 2020 11:18:32 +0530 Subject: [PATCH 04/22] feat: production forecasting using exponential smoothing method --- .../accounts/report/financial_statements.py | 7 +- .../__init__.py | 0 .../exponential_smoothing_forecasting.js | 97 ++++++++ .../exponential_smoothing_forecasting.json | 40 ++++ .../exponential_smoothing_forecasting.py | 222 ++++++++++++++++++ 5 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 7fb598bd68..4a35a66865 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -19,7 +19,7 @@ from six import itervalues from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False, - company=None, reset_period_on_fy_change=True): + company=None, reset_period_on_fy_change=True, ignore_fiscal_year=False): """Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label} Periodicity can be (Yearly, Quarterly, Monthly)""" @@ -67,8 +67,9 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ # if a fiscal year ends before a 12 month period period.to_date = year_end_date - period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] - period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] + if not ignore_fiscal_year: + period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] + period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] period_list.append(period) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js new file mode 100644 index 0000000000..123a82a388 --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js @@ -0,0 +1,97 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Exponential Smoothing Forecasting"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1 + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), 12), + "reqd": 1 + }, + { + "fieldname":"based_on_document", + "label": __("Based On Document"), + "fieldtype": "Select", + "options": ["Sales Order", "Delivery Note", "Quotation"], + "default": "Sales Order", + "reqd": 1 + }, + { + "fieldname":"based_on_field", + "label": __("Based On"), + "fieldtype": "Select", + "options": ["Qty", "Amount"], + "default": "Qty", + "reqd": 1 + }, + { + "fieldname":"no_of_years", + "label": __("Based On Data ( in years )"), + "fieldtype": "Select", + "options": [3, 6, 9], + "default": 3, + "reqd": 1 + }, + { + "fieldname": "periodicity", + "label": __("Periodicity"), + "fieldtype": "Select", + "options": [ + { "value": "Monthly", "label": __("Monthly") }, + { "value": "Quarterly", "label": __("Quarterly") }, + { "value": "Half-Yearly", "label": __("Half-Yearly") }, + { "value": "Yearly", "label": __("Yearly") } + ], + "default": "Yearly", + "reqd": 1 + }, + { + "fieldname":"smoothing_constant", + "label": __("Smoothing Constant"), + "fieldtype": "Select", + "options": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], + "reqd": 1, + "default": 0.3 + }, + { + "fieldname":"item_code", + "label": __("Item Code"), + "fieldtype": "Link", + "options": "Item" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + if (company) { + return { + filters: { + 'company': company + } + }; + } + } + } + ] +}; diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json new file mode 100644 index 0000000000..5092ef4e7a --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json @@ -0,0 +1,40 @@ +{ + "add_total_row": 0, + "creation": "2020-05-15 05:18:55.838030", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-05-15 05:18:55.838030", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Exponential Smoothing Forecasting", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Exponential Smoothing Forecasting", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Sales Manager" + }, + { + "role": "Sales User" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py new file mode 100644 index 0000000000..b5127f133c --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -0,0 +1,222 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ +from frappe.utils import flt, nowdate, add_years, cint, getdate +from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + +def execute(filters=None): + return ForecastingReport(filters).execute_report() + +class ExponentialSmoothingForecast(object): + def forecast_future_data(self): + for key, value in self.period_wise_data.items(): + forecast_data = [] + for period in self.period_list: + forecast_key = "forecast_" + period.key + + if value.get(period.key) and not forecast_data: + value[forecast_key] = flt(value.get("avg", 0)) or flt(value.get(period.key)) + + # will be use to forecaset next period + forecast_data.append([value.get(period.key), value.get(forecast_key)]) + elif forecast_data: + previous_period_data = forecast_data[-1] + value[forecast_key] = (previous_period_data[1] + + flt(self.filters.smoothing_constant) * ( + flt(previous_period_data[0]) - flt(previous_period_data[1]) + ) + ) + +class ForecastingReport(ExponentialSmoothingForecast): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.data = [] + self.doctype = self.filters.based_on_document + self.child_doctype = self.doctype + " Item" + self.based_on_field = ("qty" + if self.filters.based_on_field == "Qty" else "amount") + self.fieldtype = "Float" if self.based_on_field == "qty" else "Currency" + self.company_currency = erpnext.get_company_currency(self.filters.company) + + def execute_report(self): + self.prepare_periodical_data() + self.forecast_future_data() + self.data = self.period_wise_data.values() + self.add_total() + + columns = self.get_columns() + charts = self.get_chart_data() + summary_data = self.get_summary_data() + + return columns, self.data, None, charts, summary_data + + def prepare_periodical_data(self): + self.period_wise_data = {} + + from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1) + self.period_list = get_period_list(from_date, self.filters.to_date, + from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True) + + order_data = self.get_data_for_forecast() or [] + + for entry in order_data: + key = (entry.item_code, entry.warehouse) + if key not in self.period_wise_data: + self.period_wise_data[key] = entry + + period_data = self.period_wise_data[key] + for period in self.period_list: + # check if posting date is within the period + if (entry.posting_date >= period.from_date and entry.posting_date <= period.to_date): + period_data[period.key] = period_data.get(period.key, 0.0) + flt(entry.get(self.based_on_field)) + + for key, value in self.period_wise_data.items(): + list_of_period_value = [value.get(p.key, 0) for p in self.period_list] + + if list_of_period_value: + value["avg"] = sum(list_of_period_value) / len(list_of_period_value) + + def get_data_for_forecast(self): + cond = "" + if self.filters.item_code: + cond = " AND soi.item_code = %s" %(frappe.db.escape(self.filters.item_code)) + + warehouses = [] + if self.filters.warehouse: + warehouses = get_child_warehouses(self.filters.warehouse) + cond += " AND soi.warehouse in ({})".format(','.join(['%s'] * len(warehouses))) + + input_data = [self.filters.from_date, self.filters.company] + if warehouses: + input_data.extend(warehouses) + + date_field = "posting_date" if self.doctype == "Delivery Note" else "transaction_date" + + return frappe.db.sql(""" + SELECT + so.{date_field} as posting_date, soi.item_code, soi.warehouse, + soi.item_name, soi.stock_qty as qty, soi.base_amount as amount + FROM + `tab{doc}` so, `tab{child_doc}` soi + WHERE + so.docstatus = 1 AND so.name = soi.parent AND + so.{date_field} < %s AND so.company = %s {cond} + """.format(doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond), + tuple(input_data), as_dict=1) + + def add_total(self): + total_row = { + "item_code": _(frappe.bold("Total Quantity")) + } + + for value in self.data: + for period in self.period_list: + forecast_key = "forecast_" + period.key + if forecast_key not in total_row: + total_row.setdefault(forecast_key, 0.0) + + if period.key not in total_row: + total_row.setdefault(period.key, 0.0) + + total_row[forecast_key] += value.get(forecast_key, 0.0) + total_row[period.key] += value.get(period.key, 0.0) + + self.data.append(total_row) + + def get_columns(self): + columns = [{ + "label": _("Item Code"), + "options": "Item", + "fieldname": "item_code", + "fieldtype": "Link", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "width": 130 + }] + + width = 150 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 + for period in self.period_list: + if (self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] + or period.from_date >= getdate(self.filters.from_date)): + + forecast_key = 'forecast_' + period.key + + columns.append({ + "label": _(period.label), + "fieldname": forecast_key, + "fieldtype": self.fieldtype, + "width": width, + "default": 0.0 + }) + + return columns + + def get_chart_data(self): + if not self.data: return + + labels = [] + self.total_demand = [] + self.total_forecast = [] + self.total_history_forecast = [] + self.total_future_forecast = [] + + for period in self.period_list: + forecast_key = "forecast_" + period.key + + labels.append(_(period.label)) + + if period.from_date < getdate(self.filters.from_date): + self.total_demand.append(self.data[-1].get(period.key, 0)) + self.total_history_forecast.append(self.data[-1].get(forecast_key, 0)) + else: + self.total_future_forecast.append(self.data[-1].get(forecast_key, 0)) + + self.total_forecast.append(self.data[-1].get(forecast_key, 0)) + + return { + "data": { + "labels": labels, + "datasets": [ + { + "name": "Demand", + "values": self.total_demand + }, + { + "name": "Forecast", + "values": self.total_forecast + } + ] + }, + "type": "line" + } + + def get_summary_data(self): + return [ + { + "value": sum(self.total_demand), + "label": _("Total Demand (Past Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + }, + { + "value": sum(self.total_history_forecast), + "label": _("Total Forecast (Past Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + }, + { + "value": sum(self.total_future_forecast), + "indicator": "Green", + "label": _("Total Forecast (Future Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + } + ] \ No newline at end of file From 841a8db5d1d28e3618924f8548ee7e100f485c60 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 20 May 2020 12:06:03 +0530 Subject: [PATCH 05/22] updated manufacturing desk --- .../manufacturing/manufacturing.json | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index ecd2dc9b76..f2e07bfe20 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -43,10 +43,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 14:05:59.100891", + "modified": "2020-05-20 11:50:20.029056", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -88,6 +89,17 @@ "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, + { + "label": "Dashboard", + "link_to": "Manufacturing", + "restrict_to_domain": "Manufacturing", + "type": "Dashboard" + }, + { + "label": "Forecasting", + "link_to": "Exponential Smoothing Forecasting", + "type": "Report" + }, { "label": "Work Order Summary", "link_to": "Work Order Summary", @@ -95,10 +107,14 @@ "type": "Report" }, { - "label": "Dashboard", - "link_to": "Manufacturing", - "restrict_to_domain": "Manufacturing", - "type": "Dashboard" + "label": "BOM Stock Report", + "link_to": "BOM Stock Report", + "type": "Report" + }, + { + "label": "Production Planning Report", + "link_to": "Production Planning Report", + "type": "Report" } ] } \ No newline at end of file From 7cbbd68176680aa62ba1249fea799337d2ddbf81 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2020 12:55:34 +0530 Subject: [PATCH 06/22] fix: CRM module dashboard and onboarding --- erpnext/crm/dashboard_fixtures.py | 135 +++++++++--------- erpnext/crm/desk_page/crm/crm.json | 8 +- erpnext/crm/module_onboarding/crm/crm.json | 11 +- .../create_and_send_quotation.json | 4 +- .../create_lead/create_lead.json | 4 +- .../create_opportunity.json | 4 +- .../introduction_to_crm.json | 2 + .../start_campaign/start_campaign.json | 17 --- 8 files changed, 89 insertions(+), 96 deletions(-) delete mode 100644 erpnext/crm/onboarding_step/start_campaign/start_campaign.json diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py index 5b1ac9bf5e..16904b3429 100644 --- a/erpnext/crm/dashboard_fixtures.py +++ b/erpnext/crm/dashboard_fixtures.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe, erpnext, json +from frappe import _ def get_data(): return frappe._dict({ @@ -16,19 +17,19 @@ def get_dashboards(): "name": "CRM", "dashboard_name": "CRM", "charts": [ - { "chart": "Lead", "width": "Full" }, - { "chart": "Opportunity", "width": "Full"}, - { "chart": "Campaign", "width": "Half" }, - { "chart": "Opportunities Won", "width": "Half" }, - { "chart": "Territory Wise Opportunity", "width": "Half"}, + { "chart": "Incoming Leads", "width": "Full" }, + { "chart": "Opportunity Trends", "width": "Full"}, + { "chart": "Won Opportunities", "width": "Full" }, + { "chart": "Territory Wise Opportunity Count", "width": "Half"}, { "chart": "Territory Wise Sales", "width": "Half"}, - { "chart": "Qualified For Call", "width": "Full"}, + { "chart": "Opportunities via Campaigns", "width": "Half" }, { "chart": "Lead Source", "width": "Half"} ], "cards": [ - { "card": "New Lead" }, - { "card": "New Opportunity" }, - { "card": "Won Opportunity" }, + { "card": "New Lead (Last 1 Month)" }, + { "card": "New Opportunity (Last 1 Month)" }, + { "card": "Won Opportunity (Last 1 Month)" }, + { "card": "Open Opportunity"}, ] }] @@ -46,11 +47,11 @@ def get_charts(): company = get_company_for_dashboards() return [{ - "name": "Lead", + "name": "Incoming Leads", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Lead", + "chart_name": _("Incoming Leads"), "timespan": "Last Quarter", "time_interval": "Weekly", "document_type": "Lead", @@ -62,11 +63,11 @@ def get_charts(): "type": "Bar" }, { - "name": "Opportunity", + "name": "Opportunity Trends", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Opportunity", + "chart_name": _("Opportunity Trends"), "timespan": "Last Quarter", "time_interval": "Weekly", "document_type": "Opportunity", @@ -78,28 +79,25 @@ def get_charts(): "type": "Bar" }, { - "name": "Campaign", + "name": "Opportunities via Campaigns", + "chart_name": _("Opportunities via Campaigns"), "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Count", - "chart_name": "Campaign", - "timespan": "Last Year", - "time_interval": "Monthly", - "document_type": "Campaign", - "based_on": "creation", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "campaign", + "document_type": "Opportunity", 'is_public': 1, 'timeseries': 1, "owner": "Administrator", "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), - "type": "Line", - "color": "#428b46" + "type": "Pie" }, { - "name": "Opportunities Won", + "name": "Won Opportunities", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Opportunities Won", + "chart_name": _("Won Opportunities"), "timespan": "Last Year", "time_interval": "Monthly", "document_type": "Opportunity", @@ -107,20 +105,25 @@ def get_charts(): 'is_public': 1, 'timeseries': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False], + ["Opportunity", "status", "=", "Converted", False]]), "type": "Bar" }, { - "name": "Territory Wise Opportunity", + "name": "Territory Wise Opportunity Count", "doctype": "Dashboard Chart", "chart_type": "Group By", "group_by_type": "Count", "group_by_based_on": "territory", - "chart_name": "Territory Wise Opportunity", + "chart_name": _("Territory Wise Opportunity Count"), "document_type": "Opportunity", 'is_public': 1, + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False] + ]), "owner": "Administrator", - "type": "Line" + "type": "Donut" }, { "name": "Territory Wise Sales", @@ -128,31 +131,16 @@ def get_charts(): "chart_type": "Group By", "group_by_type": "Sum", "group_by_based_on": "territory", - "chart_name": "Territory Wise Sales", + "chart_name": _("Territory Wise Sales"), "aggregate_function_based_on": "opportunity_amount", "document_type": "Opportunity", 'is_public': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), - "type": "Bar", - "color": "#7575ff" - }, - { - "name": "Qualified For Call", - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Count", - "chart_name": "Qualified For Call", - "timespan": "Last Quarter", - "time_interval": "Weekly", - "document_type": "Opportunity", - "based_on": "modified", - 'is_public': 1, - 'timeseries': 1, - "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Qualification", False]]), - "type": "Line", - "color": "#fff168" + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False], + ["Opportunity", "status", "=", "Converted", False] + ]), + "type": "Donut" }, { "name": "Lead Source", @@ -160,44 +148,55 @@ def get_charts(): "chart_type": "Group By", "group_by_type": "Count", "group_by_based_on": "source", - "chart_name": "Lead Source", + "chart_name": _("Lead Source"), "document_type": "Lead", 'is_public': 1, "owner": "Administrator", - "type": "Donut" + "type": "Pie" }] def get_number_cards(): return [{ "doctype": "Number Card", "document_type": "Lead", - "name": "New Lead", - "filters_json": json.dumps([["Lead","status","=","Lead",False]]), + "name": "New Lead (Last 1 Month)", + "filters_json": json.dumps([["Lead","creation","Previous","1 month",False]]), "function": "Count", "is_public": 1, - "label": "New Lead", + "label": _("New Lead (Last 1 Month)"), "show_percentage_stats": 1, "stats_time_interval": "Daily" }, { "doctype": "Number Card", "document_type": "Opportunity", - "name": "New Opportunity", + "name": "New Opportunity (Last 1 Month)", + "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "function": "Count", + "is_public": 1, + "label": _("New Opportunity (Last 1 Month)"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Won Opportunity (Last 1 Month)", + "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "function": "Count", + "is_public": 1, + "label": _("Won Opportunity (Last 1 Month)"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Open Opportunity", "filters_json": json.dumps([["Opportunity","status","=","Open",False]]), "function": "Count", "is_public": 1, - "label": "New Opportunity", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "doctype": "Number Card", - "document_type": "Opportunity", - "name": "Won Opportunity", - "filters_json": json.dumps([["Opportunity","status","=","Converted",False]]), - "function": "Count", - "is_public": 1, - "label": "Won Opportunity", + "label": _("Open Opportunity"), "show_percentage_stats": 1, "stats_time_interval": "Daily" }] \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 80d3d18de9..2fc4582917 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -38,10 +38,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-14 17:20:07.636751", + "modified": "2020-05-20 12:11:36.250491", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -73,6 +74,11 @@ "label": "Sales Analytics", "link_to": "Sales Analytics", "type": "Report" + }, + { + "label": "CRM Dashboard", + "link_to": "CRM", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 9129c2625a..694763f7c7 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -4,10 +4,10 @@ "role": "Sales Master Manager" }, { - "role": "Administrator" + "role": "Sales Manager" }, { - "role": "Sales Manager" + "role": "Sales User" } ], "creation": "2020-05-09 23:42:50.901548", @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-14 17:41:11.083917", + "modified": "2020-05-20 12:53:47.029412", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -25,9 +25,6 @@ { "step": "Introduction to CRM" }, - { - "step": "Start Campaign" - }, { "step": "Create Lead" }, @@ -38,7 +35,7 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "subtitle": "Lead, Opportunity, Customer and more", "success_message": "CRM Module is all setup!", "title": "Let's Setup Your CRM", "user_can_dismiss": 1 diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index 0997017933..a6edfd7e53 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -13,5 +13,7 @@ "name": "Create and Send Quotation", "owner": "Administrator", "reference_document": "Quotation", - "title": "Create and Send Quotation" + "show_full_form": 0, + "title": "Create and Send Quotation", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index b1076e0bf9..47a45d70a8 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -13,5 +13,7 @@ "name": "Create Lead", "owner": "Administrator", "reference_document": "Lead", - "title": "Create Lead" + "show_full_form": 0, + "title": "Create Lead", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json index 20949a435b..231cf17bb5 100644 --- a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -13,5 +13,7 @@ "name": "Create Opportunity", "owner": "Administrator", "reference_document": "Opportunity", - "title": "Create Opportunity" + "show_full_form": 0, + "title": "Create Opportunity", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index c923a77e1b..552ade0fdd 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -12,6 +12,8 @@ "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to CRM", + "validate_action": 0, "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json deleted file mode 100644 index 53c3becea3..0000000000 --- a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-09 23:38:10.284957", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-14 17:28:54.285958", - "modified_by": "Administrator", - "name": "Start Campaign", - "owner": "Administrator", - "reference_document": "Campaign", - "title": "Start Campaign" -} \ No newline at end of file From 91a9d3fbe56ddee81babd3c64c0760749804bfcb Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:12:46 +0530 Subject: [PATCH 07/22] fix: Reload doc in Healthcare company patch (#21808) --- .../patches/v13_0/set_company_field_in_healthcare_doctypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index 2c646b164f..9d0dae4553 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -6,4 +6,5 @@ def execute(): doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] for entry in doctypes: if frappe.db.exists('DocType', entry): + frappe.reload_doc("Healthcare", "doctype", entry) frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 2b04e8eef096f52d93473f4537131c2233fb441e Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:13:43 +0530 Subject: [PATCH 08/22] fix: Validate Payment Gateway only if it exists in Payment Request. (#21805) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 68aeb6d1d6..287e00f70f 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -69,7 +69,7 @@ class PaymentRequest(Document): elif self.payment_request_type == 'Inward': self.db_set('status', 'Requested') - send_mail = self.payment_gateway_validation() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ From 2346353a2231082868cd07de23a0ab12ea310c5e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 20 May 2020 16:14:09 +0530 Subject: [PATCH 09/22] fix: missing parameter (#21802) --- .../monthly_attendance_sheet/monthly_attendance_sheet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index efc43bf1d1..60767b5db0 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -66,11 +66,11 @@ def execute(filters=None): emp_att_map = {} for parameter in group_by_parameters: data.append([ ""+ parameter + ""]) - record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) emp_att_map.update(aaa) data += record else: - record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) data += record chart_data = get_chart_data(emp_att_map, days) @@ -120,7 +120,7 @@ def get_chart_data(emp_att_map, days): return chart -def add_data(employee_map, att_map, filters, holiday_map, conditions,leave_list=None): +def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=None): record = [] emp_att_map = {} From ab250e4ed55aac4f3f451108b548f3fb71a31e09 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 20 May 2020 16:15:48 +0530 Subject: [PATCH 10/22] enable Allow Rename in sales stage (#21799) --- .../crm/doctype/sales_stage/sales_stage.json | 120 +++++------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 4374bb5831..77aa559b77 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -1,96 +1,44 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:stage_name", - "beta": 0, - "creation": "2018-10-01 09:28:16.399518", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:stage_name", + "creation": "2018-10-01 09:28:16.399518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stage_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stage_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stage Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "stage_name", + "fieldtype": "Data", + "label": "Stage Name", "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-01 09:29:43.230378", - "modified_by": "Administrator", - "module": "CRM", - "name": "Sales Stage", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-20 12:22:01.866472", + "modified_by": "Administrator", + "module": "CRM", + "name": "Sales Stage", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From e091789332662a4e27d4c99cc788cb2cf321e7b4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 20 May 2020 16:17:12 +0530 Subject: [PATCH 11/22] feat: remove website settings from boot (#21801) --- erpnext/startup/boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 4ca43a89b8..2b80fb8dfa 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -10,7 +10,6 @@ def boot_session(bootinfo): """boot session - send website info if guest""" bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or '' - bootinfo.website_settings = frappe.get_doc('Website Settings') if frappe.session['user']!='Guest': update_page_info(bootinfo) From 22aec57bebb395b1f309ead0211d1819d197c622 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 8 May 2020 16:39:17 +0530 Subject: [PATCH 12/22] feat: asset onboarding, dashboards --- erpnext/assets/dashboard_fixtures.py | 96 ++++++++ erpnext/assets/desk_page/assets/assets.json | 4 +- erpnext/assets/doctype/asset/asset.js | 3 +- erpnext/assets/doctype/asset/asset.py | 2 + erpnext/assets/onboarding/assets/assets.json | 39 ++++ .../create_a_fixed_asset_item.json | 16 ++ .../create_the_asset/create_the_asset.json | 16 ++ .../introduction_to_assets.json | 16 ++ .../purchase_the_asset_item.json | 16 ++ .../category_wise_asset_value/__init__.py | 0 .../category_wise_asset_value.js | 43 ++++ .../category_wise_asset_value.json | 29 +++ .../category_wise_asset_value.py | 137 ++++++++++++ .../fixed_asset_register.js | 18 +- .../fixed_asset_register.py | 211 ++++++++++-------- .../location_wise_asset_value/__init__.py | 0 .../location_wise_asset_value.js | 43 ++++ .../location_wise_asset_value.json | 29 +++ .../location_wise_asset_value.py | 137 ++++++++++++ 19 files changed, 752 insertions(+), 103 deletions(-) create mode 100644 erpnext/assets/dashboard_fixtures.py create mode 100644 erpnext/assets/onboarding/assets/assets.json create mode 100644 erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json create mode 100644 erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json create mode 100644 erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json create mode 100644 erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json create mode 100644 erpnext/assets/report/category_wise_asset_value/__init__.py create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py create mode 100644 erpnext/assets/report/location_wise_asset_value/__init__.py create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py new file mode 100644 index 0000000000..aa4eed2f74 --- /dev/null +++ b/erpnext/assets/dashboard_fixtures.py @@ -0,0 +1,96 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "Asset", + "dashboard_name": "Asset", + "charts": [ + { "chart": "Category-wise Asset Value" }, + { "chart": "Location-wise Asset Value" }, + ] + }] + +def get_charts(): + return [ + { + "name": "Category-wise Asset Value", + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "report_name": "Category-wise Asset Value", + "is_custom": 1, + "x_field": "asset_category", + "timeseries": 0, + "filters_json": "{}", + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Category-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Location-wise Asset Value", + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "report_name": "Location-wise Asset Value", + "is_custom": 1, + "x_field": "location", + "timeseries": 0, + "filters_json": "{}", + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Location-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + } + ] + + +def get_number_cards(): + return [ + { + "name": "Asset Value", + "label": "Asset Value ", + "function": "Sum", + "aggregate_function_based_on": "gross_purchase_amount", + "document_type": "Asset", + "is_public": 0, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card" + } + ] \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 03094160e5..13709e370b 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -24,14 +24,14 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-04-01 11:28:51.072198", + "modified": "2020-05-08 16:07:04.671296", "modified_by": "Administrator", "module": "Assets", "name": "Assets", + "onboarding": "Assets", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index a53ff88177..fba20c0c87 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -387,7 +387,8 @@ frappe.ui.form.on('Asset', { } frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); - frm.set_value('location', item.asset_location); + item.asset_location && frm.set_value('location', item.asset_location); + frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); }, set_depreciation_rate: function(frm, row) { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 505ba4c6b6..2ecabe60f0 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -127,6 +127,8 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): + if self.is_existing_asset: return + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ Please do not book expense of multiple assets against one single Asset.") diff --git a/erpnext/assets/onboarding/assets/assets.json b/erpnext/assets/onboarding/assets/assets.json new file mode 100644 index 0000000000..b32a7f2be5 --- /dev/null +++ b/erpnext/assets/onboarding/assets/assets.json @@ -0,0 +1,39 @@ +{ + "allow_roles": [ + { + "role": "Accounts User" + }, + { + "role": "Maintenance User" + } + ], + "creation": "2020-05-08 15:10:45.571457", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-08 16:17:31.685943", + "modified_by": "Administrator", + "module": "Assets", + "name": "Assets", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Assets" + }, + { + "step": "Create a Fixed Asset Item" + }, + { + "step": "Purchase the Asset Item" + }, + { + "step": "Create the Asset" + } + ], + "subtitle": "Assets, Depreciations, Repairs and more", + "success_message": "The Asset Module is all set up!", + "title": "Let's Setup Asset Management", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json new file mode 100644 index 0000000000..f5818c091f --- /dev/null +++ b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:20:00.259985", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:20:00.259985", + "modified_by": "Administrator", + "name": "Create a Fixed Asset Item", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create a Fixed Asset Item" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json b/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json new file mode 100644 index 0000000000..28d8485f97 --- /dev/null +++ b/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create the Asset", + "owner": "Administrator", + "reference_document": "Asset", + "title": "Create the Asset" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json new file mode 100644 index 0000000000..8aceafd7e5 --- /dev/null +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -0,0 +1,16 @@ +{ + "action": "Watch Video", + "creation": "2020-05-08 13:18:25.424715", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 16:06:16.625646", + "modified_by": "Administrator", + "name": "Introduction to Assets", + "owner": "Administrator", + "title": "Introduction to Fixed Asset Management", + "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json b/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json new file mode 100644 index 0000000000..b6f38decb9 --- /dev/null +++ b/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:28.208059", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:28.208059", + "modified_by": "Administrator", + "name": "Purchase the Asset Item", + "owner": "Administrator", + "reference_document": "Purchase Receipt", + "title": "Purchase the Asset Item" +} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/__init__.py b/erpnext/assets/report/category_wise_asset_value/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js new file mode 100644 index 0000000000..aa643efb2d --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js @@ -0,0 +1,43 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Category-wise Asset Value"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, + ] +}; diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json new file mode 100644 index 0000000000..a6dbce0516 --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "creation": "2020-05-08 15:36:02.116096", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "javascript": "", + "modified": "2020-05-08 15:36:02.116096", + "modified_by": "Administrator", + "module": "Assets", + "name": "Category-wise Asset Value", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Asset", + "report_name": "Category-wise Asset Value", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Quality Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py new file mode 100644 index 0000000000..96131e9260 --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py @@ -0,0 +1,137 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cstr, today, flt + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_conditions(filters): + conditions = { 'docstatus': 1 } + + if filters.get('company'): + conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') + + return conditions + +def get_data(filters): + + data = [] + depreciation_amount_map = get_finance_book_value_map(filters) + + assets_record = frappe.db.get_all("Asset", + filters=get_conditions(filters), + fields=["name", "asset_name", "asset_category", "gross_purchase_amount", + "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], + group_by="asset_category") + + for asset in assets_record: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: + row = { + "asset_category": asset.asset_category, + "asset_id": asset.name, + "asset_name": asset.asset_name, + "purchase_date": asset.purchase_date, + "available_for_use_date": asset.available_for_use_date, + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "asset_value": asset_value + } + data.append(row) + + return data + +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + + return frappe._dict(frappe.db.sql(''' Select + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` + WHERE + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) + +def get_columns(filters): + return [ + { + "label": _("Asset Category"), + "fieldtype": "Link", + "fieldname": "asset_category", + "options": "Asset Category", + "width": 120 + }, + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 100 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 91ce9ce7fe..d68993c297 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -30,18 +30,24 @@ frappe.query_reports["Fixed Asset Register"] = { label: __("Available For Use Date"), fieldtype: "Date" }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, { fieldname:"asset_category", label: __("Asset Category"), fieldtype: "Link", options: "Asset Category" }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, { fieldname:"is_existing_asset", label: __("Is Existing Asset"), diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index fa2fe7b4a3..64ee6a3503 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -10,105 +10,13 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - return columns, data + chart = prepare_chart_data(data, columns) -def get_columns(filters): - return [ - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Asset Category"), - "fieldtype": "Link", - "fieldname": "asset_category", - "options": "Asset Category", - "width": 100 - }, - { - "label": _("Status"), - "fieldtype": "Data", - "fieldname": "status", - "width": 90 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "options": "Currency", - "width": 90 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "options": "Currency", - "width": 90 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "options": "Currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "options": "Currency", - "width": 90 - }, - { - "label": _("Cost Center"), - "fieldtype": "Link", - "fieldname": "cost_center", - "options": "Cost Center", - "width": 100 - }, - { - "label": _("Department"), - "fieldtype": "Link", - "fieldname": "department", - "options": "Department", - "width": 100 - }, - { - "label": _("Vendor Name"), - "fieldtype": "Data", - "fieldname": "vendor_name", - "width": 100 - }, - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 100 - }, - ] + return columns, data, None, chart def get_conditions(filters): conditions = { 'docstatus': 1 } status = filters.status - date = filters.date if filters.get('company'): conditions["company"] = filters.company @@ -120,6 +28,8 @@ def get_conditions(filters): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): conditions["asset_category"] = filters.get('asset_category') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') # In Store assets are those that are not sold or scrapped operand = 'not in' @@ -169,6 +79,22 @@ def get_data(filters): return data +def prepare_chart_data(data, columns): + label_values_map = {} + for d in data: + if not label_values_map.get(d.get('asset_category')): + label_values_map[d.get('asset_category')] = 0 + label_values_map[d.get('asset_category')] += d.get('asset_value') + + return { + "data" : { + "labels": label_values_map.keys(), + "datasets": [{ "values": label_values_map.values() }] + }, + "type": 'donut', + "height": 250 + } + def get_finance_book_value_map(filters): date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() @@ -201,3 +127,100 @@ def get_purchase_invoice_supplier_map(): AND pii.is_fixed_asset=1 AND pi.docstatus=1 AND pi.is_return=0''')) + +def get_columns(filters): + return [ + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 60 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Asset Category"), + "fieldtype": "Link", + "fieldname": "asset_category", + "options": "Asset Category", + "width": 100 + }, + { + "label": _("Status"), + "fieldtype": "Data", + "fieldname": "status", + "width": 80 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Cost Center"), + "fieldtype": "Link", + "fieldname": "cost_center", + "options": "Cost Center", + "width": 100 + }, + { + "label": _("Department"), + "fieldtype": "Link", + "fieldname": "department", + "options": "Department", + "width": 100 + }, + { + "label": _("Vendor Name"), + "fieldtype": "Data", + "fieldname": "vendor_name", + "width": 100 + }, + { + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 100 + }, + ] \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/__init__.py b/erpnext/assets/report/location_wise_asset_value/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js new file mode 100644 index 0000000000..efdafa18c5 --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js @@ -0,0 +1,43 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Location-wise Asset Value"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, + ] +}; diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json new file mode 100644 index 0000000000..2a554d83d0 --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "creation": "2020-05-08 15:47:55.036143", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "javascript": "", + "modified": "2020-05-08 15:47:55.036143", + "modified_by": "Administrator", + "module": "Assets", + "name": "Location-wise Asset Value", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Asset", + "report_name": "Location-wise Asset Value", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Quality Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py new file mode 100644 index 0000000000..6aade0e27a --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py @@ -0,0 +1,137 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cstr, today, flt + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_conditions(filters): + conditions = { 'docstatus': 1 } + + if filters.get('company'): + conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') + + return conditions + +def get_data(filters): + + data = [] + depreciation_amount_map = get_finance_book_value_map(filters) + + assets_record = frappe.db.get_all("Asset", + filters=get_conditions(filters), + fields=["name", "asset_name", "location", "gross_purchase_amount", + "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], + group_by="location") + + for asset in assets_record: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: + row = { + "location": asset.location, + "asset_id": asset.name, + "asset_name": asset.asset_name, + "purchase_date": asset.purchase_date, + "available_for_use_date": asset.available_for_use_date, + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "asset_value": asset_value + } + data.append(row) + + return data + +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + + return frappe._dict(frappe.db.sql(''' Select + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` + WHERE + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) + +def get_columns(filters): + return [ + { + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 120 + }, + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 100 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] \ No newline at end of file From 03007de4d79273df5072731eb5ee4139d2d25b88 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 8 May 2020 16:55:19 +0530 Subject: [PATCH 13/22] fix: unexpected removal of finance book filter --- .../report/fixed_asset_register/fixed_asset_register.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index d68993c297..a88f6accb8 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -36,6 +36,12 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Asset Category" }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, { fieldname:"cost_center", label: __("Cost Center"), From 1dcf10339879e58dbbc4354eaa7ab8582e2c48c3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 14:55:38 +0530 Subject: [PATCH 14/22] chore: enhance fixed asset report and remove redundant reports --- .../category_wise_asset_value/__init__.py | 0 .../category_wise_asset_value.js | 43 ------ .../category_wise_asset_value.json | 29 ---- .../category_wise_asset_value.py | 137 ------------------ .../fixed_asset_register.js | 7 + .../fixed_asset_register.py | 61 +++++++- .../location_wise_asset_value/__init__.py | 0 .../location_wise_asset_value.js | 43 ------ .../location_wise_asset_value.json | 29 ---- .../location_wise_asset_value.py | 137 ------------------ 10 files changed, 62 insertions(+), 424 deletions(-) delete mode 100644 erpnext/assets/report/category_wise_asset_value/__init__.py delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py delete mode 100644 erpnext/assets/report/location_wise_asset_value/__init__.py delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py diff --git a/erpnext/assets/report/category_wise_asset_value/__init__.py b/erpnext/assets/report/category_wise_asset_value/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js deleted file mode 100644 index aa643efb2d..0000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Category-wise Asset Value"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" - }, - { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" - }, - { - fieldname:"cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" - }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, - { - fieldname:"is_existing_asset", - label: __("Is Existing Asset"), - fieldtype: "Check" - }, - ] -}; diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json deleted file mode 100644 index a6dbce0516..0000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-08 15:36:02.116096", - "disable_prepared_report": 1, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "javascript": "", - "modified": "2020-05-08 15:36:02.116096", - "modified_by": "Administrator", - "module": "Assets", - "name": "Category-wise Asset Value", - "owner": "Administrator", - "prepared_report": 0, - "query": "", - "ref_doctype": "Asset", - "report_name": "Category-wise Asset Value", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Quality Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py deleted file mode 100644 index 96131e9260..0000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import cstr, today, flt - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - -def get_conditions(filters): - conditions = { 'docstatus': 1 } - - if filters.get('company'): - conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) - if filters.get('is_existing_asset'): - conditions["is_existing_asset"] = filters.get('is_existing_asset') - if filters.get('cost_center'): - conditions["cost_center"] = filters.get('cost_center') - - return conditions - -def get_data(filters): - - data = [] - depreciation_amount_map = get_finance_book_value_map(filters) - - assets_record = frappe.db.get_all("Asset", - filters=get_conditions(filters), - fields=["name", "asset_name", "asset_category", "gross_purchase_amount", - "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], - group_by="asset_category") - - for asset in assets_record: - asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "asset_category": asset.asset_category, - "asset_id": asset.name, - "asset_name": asset.asset_name, - "purchase_date": asset.purchase_date, - "available_for_use_date": asset.available_for_use_date, - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, - "asset_value": asset_value - } - data.append(row) - - return data - -def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() - - return frappe._dict(frappe.db.sql(''' Select - parent, SUM(depreciation_amount) - FROM `tabDepreciation Schedule` - WHERE - parentfield='schedules' - AND schedule_date<=%s - AND journal_entry IS NOT NULL - AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(filters.finance_book or '')))) - -def get_columns(filters): - return [ - { - "label": _("Asset Category"), - "fieldtype": "Link", - "fieldname": "asset_category", - "options": "Asset Category", - "width": 120 - }, - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "options": "company:currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - } - ] \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index a88f6accb8..e886a35e5d 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -54,6 +54,13 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Finance Book" }, + { + fieldname:"group_by", + label: __("Group By"), + fieldtype: "Select", + options: " \nAsset Category\nLocation", + default: '', + }, { fieldname:"is_existing_asset", label: __("Is Existing Asset"), diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 64ee6a3503..c6b0c4ecf5 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -44,23 +44,33 @@ def get_data(filters): data = [] - conditions = get_conditions(filters) depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() - assets_record = frappe.db.get_all("Asset", - filters=conditions, - fields=["name", "asset_name", "department", "cost_center", "purchase_receipt", + conditions = get_conditions(filters) + group_by = frappe.scrub(filters.get("group_by") or "") + + if group_by: + if group_by == "asset_category": + fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] + else: + fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) + print(assets_record) + else: + fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", - "available_for_use_date", "status", "purchase_invoice", "opening_accumulated_depreciation"]) + "available_for_use_date", "purchase_invoice", "opening_accumulated_depreciation"] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) for asset in assets_record: asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - flt(depreciation_amount_map.get(asset.name)) if asset_value: row = { - "asset_id": asset.name, + "asset_id": asset.asset_id, "asset_name": asset.asset_name, "status": asset.status, "department": asset.department, @@ -129,6 +139,45 @@ def get_purchase_invoice_supplier_map(): AND pi.is_return=0''')) def get_columns(filters): + if filters.get("group_by"): + return [ + { + "label": _("{}").format(filters.get("group_by")), + "fieldtype": "Link", + "fieldname": frappe.scrub(filters.get("group_by")), + "options": filters.get("group_by"), + "width": 120 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] + return [ { "label": _("Asset Id"), diff --git a/erpnext/assets/report/location_wise_asset_value/__init__.py b/erpnext/assets/report/location_wise_asset_value/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js deleted file mode 100644 index efdafa18c5..0000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Location-wise Asset Value"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" - }, - { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" - }, - { - fieldname:"cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" - }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, - { - fieldname:"is_existing_asset", - label: __("Is Existing Asset"), - fieldtype: "Check" - }, - ] -}; diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json deleted file mode 100644 index 2a554d83d0..0000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-08 15:47:55.036143", - "disable_prepared_report": 1, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "javascript": "", - "modified": "2020-05-08 15:47:55.036143", - "modified_by": "Administrator", - "module": "Assets", - "name": "Location-wise Asset Value", - "owner": "Administrator", - "prepared_report": 0, - "query": "", - "ref_doctype": "Asset", - "report_name": "Location-wise Asset Value", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Quality Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py deleted file mode 100644 index 6aade0e27a..0000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import cstr, today, flt - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - -def get_conditions(filters): - conditions = { 'docstatus': 1 } - - if filters.get('company'): - conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) - if filters.get('is_existing_asset'): - conditions["is_existing_asset"] = filters.get('is_existing_asset') - if filters.get('cost_center'): - conditions["cost_center"] = filters.get('cost_center') - - return conditions - -def get_data(filters): - - data = [] - depreciation_amount_map = get_finance_book_value_map(filters) - - assets_record = frappe.db.get_all("Asset", - filters=get_conditions(filters), - fields=["name", "asset_name", "location", "gross_purchase_amount", - "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], - group_by="location") - - for asset in assets_record: - asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "location": asset.location, - "asset_id": asset.name, - "asset_name": asset.asset_name, - "purchase_date": asset.purchase_date, - "available_for_use_date": asset.available_for_use_date, - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, - "asset_value": asset_value - } - data.append(row) - - return data - -def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() - - return frappe._dict(frappe.db.sql(''' Select - parent, SUM(depreciation_amount) - FROM `tabDepreciation Schedule` - WHERE - parentfield='schedules' - AND schedule_date<=%s - AND journal_entry IS NOT NULL - AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(filters.finance_book or '')))) - -def get_columns(filters): - return [ - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 120 - }, - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "options": "company:currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - } - ] \ No newline at end of file From ecddf3358d69c56a438d404886d9458e86a1f51f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 15:12:11 +0530 Subject: [PATCH 15/22] fix: dashboard fixtures --- erpnext/assets/dashboard_fixtures.py | 125 ++++++++---------- .../fixed_asset_register.py | 25 ++-- 2 files changed, 68 insertions(+), 82 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index aa4eed2f74..22c4a3e4f9 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -8,8 +8,7 @@ import json def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts() }) def get_dashboards(): @@ -24,73 +23,55 @@ def get_dashboards(): def get_charts(): return [ - { - "name": "Category-wise Asset Value", - "chart_name": "Category-wise Asset Value", - "chart_type": "Report", - "report_name": "Category-wise Asset Value", - "is_custom": 1, - "x_field": "asset_category", - "timeseries": 0, - "filters_json": "{}", - "type": "Donut", - "doctype": "Dashboard Chart", - "y_axis": [ - { - "parent": "Category-wise Asset Value", - "parentfield": "y_axis", - "parenttype": "Dashboard Chart", - "y_field": "asset_value", - "doctype": "Dashboard Chart Field" - } - ], - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "name": "Location-wise Asset Value", - "chart_name": "Location-wise Asset Value", - "chart_type": "Report", - "report_name": "Location-wise Asset Value", - "is_custom": 1, - "x_field": "location", - "timeseries": 0, - "filters_json": "{}", - "type": "Donut", - "doctype": "Dashboard Chart", - "y_axis": [ - { - "parent": "Location-wise Asset Value", - "parentfield": "y_axis", - "parenttype": "Dashboard Chart", - "y_field": "asset_value", - "doctype": "Dashboard Chart Field" - } - ], - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - } - ] - - -def get_number_cards(): - return [ - { - "name": "Asset Value", - "label": "Asset Value ", - "function": "Sum", - "aggregate_function_based_on": "gross_purchase_amount", - "document_type": "Asset", - "is_public": 0, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "filters_json": "[]", - "doctype": "Number Card" - } - ] \ No newline at end of file + { + "name": "Category-wise Asset Value", + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "report_name": "Fixed Asset Report", + "is_custom": 1, + "x_field": "asset_category", + "timeseries": 0, + "filters_json": json.dumps("""{"status":"In Location","group_by":"Asset Category","is_existing_asset":0}"""), + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Category-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Location-wise Asset Value", + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "report_name": "Location-wise Asset Value", + "is_custom": 1, + "x_field": "location", + "timeseries": 0, + "filters_json": json.dumps("""{"status":"In Location","group_by":"Location","is_existing_asset":0}"""), + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Location-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }] diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index c6b0c4ecf5..81db57636b 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -10,7 +10,7 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - chart = prepare_chart_data(data, columns) + chart = prepare_chart_data(data) if not filters.get("group_by") else {} return columns, data, None, chart @@ -89,20 +89,25 @@ def get_data(filters): return data -def prepare_chart_data(data, columns): - label_values_map = {} +def prepare_chart_data(data): + labels, asset_values, depreciated_amounts = [], [], [] for d in data: - if not label_values_map.get(d.get('asset_category')): - label_values_map[d.get('asset_category')] = 0 - label_values_map[d.get('asset_category')] += d.get('asset_value') + labels.append(d.asset_id) + asset_values.append(d.asset_value) + depreciated_amounts.append(d.depreciated_amount) return { "data" : { - "labels": label_values_map.keys(), - "datasets": [{ "values": label_values_map.values() }] + "labels": labels, + "datasets": [ + { 'name': _('Asset Value'), 'values': asset_values }, + { 'name': _('Depreciatied Amount'), 'values': depreciated_amounts} + ] + }, + "type": "bar", + "barOptions": { + "stacked": 1 }, - "type": 'donut', - "height": 250 } def get_finance_book_value_map(filters): From 3882939fd9daf057f93871c148cd89e340503aa2 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 15:49:18 +0530 Subject: [PATCH 16/22] fix: dashboard fixtures and FAR chart --- erpnext/assets/dashboard_fixtures.py | 2 +- .../introduction_to_assets/introduction_to_assets.json | 2 +- .../report/fixed_asset_register/fixed_asset_register.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 22c4a3e4f9..93a06544f0 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -53,7 +53,7 @@ def get_charts(): "name": "Location-wise Asset Value", "chart_name": "Location-wise Asset Value", "chart_type": "Report", - "report_name": "Location-wise Asset Value", + "report_name": "Fixed Asset Report", "is_custom": 1, "x_field": "location", "timeseries": 0, diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json index 8aceafd7e5..d48dd1cd3d 100644 --- a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -11,6 +11,6 @@ "modified_by": "Administrator", "name": "Introduction to Assets", "owner": "Administrator", - "title": "Introduction to Fixed Asset Management", + "title": "Introduction to Assets", "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" } \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 81db57636b..0b4b334326 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -92,9 +92,9 @@ def get_data(filters): def prepare_chart_data(data): labels, asset_values, depreciated_amounts = [], [], [] for d in data: - labels.append(d.asset_id) - asset_values.append(d.asset_value) - depreciated_amounts.append(d.depreciated_amount) + labels.append(d.get("asset_id")) + asset_values.append(d.get("asset_value")) + depreciated_amounts.append(d.get("depreciated_amount")) return { "data" : { From 8990697fee02a5680ecfe2de4ffe5dd6f6904e36 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 May 2020 17:38:34 +0530 Subject: [PATCH 17/22] feat: fixed asset register based on fiscal year *asset value chart --- erpnext/assets/dashboard_fixtures.py | 105 ++++++++++++++++-- erpnext/assets/desk_page/assets/assets.json | 13 ++- .../fixed_asset_register.js | 70 +++++++++--- .../fixed_asset_register.py | 62 +++++++---- 4 files changed, 199 insertions(+), 51 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 93a06544f0..84e67cf648 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -3,12 +3,13 @@ import frappe import json - +from frappe.utils import nowdate, add_months def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts() + "charts": get_charts(), + "number_cards": get_number_cards(), }) def get_dashboards(): @@ -16,22 +17,63 @@ def get_dashboards(): "name": "Asset", "dashboard_name": "Asset", "charts": [ - { "chart": "Category-wise Asset Value" }, - { "chart": "Location-wise Asset Value" }, + { "chart": "Asset Value Analytics", "width": "Full" }, + { "chart": "Category-wise Asset Value", "width": "Half" }, + { "chart": "Location-wise Asset Value", "width": "Half" }, ] }] def get_charts(): + company = get_company_for_dashboards() + fiscal_year = get_fiscal_year() + return [ + { + "name": "Asset Value Analytics", + "chart_name": "Asset Value Analytics", + "chart_type": "Report", + "report_name": "Fixed Asset Register", + "is_custom": 1, + "group_by_type": "Count", + "number_of_groups": 0, + "is_public": 0, + "timespan": "Last Year", + "time_interval": "Yearly", + "timeseries": 0, + "filters_json": json.dumps({ + "company": company, + "status": "In Location", + "filter_based_on": "Fiscal Year", + "from_fiscal_year": fiscal_year, + "to_fiscal_year": fiscal_year, + "period_start_date": add_months(nowdate(), -12), + "period_end_date": nowdate(), + "date_based_on": "Purchase Date", + "group_by": "--Select a group--" + }), + "type": "Bar", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": { "stacked": 1 }, + "axisOptions": { "shortenYAxisNumbers": 1 }, + "tooltipOptions": {} + }), + "doctype": "Dashboard Chart", + "y_axis": [] + }, { "name": "Category-wise Asset Value", "chart_name": "Category-wise Asset Value", "chart_type": "Report", - "report_name": "Fixed Asset Report", - "is_custom": 1, + "report_name": "Fixed Asset Register", "x_field": "asset_category", "timeseries": 0, - "filters_json": json.dumps("""{"status":"In Location","group_by":"Asset Category","is_existing_asset":0}"""), + "filters_json": json.dumps({ + "company": company, + "status":"In Location", + "group_by":"Asset Category", + "is_existing_asset":0 + }), "type": "Donut", "doctype": "Dashboard Chart", "y_axis": [ @@ -53,11 +95,15 @@ def get_charts(): "name": "Location-wise Asset Value", "chart_name": "Location-wise Asset Value", "chart_type": "Report", - "report_name": "Fixed Asset Report", - "is_custom": 1, + "report_name": "Fixed Asset Register", "x_field": "location", "timeseries": 0, - "filters_json": json.dumps("""{"status":"In Location","group_by":"Location","is_existing_asset":0}"""), + "filters_json": json.dumps({ + "company": company, + "status":"In Location", + "group_by":"Location", + "is_existing_asset":0 + }), "type": "Donut", "doctype": "Dashboard Chart", "y_axis": [ @@ -74,4 +120,41 @@ def get_charts(): "height": 300, "axisOptions": {"shortenYAxisNumbers": 1} }) - }] + } + ] + +def get_number_cards(): + return [ + { + "name": "Asset Value", + "label": "Asset Value", + "function": "Sum", + "aggregate_function_based_on": "value_after_depreciation", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card", + } + ] + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_fiscal_year(): + fiscal_year = frappe.defaults.get_defaults().fiscal_year + if fiscal_year: + return fiscal_year + else: + fiscal_year_list = frappe.get_list("Fiscal Year") + if fiscal_year_list: + return fiscal_year_list[0].name + return None \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 13709e370b..429a8a897c 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -17,7 +17,12 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Asset Value Analytics", + "label": "Asset Value Analytics" + } + ], "creation": "2020-03-02 15:43:27.634865", "developer_mode_only": 0, "disable_user_customization": 0, @@ -27,7 +32,7 @@ "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-08 16:07:04.671296", + "modified": "2020-05-12 17:35:14.770662", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -42,8 +47,8 @@ "type": "DocType" }, { - "label": "Asset Movement", - "link_to": "Asset Movement", + "label": "Asset Category", + "link_to": "Asset Category", "type": "DocType" }, { diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index e886a35e5d..a08851743c 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -21,14 +21,61 @@ frappe.query_reports["Fixed Asset Register"] = { reqd: 1 }, { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" + "fieldname":"filter_based_on", + "label": __("Period Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('from_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('to_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } }, { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" + "fieldname":"from_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"to_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"from_fiscal_year", + "label": __("Start Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"to_fiscal_year", + "label": __("End Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"date_based_on", + "label": __("Date Based On"), + "fieldtype": "Select", + "options": ["Purchase Date", "Available For Use Date"], + "default": "Purchase Date", + "reqd": 1 }, { fieldname:"asset_category", @@ -48,18 +95,13 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Cost Center" }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, { fieldname:"group_by", label: __("Group By"), fieldtype: "Select", - options: " \nAsset Category\nLocation", - default: '', + options: ["--Select a group--", "Asset Category", "Location"], + default: "--Select a group--", + reqd: 1 }, { fieldname:"is_existing_asset", diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 0b4b334326..23714e6f66 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -4,26 +4,33 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr, today, flt +from frappe.utils import cstr, today, flt, add_years, formatdate, getdate +from erpnext.accounts.report.financial_statements import get_period_list, get_fiscal_year_data, validate_fiscal_year def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - chart = prepare_chart_data(data) if not filters.get("group_by") else {} + chart = prepare_chart_data(data, filters) if filters.get("group_by") not in ("Asset Category", "Location") else {} return columns, data, None, chart def get_conditions(filters): conditions = { 'docstatus': 1 } status = filters.status + date_field = frappe.scrub(filters.date_based_on or "Purchase Date") if filters.get('company'): conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.filter_based_on == "Date Range": + conditions[date_field] = ["between", [filters.from_date, filters.to_date]] + if filters.filter_based_on == "Fiscal Year": + fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year) + validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year) + year_start_date = getdate(fiscal_year.year_start_date) + year_end_date = getdate(fiscal_year.year_end_date) + + conditions[date_field] = ["between", [year_start_date, year_end_date]] if filters.get('is_existing_asset'): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): @@ -49,16 +56,17 @@ def get_data(filters): pi_supplier_map = get_purchase_invoice_supplier_map() conditions = get_conditions(filters) - group_by = frappe.scrub(filters.get("group_by") or "") - if group_by: - if group_by == "asset_category": - fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] - else: - fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + group_by = frappe.scrub(filters.get("group_by")) + if group_by == "asset_category": + fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) - print(assets_record) + + elif group_by == "location": + fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) + else: fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", @@ -89,19 +97,29 @@ def get_data(filters): return data -def prepare_chart_data(data): - labels, asset_values, depreciated_amounts = [], [], [] +def prepare_chart_data(data, filters): + labels_values_map = {} + date_field = frappe.scrub(filters.date_based_on) + + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.from_date, filters.to_date, filters.filter_based_on, "Monthly", company=filters.company) + + for d in period_list: + labels_values_map.setdefault(d.get('label'), frappe._dict({'asset_value': 0, 'depreciated_amount': 0})) + for d in data: - labels.append(d.get("asset_id")) - asset_values.append(d.get("asset_value")) - depreciated_amounts.append(d.get("depreciated_amount")) + date = d.get(date_field) + belongs_to_month = formatdate(date, "MMM YYYY") + + labels_values_map[belongs_to_month].asset_value += d.get("asset_value") + labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount") return { "data" : { - "labels": labels, + "labels": labels_values_map.keys(), "datasets": [ - { 'name': _('Asset Value'), 'values': asset_values }, - { 'name': _('Depreciatied Amount'), 'values': depreciated_amounts} + { 'name': _('Asset Value'), 'values': [d.get("asset_value") for d in labels_values_map.values()] }, + { 'name': _('Depreciatied Amount'), 'values': [d.get("depreciated_amount") for d in labels_values_map.values()] } ] }, "type": "bar", @@ -144,7 +162,7 @@ def get_purchase_invoice_supplier_map(): AND pi.is_return=0''')) def get_columns(filters): - if filters.get("group_by"): + if filters.get("group_by") in ["Asset Category", "Location"]: return [ { "label": _("{}").format(filters.get("group_by")), From 37c3bd28928ba0510429f8b015ad0fe8e2affba9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 14:10:05 +0530 Subject: [PATCH 18/22] chore: rename onboarding to module onboarding --- .../assets/{onboarding => module_onboarding}/assets/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename erpnext/assets/{onboarding => module_onboarding}/assets/assets.json (95%) diff --git a/erpnext/assets/onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json similarity index 95% rename from erpnext/assets/onboarding/assets/assets.json rename to erpnext/assets/module_onboarding/assets/assets.json index b32a7f2be5..dc77726d28 100644 --- a/erpnext/assets/onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -9,7 +9,7 @@ ], "creation": "2020-05-08 15:10:45.571457", "docstatus": 0, - "doctype": "Onboarding", + "doctype": "Module Onboarding", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", "idx": 0, "is_complete": 0, From 7c5e22e9d89b947061d3f94565621e4f8d3237fa Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 15:24:29 +0530 Subject: [PATCH 19/22] fix: depreciation amount not shown in asset register --- .../fixed_asset_register/fixed_asset_register.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 23714e6f66..af08a2a601 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -27,10 +27,10 @@ def get_conditions(filters): if filters.filter_based_on == "Fiscal Year": fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year) validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year) - year_start_date = getdate(fiscal_year.year_start_date) - year_end_date = getdate(fiscal_year.year_end_date) + filters.year_start_date = getdate(fiscal_year.year_start_date) + filters.year_end_date = getdate(fiscal_year.year_end_date) - conditions[date_field] = ["between", [year_start_date, year_end_date]] + conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]] if filters.get('is_existing_asset'): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): @@ -51,12 +51,11 @@ def get_data(filters): data = [] + conditions = get_conditions(filters) depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() - conditions = get_conditions(filters) - group_by = frappe.scrub(filters.get("group_by")) if group_by == "asset_category": @@ -86,7 +85,7 @@ def get_data(filters): "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), "gross_purchase_amount": asset.gross_purchase_amount, "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0, "available_for_use_date": asset.available_for_use_date, "location": asset.location, "asset_category": asset.asset_category, @@ -129,7 +128,7 @@ def prepare_chart_data(data, filters): } def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date return frappe._dict(frappe.db.sql(''' Select parent, SUM(depreciation_amount) From 4f024128af7b796c6c5a863404a82332d7605f7d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 14 May 2020 15:33:37 +0530 Subject: [PATCH 20/22] fix: dashboard chart dialog filters --- .../fixed_asset_register.js | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index a08851743c..1a6ef54a83 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -26,31 +26,22 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Select", "options": ["Fiscal Year", "Date Range"], "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('from_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('to_date', filter_based_on === 'Fiscal Year'); - - frappe.query_report.refresh(); - } + "reqd": 1 }, { "fieldname":"from_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), - "hidden": 1, + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12), + "depends_on": "eval: doc.filter_based_on == 'Date Range'", "reqd": 1 }, { "fieldname":"to_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), - "hidden": 1, + "default": frappe.datetime.nowdate(), + "depends_on": "eval: doc.filter_based_on == 'Date Range'", "reqd": 1 }, { @@ -59,6 +50,7 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", "reqd": 1 }, { @@ -67,6 +59,7 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", "reqd": 1 }, { From 054aafa1d7c10ed91798e66de34fbe866943f942 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2020 18:21:51 +0530 Subject: [PATCH 21/22] fix: Assets module onboarding and dashboard --- erpnext/assets/dashboard_fixtures.py | 69 +++++++++++++------ erpnext/assets/desk_page/assets/assets.json | 8 ++- .../module_onboarding/assets/assets.json | 7 +- .../create_an_asset.json} | 4 +- .../create_an_asset_category.json | 16 +++++ .../purchase_an_asset_item.json} | 4 +- 6 files changed, 79 insertions(+), 29 deletions(-) rename erpnext/assets/onboarding_step/{create_the_asset/create_the_asset.json => create_an_asset/create_an_asset.json} (84%) create mode 100644 erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json rename erpnext/assets/onboarding_step/{purchase_the_asset_item/purchase_the_asset_item.json => purchase_an_asset_item/purchase_an_asset_item.json} (82%) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 84e67cf648..9af45d16b6 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -3,7 +3,10 @@ import frappe import json -from frappe.utils import nowdate, add_months +from frappe.utils import nowdate, add_months, get_date_str +from frappe import _ +from erpnext.accounts.utils import get_fiscal_year + def get_data(): return frappe._dict({ @@ -20,17 +23,25 @@ def get_dashboards(): { "chart": "Asset Value Analytics", "width": "Full" }, { "chart": "Category-wise Asset Value", "width": "Half" }, { "chart": "Location-wise Asset Value", "width": "Half" }, - ] + ], + "cards": [ + {"card": "Total Assets"}, + {"card": "New Assets (This Year)"}, + {"card": "Asset Value"} + ] }] +fiscal_year = get_fiscal_year(date=nowdate()) +year_start_date = get_date_str(fiscal_year[1]) +year_end_date = get_date_str(fiscal_year[2]) + + def get_charts(): company = get_company_for_dashboards() - fiscal_year = get_fiscal_year() - return [ { "name": "Asset Value Analytics", - "chart_name": "Asset Value Analytics", + "chart_name": _("Asset Value Analytics"), "chart_type": "Report", "report_name": "Fixed Asset Register", "is_custom": 1, @@ -44,10 +55,10 @@ def get_charts(): "company": company, "status": "In Location", "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year, - "to_fiscal_year": fiscal_year, - "period_start_date": add_months(nowdate(), -12), - "period_end_date": nowdate(), + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], + "period_start_date": year_start_date, + "period_end_date": year_end_date, "date_based_on": "Purchase Date", "group_by": "--Select a group--" }), @@ -63,7 +74,7 @@ def get_charts(): }, { "name": "Category-wise Asset Value", - "chart_name": "Category-wise Asset Value", + "chart_name": _("Category-wise Asset Value"), "chart_type": "Report", "report_name": "Fixed Asset Register", "x_field": "asset_category", @@ -125,9 +136,33 @@ def get_charts(): def get_number_cards(): return [ + { + "name": "Total Assets", + "label": _("Total Assets"), + "function": "Count", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card", + }, + { + "name": "New Assets (This Year)", + "label": _("New Assets (This Year)"), + "function": "Count", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": json.dumps([ + ['Asset', 'creation', 'between', [year_start_date, year_end_date]] + ]), + "doctype": "Number Card", + }, { "name": "Asset Value", - "label": "Asset Value", + "label": _("Asset Value"), "function": "Sum", "aggregate_function_based_on": "value_after_depreciation", "document_type": "Asset", @@ -135,7 +170,7 @@ def get_number_cards(): "show_percentage_stats": 1, "stats_time_interval": "Monthly", "filters_json": "[]", - "doctype": "Number Card", + "doctype": "Number Card" } ] @@ -147,14 +182,4 @@ def get_company_for_dashboards(): company_list = frappe.get_list("Company") if company_list: return company_list[0].name - return None - -def get_fiscal_year(): - fiscal_year = frappe.defaults.get_defaults().fiscal_year - if fiscal_year: - return fiscal_year - else: - fiscal_year_list = frappe.get_list("Fiscal Year") - if fiscal_year_list: - return fiscal_year_list[0].name return None \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 429a8a897c..94939fdd2a 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -29,10 +29,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-12 17:35:14.770662", + "modified": "2020-05-20 18:05:23.994795", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -55,6 +56,11 @@ "label": "Fixed Asset Register", "link_to": "Fixed Asset Register", "type": "Report" + }, + { + "label": "Assets Dashboard", + "link_to": "Asset", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/assets/module_onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json index dc77726d28..66dd60ae81 100644 --- a/erpnext/assets/module_onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -26,10 +26,13 @@ "step": "Create a Fixed Asset Item" }, { - "step": "Purchase the Asset Item" + "step": "Create an Asset Category" + }, + { + "step": "Purchase an Asset Item" }, { - "step": "Create the Asset" + "step": "Create an Asset" } ], "subtitle": "Assets, Depreciations, Repairs and more", diff --git a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json similarity index 84% rename from erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json rename to erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json index 28d8485f97..5488b1d7b4 100644 --- a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json +++ b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json @@ -9,8 +9,8 @@ "is_skipped": 0, "modified": "2020-05-08 13:21:53.332538", "modified_by": "Administrator", - "name": "Create the Asset", + "name": "Create an Asset", "owner": "Administrator", "reference_document": "Asset", - "title": "Create the Asset" + "title": "Create an Asset" } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json new file mode 100644 index 0000000000..3bf54af348 --- /dev/null +++ b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create an Asset Category", + "owner": "Administrator", + "reference_document": "Asset Category", + "title": "Create an Asset Category" + } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json similarity index 82% rename from erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json rename to erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json index b6f38decb9..732ff7f733 100644 --- a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json +++ b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json @@ -9,8 +9,8 @@ "is_skipped": 0, "modified": "2020-05-08 13:21:28.208059", "modified_by": "Administrator", - "name": "Purchase the Asset Item", + "name": "Purchase an Asset Item", "owner": "Administrator", "reference_document": "Purchase Receipt", - "title": "Purchase the Asset Item" + "title": "Purchase an Asset Item" } \ No newline at end of file From 29c748184c7e5fc2df43176ae398ab7f7f83cebe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 May 2020 22:15:12 +0530 Subject: [PATCH 22/22] fix: Project filter in Trial Baalance Report --- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 8bd4399e60..5a699b6580 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -71,7 +71,8 @@ def get_data(filters): opening_balances = get_opening_balances(filters) #add filter inside list so that the query in financial_statements.py doesn't break - filters.project = [filters.project] + if filters.project: + filters.project = [filters.project] set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))