Merge branch 'develop' of https://github.com/frappe/erpnext into develop
This commit is contained in:
commit
d3961b2a78
@ -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") \
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
185
erpnext/assets/dashboard_fixtures.py
Normal file
185
erpnext/assets/dashboard_fixtures.py
Normal file
@ -0,0 +1,185 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
import json
|
||||
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({
|
||||
"dashboards": get_dashboards(),
|
||||
"charts": get_charts(),
|
||||
"number_cards": get_number_cards(),
|
||||
})
|
||||
|
||||
def get_dashboards():
|
||||
return [{
|
||||
"name": "Asset",
|
||||
"dashboard_name": "Asset",
|
||||
"charts": [
|
||||
{ "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()
|
||||
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[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--"
|
||||
}),
|
||||
"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 Register",
|
||||
"x_field": "asset_category",
|
||||
"timeseries": 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": [
|
||||
{
|
||||
"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": "Fixed Asset Register",
|
||||
"x_field": "location",
|
||||
"timeseries": 0,
|
||||
"filters_json": json.dumps({
|
||||
"company": company,
|
||||
"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}
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
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"),
|
||||
"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
|
@ -17,21 +17,27 @@
|
||||
}
|
||||
],
|
||||
"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,
|
||||
"docstatus": 0,
|
||||
"doctype": "Desk Page",
|
||||
"extends_another_page": 0,
|
||||
"icon": "",
|
||||
"hide_custom": 0,
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Assets",
|
||||
"modified": "2020-04-01 11:28:51.072198",
|
||||
"modified": "2020-05-20 18:05:23.994795",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Assets",
|
||||
"onboarding": "Assets",
|
||||
"owner": "Administrator",
|
||||
"pin_to_bottom": 0,
|
||||
"pin_to_top": 0,
|
||||
@ -42,14 +48,19 @@
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Asset Movement",
|
||||
"link_to": "Asset Movement",
|
||||
"label": "Asset Category",
|
||||
"link_to": "Asset Category",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Fixed Asset Register",
|
||||
"link_to": "Fixed Asset Register",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Assets Dashboard",
|
||||
"link_to": "Asset",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
]
|
||||
}
|
@ -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) {
|
||||
|
@ -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.")
|
||||
|
42
erpnext/assets/module_onboarding/assets/assets.json
Normal file
42
erpnext/assets/module_onboarding/assets/assets.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"allow_roles": [
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Maintenance User"
|
||||
}
|
||||
],
|
||||
"creation": "2020-05-08 15:10:45.571457",
|
||||
"docstatus": 0,
|
||||
"doctype": "Module 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": "Create an Asset Category"
|
||||
},
|
||||
{
|
||||
"step": "Purchase an Asset Item"
|
||||
},
|
||||
{
|
||||
"step": "Create an 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
|
||||
}
|
@ -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"
|
||||
}
|
@ -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",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Asset",
|
||||
"title": "Create an Asset"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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 Assets",
|
||||
"video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo"
|
||||
}
|
@ -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 an Asset Item",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Purchase Receipt",
|
||||
"title": "Purchase an Asset Item"
|
||||
}
|
@ -21,20 +21,54 @@ 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
|
||||
},
|
||||
{
|
||||
fieldname:"available_for_use_date",
|
||||
label: __("Available For Use Date"),
|
||||
fieldtype: "Date"
|
||||
"fieldname":"from_date",
|
||||
"label": __("Start Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12),
|
||||
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
fieldname:"finance_book",
|
||||
label: __("Finance Book"),
|
||||
fieldtype: "Link",
|
||||
options: "Finance Book"
|
||||
"fieldname":"to_date",
|
||||
"label": __("End Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.nowdate(),
|
||||
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"from_fiscal_year",
|
||||
"label": __("Start Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"to_fiscal_year",
|
||||
"label": __("End Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"depends_on": "eval: doc.filter_based_on == '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",
|
||||
@ -42,6 +76,26 @@ 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"),
|
||||
fieldtype: "Link",
|
||||
options: "Cost Center"
|
||||
},
|
||||
{
|
||||
fieldname:"group_by",
|
||||
label: __("Group By"),
|
||||
fieldtype: "Select",
|
||||
options: ["--Select a group--", "Asset Category", "Location"],
|
||||
default: "--Select a group--",
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"is_existing_asset",
|
||||
label: __("Is Existing Asset"),
|
||||
|
@ -4,122 +4,39 @@
|
||||
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)
|
||||
return columns, data
|
||||
chart = prepare_chart_data(data, filters) if filters.get("group_by") not in ("Asset Category", "Location") else {}
|
||||
|
||||
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
|
||||
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)
|
||||
filters.year_start_date = getdate(fiscal_year.year_start_date)
|
||||
filters.year_end_date = getdate(fiscal_year.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'):
|
||||
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'
|
||||
@ -139,18 +56,28 @@ def get_data(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",
|
||||
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)
|
||||
|
||||
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",
|
||||
"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,
|
||||
@ -158,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,
|
||||
@ -169,8 +96,39 @@ def get_data(filters):
|
||||
|
||||
return data
|
||||
|
||||
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:
|
||||
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_values_map.keys(),
|
||||
"datasets": [
|
||||
{ '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",
|
||||
"barOptions": {
|
||||
"stacked": 1
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
@ -201,3 +159,139 @@ 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):
|
||||
if filters.get("group_by") in ["Asset Category", "Location"]:
|
||||
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"),
|
||||
"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
|
||||
},
|
||||
]
|
202
erpnext/crm/dashboard_fixtures.py
Normal file
202
erpnext/crm/dashboard_fixtures.py
Normal file
@ -0,0 +1,202 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe, erpnext, json
|
||||
from frappe import _
|
||||
|
||||
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": "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": "Opportunities via Campaigns", "width": "Half" },
|
||||
{ "chart": "Lead Source", "width": "Half"}
|
||||
],
|
||||
"cards": [
|
||||
{ "card": "New Lead (Last 1 Month)" },
|
||||
{ "card": "New Opportunity (Last 1 Month)" },
|
||||
{ "card": "Won Opportunity (Last 1 Month)" },
|
||||
{ "card": "Open 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": "Incoming Leads",
|
||||
"doctype": "Dashboard Chart",
|
||||
"time_interval": "Yearly",
|
||||
"chart_type": "Count",
|
||||
"chart_name": _("Incoming Leads"),
|
||||
"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 Trends",
|
||||
"doctype": "Dashboard Chart",
|
||||
"time_interval": "Yearly",
|
||||
"chart_type": "Count",
|
||||
"chart_name": _("Opportunity Trends"),
|
||||
"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": "Opportunities via Campaigns",
|
||||
"chart_name": _("Opportunities via Campaigns"),
|
||||
"doctype": "Dashboard Chart",
|
||||
"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": "Pie"
|
||||
},
|
||||
{
|
||||
"name": "Won Opportunities",
|
||||
"doctype": "Dashboard Chart",
|
||||
"time_interval": "Yearly",
|
||||
"chart_type": "Count",
|
||||
"chart_name": _("Won Opportunities"),
|
||||
"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", "status", "=", "Converted", False]]),
|
||||
"type": "Bar"
|
||||
},
|
||||
{
|
||||
"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 Count"),
|
||||
"document_type": "Opportunity",
|
||||
'is_public': 1,
|
||||
"filters_json": json.dumps([
|
||||
["Opportunity", "company", "=", company, False]
|
||||
]),
|
||||
"owner": "Administrator",
|
||||
"type": "Donut"
|
||||
},
|
||||
{
|
||||
"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", "status", "=", "Converted", False]
|
||||
]),
|
||||
"type": "Donut"
|
||||
},
|
||||
{
|
||||
"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": "Pie"
|
||||
}]
|
||||
|
||||
def get_number_cards():
|
||||
return [{
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Lead",
|
||||
"name": "New Lead (Last 1 Month)",
|
||||
"filters_json": json.dumps([["Lead","creation","Previous","1 month",False]]),
|
||||
"function": "Count",
|
||||
"is_public": 1,
|
||||
"label": _("New Lead (Last 1 Month)"),
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Daily"
|
||||
},
|
||||
{
|
||||
"doctype": "Number Card",
|
||||
"document_type": "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": _("Open Opportunity"),
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Daily"
|
||||
}]
|
@ -27,21 +27,26 @@
|
||||
}
|
||||
],
|
||||
"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": "",
|
||||
"hide_custom": 0,
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "CRM",
|
||||
"modified": "2020-04-27 22:32:26.682911",
|
||||
"modified": "2020-05-20 12:11:36.250491",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "CRM",
|
||||
"onboarding": "CRM",
|
||||
"owner": "Administrator",
|
||||
"pin_to_bottom": 0,
|
||||
"pin_to_top": 0,
|
||||
@ -69,6 +74,11 @@
|
||||
"label": "Sales Analytics",
|
||||
"link_to": "Sales Analytics",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "CRM Dashboard",
|
||||
"link_to": "CRM",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
42
erpnext/crm/module_onboarding/crm/crm.json
Normal file
42
erpnext/crm/module_onboarding/crm/crm.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"allow_roles": [
|
||||
{
|
||||
"role": "Sales Master Manager"
|
||||
},
|
||||
{
|
||||
"role": "Sales Manager"
|
||||
},
|
||||
{
|
||||
"role": "Sales User"
|
||||
}
|
||||
],
|
||||
"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-20 12:53:47.029412",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "CRM",
|
||||
"owner": "Administrator",
|
||||
"steps": [
|
||||
{
|
||||
"step": "Introduction to CRM"
|
||||
},
|
||||
{
|
||||
"step": "Create Lead"
|
||||
},
|
||||
{
|
||||
"step": "Create Opportunity"
|
||||
},
|
||||
{
|
||||
"step": "Create and Send Quotation"
|
||||
}
|
||||
],
|
||||
"subtitle": "Lead, Opportunity, Customer and more",
|
||||
"success_message": "CRM Module is all setup!",
|
||||
"title": "Let's Setup Your CRM",
|
||||
"user_can_dismiss": 1
|
||||
}
|
45
erpnext/crm/onboarding/crm/crm.json
Normal file
45
erpnext/crm/onboarding/crm/crm.json
Normal file
@ -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
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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",
|
||||
"show_full_form": 0,
|
||||
"title": "Create and Send Quotation",
|
||||
"validate_action": 0
|
||||
}
|
19
erpnext/crm/onboarding_step/create_lead/create_lead.json
Normal file
19
erpnext/crm/onboarding_step/create_lead/create_lead.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Lead",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Opportunity",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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",
|
||||
"show_full_form": 0,
|
||||
"title": "Introduction to CRM",
|
||||
"validate_action": 0,
|
||||
"video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA"
|
||||
}
|
228
erpnext/hr/dashboard_fixtures.py
Normal file
228
erpnext/hr/dashboard_fixtures.py
Normal file
@ -0,0 +1,228 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
import erpnext
|
||||
import json
|
||||
from frappe import _
|
||||
|
||||
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": "Outgoing Salary", "width": "Full"},
|
||||
{ "chart": "Gender Diversity Ratio", "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": "Attendance Count", "width": "Full"}
|
||||
],
|
||||
"cards": [
|
||||
{"card": "Total Employees"},
|
||||
{"card": "New Joinees (Last year)"},
|
||||
{'card': "Employees Left (Last year)"},
|
||||
{'card': "Total Job Openings (Last month)"},
|
||||
{'card': "Total Applicants (Last month)"},
|
||||
{'card': "Shortlisted Candidates (Last month)"},
|
||||
{'card': "Rejected Candidates (Last month)"},
|
||||
{'card': "Total Job Offered (Last month)"},
|
||||
]
|
||||
}
|
||||
|
||||
def get_recruitment_dashboard():
|
||||
pass
|
||||
|
||||
|
||||
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")
|
||||
|
||||
dashboard_charts = [
|
||||
get_dashboards_chart_doc('Gender Diversity Ratio', "Group By", "Pie",
|
||||
document_type = "Employee", group_by_type="Count", group_by_based_on="gender",
|
||||
filters_json = json.dumps([["Employee", "status", "=", "Active"]]))
|
||||
]
|
||||
|
||||
dashboard_charts.append(
|
||||
get_dashboards_chart_doc('Job Application Status', "Group By", "Pie",
|
||||
document_type = "Job Applicant", group_by_type="Count", group_by_based_on="status",
|
||||
filters_json = json.dumps([["Job Applicant", "creation", "Previous", "1 month"]]))
|
||||
)
|
||||
|
||||
dashboard_charts.append(
|
||||
get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line",
|
||||
document_type = "Salary Slip", 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": "line",
|
||||
"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('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"]]))
|
||||
)
|
||||
|
||||
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"]]))
|
||||
)
|
||||
|
||||
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():
|
||||
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 (Last year)", filters_json = json.dumps([
|
||||
["Employee","date_of_joining","Previous","1 year"],
|
||||
["Employee","status","=","Active"]
|
||||
])
|
||||
)
|
||||
)
|
||||
|
||||
number_cards.append(
|
||||
get_number_cards_doc("Employee", "Employees Left (Last year)", filters_json = json.dumps([
|
||||
["Employee", "modified", "Previous", "1 year"],
|
||||
["Employee", "status", "=", "Left"]
|
||||
])
|
||||
)
|
||||
)
|
||||
|
||||
number_cards.append(
|
||||
get_number_cards_doc("Job Applicant", "Total Applicants (Last month)", filters_json = json.dumps([
|
||||
["Job Applicant", "creation", "Previous", "1 month"]
|
||||
])
|
||||
)
|
||||
)
|
||||
|
||||
number_cards.append(
|
||||
get_number_cards_doc("Job Opening", "Total Job Openings (Last month)", 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", "Shortlisted Candidates (Last month)", 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 (Last month)", 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 (Last month)",
|
||||
filters_json = json.dumps([["Job Offer", "creation", "Previous", "1 month"]])
|
||||
)
|
||||
)
|
||||
|
||||
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 '[]',
|
||||
"aggregate_function_based_on": args.aggregate_function_based_on or None
|
||||
}
|
||||
|
||||
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",
|
||||
"aggregate_function_based_on": args.aggregate_function_based_on or None
|
||||
}
|
@ -77,21 +77,27 @@
|
||||
}
|
||||
],
|
||||
"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": "",
|
||||
"hide_custom": 0,
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "HR",
|
||||
"modified": "2020-04-29 20:29:22.114309",
|
||||
"modified": "2020-05-20 11:20:54.255557",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "HR",
|
||||
"onboarding": "Human Resource",
|
||||
"owner": "Administrator",
|
||||
"pin_to_bottom": 0,
|
||||
"pin_to_top": 0,
|
||||
@ -104,33 +110,33 @@
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"format": "{} Unpaid",
|
||||
"label": "Expense Claim",
|
||||
"link_to": "Expense Claim",
|
||||
"stats_filter": "{\"approval_status\":\"Draft\"}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"format": "{} Open",
|
||||
"label": "Job Applicant",
|
||||
"link_to": "Job Applicant",
|
||||
"stats_filter": "{\n \"status\": \"Open\"\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Salary Structure",
|
||||
"link_to": "Salary Structure",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"label": "Salary Register",
|
||||
"link_to": "Salary Register",
|
||||
"link_to": "Monthly Attendance Sheet",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"format": "{} Open",
|
||||
"label": "HR Dashboard",
|
||||
"link_to": "Human Resource",
|
||||
"stats_filter": "{\n \"status\": \"Open\"\n}",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
]
|
||||
}
|
@ -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]
|
||||
])
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"allow_roles": [
|
||||
{
|
||||
"role": "HR Manager"
|
||||
},
|
||||
{
|
||||
"role": "HR User"
|
||||
}
|
||||
],
|
||||
"creation": "2020-05-14 11:51:45.050242",
|
||||
"docstatus": 0,
|
||||
"doctype": "Module Onboarding",
|
||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"modified": "2020-05-20 11:20:07.992597",
|
||||
"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 Type"
|
||||
},
|
||||
{
|
||||
"step": "Create Leave Allocation"
|
||||
},
|
||||
{
|
||||
"step": "Create Leave Application"
|
||||
},
|
||||
{
|
||||
"step": "HR Settings"
|
||||
}
|
||||
],
|
||||
"subtitle": "Employee, Leaves and more.",
|
||||
"success_message": "The HR Module is all set up!",
|
||||
"title": "Let's Setup the Human Resource Module. ",
|
||||
"user_can_dismiss": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 12:22:26.448420",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Department",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Department",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Department",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 12:22:41.500795",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Designation",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Designation",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Designation",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 12:26:28.629074",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Employee",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Employee",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Employee",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 12:25:38.068582",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Holiday list",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Holiday List",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Holiday list",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 11:48:56.123718",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Leave Allocation",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Leave Allocation",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Leave Allocation",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 11:49:45.400764",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Leave Application",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Leave Application",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Leave Application",
|
||||
"validate_action": 0
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Create Entry",
|
||||
"creation": "2020-05-20 11:17:31.119312",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 1,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-20 11:17:31.119312",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create Leave Type",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Leave Type",
|
||||
"show_full_form": 0,
|
||||
"title": "Create Leave Type",
|
||||
"validate_action": 0
|
||||
}
|
19
erpnext/hr/onboarding_step/hr_settings/hr_settings.json
Normal file
19
erpnext/hr/onboarding_step/hr_settings/hr_settings.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"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_single": 1,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-20 11:16:42.430974",
|
||||
"modified_by": "Administrator",
|
||||
"name": "HR Settings",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "HR Settings",
|
||||
"show_full_form": 0,
|
||||
"title": "HR settings",
|
||||
"validate_action": 0
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
30
erpnext/hr/report/employee_analytics/employee_analytics.json
Normal file
30
erpnext/hr/report/employee_analytics/employee_analytics.json
Normal file
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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(["Not Set"])
|
||||
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
|
||||
|
@ -30,8 +30,11 @@ 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 = get_columns(filters)
|
||||
columns, days = get_columns(filters)
|
||||
att_map = get_attendance_list(conditions, filters)
|
||||
|
||||
if filters.group_by:
|
||||
@ -60,20 +63,67 @@ 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([ "<b>"+ parameter + "</b>"])
|
||||
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, default_holiday_list, 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, default_holiday_list, 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):
|
||||
labels = []
|
||||
datasets = [
|
||||
{"name": "Absent", "values": []},
|
||||
{"name": "Present", "values": []},
|
||||
{"name": "Leave", "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
|
||||
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
|
||||
|
||||
|
||||
def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list=None):
|
||||
datasets[0]["values"].append(total_absent_on_day)
|
||||
datasets[1]["values"].append(total_present_on_day)
|
||||
datasets[2]["values"].append(total_leave_on_day)
|
||||
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
'labels': labels,
|
||||
'datasets': datasets
|
||||
}
|
||||
}
|
||||
|
||||
chart["type"] = "line"
|
||||
|
||||
return chart
|
||||
|
||||
def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, 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 +135,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 +152,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 +203,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 +218,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,
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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
|
||||
}
|
||||
]
|
@ -55,32 +55,27 @@ def get_periodic_data(filters, entry):
|
||||
if d.status == 'Completed':
|
||||
if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date):
|
||||
periodic_data = update_periodic_data(periodic_data, "Completed", period)
|
||||
|
||||
elif getdate(d.actual_start_date) < getdate(from_date) :
|
||||
periodic_data = update_periodic_data(periodic_data, "Pending", period)
|
||||
|
||||
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)
|
||||
|
||||
elif d.status == 'In Process':
|
||||
if getdate(d.actual_start_date) < getdate(from_date) :
|
||||
periodic_data = update_periodic_data(periodic_data, "Pending", period)
|
||||
|
||||
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)
|
||||
|
||||
elif d.status == 'Not Started':
|
||||
if 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)
|
||||
|
||||
return periodic_data
|
||||
|
||||
def update_periodic_data(periodic_data, status, period):
|
||||
|
@ -690,3 +690,4 @@ execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
|
||||
erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
|
||||
erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes
|
||||
erpnext.patches.v12_0.update_bom_in_so_mr
|
||||
execute:frappe.delete_doc("Report", "Department Analytics")
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user