215 lines
5.2 KiB
Python
215 lines
5.2 KiB
Python
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
|
# License: GNU General Public License v3. See license.txt
|
|
|
|
|
|
import frappe
|
|
from frappe import _
|
|
from frappe.query_builder.functions import IfNull, Sum
|
|
from frappe.utils import flt
|
|
|
|
|
|
def execute(filters=None):
|
|
if not filters:
|
|
filters = {}
|
|
|
|
columns = get_columns(filters)
|
|
item_map = get_item_details(filters)
|
|
pl = get_price_list()
|
|
last_purchase_rate = get_last_purchase_rate()
|
|
bom_rate = get_item_bom_rate()
|
|
val_rate_map = get_valuation_rate()
|
|
|
|
from erpnext.accounts.utils import get_currency_precision
|
|
|
|
precision = get_currency_precision() or 2
|
|
data = []
|
|
for item in sorted(item_map):
|
|
data.append(
|
|
[
|
|
item,
|
|
item_map[item]["item_name"],
|
|
item_map[item]["item_group"],
|
|
item_map[item]["brand"],
|
|
item_map[item]["description"],
|
|
item_map[item]["stock_uom"],
|
|
flt(last_purchase_rate.get(item, 0), precision),
|
|
flt(val_rate_map.get(item, 0), precision),
|
|
pl.get(item, {}).get("Selling"),
|
|
pl.get(item, {}).get("Buying"),
|
|
flt(bom_rate.get(item, 0), precision),
|
|
]
|
|
)
|
|
|
|
return columns, data
|
|
|
|
|
|
def get_columns(filters):
|
|
"""return columns based on filters"""
|
|
|
|
columns = [
|
|
_("Item") + ":Link/Item:100",
|
|
_("Item Name") + "::150",
|
|
_("Item Group") + ":Link/Item Group:125",
|
|
_("Brand") + "::100",
|
|
_("Description") + "::150",
|
|
_("UOM") + ":Link/UOM:80",
|
|
_("Last Purchase Rate") + ":Currency:90",
|
|
_("Valuation Rate") + ":Currency:80",
|
|
_("Sales Price List") + "::180",
|
|
_("Purchase Price List") + "::180",
|
|
_("BOM Rate") + ":Currency:90",
|
|
]
|
|
|
|
return columns
|
|
|
|
|
|
def get_item_details(filters):
|
|
"""returns all items details"""
|
|
|
|
item_map = {}
|
|
|
|
item = frappe.qb.DocType("Item")
|
|
query = (
|
|
frappe.qb.from_(item)
|
|
.select(item.name, item.item_group, item.item_name, item.description, item.brand, item.stock_uom)
|
|
.orderby(item.item_code, item.item_group)
|
|
)
|
|
|
|
if filters.get("items") == "Enabled Items only":
|
|
query = query.where(item.disabled == 0)
|
|
elif filters.get("items") == "Disabled Items only":
|
|
query = query.where(item.disabled == 1)
|
|
|
|
for i in query.run(as_dict=True):
|
|
item_map.setdefault(i.name, i)
|
|
|
|
return item_map
|
|
|
|
|
|
def get_price_list():
|
|
"""Get selling & buying price list of every item"""
|
|
|
|
rate = {}
|
|
|
|
ip = frappe.qb.DocType("Item Price")
|
|
pl = frappe.qb.DocType("Price List")
|
|
cu = frappe.qb.DocType("Currency")
|
|
|
|
price_list = (
|
|
frappe.qb.from_(ip)
|
|
.from_(pl)
|
|
.from_(cu)
|
|
.select(
|
|
ip.item_code,
|
|
ip.buying,
|
|
ip.selling,
|
|
(IfNull(cu.symbol, ip.currency)).as_("currency"),
|
|
ip.price_list_rate,
|
|
ip.price_list,
|
|
)
|
|
.where((ip.price_list == pl.name) & (pl.currency == cu.name) & (pl.enabled == 1))
|
|
).run(as_dict=True)
|
|
|
|
for d in price_list:
|
|
d.update(
|
|
{"price": "{0} {1} - {2}".format(d.currency, round(d.price_list_rate, 2), d.price_list)}
|
|
)
|
|
d.pop("currency")
|
|
d.pop("price_list_rate")
|
|
d.pop("price_list")
|
|
|
|
if d.price:
|
|
rate.setdefault(d.item_code, {}).setdefault("Buying" if d.buying else "Selling", []).append(
|
|
d.price
|
|
)
|
|
|
|
item_rate_map = {}
|
|
|
|
for item in rate:
|
|
for buying_or_selling in rate[item]:
|
|
item_rate_map.setdefault(item, {}).setdefault(
|
|
buying_or_selling, ", ".join(rate[item].get(buying_or_selling, []))
|
|
)
|
|
|
|
return item_rate_map
|
|
|
|
|
|
def get_last_purchase_rate():
|
|
item_last_purchase_rate_map = {}
|
|
|
|
po = frappe.qb.DocType("Purchase Order")
|
|
pr = frappe.qb.DocType("Purchase Receipt")
|
|
pi = frappe.qb.DocType("Purchase Invoice")
|
|
po_item = frappe.qb.DocType("Purchase Order Item")
|
|
pr_item = frappe.qb.DocType("Purchase Receipt Item")
|
|
pi_item = frappe.qb.DocType("Purchase Invoice Item")
|
|
|
|
query = (
|
|
frappe.qb.from_(
|
|
(
|
|
frappe.qb.from_(po)
|
|
.from_(po_item)
|
|
.select(po_item.item_code, po.transaction_date.as_("posting_date"), po_item.base_rate)
|
|
.where((po.name == po_item.parent) & (po.docstatus == 1))
|
|
)
|
|
+ (
|
|
frappe.qb.from_(pr)
|
|
.from_(pr_item)
|
|
.select(pr_item.item_code, pr.posting_date, pr_item.base_rate)
|
|
.where((pr.name == pr_item.parent) & (pr.docstatus == 1))
|
|
)
|
|
+ (
|
|
frappe.qb.from_(pi)
|
|
.from_(pi_item)
|
|
.select(pi_item.item_code, pi.posting_date, pi_item.base_rate)
|
|
.where((pi.name == pi_item.parent) & (pi.docstatus == 1) & (pi.update_stock == 1))
|
|
)
|
|
)
|
|
.select("*")
|
|
.orderby("item_code", "posting_date")
|
|
)
|
|
|
|
for d in query.run(as_dict=True):
|
|
item_last_purchase_rate_map[d.item_code] = d.base_rate
|
|
|
|
return item_last_purchase_rate_map
|
|
|
|
|
|
def get_item_bom_rate():
|
|
"""Get BOM rate of an item from BOM"""
|
|
|
|
item_bom_map = {}
|
|
|
|
bom = frappe.qb.DocType("BOM")
|
|
bom_data = (
|
|
frappe.qb.from_(bom)
|
|
.select(bom.item, (bom.total_cost / bom.quantity).as_("bom_rate"))
|
|
.where((bom.is_active == 1) & (bom.is_default == 1))
|
|
).run(as_dict=True)
|
|
|
|
for d in bom_data:
|
|
item_bom_map.setdefault(d.item, flt(d.bom_rate))
|
|
|
|
return item_bom_map
|
|
|
|
|
|
def get_valuation_rate():
|
|
"""Get an average valuation rate of an item from all warehouses"""
|
|
|
|
item_val_rate_map = {}
|
|
|
|
bin = frappe.qb.DocType("Bin")
|
|
bin_data = (
|
|
frappe.qb.from_(bin)
|
|
.select(
|
|
bin.item_code, Sum(bin.actual_qty * bin.valuation_rate) / Sum(bin.actual_qty).as_("val_rate")
|
|
)
|
|
.where(bin.actual_qty > 0)
|
|
.groupby(bin.item_code)
|
|
).run(as_dict=True)
|
|
|
|
for d in bin_data:
|
|
item_val_rate_map.setdefault(d.item_code, d.val_rate)
|
|
|
|
return item_val_rate_map
|