fix: source document in batch

This commit is contained in:
Rohit Waghchaure
2025-08-06 20:16:32 +05:30
parent 07d1663f2c
commit 8b0b938595
2 changed files with 118 additions and 95 deletions

View File

@@ -1146,10 +1146,13 @@ class SerialandBatchBundle(Document):
if self.flags.ignore_validate_serial_batch:
return
if not self.has_serial_no:
if not self.has_serial_no and not self.has_batch_no:
return
if self.total_qty > 0:
if self.total_qty <= 0:
return
if self.has_serial_no:
serial_nos = [d.serial_no for d in self.entries if d.serial_no]
sn_table = frappe.qb.DocType("Serial No")
(
@@ -1157,7 +1160,17 @@ class SerialandBatchBundle(Document):
.set(sn_table.reference_doctype, self.voucher_type)
.set(sn_table.reference_name, self.voucher_no)
.set(sn_table.posting_date, self.posting_date)
.where(sn_table.name.isin(serial_nos))
.where((sn_table.name.isin(serial_nos)) & (sn_table.reference_name.isnull()))
).run()
if self.has_batch_no:
batch_nos = [d.batch_no for d in self.entries if d.batch_no]
batch_table = frappe.qb.DocType("Batch")
(
frappe.qb.update(batch_table)
.set(batch_table.reference_doctype, self.voucher_type)
.set(batch_table.reference_name, self.voucher_no)
.where((batch_table.name.isin(batch_nos)) & (batch_table.reference_name.isnull()))
).run()
def validate_serial_and_batch_inventory(self):

View File

