feat: Selling Desk, Dashboard and Onboarding (#22055)

* feat: Selling Desk, Dashboard and Onboarding

* chore: Selling Onboarding and fixes in Other onboardings

* chore: Dashboard and Number card Fixtures

* fix: Escape filters and Reposition Accounts Dashboard shortcut.

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
Marica 2020-06-19 15:33:21 +05:30 committed by GitHub
parent 22d49726d1
commit 383807f72e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1079 additions and 168 deletions

View File

@ -13,7 +13,7 @@
{
"hidden": 0,
"label": "Accounts Receivable",
"links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
"links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@ -98,7 +98,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
"modified": "2020-05-27 20:34:50.949772",
"modified": "2020-06-19 12:42:44.054598",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
@ -122,11 +122,6 @@
"link_to": "Purchase Invoice",
"type": "DocType"
},
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
},
{
"label": "Journal Entry",
"link_to": "Journal Entry",
@ -151,6 +146,11 @@
"label": "Trial Balance",
"link_to": "Trial Balance",
"type": "Report"
},
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
}
]
}

View File

@ -1,8 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Ordered Items To Be Billed"] = {
"filters": [
]
}

View File

@ -1,27 +0,0 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-02-21 14:26:44",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-11-06 13:04:51.559061",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Ordered Items To Be Billed",
"owner": "Administrator",
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name:150\",\n`tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (`tabSales Order Item`.base_amount - (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Closed\"\n and `tabSales Order Item`.amount > 0\n and `tabSales Order Item`.billed_amt < `tabSales Order Item`.amount\norder by `tabSales Order`.transaction_date asc",
"ref_doctype": "Sales Invoice",
"report_name": "Ordered Items To Be Billed",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
}
]
}

View File

@ -1,26 +0,0 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
return columns, data
def get_column():
return [
_("Sales Order") + ":Link/Sales Order:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
]
def get_args():
return {'doctype': 'Sales Order', 'party': 'customer',
'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'}

View File

@ -155,8 +155,7 @@ def get_number_cards(company, fiscal_year_name, start_date, end_date):
["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]
["Purchase Order", "company", "=", company.name, False]
]),
"function": "Sum",
"is_public": 1,

View File

@ -11,21 +11,21 @@ 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' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>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.")
description: __("Configure the default Price List when creating a new Purchase transaction. 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.")
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 the 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.")
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 the Supplier master.")
}
];

View File

@ -19,7 +19,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
"idx": 0,
"is_complete": 0,
"modified": "2020-05-27 17:17:52.075947",
"modified": "2020-06-01 12:55:09.234944",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",

View File

@ -1,19 +1,19 @@
{
"action": "Update Settings",
"action": "Show Form Tour",
"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_mandatory": 1,
"is_single": 1,
"is_skipped": 0,
"modified": "2020-05-12 18:30:06.323797",
"modified": "2020-06-01 12:52:57.668870",
"modified_by": "Administrator",
"name": "Buying Settings",
"owner": "Administrator",
"reference_document": "Buying Settings",
"show_full_form": 0,
"title": "Configure Buying Settings.",
"validate_action": 1
"validate_action": 0
}

View File

@ -700,4 +700,5 @@ erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
erpnext.patches.v13_0.update_sla_enhancements
erpnext.patches.v12_0.update_address_template_for_india
erpnext.patches.v12_0.set_multi_uom_in_rfq
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")
erpnext.patches.v13_0.delete_old_sales_reports
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")

View File

@ -0,0 +1,21 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
reports_to_delete = ["Ordered Items To Be Delivered", "Ordered Items To Be Billed"]
for report in reports_to_delete:
if frappe.db.exists("Report", report):
delete_auto_email_reports(report)
frappe.delete_doc("Report", report)
def delete_auto_email_reports(report):
""" Check for one or multiple Auto Email Reports and delete """
auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"])
for auto_email_report in auto_email_reports:
frappe.delete_doc("Auto Email Report", auto_email_report[0])

View File

