diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 137c352e99a0..94655747e430 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1247,6 +1247,25 @@ def test_non_internal_transfer_delivery_note(self): dn.reload() self.assertFalse(dn.items[0].target_warehouse) + def test_serial_no_status(self): + from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt + + item = make_item( + "Test Serial Item For Status", + {"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "TESTSERIAL.#####"}, + ) + + item_code = item.name + pi = make_purchase_receipt(qty=1, item_code=item.name) + pi.reload() + serial_no = get_serial_nos_from_bundle(pi.items[0].serial_and_batch_bundle) + + self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Active") + + dn = create_delivery_note(qty=1, item_code=item_code, serial_no=serial_no) + dn.reload() + self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Delivered") + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/serial_no/serial_no.js b/erpnext/stock/doctype/serial_no/serial_no.js index 9d5555ed6313..1cb9fd1800e6 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.js +++ b/erpnext/stock/doctype/serial_no/serial_no.js @@ -18,3 +18,22 @@ cur_frm.cscript.onload = function() { frappe.ui.form.on("Serial No", "refresh", function(frm) { frm.toggle_enable("item_code", frm.doc.__islocal); }); + + +frappe.ui.form.on("Serial No", { + refresh(frm) { + frm.trigger("view_ledgers") + }, + + view_ledgers(frm) { + frm.add_custom_button(__("View Ledgers"), () => { + frappe.route_options = { + "item_code": frm.doc.item_code, + "serial_no": frm.doc.name, + "posting_date": frappe.datetime.now_date(), + "posting_time": frappe.datetime.now_time() + }; + frappe.set_route("query-report", "Serial No Ledger"); + }).addClass('btn-primary'); + } +}) \ No newline at end of file diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index ed1b0af30a66..b4ece00fe64e 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -269,7 +269,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Status", - "options": "\nActive\nInactive\nExpired", + "options": "\nActive\nInactive\nDelivered\nExpired", "read_only": 1 }, { @@ -280,7 +280,7 @@ "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2023-04-16 15:58:46.139887", + "modified": "2023-11-28 15:37:59.489945", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", 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 7212b92bb310..ae12fbb3e4f0 100644 --- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py +++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py @@ -36,21 +36,27 @@ def get_columns(filters): "fieldtype": "Link", "fieldname": "company", "options": "Company", - "width": 150, + "width": 120, }, { "label": _("Warehouse"), "fieldtype": "Link", "fieldname": "warehouse", "options": "Warehouse", - "width": 150, + "width": 120, + }, + { + "label": _("Status"), + "fieldtype": "Data", + "fieldname": "status", + "width": 120, }, { "label": _("Serial No"), "fieldtype": "Link", "fieldname": "serial_no", "options": "Serial No", - "width": 150, + "width": 130, }, { "label": _("Valuation Rate"), @@ -58,6 +64,12 @@ def get_columns(filters): "fieldname": "valuation_rate", "width": 150, }, + { + "label": _("Qty"), + "fieldtype": "Float", + "fieldname": "qty", + "width": 150, + }, ] return columns @@ -83,12 +95,16 @@ def get_data(filters): "posting_time": row.posting_time, "voucher_type": row.voucher_type, "voucher_no": row.voucher_no, + "status": "Active" if row.actual_qty > 0 else "Delivered", "company": row.company, "warehouse": row.warehouse, + "qty": 1 if row.actual_qty > 0 else -1, } ) - serial_nos = bundle_wise_serial_nos.get(row.serial_and_batch_bundle, []) + serial_nos = [{"serial_no": row.serial_no, "valuation_rate": row.valuation_rate}] + if row.serial_and_batch_bundle: + serial_nos = bundle_wise_serial_nos.get(row.serial_and_batch_bundle, []) for index, bundle_data in enumerate(serial_nos): if index == 0: diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index da98455b5cbe..de28be1c3575 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -255,11 +255,15 @@ def set_warehouse_and_status_in_serial_nos(self): if not serial_nos: return + status = "Inactive" + if self.sle.actual_qty < 0: + status = "Delivered" + sn_table = frappe.qb.DocType("Serial No") ( frappe.qb.update(sn_table) .set(sn_table.warehouse, warehouse) - .set(sn_table.status, "Active" if warehouse else "Inactive") + .set(sn_table.status, "Active" if warehouse else status) .where(sn_table.name.isin(serial_nos)) ).run()