@@ -8,6 +8,7 @@ from frappe.query_builder import Case
def execute(filters: dict | None = None):
report = ReportData(filters)
report.validate_filters()
data = report.get_data()
columns = report.get_columns()
@@ -19,6 +20,13 @@ class ReportData:
self.filters = filters
self.doctype_name = self.get_doctype()
def validate_filters(self):
if not self.filters.item_code and not self.filters.batches and not self.filters.serial_nos:
frappe.throw(
_("Please select at least one filter: Item Code, Batch, or Serial No."),
title=_("Missing Filters"),
)
def get_data(self):
result_data = []
@@ -93,6 +101,11 @@ class ReportData:
source_data = frappe._dict({})
for row in data:
key = (row.item_code, row.reference_name)
value = row.serial_no or row.batch_no
if value:
key = (row.item_code, row.reference_name, value)
if self.doctype_name == "Batch":
sabb_details = self.get_qty_from_sabb(row)
row.update(sabb_details)
@@ -139,19 +152,24 @@ class ReportData:
materials = self.get_materials(sabb_data)
for material in materials:
key = (material.item_code, material.batch_no)
# Recursive: batch has sub-components
if material.serial_no or material.batch_no:
key = (material.item_code, material.reference_name)
value = material.serial_no or material.batch_no
if key not in sabb_data.raw_materials:
details = self.get_serial_no_batches(value)
if not details:
inward_data = self.get_sabb_entries(value, "Inward")
if inward_data:
details = inward_data[-1]
if details:
details.update(self.get_qty_from_sabb(details))
sabb_data.raw_materials[key] = details
self.set_backward_data(sabb_data.raw_materials[key], material.qty)
if sabb_data.raw_materials.get(key):
self.set_backward_data(sabb_data.raw_materials[key], material.qty)
else:
sub_key = material.item_code
if sub_key not in sabb_data.raw_materials:
@@ -269,7 +287,7 @@ class ReportData:
return query.run(as_dict=True)
def set_forward_data(self, value, sabb_data):
outward_entries = self.get_outward_sabb_entries(value)
outward_entries = self.get_sabb_entries(value)
for row in outward_entries:
if row.reference_doctype == "Stock Entry":
@@ -283,7 +301,10 @@ class ReportData:
row["indent"] = 0
batch_details[key] = row
def get_outward_sabb_entries(self, value):
def get_sabb_entries(self, value, type_of_transaction=None):
if not type_of_transaction:
type_of_transaction = "Outward"
SABB = frappe.qb.DocType("Serial and Batch Bundle")
SABE = frappe.qb.DocType("Serial and Batch Entry")
@@ -302,13 +323,16 @@ class ReportData:
SABB.posting_date,
SABB.warehouse,
)
.where((SABB.is_cancelled == 0) & (SABE.docstatus == 1) & (SABB.type_of_transaction == "Outward"))
.where(
(SABB.is_cancelled == 0)
& (SABE.docstatus == 1)
& (SABB.type_of_transaction == type_of_transaction)
)
.orderby(SABB.posting_date)
.orderby(SABE.posting_time)
)
if self.doctype_name == "Serial No":
query = query.where(SABE.serial_no == value)
else:
query = query.where(SABE.batch_no == value)
query = query.where((SABE.serial_no == value) | (SABE.batch_no == value))
return query.run(as_dict=True)
@@ -364,7 +388,7 @@ class ReportData:
return (sabb_details.serial_no, sabb_details.batch_no) if sabb_details else (None, None)
def get_columns(self):
columns = [
return [
{
"fieldname": "item_code",
"label": _("Item Code"),
@@ -378,85 +402,71 @@ class ReportData:
"fieldtype": "Data",
"width": 120,
},
{
"fieldname": "serial_no",
"label": _("Serial No"),
"fieldtype": "Link",
"options": "Serial No",
"width": 150,
},
{
"fieldname": "batch_no",
"label": _("Batch No"),
"fieldtype": "Link",
"options": "Batch",
"width": 140,
},
{
"fieldname": "qty",
"label": _("Quantity"),
"fieldtype": "Float",
"width": 90,
},
{
"fieldname": "reference_doctype",
"label": _("Voucher Type"),
"fieldtype": "Data",
"width": 130,
},
{
"fieldname": "reference_name",
"label": _("Source Document No"),
"fieldtype": "Dynamic Link",
"options": "reference_doctype",
"width": 200,
},
{
"fieldname": "warehouse",
"label": _("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse",
"width": 120,
},
{
"fieldname": "posting_date",
"label": _("Posting Date"),
"fieldtype": "Date",
"width": 120,
},
{
"fieldname": "work_order",
"label": _("Work Order"),
"fieldtype": "Link",
"options": "Work Order",
"width": 160,
},
{
"fieldname": "supplier",
"label": _("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
"width": 150,
},
{
"fieldname": "customer",
"label": _("Customer"),
"fieldtype": "Link",
"options": "Customer",
"width": 150,
},
]
if self.doctype_name == "Serial No":
columns.append(
{
"fieldname": "serial_no",
"label": _("Serial No"),
"fieldtype": "Link",
"options": "Serial No",
"width": 150,
}
)
else:
columns.append(
{
"fieldname": "batch_no",
"label": _("Batch No"),
"fieldtype": "Link",
"options": "Batch",
"width": 140,
}
)
columns.extend(
[
{
"fieldname": "qty",
"label": _("Quantity"),
"fieldtype": "Float",
"width": 90,
},
{
"fieldname": "reference_doctype",
"label": _("Voucher Type"),
"fieldtype": "Data",
"width": 130,
},
{
"fieldname": "reference_name",
"label": _("Source Document No"),
"fieldtype": "Dynamic Link",
"options": "reference_doctype",
"width": 200,
},
{
"fieldname": "warehouse",
"label": _("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse",
"width": 120,
},
{
"fieldname": "posting_date",
"label": _("Posting Date"),
"fieldtype": "Date",
"width": 120,
},
{
"fieldname": "work_order",
"label": _("Work Order"),
"fieldtype": "Link",
"options": "Work Order",
"width": 160,
},
{
"fieldname": "supplier",
"label": _("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
"width": 150,
},
{
"fieldname": "customer",
"label": _("Customer"),
"fieldtype": "Link",
"options": "Customer",
"width": 150,
},
]
)
return columns