Merge branch 'marination-purchase-dashboard' into develop
This commit is contained in:
commit
939441117a
207
erpnext/buying/dashboard_fixtures.py
Normal file
207
erpnext/buying/dashboard_fixtures.py
Normal file
@ -0,0 +1,207 @@
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _
|
||||
from frappe.utils import nowdate
|
||||
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_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
|
||||
|
||||
company = frappe.get_doc("Company", get_company_for_dashboards())
|
||||
fiscal_year = get_fiscal_year(nowdate(), as_dict=1)
|
||||
fiscal_year_name = fiscal_year.get("name")
|
||||
start_date = str(fiscal_year.get("year_start_date"))
|
||||
end_date = str(fiscal_year.get("year_end_date"))
|
||||
|
||||
def get_dashboards():
|
||||
return [{
|
||||
"name": "Buying",
|
||||
"dashboard_name": "Buying",
|
||||
"charts": [
|
||||
{ "chart": "Purchase Order Trends", "width": "Full"},
|
||||
{ "chart": "Material Request Analysis", "width": "Half"},
|
||||
{ "chart": "Purchase Order Analysis", "width": "Half"},
|
||||
{ "chart": "Top Suppliers", "width": "Full"}
|
||||
],
|
||||
"cards": [
|
||||
{ "card": "This Year Purchases"},
|
||||
{ "card": "Purchase Orders to Receive"},
|
||||
{ "card": "Purchase Orders to Bill"},
|
||||
{ "card": "Active Suppliers"}
|
||||
]
|
||||
}]
|
||||
|
||||
def get_charts():
|
||||
return [
|
||||
{
|
||||
"name": "Purchase Order Analysis",
|
||||
"chart_name": _("Purchase Order Analysis"),
|
||||
"chart_type": "Report",
|
||||
"custom_options": json.dumps({
|
||||
"type": "donut",
|
||||
"height": 300,
|
||||
"axisOptions": {"shortenYAxisNumbers": 1}
|
||||
}),
|
||||
"doctype": "Dashboard Chart",
|
||||
"filters_json": json.dumps({
|
||||
"company": company.name,
|
||||
"from_date": start_date,
|
||||
"to_date": end_date
|
||||
}),
|
||||
"is_custom": 1,
|
||||
"is_public": 1,
|
||||
"owner": "Administrator",
|
||||
"report_name": "Purchase Order Analysis",
|
||||
"type": "Donut"
|
||||
},
|
||||
{
|
||||
"name": "Material Request Analysis",
|
||||
"chart_name": _("Material Request Analysis"),
|
||||
"chart_type": "Group By",
|
||||
"custom_options": json.dumps({"height": 300}),
|
||||
"doctype": "Dashboard Chart",
|
||||
"document_type": "Material Request",
|
||||
"filters_json": json.dumps(
|
||||
[["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False],
|
||||
["Material Request", "material_request_type", "=", "Purchase", False],
|
||||
["Material Request", "company", "=", company.name, False],
|
||||
["Material Request", "docstatus", "=", 1, False],
|
||||
["Material Request", "transaction_date", "Between", [start_date, end_date], False]]
|
||||
),
|
||||
"group_by_based_on": "status",
|
||||
"group_by_type": "Count",
|
||||
"is_custom": 0,
|
||||
"is_public": 1,
|
||||
"number_of_groups": 0,
|
||||
"owner": "Administrator",
|
||||
"type": "Donut"
|
||||
},
|
||||
{
|
||||
"name": "Purchase Order Trends",
|
||||
"chart_name": _("Purchase Order Trends"),
|
||||
"chart_type": "Report",
|
||||
"custom_options": json.dumps({
|
||||
"type": "line",
|
||||
"axisOptions": {"shortenYAxisNumbers": 1},
|
||||
"tooltipOptions": {},
|
||||
"lineOptions": {
|
||||
"regionFill": 1
|
||||
}
|
||||
}),
|
||||
"doctype": "Dashboard Chart",
|
||||
"filters_json": json.dumps({
|
||||
"company": company.name,
|
||||
"period": "Monthly",
|
||||
"fiscal_year": fiscal_year_name,
|
||||
"period_based_on": "posting_date",
|
||||
"based_on": "Item"
|
||||
}),
|
||||
"is_custom": 1,
|
||||
"is_public": 1,
|
||||
"owner": "Administrator",
|
||||
"report_name": "Purchase Order Trends",
|
||||
"type": "Line"
|
||||
},
|
||||
{
|
||||
"name": "Top Suppliers",
|
||||
"chart_name": _("Top Suppliers"),
|
||||
"chart_type": "Report",
|
||||
"doctype": "Dashboard Chart",
|
||||
"filters_json": json.dumps({
|
||||
"company": company.name,
|
||||
"period": "Monthly",
|
||||
"fiscal_year": fiscal_year_name,
|
||||
"period_based_on": "posting_date",
|
||||
"based_on": "Supplier"
|
||||
}),
|
||||
"is_custom": 1,
|
||||
"is_public": 1,
|
||||
"owner": "Administrator",
|
||||
"report_name": "Purchase Receipt Trends",
|
||||
"type": "Bar"
|
||||
}
|
||||
]
|
||||
|
||||
def get_number_cards():
|
||||
return [
|
||||
{
|
||||
"name": "This Year Purchases",
|
||||
"aggregate_function_based_on": "base_net_total",
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Purchase Order",
|
||||
"filters_json": json.dumps([
|
||||
["Purchase Order", "transaction_date", "Between", [start_date, end_date], False],
|
||||
["Purchase Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False],
|
||||
["Purchase Order", "docstatus", "=", 1, False],
|
||||
["Purchase Order", "company", "=", company.name, False],
|
||||
["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]
|
||||
]),
|
||||
"function": "Sum",
|
||||
"is_public": 1,
|
||||
"label": _("This Year Purchases"),
|
||||
"owner": "Administrator",
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Monthly"
|
||||
},
|
||||
{
|
||||
"name": "Purchase Orders to Receive",
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Purchase Order",
|
||||
"filters_json": json.dumps([
|
||||
["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False],
|
||||
["Purchase Order", "docstatus", "=", 1, False],
|
||||
["Purchase Order", "company", "=", company.name, False]
|
||||
]),
|
||||
"function": "Count",
|
||||
"is_public": 1,
|
||||
"label": _("Purchase Orders to Receive"),
|
||||
"owner": "Administrator",
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Weekly"
|
||||
},
|
||||
{
|
||||
"name": "Purchase Orders to Bill",
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Purchase Order",
|
||||
"filters_json": json.dumps([
|
||||
["Purchase Order", "status", "in", ["To Receive and Bill", "To Bill", None], False],
|
||||
["Purchase Order", "docstatus", "=", 1, False],
|
||||
["Purchase Order", "company", "=", company.name, False]
|
||||
]),
|
||||
"function": "Count",
|
||||
"is_public": 1,
|
||||
"label": _("Purchase Orders to Bill"),
|
||||
"owner": "Administrator",
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Weekly"
|
||||
},
|
||||
{
|
||||
"name": "Active Suppliers",
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Supplier",
|
||||
"filters_json": json.dumps([["Supplier", "disabled", "=", "0"]]),
|
||||
"function": "Count",
|
||||
"is_public": 1,
|
||||
"label": "Active Suppliers",
|
||||
"owner": "Administrator",
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Monthly"
|
||||
}
|
||||
]
|
@ -2,24 +2,24 @@
|
||||
"cards": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Supplier",
|
||||
"links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]"
|
||||
"label": "Buying",
|
||||
"links": "[ \n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Purchasing",
|
||||
"links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Items and Pricing",
|
||||
"links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]"
|
||||
"label": "Items & Pricing",
|
||||
"links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Settings",
|
||||
"links": "[\n {\n \"description\": \"Default settings for buying transactions.\",\n \"label\": \"Buying Settings\",\n \"name\": \"Buying Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Supplier",
|
||||
"links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Supplier Scorecard",
|
||||
@ -28,67 +28,81 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Key Reports",
|
||||
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Ordered\",\n \"name\": \"Requested Items To Be Ordered\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n }\n]"
|
||||
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items to Order\",\n \"name\": \"Requested Items to Order\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Other Reports",
|
||||
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
|
||||
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
|
||||
}
|
||||
],
|
||||
"cards_label": "Masters & Reports ",
|
||||
"category": "Modules",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "Expenses",
|
||||
"label": "Expenses"
|
||||
"chart_name": "Purchase Order Trends",
|
||||
"label": "Purchase Order Trends"
|
||||
}
|
||||
],
|
||||
"charts_label": "Buying Dashboard",
|
||||
"creation": "2020-01-28 11:50:26.195467",
|
||||
"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": "Buying",
|
||||
"modified": "2020-04-01 11:28:51.192097",
|
||||
"modified": "2020-05-19 19:44:36.260982",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying",
|
||||
"onboarding": "Buying",
|
||||
"owner": "Administrator",
|
||||
"pin_to_bottom": 0,
|
||||
"pin_to_top": 0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"format": "{} Unpaid",
|
||||
"label": "Purchase Invoice",
|
||||
"link_to": "Purchase Invoice",
|
||||
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Unpaid\"\n}",
|
||||
"color": "#cef6d1",
|
||||
"format": "{} available",
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"stats_filter": "{\n \"disabled\": 0\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"format": "{} to receive",
|
||||
"color": "#ffe8cd",
|
||||
"format": "{} Pending",
|
||||
"label": "Material Request",
|
||||
"link_to": "Material Request",
|
||||
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Pending\"\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "#ffe8cd",
|
||||
"format": "{} to Receive",
|
||||
"label": "Purchase Order",
|
||||
"link_to": "Purchase Order",
|
||||
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Receive\"\n}",
|
||||
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Supplier Quotation",
|
||||
"link_to": "Supplier Quotation",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Accounts Payable",
|
||||
"link_to": "Accounts Payable",
|
||||
"label": "Purchase Analytics",
|
||||
"link_to": "Purchase Analytics",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Purchase Register",
|
||||
"link_to": "Purchase Register",
|
||||
"label": "Purchase Order Analysis",
|
||||
"link_to": "Purchase Order Analysis",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Buying Dashboard",
|
||||
"link_to": "Buying",
|
||||
"type": "Dashboard"
|
||||
}
|
||||
]
|
||||
],
|
||||
"shortcuts_label": "Quick Access"
|
||||
}
|
@ -6,3 +6,26 @@ frappe.ui.form.on('Buying Settings', {
|
||||
|
||||
// }
|
||||
});
|
||||
|
||||
frappe.tour['Buying Settings'] = [
|
||||
{
|
||||
fieldname: "supp_master_name",
|
||||
title: "Supplier Naming By",
|
||||
description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
|
||||
},
|
||||
{
|
||||
fieldname: "buying_price_list",
|
||||
title: "Default Buying Price List",
|
||||
description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.")
|
||||
},
|
||||
{
|
||||
fieldname: "po_required",
|
||||
title: "Purchase Order Required for Purchase Invoice & Receipt Creation",
|
||||
description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.")
|
||||
},
|
||||
{
|
||||
fieldname: "pr_required",
|
||||
title: "Purchase Receipt Required for Purchase Invoice Creation",
|
||||
description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.")
|
||||
}
|
||||
];
|
@ -1,8 +1,10 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2013-06-25 11:04:03",
|
||||
"description": "Settings for Buying Module",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"supp_master_name",
|
||||
"supplier_group",
|
||||
@ -44,13 +46,13 @@
|
||||
{
|
||||
"fieldname": "po_required",
|
||||
"fieldtype": "Select",
|
||||
"label": "Purchase Order Required",
|
||||
"label": "Purchase Order Required for Purchase Invoice & Receipt Creation",
|
||||
"options": "No\nYes"
|
||||
},
|
||||
{
|
||||
"fieldname": "pr_required",
|
||||
"fieldtype": "Select",
|
||||
"label": "Purchase Receipt Required",
|
||||
"label": "Purchase Receipt Required for Purchase Invoice Creation",
|
||||
"options": "No\nYes"
|
||||
},
|
||||
{
|
||||
@ -92,7 +94,8 @@
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2019-08-20 13:13:09.055189",
|
||||
"links": [],
|
||||
"modified": "2020-05-15 14:49:32.513611",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
@ -107,5 +110,7 @@
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -761,7 +761,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "status",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nDraft\nSubmitted\nStopped\nCancelled",
|
||||
"options": "\nDraft\nSubmitted\nStopped\nCancelled\nExpired",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
@ -803,7 +803,7 @@
|
||||
"idx": 29,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-15 11:44:52.958022",
|
||||
"modified": "2020-05-15 21:24:12.639482",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
54
erpnext/buying/module_onboarding/buying/buying.json
Normal file
54
erpnext/buying/module_onboarding/buying/buying.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"allow_roles": [
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
],
|
||||
"creation": "2020-05-06 15:56:35.049205",
|
||||
"docstatus": 0,
|
||||
"doctype": "Module Onboarding",
|
||||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"modified": "2020-05-19 20:03:55.776080",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying",
|
||||
"owner": "Administrator",
|
||||
"steps": [
|
||||
{
|
||||
"step": "Introduction to Buying"
|
||||
},
|
||||
{
|
||||
"step": "Create a Supplier"
|
||||
},
|
||||
{
|
||||
"step": "Setup your Warehouse"
|
||||
},
|
||||
{
|
||||
"step": "Create a Product"
|
||||
},
|
||||
{
|
||||
"step": "Create a Material Request"
|
||||
},
|
||||
{
|
||||
"step": "Create your first Purchase Order"
|
||||
},
|
||||
{
|
||||
"step": "Buying Settings"
|
||||
}
|
||||
],
|
||||
"subtitle": "Products, Purchases, Analysis and more.",
|
||||
"success_message": "The Buying Module is all set up!",
|
||||
"title": "Let's Setup the Buying Module.",
|
||||
"user_can_dismiss": 1
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Update Settings",
|
||||
"creation": "2020-05-06 15:53:44.667414",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-12 18:30:06.323797",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Buying Settings",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Buying Settings",
|
||||
"show_full_form": 0,
|
||||
"title": "Configure Buying Settings.",
|
||||
"validate_action": 1
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Create Entry",
|
||||
"creation": "2020-05-15 14:39:09.818764",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 1,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-15 14:39:09.818764",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create a Material Request",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Material Request",
|
||||
"show_full_form": 1,
|
||||
"title": "Create a Material Request",
|
||||
"validate_action": 1
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Create Entry",
|
||||
"creation": "2020-05-12 18:16:06.624554",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-12 18:30:02.489949",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create a Product",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Item",
|
||||
"show_full_form": 0,
|
||||
"title": "Create a Product",
|
||||
"validate_action": 1
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Create Entry",
|
||||
"creation": "2020-05-14 22:09:10.043554",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-14 22:09:10.043554",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create a Supplier",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Supplier",
|
||||
"show_full_form": 0,
|
||||
"title": "Create a Supplier",
|
||||
"validate_action": 1
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Create Entry",
|
||||
"creation": "2020-05-12 18:17:49.976035",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-12 18:31:56.856112",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Create your first Purchase Order",
|
||||
"owner": "Administrator",
|
||||
"reference_document": "Purchase Order",
|
||||
"show_full_form": 0,
|
||||
"title": "Create your first Purchase Order",
|
||||
"validate_action": 1
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"action": "Watch Video",
|
||||
"creation": "2020-05-06 15:37:09.477765",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-12 18:25:08.509900",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Introduction to Buying",
|
||||
"owner": "Administrator",
|
||||
"show_full_form": 0,
|
||||
"title": "Introduction to Buying",
|
||||
"validate_action": 1,
|
||||
"video_url": "https://youtu.be/efFajTTQBa8"
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"action": "Go to Page",
|
||||
"creation": "2020-05-19 18:54:19.383397",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_mandatory": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2020-05-19 18:54:19.383397",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Setup your Warehouse",
|
||||
"owner": "Administrator",
|
||||
"path": "Tree/Warehouse",
|
||||
"reference_document": "Warehouse",
|
||||
"show_full_form": 0,
|
||||
"title": "Setup your Warehouse",
|
||||
"validate_action": 1
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Purchase Order Analysis"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_default("company")
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_order",
|
||||
"label": __("Purchase Order"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
"options": "Purchase Order",
|
||||
"get_query": () =>{
|
||||
return {
|
||||
filters: { "docstatus": 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"label": __("Status"),
|
||||
"fieldtype": "MultiSelectList",
|
||||
"width": "80",
|
||||
get_data: function(txt) {
|
||||
let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"]
|
||||
let options = []
|
||||
for (let option of status){
|
||||
options.push({
|
||||
"value": option,
|
||||
"description": ""
|
||||
})
|
||||
}
|
||||
return options
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "group_by_po",
|
||||
"label": __("Group by Purchase Order"),
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
}
|
||||
],
|
||||
|
||||
"formatter": function (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
let format_fields = ["received_qty", "billed_amount"];
|
||||
|
||||
if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) {
|
||||
value = "<span style='color:green'>" + value + "</span>";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"creation": "2020-05-04 18:41:28.625119",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2020-05-15 20:57:52.623455",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Analysis",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Purchase Order",
|
||||
"report_name": "Purchase Order Analysis",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Supplier"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import copy
|
||||
from frappe import _
|
||||
from frappe.utils import flt, date_diff, getdate
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
return [], []
|
||||
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
data = get_data(conditions, filters)
|
||||
|
||||
if not data:
|
||||
return [], [], None, []
|
||||
|
||||
data, chart_data = prepare_data(data, filters)
|
||||
|
||||
return columns, data, None, chart_data
|
||||
|
||||
def validate_filters(filters):
|
||||
from_date, to_date = filters.get("from_date"), filters.get("to_date")
|
||||
|
||||
if not from_date and to_date:
|
||||
frappe.throw(_("From and To Dates are required."))
|
||||
elif date_diff(to_date, from_date) < 0:
|
||||
frappe.throw(_("To Date cannot be before From Date."))
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
if filters.get("from_date") and filters.get("to_date"):
|
||||
conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
|
||||
|
||||
if filters.get("company"):
|
||||
conditions += " and po.company = %(company)s"
|
||||
|
||||
if filters.get("purchase_order"):
|
||||
conditions += " and po.name = %(purchase_order)s"
|
||||
|
||||
if filters.get("status"):
|
||||
conditions += " and po.status in %(status)s"
|
||||
|
||||
return conditions
|
||||
|
||||
def get_data(conditions, filters):
|
||||
data = frappe.db.sql("""
|
||||
SELECT
|
||||
po.transaction_date as date,
|
||||
poi.schedule_date as required_date,
|
||||
po.name as purchase_order,
|
||||
po.status, po.supplier, poi.item_code,
|
||||
poi.qty, poi.received_qty,
|
||||
(poi.qty - poi.received_qty) AS pending_qty,
|
||||
IFNULL(pii.qty, 0) as billed_qty,
|
||||
poi.base_amount as amount,
|
||||
(poi.received_qty * poi.base_rate) as received_qty_amount,
|
||||
(poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount,
|
||||
(poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount,
|
||||
po.set_warehouse as warehouse,
|
||||
po.company, poi.name
|
||||
FROM
|
||||
`tabPurchase Order` po,
|
||||
`tabPurchase Order Item` poi
|
||||
LEFT JOIN `tabPurchase Invoice Item` pii
|
||||
ON pii.po_detail = poi.name
|
||||
WHERE
|
||||
poi.parent = po.name
|
||||
and po.status not in ('Stopped', 'Closed')
|
||||
and po.docstatus = 1
|
||||
{0}
|
||||
GROUP BY poi.name
|
||||
ORDER BY po.transaction_date ASC
|
||||
""".format(conditions), filters, as_dict=1)
|
||||
|
||||
return data
|
||||
|
||||
def prepare_data(data, filters):
|
||||
completed, pending = 0, 0
|
||||
pending_field = "pending_amount"
|
||||
completed_field = "billed_amount"
|
||||
|
||||
if filters.get("group_by_po"):
|
||||
purchase_order_map = {}
|
||||
|
||||
for row in data:
|
||||
# sum data for chart
|
||||
completed += row[completed_field]
|
||||
pending += row[pending_field]
|
||||
|
||||
# prepare data for report view
|
||||
row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"])
|
||||
|
||||
if filters.get("group_by_po"):
|
||||
po_name = row["purchase_order"]
|
||||
|
||||
if not po_name in purchase_order_map:
|
||||
# create an entry
|
||||
row_copy = copy.deepcopy(row)
|
||||
purchase_order_map[po_name] = row_copy
|
||||
else:
|
||||
# update existing entry
|
||||
po_row = purchase_order_map[po_name]
|
||||
po_row["required_date"] = min(getdate(po_row["required_date"]), getdate(row["required_date"]))
|
||||
|
||||
# sum numeric columns
|
||||
fields = ["qty", "received_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount",
|
||||
"received_qty_amount", "billed_amount", "pending_amount"]
|
||||
for field in fields:
|
||||
po_row[field] = flt(row[field]) + flt(po_row[field])
|
||||
|
||||
chart_data = prepare_chart_data(pending, completed)
|
||||
|
||||
if filters.get("group_by_po"):
|
||||
data = []
|
||||
for po in purchase_order_map:
|
||||
data.append(purchase_order_map[po])
|
||||
return data, chart_data
|
||||
|
||||
return data, chart_data
|
||||
|
||||
def prepare_chart_data(pending, completed):
|
||||
labels = ["Amount to Bill", "Billed Amount"]
|
||||
|
||||
return {
|
||||
"data" : {
|
||||
"labels": labels,
|
||||
"datasets": [
|
||||
{"values": [pending, completed]}
|
||||
]
|
||||
},
|
||||
"type": 'donut',
|
||||
"height": 300
|
||||
}
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [
|
||||
{
|
||||
"label":_("Date"),
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
"label":_("Required By"),
|
||||
"fieldname": "required_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
"label": _("Purchase Order"),
|
||||
"fieldname": "purchase_order",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Order",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label":_("Status"),
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Data",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"label": _("Supplier"),
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier",
|
||||
"width": 130
|
||||
}]
|
||||
|
||||
if not filters.get("group_by_po"):
|
||||
columns.append({
|
||||
"label":_("Item Code"),
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 100
|
||||
})
|
||||
|
||||
columns.extend([
|
||||
{
|
||||
"label": _("Qty"),
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 120,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Received Qty"),
|
||||
"fieldname": "received_qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 120,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Pending Qty"),
|
||||
"fieldname": "pending_qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 80,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Billed Qty"),
|
||||
"fieldname": "billed_qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 80,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Qty to Bill"),
|
||||
"fieldname": "qty_to_bill",
|
||||
"fieldtype": "Float",
|
||||
"width": 80,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Amount"),
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"width": 110,
|
||||
"options": "Company:company:default_currency",
|
||||
"convertible": "rate"
|
||||
},
|
||||
{
|
||||
"label": _("Billed Amount"),
|
||||
"fieldname": "billed_amount",
|
||||
"fieldtype": "Currency",
|
||||
"width": 110,
|
||||
"options": "Company:company:default_currency",
|
||||
"convertible": "rate"
|
||||
},
|
||||
{
|
||||
"label": _("Pending Amount"),
|
||||
"fieldname": "pending_amount",
|
||||
"fieldtype": "Currency",
|
||||
"width": 130,
|
||||
"options": "Company:company:default_currency",
|
||||
"convertible": "rate"
|
||||
},
|
||||
{
|
||||
"label": _("Received Qty Amount"),
|
||||
"fieldname": "received_qty_amount",
|
||||
"fieldtype": "Currency",
|
||||
"width": 130,
|
||||
"options": "Company:company:default_currency",
|
||||
"convertible": "rate"
|
||||
},
|
||||
{
|
||||
"label": _("Warehouse"),
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Company"),
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"width": 100
|
||||
}
|
||||
])
|
||||
|
||||
return columns
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from erpnext.controllers.trends import get_columns,get_data
|
||||
|
||||
def execute(filters=None):
|
||||
@ -10,5 +11,48 @@ def execute(filters=None):
|
||||
data = []
|
||||
conditions = get_columns(filters, "Purchase Order")
|
||||
data = get_data(filters, conditions)
|
||||
chart_data = get_chart_data(data, conditions, filters)
|
||||
|
||||
return conditions["columns"], data
|
||||
return conditions["columns"], data, None, chart_data
|
||||
|
||||
def get_chart_data(data, conditions, filters):
|
||||
if not (data and conditions):
|
||||
return []
|
||||
|
||||
datapoints = []
|
||||
|
||||
start = 2 if filters.get("based_on") in ["Item", "Supplier"] else 1
|
||||
if filters.get("group_by"):
|
||||
start += 1
|
||||
|
||||
# fetch only periodic columns as labels
|
||||
columns = conditions.get("columns")[start:-2][1::2]
|
||||
labels = [column.split(':')[0] for column in columns]
|
||||
datapoints = [0] * len(labels)
|
||||
|
||||
for row in data:
|
||||
# If group by filter, don't add first row of group (it's already summed)
|
||||
if not row[start-1]:
|
||||
continue
|
||||
# Remove None values and compute only periodic data
|
||||
row = [x if x else 0 for x in row[start:-2]]
|
||||
row = row[1::2]
|
||||
|
||||
for i in range(len(row)):
|
||||
datapoints[i] += row[i]
|
||||
|
||||
return {
|
||||
"data" : {
|
||||
"labels" : labels,
|
||||
"datasets" : [
|
||||
{
|
||||
"name" : _("{0}").format(filters.get("period")) + _(" Purchase Value"),
|
||||
"values" : datapoints
|
||||
}
|
||||
]
|
||||
},
|
||||
"type" : "line",
|
||||
"lineOptions": {
|
||||
"regionFill": 1
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Requested Items to Order"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_default("company")
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80",
|
||||
"reqd": 1,
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname": "material_request",
|
||||
"label": __("Material Request"),
|
||||
"fieldtype": "Link",
|
||||
"width": "80",
|
||||
"options": "Material Request",
|
||||
"get_query": () =>{
|
||||
return {
|
||||
filters: {
|
||||
"docstatus": 1,
|
||||
"material_request_type": "Purchase",
|
||||
"per_received": ["<", 100]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "group_by_mr",
|
||||
"label": __("Group by Material Request"),
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
}
|
||||
],
|
||||
|
||||
"formatter": function (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (column.fieldname == "ordered_qty" && data && data.ordered_qty > 0) {
|
||||
value = "<span style='color:green'>" + value + "</span>";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"creation": "2020-05-04 20:23:57.750719",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2020-05-05 13:05:51.723951",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Requested Items to Order",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"query": "",
|
||||
"ref_doctype": "Material Request",
|
||||
"report_name": "Requested Items to Order",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import copy
|
||||
from frappe import _
|
||||
from frappe.utils import flt, date_diff, getdate
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
return [],[]
|
||||
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
#get queried data
|
||||
data = get_data(filters, conditions)
|
||||
|
||||
#prepare data for report and chart views
|
||||
data, chart_data = prepare_data(data, filters)
|
||||
|
||||
return columns, data, None, chart_data
|
||||
|
||||
def validate_filters(filters):
|
||||
from_date, to_date = filters.get("from_date"), filters.get("to_date")
|
||||
|
||||
if not from_date and to_date:
|
||||
frappe.throw(_("From and To Dates are required."))
|
||||
elif date_diff(to_date, from_date) < 0:
|
||||
frappe.throw(_("To Date cannot be before From Date."))
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ''
|
||||
|
||||
if filters.get("from_date") and filters.get("to_date"):
|
||||
conditions += " and mr.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date"))
|
||||
|
||||
if filters.get("company"):
|
||||
conditions += " and mr.company = '{0}'".format(filters.get("company"))
|
||||
|
||||
if filters.get("material_request"):
|
||||
conditions += " and mr.name = '{0}'".format(filters.get("material_request"))
|
||||
|
||||
return conditions
|
||||
|
||||
def get_data(filters, conditions):
|
||||
data = frappe.db.sql("""
|
||||
select
|
||||
mr.name as material_request,
|
||||
mr.transaction_date as date,
|
||||
mr_item.schedule_date as required_date,
|
||||
mr_item.item_code as item_code,
|
||||
sum(ifnull(mr_item.stock_qty, 0)) as qty,
|
||||
ifnull(mr_item.stock_uom, '') as uom,
|
||||
sum(ifnull(mr_item.ordered_qty, 0)) as ordered_qty,
|
||||
(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as qty_to_order,
|
||||
mr_item.item_name as item_name,
|
||||
mr.company as company
|
||||
from
|
||||
`tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
||||
where
|
||||
mr_item.parent = mr.name
|
||||
and mr.material_request_type = "Purchase"
|
||||
and mr.docstatus = 1
|
||||
and mr.status != "Stopped"
|
||||
{conditions}
|
||||
group by mr.name, mr_item.item_code
|
||||
having
|
||||
sum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))
|
||||
order by mr.transaction_date, mr.schedule_date""".format(conditions=conditions), as_dict=1)
|
||||
|
||||
return data
|
||||
|
||||
def prepare_data(data, filters):
|
||||
"""Prepare consolidated Report data and Chart data"""
|
||||
material_request_map = {}
|
||||
|
||||
for row in data:
|
||||
if not row["material_request"] in material_request_map:
|
||||
# create an entry with mr as key
|
||||
row_copy = copy.deepcopy(row)
|
||||
material_request_map[row["material_request"]] = row_copy
|
||||
else:
|
||||
mr_row = material_request_map[row["material_request"]]
|
||||
mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"]))
|
||||
|
||||
#sum numeric rows
|
||||
fields = ["qty", "ordered_qty", "qty_to_order"]
|
||||
for field in fields:
|
||||
mr_row[field] = flt(mr_row[field]) + flt(row[field])
|
||||
|
||||
chart_data = prepare_chart_data(material_request_map)
|
||||
|
||||
if filters.get("group_by_mr"):
|
||||
data =[]
|
||||
for mr in material_request_map:
|
||||
data.append(material_request_map[mr])
|
||||
return data, chart_data
|
||||
|
||||
return data, chart_data
|
||||
|
||||
def prepare_chart_data(data):
|
||||
labels, qty_to_order, ordered_qty = [], [], []
|
||||
|
||||
for row in data:
|
||||
mr_row = data[row]
|
||||
labels.append(mr_row["material_request"])
|
||||
qty_to_order.append(mr_row["qty_to_order"])
|
||||
ordered_qty.append(mr_row["ordered_qty"])
|
||||
|
||||
chart_data = {
|
||||
"data" : {
|
||||
"labels": labels,
|
||||
"datasets": [
|
||||
{
|
||||
'name': _('Qty to Order'),
|
||||
'values': qty_to_order
|
||||
},
|
||||
{
|
||||
'name': _('Ordered Qty'),
|
||||
'values': ordered_qty
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "bar",
|
||||
"barOptions": {
|
||||
"stacked": 1
|
||||
},
|
||||
}
|
||||
|
||||
return chart_data
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [
|
||||
{
|
||||
"label": _("Material Request"),
|
||||
"fieldname": "material_request",
|
||||
"fieldtype": "Link",
|
||||
"options": "Material Request",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label":_("Date"),
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
"label":_("Required By"),
|
||||
"fieldname": "required_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
if not filters.get("group_by_mr"):
|
||||
columns.extend([{
|
||||
"label":_("Item Code"),
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label":_("Item Name"),
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("UOM"),
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Data",
|
||||
"width": 100,
|
||||
}])
|
||||
|
||||
columns.extend([
|
||||
{
|
||||
"label": _("Qty"),
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 120,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Ordered Qty"),
|
||||
"fieldname": "ordered_qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 120,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Qty to Order"),
|
||||
"fieldname": "qty_to_order",
|
||||
"fieldtype": "Float",
|
||||
"width": 120,
|
||||
"convertible": "qty"
|
||||
},
|
||||
{
|
||||
"label": _("Company"),
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"width": 100
|
||||
}
|
||||
])
|
||||
|
||||
return columns
|
@ -166,7 +166,7 @@ def get_data():
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Requested Items To Be Ordered",
|
||||
"name": "Requested Items To Order",
|
||||
"reference_doctype": "Material Request",
|
||||
"onboard": 1,
|
||||
},
|
||||
|
@ -53,7 +53,7 @@ erpnext.get_purchase_trends_filters = function() {
|
||||
],
|
||||
"default": "Item",
|
||||
"dashboard_config": {
|
||||
"read_only": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -26,9 +26,10 @@ def get_chart_data(data, filters):
|
||||
# consider only consolidated row
|
||||
data = [row for row in data if row[0]]
|
||||
|
||||
data = sorted(data, key = lambda i: i[-1], reverse=True)
|
||||
|
||||
if len(data) > 10:
|
||||
# get top 10 if data too long
|
||||
data = sorted(data, key = lambda i: i[-1],reverse=True)
|
||||
data = data[:10]
|
||||
|
||||
for row in data:
|
||||
|
Loading…
Reference in New Issue
Block a user