feat: report to find incorrect SABB
(cherry picked from commit 7e24395e00)
This commit is contained in:
committed by
Mergify
parent
6113cc1e43
commit
a948f2e095
@@ -169,7 +169,7 @@ class DeprecatedBatchNoValuation:
|
|||||||
if not self.non_batchwise_balance_qty:
|
if not self.non_batchwise_balance_qty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.non_batchwise_balance_qty.get(batch_no) == 0:
|
if not self.non_batchwise_balance_qty.get(batch_no):
|
||||||
self.batch_avg_rate[batch_no] = 0.0
|
self.batch_avg_rate[batch_no] = 0.0
|
||||||
self.stock_value_differece[batch_no] = 0.0
|
self.stock_value_differece[batch_no] = 0.0
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.query_reports["Incorrect Serial and Batch Bundle"] = {
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
fieldname: "item_code",
|
||||||
|
label: __("Item Code"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "warehouse",
|
||||||
|
label: __("Warehouse"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Warehouse",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
get_datatable_options(options) {
|
||||||
|
return Object.assign(options, {
|
||||||
|
checkboxColumn: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onload(report) {
|
||||||
|
report.page.add_inner_button(__("Remove SABB Entry"), () => {
|
||||||
|
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
|
||||||
|
let selected_rows = indexes.map((i) => frappe.query_report.data[i]);
|
||||||
|
|
||||||
|
if (!selected_rows.length) {
|
||||||
|
frappe.throw(__("Please select a row to create a Reposting Entry"));
|
||||||
|
} else {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.stock.report.incorrect_serial_and_batch_bundle.incorrect_serial_and_batch_bundle.remove_sabb_entry",
|
||||||
|
freeze: true,
|
||||||
|
args: {
|
||||||
|
selected_rows: selected_rows,
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
frappe.query_report.refresh();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2025-02-03 15:39:44.521366",
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"json": "{}",
|
||||||
|
"letter_head": "Test",
|
||||||
|
"letterhead": null,
|
||||||
|
"modified": "2025-02-03 15:39:47.613040",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Incorrect Serial and Batch Bundle",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Serial and Batch Bundle",
|
||||||
|
"report_name": "Incorrect Serial and Batch Bundle",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Stock User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Purchase Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Delivery Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "System Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Delivery User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Manufacturing User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Purchase User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Stock Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Manufacturing Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Maintenance User"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeout": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters: dict | None = None):
|
||||||
|
"""Return columns and data for the report.
|
||||||
|
|
||||||
|
This is the main entry point for the report. It accepts the filters as a
|
||||||
|
dictionary and should return columns and data. It is called by the framework
|
||||||
|
every time the report is refreshed or a filter is updated.
|
||||||
|
"""
|
||||||
|
columns = get_columns()
|
||||||
|
data = get_data(filters)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
def get_columns() -> list[dict]:
|
||||||
|
"""Return columns for the report.
|
||||||
|
|
||||||
|
One field definition per column, just like a DocType field definition.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"label": _("Serial and Batch Bundle"),
|
||||||
|
"fieldname": "name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Serial and Batch Bundle",
|
||||||
|
"width": 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Voucher Type"),
|
||||||
|
"fieldname": "voucher_type",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Voucher No"),
|
||||||
|
"fieldname": "voucher_no",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "voucher_type",
|
||||||
|
"width": 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Voucher Detail No"),
|
||||||
|
"fieldname": "voucher_detail_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 200,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(filters) -> list[list]:
|
||||||
|
"""Return data for the report.
|
||||||
|
|
||||||
|
The report data is a list of rows, with each row being a list of cell values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
SABB = frappe.qb.DocType("Serial And Batch Bundle")
|
||||||
|
SLE = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
|
ignore_voycher_types = [
|
||||||
|
"Installation Note",
|
||||||
|
"Job Card",
|
||||||
|
"Maintenance Schedule",
|
||||||
|
"Pick List",
|
||||||
|
]
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(SABB)
|
||||||
|
.left_join(SLE)
|
||||||
|
.on(SABB.name == SLE.serial_and_batch_bundle)
|
||||||
|
.select(
|
||||||
|
SABB.name,
|
||||||
|
SABB.voucher_type,
|
||||||
|
SABB.voucher_no,
|
||||||
|
SABB.voucher_detail_no,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(SLE.serial_and_batch_bundle.isnull())
|
||||||
|
& (SABB.docstatus == 1)
|
||||||
|
& (SABB.is_cancelled == 0)
|
||||||
|
& (SABB.voucher_type.notin(ignore_voycher_types))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for field in filters:
|
||||||
|
query = query.where(SABB[field] == filters[field])
|
||||||
|
|
||||||
|
data = query.run(as_dict=1)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def remove_sabb_entry(selected_rows):
|
||||||
|
if isinstance(selected_rows, str):
|
||||||
|
selected_rows = frappe.parse_json(selected_rows)
|
||||||
|
|
||||||
|
for row in selected_rows:
|
||||||
|
doc = frappe.get_doc("Serial and Batch Bundle", row.get("name"))
|
||||||
|
doc.cancel()
|
||||||
|
doc.delete()
|
||||||
|
|
||||||
|
frappe.msgprint(_("Selected Serial and Batch Bundle entries have been removed."))
|
||||||
Reference in New Issue
Block a user