feat: Buying Desk, Dashboard and Reports

This commit is contained in:
marination 2020-05-05 23:58:08 +05:30
parent a4567a446f
commit cc989b62bd
9 changed files with 753 additions and 31 deletions

View File

@ -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,32 +28,33 @@
{
"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 \"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 Analytics",
"label": "Buying Analytics"
}
],
"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": "",
"idx": 0,
"is_standard": 1,
"label": "Buying",
"modified": "2020-04-01 11:28:51.192097",
"modified": "2020-05-05 23:48:25.788598",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
@ -62,32 +63,48 @@
"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}",
"type": "DocType"
},
{
"format": "{} to receive",
"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"
},
{
"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 Bill ",
"label": "Purchase Receipt",
"link_to": "Purchase Receipt",
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}",
"type": "DocType"
},
{
"color": "#ffe8cd",
"format": "{} Unpaid / Overdue",
"label": "Purchase Invoice",
"link_to": "Purchase Invoice",
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}",
"type": "DocType"
},
{
"color": "#cef6d1",
"format": "{} Active",
"label": "Supplier Quotation",
"link_to": "Supplier Quotation",
"stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}",
"type": "DocType"
},
{
"label": "Accounts Payable",
"link_to": "Accounts Payable",
"type": "Report"
},
{
"label": "Purchase Register",
"link_to": "Purchase Register",
"label": "Item-wise Purchase Register",
"link_to": "Item-wise Purchase Register",
"type": "Report"
}
]

View File

@ -0,0 +1,86 @@
// 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": "chart_based_on",
"label": __("Chart Based On"),
"fieldtype": "Select",
"width": "80",
"options": "Quantity\nAmount",
"default": "Quantity"
},
{
"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;
}
};

View File

@ -0,0 +1,33 @@
{
"add_total_row": 0,
"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-04 18:41:28.625119",
"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"
}
]
}

View File

@ -0,0 +1,277 @@
# 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 [], []
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 '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date"))
if filters.get("company"):
conditions += " and po.company = '{0}'".format(filters.get("company"))
if filters.get("purchase_order"):
conditions += " and po.name = '{0}'".format(filters.get("purchase_order"))
if filters.get("status"):
conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status")))
return conditions
def get_data(conditions, filters):
status = filters.get("status")
# temporary fix for dashboard chart
if status is None:
status = []
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), tuple(status), as_dict=1)
return data
def prepare_data(data, filters):
completed, pending = 0,0
chart_based_on = filters.get("chart_based_on")
pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount"
completed_field = "received_qty" if chart_based_on == "Quantity" else "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(chart_based_on, 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(chart_based_on, pending, completed):
labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["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

View File

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

View File

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

View File

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