@ -0,0 +1,198 @@
# 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": "Selling",
"dashboard_name": "Selling",
"charts": [
{ "chart": "Sales Order Trends", "width": "Full"},
{ "chart": "Top Customers", "width": "Half"},
{ "chart": "Sales Order Analysis", "width": "Half"},
{ "chart": "Item-wise Annual Sales", "width": "Full"}
],
"cards": [
{ "card": "Annual Sales"},
{ "card": "Sales Orders to Deliver"},
{ "card": "Sales Orders to Bill"},
{ "card": "Active Customers"}
]
}]
def get_charts():
return [
{
"name": "Sales Order Analysis",
"chart_name": _("Sales 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": "Sales Order Analysis",
"type": "Donut"
},
{
"name": "Item-wise Annual Sales",
"chart_name": _("Item-wise Annual Sales"),
"chart_type": "Report",
"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": "Item-wise Sales History",
"type": "Bar"
},
{
"name": "Sales Order Trends",
"chart_name": _("Sales 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,
"based_on": "Item"
}),
"is_custom": 1,
"is_public": 1,
"owner": "Administrator",
"report_name": "Sales Order Trends",
"type": "Line"
},
{
"name": "Top Customers",
"chart_name": _("Top Customers"),
"chart_type": "Report",
"doctype": "Dashboard Chart",
"filters_json": json.dumps({
"company": company.name,
"period": "Monthly",
"fiscal_year": fiscal_year_name,
"based_on": "Customer"
}),
"is_custom": 1,
"is_public": 1,
"owner": "Administrator",
"report_name": "Delivery Note Trends",
"type": "Bar"
}
]
def get_number_cards():
return [
{
"name": "Annual Sales",
"aggregate_function_based_on": "base_net_total",
"doctype": "Number Card",
"document_type": "Sales Order",
"filters_json": json.dumps([
["Sales Order", "transaction_date", "Between", [start_date, end_date], False],
["Sales Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False],
["Sales Order", "docstatus", "=", 1, False],
["Sales Order", "company", "=", company.name, False]
]),
"function": "Sum",
"is_public": 1,
"label": _("Annual Sales"),
"owner": "Administrator",
"show_percentage_stats": 1,
"stats_time_interval": "Monthly"
},
{
"name": "Sales Orders to Deliver",
"doctype": "Number Card",
"document_type": "Sales Order",
"filters_json": json.dumps([
["Sales Order", "status", "in", ["To Deliver and Bill", "To Deliver", None], False],
["Sales Order", "docstatus", "=", 1, False],
["Sales Order", "company", "=", company.name, False]
]),
"function": "Count",
"is_public": 1,
"label": _("Sales Orders to Deliver"),
"owner": "Administrator",
"show_percentage_stats": 1,
"stats_time_interval": "Weekly"
},
{
"name": "Sales Orders to Bill",
"doctype": "Number Card",
"document_type": "Sales Order",
"filters_json": json.dumps([
["Sales Order", "status", "in", ["To Deliver and Bill", "To Bill", None], False],
["Sales Order", "docstatus", "=", 1, False],
["Sales Order", "company", "=", company.name, False]
]),
"function": "Count",
"is_public": 1,
"label": _("Sales Orders to Bill"),
"owner": "Administrator",
"show_percentage_stats": 1,
"stats_time_interval": "Weekly"
},
{
"name": "Active Customers",
"doctype": "Number Card",
"document_type": "Customer",
"filters_json": json.dumps([["Customer", "disabled", "=", "0"]]),
"function": "Count",
"is_public": 1,
"label": "Active Customers",
"owner": "Administrator",
"show_percentage_stats": 1,
"stats_time_interval": "Monthly"
}
]

View File

@ -1,5 +1,10 @@
{
"cards": [
{
"hidden": 0,
"label": "Selling",
"links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Items and Pricing",
@ -10,29 +15,25 @@
"label": "Settings",
"links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer 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 {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Other Reports",
"links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Sales",
"links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Key Reports",
"links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]"
"links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Other Reports",
"links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]"
}
],
"category": "Modules",
"charts": [
{
"chart_name": "Incoming Bills (Purchase Invoice)",
"label": "Income"
"chart_name": "Sales Order Trends",
"label": "Sales Order Trends"
}
],
"charts_label": "Selling ",
"creation": "2020-01-28 11:49:12.092882",
"developer_mode_only": 0,
"disable_user_customization": 0,
@ -43,52 +44,49 @@
"idx": 0,
"is_standard": 1,
"label": "Selling",
"modified": "2020-06-03 13:23:24.861706",
"modified": "2020-06-19 13:23:24.861706",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
"onboarding": "Selling",
"owner": "Administrator",
"pin_to_bottom": 0,
"pin_to_top": 0,
"shortcuts": [
{
"color": "#ffe8cd",
"format": "{} Draft",
"label": "Sales Invoice",
"link_to": "Sales Invoice",
"stats_filter": "{ \"status\": \"Draft\" }",
"color": "#cef6d1",
"format": "{} Available",
"label": "Item",
"link_to": "Item",
"stats_filter": "{\n \"disabled\":0\n}",
"type": "DocType"
},
{
"color": "#ffe8cd",
"format": "{} To Deliver",
"format": "{} To Deliver",
"label": "Sales Order",
"link_to": "Sales Order",
"stats_filter": "{\"Status\": \"To Deliver and Bill\"}",
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Deliver\", \"To Deliver and Bill\"]]\n}",
"type": "DocType"
},
{
"color": "#cef6d1",
"format": "{} Open",
"label": "Quotation",
"link_to": "Quotation",
"label": "Sales Analytics",
"link_to": "Sales Analytics",
"stats_filter": "{ \"Status\": \"Open\" }",
"type": "DocType"
},
{
"label": "Delivery Note",
"link_to": "Delivery Note",
"type": "DocType"
},
{
"label": "Accounts Receivable",
"link_to": "Accounts Receivable",
"type": "Report"
},
{
"label": "Sales Register",
"link_to": "Sales Register",
"label": "Sales Order Analysis",
"link_to": "Sales Order Analysis",
"type": "Report"
},
{
"label": "Dashboard",
"link_to": "Selling",
"type": "Dashboard"
}
]
],
"shortcuts_label": "Quick Access"
}

