feat: Available batches report as on specific date
(cherry picked from commit b8f7979794)
This commit is contained in:
committed by
Mergify
parent
3df5aa04b5
commit
791e4269d2
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Available Batch Report"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
width: "80",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_default("company"),
|
||||
},
|
||||
{
|
||||
fieldname: "to_date",
|
||||
label: __("On This Date"),
|
||||
fieldtype: "Date",
|
||||
width: "80",
|
||||
reqd: 1,
|
||||
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
},
|
||||
{
|
||||
fieldname: "item_code",
|
||||
label: __("Item"),
|
||||
fieldtype: "Link",
|
||||
width: "80",
|
||||
options: "Item",
|
||||
get_query: () => {
|
||||
return {
|
||||
filters: {
|
||||
has_batch_no: 1,
|
||||
disabled: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: "warehouse",
|
||||
label: __("Warehouse"),
|
||||
fieldtype: "Link",
|
||||
width: "80",
|
||||
options: "Warehouse",
|
||||
get_query: () => {
|
||||
let warehouse_type = frappe.query_report.get_filter_value("warehouse_type");
|
||||
let company = frappe.query_report.get_filter_value("company");
|
||||
|
||||
return {
|
||||
filters: {
|
||||
...(warehouse_type && { warehouse_type }),
|
||||
...(company && { company }),
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: "warehouse_type",
|
||||
label: __("Warehouse Type"),
|
||||
fieldtype: "Link",
|
||||
width: "80",
|
||||
options: "Warehouse Type",
|
||||
},
|
||||
{
|
||||
fieldname: "batch_no",
|
||||
label: __("Batch No"),
|
||||
fieldtype: "Link",
|
||||
width: "80",
|
||||
options: "Batch",
|
||||
get_query: () => {
|
||||
let item = frappe.query_report.get_filter_value("item_code");
|
||||
|
||||
return {
|
||||
filters: {
|
||||
...(item && { item }),
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: "include_expired_batches",
|
||||
label: __("Include Expired Batches"),
|
||||
fieldtype: "Check",
|
||||
width: "80",
|
||||
},
|
||||
{
|
||||
fieldname: "show_item_name",
|
||||
label: __("Show Item Name"),
|
||||
fieldtype: "Check",
|
||||
width: "80",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"columns": [],
|
||||
"creation": "2024-04-11 17:03:32.253275",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"json": "{}",
|
||||
"letter_head": "",
|
||||
"letterhead": null,
|
||||
"modified": "2024-04-23 17:18:19.779036",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Available Batch Report",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Stock Ledger Entry",
|
||||
"report_name": "Available Batch Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Accounts Manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import flt, today
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
data = get_data(filters)
|
||||
columns = get_columns(filters)
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [
|
||||
{
|
||||
"label": _("Item Code"),
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 200,
|
||||
}
|
||||
]
|
||||
|
||||
if filters.show_item_name:
|
||||
columns.append(
|
||||
{
|
||||
"label": _("Item Name"),
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 200,
|
||||
}
|
||||
)
|
||||
|
||||
columns.extend(
|
||||
[
|
||||
{
|
||||
"label": _("Warehouse"),
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"label": _("Batch No"),
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"width": 150,
|
||||
"options": "Batch",
|
||||
},
|
||||
{"label": _("Balance Qty"), "fieldname": "balance_qty", "fieldtype": "Float", "width": 150},
|
||||
]
|
||||
)
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
batchwise_data = get_batchwise_data_from_stock_ledger(filters)
|
||||
batchwise_data = get_batchwise_data_from_serial_batch_bundle(batchwise_data, filters)
|
||||
|
||||
data = parse_batchwise_data(batchwise_data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def parse_batchwise_data(batchwise_data):
|
||||
data = []
|
||||
for key in batchwise_data:
|
||||
d = batchwise_data[key]
|
||||
if d.balance_qty == 0:
|
||||
continue
|
||||
|
||||
data.append(d)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_batchwise_data_from_stock_ledger(filters):
|
||||
batchwise_data = frappe._dict({})
|
||||
|
||||
table = frappe.qb.DocType("Stock Ledger Entry")
|
||||
batch = frappe.qb.DocType("Batch")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.inner_join(batch)
|
||||
.on(table.batch_no == batch.name)
|
||||
.select(
|
||||
table.item_code,
|
||||
table.batch_no,
|
||||
table.warehouse,
|
||||
Sum(table.actual_qty).as_("balance_qty"),
|
||||
)
|
||||
.where(table.is_cancelled == 0)
|
||||
.groupby(table.batch_no, table.item_code, table.warehouse)
|
||||
)
|
||||
|
||||
query = get_query_based_on_filters(query, batch, table, filters)
|
||||
|
||||
for d in query.run(as_dict=True):
|
||||
key = (d.item_code, d.warehouse, d.batch_no)
|
||||
batchwise_data.setdefault(key, d)
|
||||
|
||||
return batchwise_data
|
||||
|
||||
|
||||
def get_batchwise_data_from_serial_batch_bundle(batchwise_data, filters):
|
||||
table = frappe.qb.DocType("Stock Ledger Entry")
|
||||
ch_table = frappe.qb.DocType("Serial and Batch Entry")
|
||||
batch = frappe.qb.DocType("Batch")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.inner_join(ch_table)
|
||||
.on(table.serial_and_batch_bundle == ch_table.parent)
|
||||
.inner_join(batch)
|
||||
.on(ch_table.batch_no == batch.name)
|
||||
.select(
|
||||
table.item_code,
|
||||
ch_table.batch_no,
|
||||
table.warehouse,
|
||||
Sum(ch_table.qty).as_("balance_qty"),
|
||||
)
|
||||
.where((table.is_cancelled == 0) & (table.docstatus == 1))
|
||||
.groupby(ch_table.batch_no, table.item_code, ch_table.warehouse)
|
||||
)
|
||||
|
||||
query = get_query_based_on_filters(query, batch, table, filters)
|
||||
|
||||
for d in query.run(as_dict=True):
|
||||
key = (d.item_code, d.warehouse, d.batch_no)
|
||||
if key in batchwise_data:
|
||||
batchwise_data[key].balance_qty += flt(d.balance_qty)
|
||||
else:
|
||||
batchwise_data.setdefault(key, d)
|
||||
|
||||
return batchwise_data
|
||||
|
||||
|
||||
def get_query_based_on_filters(query, batch, table, filters):
|
||||
if filters.item_code:
|
||||
query = query.where(table.item_code == filters.item_code)
|
||||
|
||||
if filters.batch_no:
|
||||
query = query.where(batch.name == filters.batch_no)
|
||||
|
||||
if not filters.include_expired_batches:
|
||||
query = query.where((batch.expiry_date >= today()) | (batch.expiry_date.isnull()))
|
||||
if filters.to_date == today():
|
||||
query = query.where(batch.batch_qty > 0)
|
||||
|
||||
if filters.warehouse:
|
||||
lft, rgt = frappe.db.get_value("Warehouse", filters.warehouse, ["lft", "rgt"])
|
||||
warehouses = frappe.get_all(
|
||||
"Warehouse", filters={"lft": (">=", lft), "rgt": ("<=", rgt), "is_group": 0}, pluck="name"
|
||||
)
|
||||
|
||||
query = query.where(table.warehouse.isin(warehouses))
|
||||
|
||||
elif filters.warehouse_type:
|
||||
warehouses = frappe.get_all(
|
||||
"Warehouse", filters={"warehouse_type": filters.warehouse_type, "is_group": 0}, pluck="name"
|
||||
)
|
||||
|
||||
query = query.where(table.warehouse.isin(warehouses))
|
||||
|
||||
if filters.show_item_name:
|
||||
query = query.select(batch.item_name)
|
||||
|
||||
return query
|
||||
Reference in New Issue
Block a user