fix: not able to reconcile expired batches (#44012)
(cherry picked from commit 8805e74784)
This commit is contained in:
@@ -415,7 +415,6 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p
|
|||||||
stock_ledger_entry.batch_no,
|
stock_ledger_entry.batch_no,
|
||||||
Sum(stock_ledger_entry.actual_qty).as_("qty"),
|
Sum(stock_ledger_entry.actual_qty).as_("qty"),
|
||||||
)
|
)
|
||||||
.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()))
|
|
||||||
.where(stock_ledger_entry.is_cancelled == 0)
|
.where(stock_ledger_entry.is_cancelled == 0)
|
||||||
.where(
|
.where(
|
||||||
(stock_ledger_entry.item_code == filters.get("item_code"))
|
(stock_ledger_entry.item_code == filters.get("item_code"))
|
||||||
@@ -428,6 +427,9 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, p
|
|||||||
.limit(page_len)
|
.limit(page_len)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not filters.get("include_expired_batches"):
|
||||||
|
query = query.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()))
|
||||||
|
|
||||||
query = query.select(
|
query = query.select(
|
||||||
Concat("MFG-", batch_table.manufacturing_date).as_("manufacturing_date"),
|
Concat("MFG-", batch_table.manufacturing_date).as_("manufacturing_date"),
|
||||||
Concat("EXP-", batch_table.expiry_date).as_("expiry_date"),
|
Concat("EXP-", batch_table.expiry_date).as_("expiry_date"),
|
||||||
@@ -466,7 +468,6 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0
|
|||||||
bundle.batch_no,
|
bundle.batch_no,
|
||||||
Sum(bundle.qty).as_("qty"),
|
Sum(bundle.qty).as_("qty"),
|
||||||
)
|
)
|
||||||
.where((batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull()))
|
|
||||||
.where(stock_ledger_entry.is_cancelled == 0)
|
.where(stock_ledger_entry.is_cancelled == 0)
|
||||||
.where(
|
.where(
|
||||||
(stock_ledger_entry.item_code == filters.get("item_code"))
|
(stock_ledger_entry.item_code == filters.get("item_code"))
|
||||||
@@ -479,6 +480,11 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0
|
|||||||
.limit(page_len)
|
.limit(page_len)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not filters.get("include_expired_batches"):
|
||||||
|
bundle_query = bundle_query.where(
|
||||||
|
(batch_table.expiry_date >= expiry_date) | (batch_table.expiry_date.isnull())
|
||||||
|
)
|
||||||
|
|
||||||
bundle_query = bundle_query.select(
|
bundle_query = bundle_query.select(
|
||||||
Concat("MFG-", batch_table.manufacturing_date),
|
Concat("MFG-", batch_table.manufacturing_date),
|
||||||
Concat("EXP-", batch_table.expiry_date),
|
Concat("EXP-", batch_table.expiry_date),
|
||||||
|
|||||||
@@ -462,6 +462,8 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
is_inward = true;
|
is_inward = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let include_expired_batches = me.include_expired_batches();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_batch_no",
|
query: "erpnext.controllers.queries.get_batch_no",
|
||||||
filters: {
|
filters: {
|
||||||
@@ -469,6 +471,7 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
warehouse:
|
warehouse:
|
||||||
this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse,
|
this.item.s_warehouse || this.item.t_warehouse || this.item.warehouse,
|
||||||
is_inward: is_inward,
|
is_inward: is_inward,
|
||||||
|
include_expired_batches: include_expired_batches,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -497,6 +500,14 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include_expired_batches() {
|
||||||
|
return (
|
||||||
|
this.frm.doc.doctype === "Stock Reconciliation" ||
|
||||||
|
(this.frm.doc.doctype === "Stock Entry" &&
|
||||||
|
["Material Receipt", "Material Transfer", "Material Issue"].includes(this.frm.doc.purpose))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
get_auto_data() {
|
get_auto_data() {
|
||||||
let { qty, based_on } = this.dialog.get_values();
|
let { qty, based_on } = this.dialog.get_values();
|
||||||
|
|
||||||
|
|||||||
@@ -689,6 +689,9 @@ class SerialandBatchBundle(Document):
|
|||||||
serial_batches = {}
|
serial_batches = {}
|
||||||
for row in self.entries:
|
for row in self.entries:
|
||||||
if not row.qty and row.batch_no and not row.serial_no:
|
if not row.qty and row.batch_no and not row.serial_no:
|
||||||
|
if self.voucher_type == "Stock Reconciliation" and self.type_of_transaction == "Inward":
|
||||||
|
continue
|
||||||
|
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("At row {0}: Qty is mandatory for the batch {1}").format(
|
_("At row {0}: Qty is mandatory for the batch {1}").format(
|
||||||
bold(row.idx), bold(row.batch_no)
|
bold(row.idx), bold(row.batch_no)
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ frappe.ui.form.on("Stock Entry", {
|
|||||||
filters["is_inward"] = 1;
|
filters["is_inward"] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (["Material Receipt", "Material Transfer", "Material Issue"].includes(doc.purpose)) {
|
||||||
|
filters["include_expired_batches"] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_batch_no",
|
query: "erpnext.controllers.queries.get_batch_no",
|
||||||
filters: filters,
|
filters: filters,
|
||||||
|
|||||||
@@ -324,6 +324,7 @@ class StockReconciliation(StockController):
|
|||||||
row.item_code,
|
row.item_code,
|
||||||
posting_date=self.posting_date,
|
posting_date=self.posting_date,
|
||||||
posting_time=self.posting_time,
|
posting_time=self.posting_time,
|
||||||
|
for_stock_levels=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
total_current_qty += current_qty
|
total_current_qty += current_qty
|
||||||
@@ -1322,7 +1323,16 @@ def get_stock_balance_for(
|
|||||||
qty, rate = data
|
qty, rate = data
|
||||||
|
|
||||||
if item_dict.get("has_batch_no"):
|
if item_dict.get("has_batch_no"):
|
||||||
qty = get_batch_qty(batch_no, warehouse, posting_date=posting_date, posting_time=posting_time) or 0
|
qty = (
|
||||||
|
get_batch_qty(
|
||||||
|
batch_no,
|
||||||
|
warehouse,
|
||||||
|
posting_date=posting_date,
|
||||||
|
posting_time=posting_time,
|
||||||
|
for_stock_levels=True,
|
||||||
|
)
|
||||||
|
or 0
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"qty": qty,
|
"qty": qty,
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class SerialBatchBundle:
|
|||||||
"Outward": self.sle.actual_qty < 0,
|
"Outward": self.sle.actual_qty < 0,
|
||||||
}.get(sn_doc.type_of_transaction)
|
}.get(sn_doc.type_of_transaction)
|
||||||
|
|
||||||
if not condition:
|
if not condition and self.sle.actual_qty:
|
||||||
correct_type = "Inward"
|
correct_type = "Inward"
|
||||||
if sn_doc.type_of_transaction == "Inward":
|
if sn_doc.type_of_transaction == "Inward":
|
||||||
correct_type = "Outward"
|
correct_type = "Outward"
|
||||||
@@ -133,7 +133,7 @@ class SerialBatchBundle:
|
|||||||
frappe.throw(_(msg), title=_("Incorrect Type of Transaction"))
|
frappe.throw(_(msg), title=_("Incorrect Type of Transaction"))
|
||||||
|
|
||||||
precision = sn_doc.precision("total_qty")
|
precision = sn_doc.precision("total_qty")
|
||||||
if flt(sn_doc.total_qty, precision) != flt(self.sle.actual_qty, precision):
|
if self.sle.actual_qty and flt(sn_doc.total_qty, precision) != flt(self.sle.actual_qty, precision):
|
||||||
msg = f"Total qty {flt(sn_doc.total_qty, precision)} of Serial and Batch Bundle {link} is not equal to Actual Qty {flt(self.sle.actual_qty, precision)} in the {self.sle.voucher_type} {self.sle.voucher_no}"
|
msg = f"Total qty {flt(sn_doc.total_qty, precision)} of Serial and Batch Bundle {link} is not equal to Actual Qty {flt(self.sle.actual_qty, precision)} in the {self.sle.voucher_type} {self.sle.voucher_no}"
|
||||||
frappe.throw(_(msg))
|
frappe.throw(_(msg))
|
||||||
|
|
||||||
|
|||||||
@@ -1183,6 +1183,7 @@ class update_entries_after:
|
|||||||
stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
|
stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
|
||||||
stock_entry.db_update()
|
stock_entry.db_update()
|
||||||
for d in stock_entry.items:
|
for d in stock_entry.items:
|
||||||
|
# Update only the row that matches the voucher_detail_no or the row containing the FG/Scrap Item.
|
||||||
if d.name == voucher_detail_no or (not d.s_warehouse and d.t_warehouse):
|
if d.name == voucher_detail_no or (not d.s_warehouse and d.t_warehouse):
|
||||||
d.db_update()
|
d.db_update()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user