View File

@ -6,3 +6,26 @@ frappe.ui.form.on('Selling Settings', {
}
});
frappe.tour['Selling Settings'] = [
{
fieldname: "cust_master_name",
title: "Customer Naming By",
description: __("By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
},
{
fieldname: "selling_price_list",
title: "Default Selling Price List",
description: __("Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.")
},
{
fieldname: "so_required",
title: "Sales Order Required for Sales Invoice & Delivery Note Creation",
description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.")
},
{
fieldname: "dn_required",
title: "Delivery Note Required for Sales Invoice Creation",
description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.")
}
];

View File

@ -1,4 +1,5 @@
{
"actions": [],
"creation": "2013-06-25 10:25:16",
"description": "Settings for Selling Module",
"doctype": "DocType",
@ -79,13 +80,13 @@
{
"fieldname": "so_required",
"fieldtype": "Select",
"label": "Sales Order Required",
"label": "Sales Order Required for Sales Invoice & Delivery Note Creation",
"options": "No\nYes"
},
{
"fieldname": "dn_required",
"fieldtype": "Select",
"label": "Delivery Note Required",
"label": "Delivery Note Required for Sales Invoice Creation",
"options": "No\nYes"
},
{
@ -137,7 +138,8 @@
"icon": "fa fa-cog",
"idx": 1,
"issingle": 1,
"modified": "2019-12-09 13:38:36.486298",
"links": [],
"modified": "2020-06-01 13:58:35.637858",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",

View File

@ -0,0 +1,54 @@
{
"allow_roles": [
{
"role": "Sales Manager"
},
{
"role": "Sales User"
},
{
"role": "Stock Manager"
},
{
"role": "Stock User"
}
],
"creation": "2020-06-01 12:44:42.589930",
"docstatus": 0,
"doctype": "Module Onboarding",
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/selling",
"idx": 0,
"is_complete": 0,
"modified": "2020-06-01 13:35:16.100512",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
"owner": "Administrator",
"steps": [
{
"step": "Introduction to Selling"
},
{
"step": "Create a Customer"
},
{
"step": "Setup your Warehouse"
},
{
"step": "Create a Product"
},
{
"step": "Create a Quotation"
},
{
"step": "Create your first Sales Order"
},
{
"step": "Selling Settings"
}
],
"subtitle": "Products, Sales, Analysis and more.",
"success_message": "The Selling Module is all set up!",
"title": "Let's Set Up the Selling Module.",
"user_can_dismiss": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:46:41.831517",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:16:19.731719",
"modified_by": "Administrator",
"name": "Create a Customer",
"owner": "Administrator",
"reference_document": "Customer",
"show_full_form": 0,
"title": "Create a Customer",
"validate_action": 1
}

View File

@ -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
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-06-01 13:34:58.958641",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:34:58.958641",
"modified_by": "Administrator",
"name": "Create a Quotation",
"owner": "Administrator",
"reference_document": "Quotation",
"show_full_form": 1,
"title": "Create a Quotation",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-05 16:42:31.476275",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-19 12:50:59.010439",
"modified_by": "Administrator",
"name": "Create Product",
"owner": "Administrator",
"reference_document": "Item",
"show_full_form": 0,
"title": "Create a Finished Good",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-06-01 12:52:27.181841",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 1,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 12:52:27.181841",
"modified_by": "Administrator",
"name": "Create your first Sales Order",
"owner": "Administrator",
"reference_document": "Sales Order",
"show_full_form": 1,
"title": "Create your first Sales Order",
"validate_action": 1
}

View File

@ -0,0 +1,19 @@
{
"action": "Watch Video",
"creation": "2020-06-01 12:44:32.089234",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:29:13.703177",
"modified_by": "Administrator",
"name": "Introduction to Selling",
"owner": "Administrator",
"show_full_form": 0,
"title": "Introduction to Selling",
"validate_action": 1,
"video_url": "https://youtu.be/1eP90MWoDQM"
}

View File

@ -0,0 +1,19 @@
{
"action": "Show Form Tour",
"creation": "2020-06-01 13:01:45.615189",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
"modified": "2020-06-01 13:04:14.980743",
"modified_by": "Administrator",
"name": "Selling Settings",
"owner": "Administrator",
"reference_document": "Selling Settings",
"show_full_form": 0,
"title": "Configure Selling Settings.",
"validate_action": 0
}

View File

@ -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
}

View File

@ -12,12 +12,6 @@ frappe.query_reports["Item-wise Sales History"] = {
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname:"item_group",
label: __("Item Group"),
fieldtype: "Link",
options: "Item Group"
},
{
fieldname:"from_date",
reqd: 1,
@ -32,6 +26,38 @@ frappe.query_reports["Item-wise Sales History"] = {
label: __("To Date"),
fieldtype: "Date",
},
{
fieldname:"item_group",
label: __("Item Group"),
fieldtype: "Link",
options: "Item Group"
},
{
fieldname:"item_code",
label: __("Item"),
fieldtype: "Link",
options: "Item",
get_query: () => {
return {
query: "erpnext.controllers.queries.item_query"
}
}
},
{
fieldname:"customer",
label: __("Customer"),
fieldtype: "Link",
options: "Customer"
}
],
]
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
let format_fields = ["delivered_quantity", "billed_amount"];
if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) {
value = "<span style='color:green;'>" + value + "</span>";
}
return value;
}
};

