Merge pull request #36151 from rohitwaghchaure/table-for-serial-and-batch
fix: Added report 'Serial and Batch Summary' to view serial / batch nos
This commit is contained in:
commit
a0742c52bb
@ -358,12 +358,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
|
||||
refresh() {
|
||||
|
||||
erpnext.toggle_naming_series();
|
||||
erpnext.hide_company();
|
||||
this.set_dynamic_labels();
|
||||
this.setup_sms();
|
||||
this.setup_quality_inspection();
|
||||
this.validate_has_items();
|
||||
erpnext.utils.view_serial_batch_nos(this.frm);
|
||||
}
|
||||
|
||||
scan_barcode() {
|
||||
|
@ -113,6 +113,23 @@ $.extend(erpnext.utils, {
|
||||
}
|
||||
},
|
||||
|
||||
view_serial_batch_nos: function(frm) {
|
||||
let bundle_ids = frm.doc.items.filter(d => d.serial_and_batch_bundle);
|
||||
|
||||
if (bundle_ids?.length) {
|
||||
frm.add_custom_button(__('Serial / Batch Nos'), () => {
|
||||
frappe.route_options = {
|
||||
"voucher_no": frm.doc.name,
|
||||
"voucher_type": frm.doc.doctype,
|
||||
"from_date": frm.doc.posting_date || frm.doc.transaction_date,
|
||||
"to_date": frm.doc.posting_date || frm.doc.transaction_date,
|
||||
"company": frm.doc.company,
|
||||
};
|
||||
frappe.set_route("query-report", "Serial and Batch Summary");
|
||||
}, __('View'));
|
||||
}
|
||||
},
|
||||
|
||||
add_indicator_for_multicompany: function(frm, info) {
|
||||
frm.dashboard.stats_area.show();
|
||||
frm.dashboard.stats_area_row.addClass('flex');
|
||||
@ -1020,4 +1037,4 @@ function attach_selector_button(inner_text, append_loction, context, grid_row) {
|
||||
$btn.on("click", function() {
|
||||
context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
|
||||
});
|
||||
}
|
||||
}
|
@ -193,7 +193,7 @@
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"label": "Naming Series",
|
||||
"options": "SBB-.####"
|
||||
"options": "SABB-.########"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@ -244,7 +244,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-04-10 20:02:42.964309",
|
||||
"modified": "2023-07-16 10:53:04.045605",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial and Batch Bundle",
|
||||
|
@ -889,13 +889,16 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=None):
|
||||
filters = get_filters_for_bundle(item_code, docstatus=docstatus, voucher_no=voucher_no, name=name)
|
||||
def get_serial_batch_ledgers(item_code=None, docstatus=None, voucher_no=None, name=None):
|
||||
filters = get_filters_for_bundle(
|
||||
item_code=item_code, docstatus=docstatus, voucher_no=voucher_no, name=name
|
||||
)
|
||||
|
||||
return frappe.get_all(
|
||||
"Serial and Batch Bundle",
|
||||
fields=[
|
||||
"`tabSerial and Batch Bundle`.`name`",
|
||||
"`tabSerial and Batch Bundle`.`item_code`",
|
||||
"`tabSerial and Batch Entry`.`qty`",
|
||||
"`tabSerial and Batch Entry`.`warehouse`",
|
||||
"`tabSerial and Batch Entry`.`batch_no`",
|
||||
@ -906,12 +909,14 @@ def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=No
|
||||
)
|
||||
|
||||
|
||||
def get_filters_for_bundle(item_code, docstatus=None, voucher_no=None, name=None):
|
||||
def get_filters_for_bundle(item_code=None, docstatus=None, voucher_no=None, name=None):
|
||||
filters = [
|
||||
["Serial and Batch Bundle", "item_code", "=", item_code],
|
||||
["Serial and Batch Bundle", "is_cancelled", "=", 0],
|
||||
]
|
||||
|
||||
if item_code:
|
||||
filters.append(["Serial and Batch Bundle", "item_code", "=", item_code])
|
||||
|
||||
if not docstatus:
|
||||
docstatus = [0, 1]
|
||||
|
||||
|
@ -925,6 +925,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
||||
this.toggle_related_fields(this.frm.doc);
|
||||
this.toggle_enable_bom();
|
||||
this.show_stock_ledger();
|
||||
erpnext.utils.view_serial_batch_nos(this.frm);
|
||||
if (this.frm.doc.docstatus===1 && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
|
||||
this.show_general_ledger();
|
||||
}
|
||||
|
@ -337,6 +337,7 @@ erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.st
|
||||
refresh() {
|
||||
if(this.frm.doc.docstatus > 0) {
|
||||
this.show_stock_ledger();
|
||||
erpnext.utils.view_serial_batch_nos(this.frm);
|
||||
if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
|
||||
this.show_general_ledger();
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Serial and Batch Summary"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company"),
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"item_code",
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
},
|
||||
{
|
||||
"fieldname":"warehouse",
|
||||
"label": __("Warehouse"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
},
|
||||
{
|
||||
"fieldname":"voucher_type",
|
||||
"label": __("Voucher Type"),
|
||||
"fieldtype": "Link",
|
||||
"options": "DocType",
|
||||
get_query: function() {
|
||||
return {
|
||||
query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_voucher_type",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"voucher_no",
|
||||
"label": __("Voucher No"),
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
if (!frappe.query_report.filters) return;
|
||||
|
||||
let voucher_type = frappe.query_report.get_filter_value('voucher_type');
|
||||
if (!voucher_type) return;
|
||||
|
||||
return frappe.db.get_link_options(voucher_type, txt);
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname":"serial_no",
|
||||
"label": __("Serial No"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Serial No",
|
||||
get_query: function() {
|
||||
return {
|
||||
query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_serial_nos",
|
||||
filters: {
|
||||
"item_code": frappe.query_report.get_filter_value('item_code'),
|
||||
"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
|
||||
"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"batch_no",
|
||||
"label": __("Batch No"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Batch",
|
||||
get_query: function() {
|
||||
return {
|
||||
query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_batch_nos",
|
||||
filters: {
|
||||
"item_code": frappe.query_report.get_filter_value('item_code'),
|
||||
"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
|
||||
"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2023-07-13 16:53:27.735091",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"json": "{}",
|
||||
"modified": "2023-07-13 16:53:33.204591",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial and Batch Summary",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Serial and Batch Bundle",
|
||||
"report_name": "Serial and Batch Summary",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "System Manager"
|
||||
},
|
||||
{
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Maintenance User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
data = get_data(filters)
|
||||
columns = get_columns(filters, data)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
filter_conditions = get_filter_conditions(filters)
|
||||
|
||||
return frappe.get_all(
|
||||
"Serial and Batch Bundle",
|
||||
fields=[
|
||||
"`tabSerial and Batch Bundle`.`voucher_type`",
|
||||
"`tabSerial and Batch Bundle`.`posting_date`",
|
||||
"`tabSerial and Batch Bundle`.`name`",
|
||||
"`tabSerial and Batch Bundle`.`company`",
|
||||
"`tabSerial and Batch Bundle`.`voucher_no`",
|
||||
"`tabSerial and Batch Bundle`.`item_code`",
|
||||
"`tabSerial and Batch Bundle`.`item_name`",
|
||||
"`tabSerial and Batch Entry`.`serial_no`",
|
||||
"`tabSerial and Batch Entry`.`batch_no`",
|
||||
"`tabSerial and Batch Entry`.`warehouse`",
|
||||
"`tabSerial and Batch Entry`.`incoming_rate`",
|
||||
"`tabSerial and Batch Entry`.`stock_value_difference`",
|
||||
"`tabSerial and Batch Entry`.`qty`",
|
||||
],
|
||||
filters=filter_conditions,
|
||||
order_by="posting_date",
|
||||
)
|
||||
|
||||
|
||||
def get_filter_conditions(filters):
|
||||
filter_conditions = [
|
||||
["Serial and Batch Bundle", "docstatus", "=", 1],
|
||||
["Serial and Batch Bundle", "is_cancelled", "=", 0],
|
||||
]
|
||||
|
||||
for field in ["voucher_type", "voucher_no", "item_code", "warehouse", "company"]:
|
||||
if filters.get(field):
|
||||
if field == "voucher_no":
|
||||
filter_conditions.append(["Serial and Batch Bundle", field, "in", filters.get(field)])
|
||||
else:
|
||||
filter_conditions.append(["Serial and Batch Bundle", field, "=", filters.get(field)])
|
||||
|
||||
if filters.get("from_date") and filters.get("to_date"):
|
||||
filter_conditions.append(
|
||||
[
|
||||
"Serial and Batch Bundle",
|
||||
"posting_date",
|
||||
"between",
|
||||
[filters.get("from_date"), filters.get("to_date")],
|
||||
]
|
||||
)
|
||||
|
||||
for field in ["serial_no", "batch_no"]:
|
||||
if filters.get(field):
|
||||
filter_conditions.append(["Serial and Batch Entry", field, "=", filters.get(field)])
|
||||
|
||||
return filter_conditions
|
||||
|
||||
|
||||
def get_columns(filters, data):
|
||||
columns = [
|
||||
{
|
||||
"label": _("Company"),
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"label": _("Serial and Batch Bundle"),
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"options": "Serial and Batch Bundle",
|
||||
"width": 110,
|
||||
},
|
||||
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
|
||||
]
|
||||
|
||||
item_details = {}
|
||||
|
||||
item_codes = []
|
||||
if filters.get("voucher_type"):
|
||||
item_codes = [d.item_code for d in data]
|
||||
|
||||
if filters.get("item_code") or (item_codes and len(list(set(item_codes))) == 1):
|
||||
item_details = frappe.get_cached_value(
|
||||
"Item",
|
||||
filters.get("item_code") or item_codes[0],
|
||||
["has_serial_no", "has_batch_no"],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
if not filters.get("voucher_no"):
|
||||
columns.extend(
|
||||
[
|
||||
{
|
||||
"label": _("Voucher Type"),
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"label": _("Voucher No"),
|
||||
"fieldname": "voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "voucher_type",
|
||||
"width": 160,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
if not filters.get("item_code"):
|
||||
columns.extend(
|
||||
[
|
||||
{
|
||||
"label": _("Item Code"),
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 120,
|
||||
},
|
||||
{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
|
||||
]
|
||||
)
|
||||
|
||||
if not filters.get("warehouse"):
|
||||
columns.append(
|
||||
{
|
||||
"label": _("Warehouse"),
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"width": 120,
|
||||
}
|
||||
)
|
||||
|
||||
if not item_details or item_details.get("has_serial_no"):
|
||||
columns.append(
|
||||
{"label": _("Serial No"), "fieldname": "serial_no", "fieldtype": "Data", "width": 120}
|
||||
)
|
||||
|
||||
if not item_details or item_details.get("has_batch_no"):
|
||||
columns.extend(
|
||||
[
|
||||
{"label": _("Batch No"), "fieldname": "batch_no", "fieldtype": "Data", "width": 120},
|
||||
{"label": _("Batch Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
|
||||
]
|
||||
)
|
||||
|
||||
columns.extend(
|
||||
[
|
||||
{"label": _("Incoming Rate"), "fieldname": "incoming_rate", "fieldtype": "Float", "width": 120},
|
||||
{
|
||||
"label": _("Change in Stock Value"),
|
||||
"fieldname": "stock_value_difference",
|
||||
"fieldtype": "Float",
|
||||
"width": 120,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_voucher_type(doctype, txt, searchfield, start, page_len, filters):
|
||||
child_doctypes = frappe.get_all(
|
||||
"DocField",
|
||||
filters={"fieldname": "serial_and_batch_bundle"},
|
||||
fields=["distinct parent as parent"],
|
||||
)
|
||||
|
||||
query_filters = {"options": ["in", [d.parent for d in child_doctypes]]}
|
||||
if txt:
|
||||
query_filters["parent"] = ["like", "%{}%".format(txt)]
|
||||
|
||||
return frappe.get_all("DocField", filters=query_filters, fields=["distinct parent"], as_list=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_serial_nos(doctype, txt, searchfield, start, page_len, filters):
|
||||
query_filters = {}
|
||||
|
||||
if txt:
|
||||
query_filters["serial_no"] = ["like", f"%{txt}%"]
|
||||
|
||||
if filters.get("voucher_no"):
|
||||
serial_batch_bundle = frappe.get_cached_value(
|
||||
"Serial and Batch Bundle",
|
||||
{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
|
||||
"name",
|
||||
)
|
||||
|
||||
query_filters["parent"] = serial_batch_bundle
|
||||
if not txt:
|
||||
query_filters["serial_no"] = ("is", "set")
|
||||
|
||||
return frappe.get_all(
|
||||
"Serial and Batch Entry", filters=query_filters, fields=["serial_no"], as_list=True
|
||||
)
|
||||
|
||||
else:
|
||||
query_filters["item_code"] = filters.get("item_code")
|
||||
return frappe.get_all("Serial No", filters=query_filters, as_list=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_batch_nos(doctype, txt, searchfield, start, page_len, filters):
|
||||
query_filters = {}
|
||||
|
||||
if txt:
|
||||
query_filters["batch_no"] = ["like", f"%{txt}%"]
|
||||
|
||||
if filters.get("voucher_no"):
|
||||
serial_batch_bundle = frappe.get_cached_value(
|
||||
"Serial and Batch Bundle",
|
||||
{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
|
||||
"name",
|
||||
)
|
||||
|
||||
query_filters["parent"] = serial_batch_bundle
|
||||
if not txt:
|
||||
query_filters["batch_no"] = ("is", "set")
|
||||
|
||||
return frappe.get_all(
|
||||
"Serial and Batch Entry", filters=query_filters, fields=["batch_no"], as_list=True
|
||||
)
|
||||
|
||||
else:
|
||||
query_filters["item"] = filters.get("item_code")
|
||||
return frappe.get_all("Batch", filters=query_filters, as_list=True)
|
Loading…
x
Reference in New Issue
Block a user