mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-21 15:48:29 +00:00
committed by
Mergify
parent
3e3c489178
commit
631363632b
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.query_reports["Negative Batch Report"] = {
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
fieldname: "company",
|
||||||
|
label: __("Company"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Company",
|
||||||
|
default: frappe.defaults.get_default("company"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "item_code",
|
||||||
|
label: __("Item Code"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Item",
|
||||||
|
get_query: function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
has_batch_no: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "warehouse",
|
||||||
|
label: __("Warehouse"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Warehouse",
|
||||||
|
get_query: function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
is_group: 0,
|
||||||
|
company: frappe.query_report.get_filter_value("company"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"add_translate_data": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2026-02-17 11:34:21.549485",
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"json": "",
|
||||||
|
"letter_head": null,
|
||||||
|
"modified": "2026-02-17 11:34:59.106045",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Negative Batch Report",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Serial and Batch Bundle",
|
||||||
|
"report_name": "Negative Batch Report",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "System Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Purchase User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Purchase Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Stock User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Stock Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Delivery User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Delivery Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Manufacturing User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Manufacturing Manager"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeout": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import add_to_date, flt, today
|
||||||
|
|
||||||
|
from erpnext.stock.report.stock_ledger.stock_ledger import execute as stock_ledger_execute
|
||||||
|
|
||||||
|
|
||||||
|
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 [
|
||||||
|
{
|
||||||
|
"label": _("Posting Datetime"),
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"width": 160,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Batch No"),
|
||||||
|
"fieldname": "batch_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Batch",
|
||||||
|
"width": 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Warehouse"),
|
||||||
|
"fieldname": "warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Warehouse",
|
||||||
|
"width": 160,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Previous Qty"),
|
||||||
|
"fieldname": "previous_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Transaction Qty"),
|
||||||
|
"fieldname": "actual_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Qty After Transaction"),
|
||||||
|
"fieldname": "qty_after_transaction",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Document Type"),
|
||||||
|
"fieldname": "voucher_type",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Document No"),
|
||||||
|
"fieldname": "voucher_no",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "voucher_type",
|
||||||
|
"width": 130,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(filters) -> list[dict]:
|
||||||
|
batches = get_batches(filters)
|
||||||
|
companies = get_companies(filters)
|
||||||
|
batch_negative_data = []
|
||||||
|
|
||||||
|
flt_precision = frappe.db.get_default("float_precision") or 2
|
||||||
|
for company in companies:
|
||||||
|
for batch in batches:
|
||||||
|
_c, data = stock_ledger_execute(
|
||||||
|
frappe._dict(
|
||||||
|
{
|
||||||
|
"company": company,
|
||||||
|
"batch_no": batch,
|
||||||
|
"from_date": add_to_date(today(), years=-12),
|
||||||
|
"to_date": today(),
|
||||||
|
"segregate_serial_batch_bundle": 1,
|
||||||
|
"warehouse": filters.get("warehouse"),
|
||||||
|
"valuation_field_type": "Currency",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
previous_qty = 0
|
||||||
|
for row in data:
|
||||||
|
if flt(row.get("qty_after_transaction"), flt_precision) < 0:
|
||||||
|
batch_negative_data.append(
|
||||||
|
{
|
||||||
|
"posting_date": row.get("date"),
|
||||||
|
"batch_no": row.get("batch_no"),
|
||||||
|
"item_code": row.get("item_code"),
|
||||||
|
"item_name": row.get("item_name"),
|
||||||
|
"warehouse": row.get("warehouse"),
|
||||||
|
"actual_qty": row.get("actual_qty"),
|
||||||
|
"qty_after_transaction": row.get("qty_after_transaction"),
|
||||||
|
"previous_qty": previous_qty,
|
||||||
|
"voucher_type": row.get("voucher_type"),
|
||||||
|
"voucher_no": row.get("voucher_no"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
previous_qty = row.get("qty_after_transaction")
|
||||||
|
|
||||||
|
return batch_negative_data
|
||||||
|
|
||||||
|
|
||||||
|
def get_batches(filters):
|
||||||
|
batch_filters = {}
|
||||||
|
if filters.get("item_code"):
|
||||||
|
batch_filters["item"] = filters["item_code"]
|
||||||
|
|
||||||
|
return frappe.get_all("Batch", pluck="name", filters=batch_filters)
|
||||||
|
|
||||||
|
|
||||||
|
def get_companies(filters):
|
||||||
|
company_filters = {}
|
||||||
|
if filters.get("company"):
|
||||||
|
company_filters["name"] = filters["company"]
|
||||||
|
|
||||||
|
return frappe.get_all("Company", pluck="name", filters=company_filters)
|
||||||
Reference in New Issue
Block a user