View File

@ -11,7 +11,10 @@ def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns(filters)
data = get_data(filters)
return columns, data
chart_data = get_chart_data(data)
return columns, data, None, chart_data
def get_columns(filters):
return [
@ -181,6 +184,12 @@ def get_conditions(filters):
if filters.get('to_date'):
conditions += "AND so.transaction_date <= '%s'" %filters.to_date
if filters.get("item_code"):
conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code)
if filters.get("customer"):
conditions += "AND so.customer = '%s'" %frappe.db.escape(filters.customer)
return conditions
def get_customer_details():
@ -212,3 +221,34 @@ def get_sales_order_details(company_list, filters):
AND so.company in ({0})
AND so.docstatus = 1 {1}
""".format(','.join(["%s"] * len(company_list)), conditions), tuple(company_list), as_dict=1)
def get_chart_data(data):
item_wise_sales_map = {}
labels, datapoints = [], []
for row in data:
item_key = row.get("item_code")
if not item_key in item_wise_sales_map:
item_wise_sales_map[item_key] = 0
item_wise_sales_map[item_key] = flt(item_wise_sales_map[item_key]) + flt(row.get("amount"))
item_wise_sales_map = { item: value for item, value in (sorted(item_wise_sales_map.items(), key = lambda i: i[1], reverse=True))}
for key in item_wise_sales_map:
labels.append(key)
datapoints.append(item_wise_sales_map[key])
return {
"data" : {
"labels" : labels[:30], # show max of 30 items in chart
"datasets" : [
{
"name" : _(" Total Sales Amount"),
"values" : datapoints[:30]
}
]
},
"type" : "bar"
}

View File

@ -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):
@ -11,4 +12,48 @@ def execute(filters=None):
conditions = get_columns(filters, "Quotation")
data = get_data(filters, conditions)
return conditions["columns"], data
chart_data = get_chart_data(data, conditions, filters)
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", "Customer"] 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")) + _(" Quoted Amount"),
"values" : datapoints
}
]
},
"type" : "line",
"lineOptions": {
"regionFill": 1
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Sales 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": "sales_order",
"label": __("Sales Order"),
"fieldtype": "MultiSelectList",
"width": "80",
"options": "Sales Order",
"get_data": function(txt) {
return frappe.db.get_link_options("Sales Order", txt);
},
"get_query": () =>{
return {
filters: { "docstatus": 1 }
}
}
},
{
"fieldname": "status",
"label": __("Status"),
"fieldtype": "MultiSelectList",
"width": "80",
get_data: function(txt) {
let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"]
let options = []
for (let option of status){
options.push({
"value": option,
"description": ""
})
}
return options
}
},
{
"fieldname": "group_by_so",
"label": __("Group by Sales Order"),
"fieldtype": "Check",
"default": 0
}
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
let format_fields = ["delivered_qty", "billed_amount"];
if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) {
value = "<span style='color:green;'>" + value + "</span>";
}
if (column.fieldname == "delay" && data && data[column.fieldname] > 0) {
value = "<span style='color:red;'>" + value + "</span>";
}
return value;
}
};

View File

@ -0,0 +1,36 @@
{
"add_total_row": 1,
"creation": "2020-05-29 14:54:53.591445",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2020-05-29 14:54:53.591445",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Analysis",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Order",
"report_name": "Sales Order Analysis",
"report_type": "Script Report",
"roles": [
{
"role": "Sales User"
},
{
"role": "Sales Manager"
},
{
"role": "Maintenance User"
},
{
"role": "Accounts User"
},
{
"role": "Stock User"
}
]
}

View File

@ -0,0 +1,279 @@
# 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 [], [], None, []
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 so.transaction_date between %(from_date)s and %(to_date)s"
if filters.get("company"):
conditions += " and so.company = %(company)s"
if filters.get("sales_order"):
conditions += " and so.name in %(sales_order)s"
if filters.get("status"):
conditions += " and so.status in %(status)s"
return conditions
def get_data(conditions, filters):
data = frappe.db.sql("""
SELECT
so.transaction_date as date,
soi.delivery_date as delivery_date,
so.name as sales_order,
so.status, so.customer, soi.item_code,
DATEDIFF(CURDATE(), soi.delivery_date) as delay_days,
IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay,
soi.qty, soi.delivered_qty,
(soi.qty - soi.delivered_qty) AS pending_qty,
IFNULL(sii.qty, 0) as billed_qty,
soi.base_amount as amount,
(soi.delivered_qty * soi.base_rate) as delivered_qty_amount,
(soi.billed_amt * IFNULL(so.conversion_rate, 1)) as billed_amount,
(soi.base_amount - (soi.billed_amt * IFNULL(so.conversion_rate, 1))) as pending_amount,
soi.warehouse as warehouse,
so.company, soi.name
FROM
`tabSales Order` so,
`tabSales Order Item` soi
LEFT JOIN `tabSales Invoice Item` sii
ON sii.so_detail = soi.name
WHERE
soi.parent = so.name
and so.status not in ('Stopped', 'Closed', 'On Hold')
and so.docstatus = 1
{conditions}
GROUP BY soi.name
ORDER BY so.transaction_date ASC
""".format(conditions=conditions), filters, as_dict=1)
return data
def prepare_data(data, filters):
completed, pending = 0, 0
if filters.get("group_by_so"):
sales_order_map = {}
for row in data:
# sum data for chart
completed += row["billed_amount"]
pending += row["pending_amount"]
# prepare data for report view
row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"])
row["delay"] = 0 if row["delay"] < 0 else row["delay"]
if filters.get("group_by_so"):
so_name = row["sales_order"]
if not so_name in sales_order_map:
# create an entry
row_copy = copy.deepcopy(row)
sales_order_map[so_name] = row_copy
else:
# update existing entry
so_row = sales_order_map[so_name]
so_row["required_date"] = max(getdate(so_row["delivery_date"]), getdate(row["delivery_date"]))
so_row["delay"] = min(so_row["delay"], row["delay"])
# sum numeric columns
fields = ["qty", "delivered_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount",
"delivered_qty_amount", "billed_amount", "pending_amount"]
for field in fields:
so_row[field] = flt(row[field]) + flt(so_row[field])
chart_data = prepare_chart_data(pending, completed)
if filters.get("group_by_so"):
data = []
for so in sales_order_map:
data.append(sales_order_map[so])
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": _("Sales Order"),
"fieldname": "sales_order",
"fieldtype": "Link",
"options": "Sales Order",
"width": 160
},
{
"label":_("Status"),
"fieldname": "status",
"fieldtype": "Data",
"width": 130
},
{
"label": _("Customer"),
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer",
"width": 130
}]
if not filters.get("group_by_so"):
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": _("Delivered Qty"),
"fieldname": "delivered_qty",
"fieldtype": "Float",
"width": 120,
"convertible": "qty"
},
{
"label": _("Qty to Deliver"),
"fieldname": "pending_qty",
"fieldtype": "Float",
"width": 120,
"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": _("Amount Delivered"),
"fieldname": "delivered_qty_amount",
"fieldtype": "Currency",
"width": 100,
"options": "Company:company:default_currency",
"convertible": "rate"
},
{
"label":_("Delivery Date"),
"fieldname": "delivery_date",
"fieldtype": "Date",
"width": 120
},
{
"label": _("Delay (in Days)"),
"fieldname": "delay",
"fieldtype": "Data",
"width": 100
}
])
if not filters.get("group_by_so"):
columns.append({
"label": _("Warehouse"),
"fieldname": "warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 100
})
columns.append({
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 100
})
return columns

View File

@ -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,4 +11,48 @@ def execute(filters=None):
data = []
conditions = get_columns(filters, "Sales Order")
data = get_data(filters, conditions)
return conditions["columns"], data
chart_data = get_chart_data(data, conditions, filters)
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", "Customer"] 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")) + _(" Sales Value"),
"values" : datapoints
}
]
},
"type" : "line",
"lineOptions": {
"regionFill": 1
}
}

View File

@ -33,7 +33,7 @@
{
"hidden": 0,
"label": "Key Reports",
"links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]"
"links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@ -58,7 +58,7 @@
"idx": 0,
"is_standard": 1,
"label": "Stock",
"modified": "2020-05-27 20:38:25.255323",
"modified": "2020-05-30 17:32:11.062681",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock",

View File

@ -20,7 +20,7 @@ frappe.tour['Stock Settings'] = [
{
fieldname: "item_naming_by",
title: __("Item 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 Naming Series choose the 'Naming Series' option.")
description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
},
{
fieldname: "default_warehouse",

View File

@ -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:

View File

@ -1,34 +0,0 @@
{
"add_total_row": 1,
"creation": "2018-01-09 18:38:23.540100",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2019-04-01 22:10:09.829361",
"modified_by": "Administrator",
"module": "Stock",
"name": "Ordered Items To Be Delivered",
"owner": "Administrator",
"prepared_report": 0,
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`status` as \"Status:Data:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name::150\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project:Link/Project:120\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order Item`.`delivery_date` as \"Item Delivery Date:Date:120\",\n DATEDIFF(CURDATE(),`tabSales Order Item`.`delivery_date`) as \"Delay Days:Int:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc",
"ref_doctype": "Delivery Note",
"report_name": "Ordered Items To Be Delivered",
"report_type": "Query Report",
"roles": [
{
"role": "Stock User"
},
{
"role": "Stock Manager"
},
{
"role": "Sales User"
},
{
"role": "Accounts User"
}
]
}