diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index bf3ee539dc..77dbc8f9b3 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -67,6 +67,12 @@ treeviews = [
"Department",
]
+jinja = {
+ "methods": [
+ "erpnext.stock.serial_batch_bundle.get_serial_or_batch_nos",
+ ],
+}
+
# website
update_website_context = [
"erpnext.e_commerce.shopping_cart.utils.update_website_context",
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index b607244591..98ad8a7cdb 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -300,20 +300,10 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) {
super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate);
- if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) &&
- in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
- if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return;
- this.set_batch_number(cdt, cdn);
- }
}
qty(doc, cdt, cdn) {
super.qty(doc, cdt, cdn);
-
- if(in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
- if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return;
- this.set_batch_number(cdt, cdn);
- }
}
pick_serial_and_batch(doc, cdt, cdn) {
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 4599c56d91..3ca4bad4e4 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -52,6 +52,7 @@ class StockLedgerEntry(Document):
def on_submit(self):
self.check_stock_frozen_date()
+ # Added to handle few test cases where serial_and_batch_bundles are not required
if frappe.flags.in_test and frappe.flags.ignore_serial_batch_bundle_validation:
return
diff --git a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
index 41f928ba3f..dff407f149 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
@@ -297,6 +297,7 @@ def create_material_receipt(
se.set_stock_entry_type()
se.insert()
se.submit()
+ se.reload()
return se
diff --git a/erpnext/stock/print_format/purchase_receipt_serial_and_batch_bundle_print/__init__.py b/erpnext/stock/print_format/purchase_receipt_serial_and_batch_bundle_print/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/stock/print_format/purchase_receipt_serial_and_batch_bundle_print/purchase_receipt_serial_and_batch_bundle_print.json b/erpnext/stock/print_format/purchase_receipt_serial_and_batch_bundle_print/purchase_receipt_serial_and_batch_bundle_print.json
new file mode 100644
index 0000000000..21132e070c
--- /dev/null
+++ b/erpnext/stock/print_format/purchase_receipt_serial_and_batch_bundle_print/purchase_receipt_serial_and_batch_bundle_print.json
@@ -0,0 +1,30 @@
+{
+ "absolute_value": 0,
+ "align_labels_right": 0,
+ "creation": "2023-06-01 23:07:25.776606",
+ "custom_format": 0,
+ "disabled": 0,
+ "doc_type": "Purchase Receipt",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font_size": 14,
+ "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
\\t\\t\\t\\t
Purchase Receipt
{{ doc.name }}\\t\\t\\t\\t \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"supplier_name\", \"print_hide\": 0, \"label\": \"Supplier Name\"}, {\"fieldname\": \"supplier_delivery_note\", \"print_hide\": 0, \"label\": \"Supplier Delivery Note\"}, {\"fieldname\": \"rack\", \"print_hide\": 0, \"label\": \"Rack\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"posting_date\", \"print_hide\": 0, \"label\": \"Date\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"apply_putaway_rule\", \"print_hide\": 0, \"label\": \"Apply Putaway Rule\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Accounting Dimensions\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"region\", \"print_hide\": 0, \"label\": \"Region\"}, {\"fieldname\": \"function\", \"print_hide\": 0, \"label\": \"Function\"}, {\"fieldname\": \"depot\", \"print_hide\": 0, \"label\": \"Depot\"}, {\"fieldname\": \"cost_center\", \"print_hide\": 0, \"label\": \"Cost Center\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"location\", \"print_hide\": 0, \"label\": \"Location\"}, {\"fieldname\": \"country\", \"print_hide\": 0, \"label\": \"Country\"}, {\"fieldname\": \"project\", \"print_hide\": 0, \"label\": \"Project\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Items\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"scan_barcode\", \"print_hide\": 0, \"label\": \"Scan Barcode\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"set_from_warehouse\", \"print_hide\": 0, \"label\": \"Set From Warehouse\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\t\\n\\t\\t\\n\\t\\t\\tSr | \\n\\t\\t\\tItem Name | \\n\\t\\t\\tDescription | \\n\\t\\t\\tQty | \\n\\t\\t\\tRate | \\n\\t\\t\\tAmount | \\n\\t\\t
\\n\\t\\t{%- for row in doc.items -%}\\n\\t\\t\\n\\t\\t {% set bundle_data = get_serial_or_batch_nos(row.serial_and_batch_bundle) %}\\n\\t\\t {% set serial_nos = [] %}\\n {% set batches = {} %}\\n\\n\\t\\t\\t{{ row.idx }} | \\n\\t\\t\\t\\n\\t\\t\\t\\t{{ row.item_name }}\\n\\t\\t\\t\\t{% if row.item_code != row.item_name -%}\\n\\t\\t\\t\\t Item Code: {{ row.item_code}}\\n\\t\\t\\t\\t{%- endif %}\\n\\t\\t\\t | \\n\\t\\t\\t\\n\\t\\t\\t\\t {{ row.description }} | \\n\\t\\t\\t{{ row.qty }} {{ row.uom or row.stock_uom }} | \\n\\t\\t\\t{{\\n\\t\\t\\t\\trow.get_formatted(\\\"rate\\\", doc) }} | \\n\\t\\t\\t{{\\n\\t\\t\\t\\trow.get_formatted(\\\"amount\\\", doc) }} | \\n\\t\\t\\t\\n\\t\\t
\\n\\t\\t{%- endfor -%}\\n\\t\\n
\\n\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"total_qty\", \"print_hide\": 0, \"label\": \"Total Quantity\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"total\", \"print_hide\": 0, \"label\": \"Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"taxes\", \"print_hide\": 0, \"label\": \"Purchase Taxes and Charges\", \"visible_columns\": [{\"fieldname\": \"category\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"add_deduct_tax\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"charge_type\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"row_id\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"included_in_print_rate\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"included_in_paid_amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"account_head\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"description\", \"print_width\": \"300px\", \"print_hide\": 0}, {\"fieldname\": \"rate\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"region\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"function\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"location\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"cost_center\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"depot\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"country\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"account_currency\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"tax_amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"total\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"Totals\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"grand_total\", \"print_hide\": 0, \"label\": \"Grand Total\"}, {\"fieldname\": \"rounded_total\", \"print_hide\": 0, \"label\": \"Rounded Total\"}, {\"fieldname\": \"in_words\", \"print_hide\": 0, \"label\": \"In Words\"}, {\"fieldname\": \"disable_rounded_total\", \"print_hide\": 0, \"label\": \"Disable Rounded Total\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Supplier Address\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"address_display\", \"print_hide\": 0, \"label\": \"Address\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"contact_display\", \"print_hide\": 0, \"label\": \"Contact\"}, {\"fieldname\": \"contact_mobile\", \"print_hide\": 0, \"label\": \"Mobile No\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Company Billing Address\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"billing_address\", \"print_hide\": 0, \"label\": \"Billing Address\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"billing_address_display\", \"print_hide\": 0, \"label\": \"Billing Address\"}, {\"fieldname\": \"terms\", \"print_hide\": 0, \"label\": \"Terms and Conditions\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"_custom_html\", \"print_hide\": 0, \"label\": \"Custom HTML\", \"fieldtype\": \"HTML\", \"options\": \"\\n\\t\\n\\t\\t\\n\\t\\t\\tSr | \\n\\t\\t\\tItem Name | \\n\\t\\t\\tQty | \\n\\t\\t\\tSerial Nos | \\n\\t\\t\\tBatch Nos (Qty) | \\n\\t\\t
\\n\\t\\t{%- for row in doc.items -%}\\n\\t\\t\\n\\t\\t {% set bundle_data = get_serial_or_batch_nos(row.serial_and_batch_bundle) %}\\n\\t\\t {% set serial_nos = [] %}\\n {% set batches = {} %}\\n \\n {% if bundle_data %}\\n\\t\\t\\t {% for data in bundle_data %}\\n\\t\\t\\t {% if data.serial_no %}\\n\\t\\t\\t {{ serial_nos.append(data.serial_no) or \\\"\\\" }}\\n\\t\\t\\t {% endif %}\\n\\t\\t\\t \\n\\t\\t\\t {% if data.batch_no %}\\n\\t\\t\\t {{ batches.update({data.batch_no: data.qty}) or \\\"\\\" }}\\n\\t\\t\\t {% endif %}\\n\\t\\t\\t {% endfor %}\\n\\t\\t\\t{% endif %}\\n\\n\\t\\t\\t{{ row.idx }} | \\n\\t\\t\\t\\n\\t\\t\\t\\t{{ row.item_name }}\\n\\t\\t\\t\\t{% if row.item_code != row.item_name -%}\\n\\t\\t\\t\\t Item Code: {{ row.item_code}}\\n\\t\\t\\t\\t{%- endif %}\\n\\t\\t\\t | \\n\\t\\t\\t{{ row.qty }} {{ row.uom or row.stock_uom }} | \\n\\t\\t\\t\\n\\t\\t\\t{{ serial_nos|join(',') }} | \\n\\t\\t\\t\\n\\t\\t\\t {% if batches %}\\n {% for batch_no, qty in batches.items() %}\\n {{batch_no}} : {{qty}} {{ row.uom or row.stock_uom }} \\n {% endfor %}\\n {% endif %}\\n\\t\\t\\t | \\n\\t\\t\\t\\n\\t\\t
\\n\\t\\t{%- endfor -%}\\n\\t\\n
\\n\"}]",
+ "idx": 0,
+ "line_breaks": 0,
+ "margin_bottom": 15.0,
+ "margin_left": 15.0,
+ "margin_right": 15.0,
+ "margin_top": 15.0,
+ "modified": "2023-06-02 00:09:37.315002",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Purchase Receipt Serial and Batch Bundle Print",
+ "owner": "Administrator",
+ "page_number": "Hide",
+ "print_format_builder": 1,
+ "print_format_builder_beta": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 0,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
index 99f1a9403b..7212b92bb3 100644
--- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
+++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py
@@ -22,35 +22,41 @@ def get_columns(filters):
"fieldtype": "Link",
"fieldname": "voucher_type",
"options": "DocType",
- "width": 220,
+ "width": 160,
},
{
"label": _("Voucher No"),
"fieldtype": "Dynamic Link",
"fieldname": "voucher_no",
"options": "voucher_type",
- "width": 220,
+ "width": 180,
},
{
"label": _("Company"),
"fieldtype": "Link",
"fieldname": "company",
"options": "Company",
- "width": 220,
+ "width": 150,
},
{
"label": _("Warehouse"),
"fieldtype": "Link",
"fieldname": "warehouse",
"options": "Warehouse",
- "width": 220,
+ "width": 150,
},
{
"label": _("Serial No"),
"fieldtype": "Link",
"fieldname": "serial_no",
"options": "Serial No",
- "width": 220,
+ "width": 150,
+ },
+ {
+ "label": _("Valuation Rate"),
+ "fieldtype": "Float",
+ "fieldname": "valuation_rate",
+ "width": 150,
},
]
@@ -84,14 +90,16 @@ def get_data(filters):
serial_nos = bundle_wise_serial_nos.get(row.serial_and_batch_bundle, [])
- for index, serial_no in enumerate(serial_nos):
+ for index, bundle_data in enumerate(serial_nos):
if index == 0:
- args.serial_no = serial_no
+ args.serial_no = bundle_data.get("serial_no")
+ args.valuation_rate = bundle_data.get("valuation_rate")
data.append(args)
else:
data.append(
{
- "serial_no": serial_no,
+ "serial_no": bundle_data.get("serial_no"),
+ "valuation_rate": bundle_data.get("valuation_rate"),
}
)
@@ -106,10 +114,15 @@ def get_serial_nos(filters, serial_bundle_ids):
for d in frappe.get_all(
"Serial and Batch Entry",
- fields=["serial_no", "parent"],
+ fields=["serial_no", "parent", "stock_value_difference as valuation_rate"],
filters=bundle_filters,
order_by="idx asc",
):
- bundle_wise_serial_nos.setdefault(d.parent, []).append(d.serial_no)
+ bundle_wise_serial_nos.setdefault(d.parent, []).append(
+ {
+ "serial_no": d.serial_no,
+ "valuation_rate": abs(d.valuation_rate),
+ }
+ )
return bundle_wise_serial_nos
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index da13354797..9c55358da2 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -296,6 +296,10 @@ def get_serial_nos_from_bundle(serial_and_batch_bundle, serial_nos=None):
return get_serial_nos(serial_and_batch_bundle, serial_nos=serial_nos)
+def get_serial_or_batch_nos(bundle):
+ return frappe.get_all("Serial and Batch Entry", fields=["*"], filters={"parent": bundle})
+
+
class SerialNoValuation(DeprecatedSerialNoValuation):
def __init__(self, **kwargs):
for key, value in kwargs.items():
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 01ba491ab5..dc481e8281 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -8,10 +8,10 @@ from typing import Optional, Set, Tuple
import frappe
from frappe import _, scrub
from frappe.model.meta import get_field_precision
+from frappe.query_builder import Case
from frappe.query_builder.functions import CombineDatetime, Sum
from frappe.utils import (
cint,
- cstr,
flt,
get_link_to_form,
getdate,