diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 22bc81194ec..8f8d76e6737 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1507,619 +1507,6 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(stock_value_difference, 100.0 * 5) -<<<<<<< HEAD -======= - def test_delivery_note_return_valuation_without_use_serial_batch_field(self): - from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return - - batch_item = make_item( - "_Test Delivery Note Return Valuation Batch Item", - properties={ - "has_batch_no": 1, - "create_new_batch": 1, - "is_stock_item": 1, - "batch_number_series": "BRTN-DNN-BI-.#####", - }, - ).name - - serial_item = make_item( - "_Test Delivery Note Return Valuation Serial Item", - properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "SRTN-DNN-TP-.#####"}, - ).name - - batches = {} - serial_nos = [] - for qty, rate in {3: 300, 2: 100}.items(): - se = make_stock_entry( - item_code=batch_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate - ) - batches[get_batch_from_bundle(se.items[0].serial_and_batch_bundle)] = qty - - for qty, rate in {2: 100, 1: 50}.items(): - make_stock_entry(item_code=serial_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate) - serial_nos.extend(get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)) - - dn = create_delivery_note( - item_code=batch_item, - qty=5, - rate=1000, - use_serial_batch_fields=0, - batches=batches, - do_not_submit=True, - ) - - bundle_id = make_serial_batch_bundle( - frappe._dict( - { - "item_code": serial_item, - "warehouse": dn.items[0].warehouse, - "qty": 3, - "voucher_type": "Delivery Note", - "serial_nos": serial_nos, - "posting_date": dn.posting_date, - "posting_time": dn.posting_time, - "type_of_transaction": "Outward", - "do_not_submit": True, - } - ) - ).name - - dn.append( - "items", - { - "item_code": serial_item, - "qty": 3, - "rate": 700, - "base_rate": 700, - "item_name": serial_item, - "uom": "Nos", - "stock_uom": "Nos", - "conversion_factor": 1, - "warehouse": dn.items[0].warehouse, - "use_serial_batch_fields": 0, - "serial_and_batch_bundle": bundle_id, - }, - ) - - dn.save() - dn.submit() - dn.reload() - - batch_no_valuation = defaultdict(float) - serial_no_valuation = defaultdict(float) - - for row in dn.items: - if row.serial_and_batch_bundle: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "serial_no", "batch_no"], - ) - - for d in bundle_data: - if d.batch_no: - batch_no_valuation[d.batch_no] = d.incoming_rate - elif d.serial_no: - serial_no_valuation[d.serial_no] = d.incoming_rate - - return_entry = make_sales_return(dn.name) - - return_entry.save() - return_entry.submit() - return_entry.reload() - - for row in return_entry.items: - if row.item_code == batch_item: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "batch_no"], - ) - - for d in bundle_data: - self.assertEqual(d.incoming_rate, batch_no_valuation[d.batch_no]) - else: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "serial_no"], - ) - - for d in bundle_data: - self.assertEqual(d.incoming_rate, serial_no_valuation[d.serial_no]) - - def test_delivery_note_return_valuation_with_use_serial_batch_field(self): - from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return - - batch_item = make_item( - "_Test Delivery Note Return Valuation WITH Batch Item", - properties={ - "has_batch_no": 1, - "create_new_batch": 1, - "is_stock_item": 1, - "batch_number_series": "BRTN-DNN-BIW-.#####", - }, - ).name - - serial_item = make_item( - "_Test Delivery Note Return Valuation WITH Serial Item", - properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "SRTN-DNN-TPW-.#####"}, - ).name - - batches = [] - serial_nos = [] - for qty, rate in {3: 300, 2: 100}.items(): - se = make_stock_entry( - item_code=batch_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate - ) - batches.append(get_batch_from_bundle(se.items[0].serial_and_batch_bundle)) - - for qty, rate in {2: 100, 1: 50}.items(): - se = make_stock_entry( - item_code=serial_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate - ) - serial_nos.extend(get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)) - - dn = create_delivery_note( - item_code=batch_item, - qty=3, - rate=1000, - use_serial_batch_fields=1, - batch_no=batches[0], - do_not_submit=True, - ) - - dn.append( - "items", - { - "item_code": batch_item, - "qty": 2, - "rate": 1000, - "base_rate": 1000, - "item_name": batch_item, - "uom": dn.items[0].uom, - "stock_uom": dn.items[0].uom, - "conversion_factor": 1, - "warehouse": dn.items[0].warehouse, - "use_serial_batch_fields": 1, - "batch_no": batches[1], - }, - ) - - dn.append( - "items", - { - "item_code": serial_item, - "qty": 2, - "rate": 700, - "base_rate": 700, - "item_name": serial_item, - "uom": "Nos", - "stock_uom": "Nos", - "conversion_factor": 1, - "warehouse": dn.items[0].warehouse, - "use_serial_batch_fields": 1, - "serial_no": "\n".join(serial_nos[0:2]), - }, - ) - - dn.append( - "items", - { - "item_code": serial_item, - "qty": 1, - "rate": 700, - "base_rate": 700, - "item_name": serial_item, - "uom": "Nos", - "stock_uom": "Nos", - "conversion_factor": 1, - "warehouse": dn.items[0].warehouse, - "use_serial_batch_fields": 1, - "serial_no": serial_nos[-1], - }, - ) - - dn.save() - dn.submit() - dn.reload() - - batch_no_valuation = defaultdict(float) - serial_no_valuation = defaultdict(float) - - for row in dn.items: - if row.serial_and_batch_bundle: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "serial_no", "batch_no"], - ) - - for d in bundle_data: - if d.batch_no: - batch_no_valuation[d.batch_no] = d.incoming_rate - elif d.serial_no: - serial_no_valuation[d.serial_no] = d.incoming_rate - - return_entry = make_sales_return(dn.name) - - return_entry.save() - return_entry.submit() - return_entry.reload() - - for row in return_entry.items: - if row.item_code == batch_item: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "batch_no"], - ) - - for d in bundle_data: - self.assertEqual(d.incoming_rate, batch_no_valuation[d.batch_no]) - else: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "serial_no"], - ) - - for d in bundle_data: - self.assertEqual(d.incoming_rate, serial_no_valuation[d.serial_no]) - - def test_auto_set_serial_batch_for_draft_dn(self): - frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1) - frappe.db.set_single_value("Stock Settings", "pick_serial_and_batch_based_on", "FIFO") - - batch_item = make_item( - "_Test Auto Set Serial Batch Draft DN", - properties={ - "has_batch_no": 1, - "create_new_batch": 1, - "is_stock_item": 1, - "batch_number_series": "TAS-BASD-.#####", - }, - ) - - serial_item = make_item( - "_Test Auto Set Serial Batch Draft DN Serial Item", - properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "TAS-SASD-.#####"}, - ) - - batch_serial_item = make_item( - "_Test Auto Set Serial Batch Draft DN Batch Serial Item", - properties={ - "has_batch_no": 1, - "has_serial_no": 1, - "is_stock_item": 1, - "create_new_batch": 1, - "batch_number_series": "TAS-BSD-.#####", - "serial_no_series": "TAS-SSD-.#####", - }, - ) - - for item in [batch_item, serial_item, batch_serial_item]: - make_stock_entry(item_code=item.name, target="_Test Warehouse - _TC", qty=5, basic_rate=100) - - dn = create_delivery_note( - item_code=batch_item, - qty=5, - rate=500, - use_serial_batch_fields=1, - do_not_submit=True, - ) - - for item in [serial_item, batch_serial_item]: - dn.append( - "items", - { - "item_code": item.name, - "qty": 5, - "rate": 500, - "base_rate": 500, - "item_name": item.name, - "uom": "Nos", - "stock_uom": "Nos", - "conversion_factor": 1, - "warehouse": dn.items[0].warehouse, - "use_serial_batch_fields": 1, - }, - ) - - dn.save() - for row in dn.items: - if row.item_code == batch_item.name: - self.assertTrue(row.batch_no) - - if row.item_code == serial_item.name: - self.assertTrue(row.serial_no) - - def test_delivery_note_return_for_batch_item_with_different_warehouse(self): - from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return - from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse - - batch_item = make_item( - "_Test Delivery Note Return Valuation WITH Batch Item", - properties={ - "has_batch_no": 1, - "create_new_batch": 1, - "is_stock_item": 1, - "batch_number_series": "BRTN-DNN-BIW-.#####", - }, - ).name - - batches = [] - for qty, rate in {5: 300}.items(): - se = make_stock_entry( - item_code=batch_item, target="_Test Warehouse - _TC", qty=qty, basic_rate=rate - ) - batches.append(get_batch_from_bundle(se.items[0].serial_and_batch_bundle)) - - warehouse = create_warehouse("Sales Return Test Warehouse 1", company="_Test Company") - - dn = create_delivery_note( - item_code=batch_item, - qty=5, - rate=1000, - use_serial_batch_fields=1, - batch_no=batches[0], - do_not_submit=True, - ) - - self.assertEqual(dn.items[0].warehouse, "_Test Warehouse - _TC") - - dn.save() - dn.submit() - dn.reload() - - batch_no_valuation = defaultdict(float) - - for row in dn.items: - if row.serial_and_batch_bundle: - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "serial_no", "batch_no"], - ) - - for d in bundle_data: - if d.batch_no: - batch_no_valuation[d.batch_no] = d.incoming_rate - - return_entry = make_sales_return(dn.name) - return_entry.items[0].warehouse = warehouse - - return_entry.save() - return_entry.submit() - return_entry.reload() - - for row in return_entry.items: - self.assertEqual(row.warehouse, warehouse) - bundle_data = frappe.get_all( - "Serial and Batch Entry", - filters={"parent": row.serial_and_batch_bundle}, - fields=["incoming_rate", "batch_no"], - ) - - for d in bundle_data: - self.assertEqual(d.incoming_rate, batch_no_valuation[d.batch_no]) - - def test_delivery_note_per_billed_after_return(self): - from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note - - so = make_sales_order(qty=2) - dn = make_delivery_note(so.name) - dn.submit() - self.assertEqual(dn.per_billed, 0) - self.assertEqual(dn.status, "To Bill") - - si = make_sales_invoice(dn.name) - si.location = "Test Location" - si.submit() - - dn_return = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, do_not_submit=True) - dn_return.items[0].dn_detail = dn.items[0].name - dn_return.submit() - - returned = frappe.get_doc("Delivery Note", dn_return.name) - returned.update_prevdoc_status() - dn.load_from_db() - self.assertEqual(dn.per_billed, 100) - self.assertEqual(dn.per_returned, 100) - self.assertEqual(returned.status, "Return") - - def test_sales_return_for_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return - from erpnext.stock.doctype.item.test_item import make_item - - rm_items = [] - for item_code, properties in { - "_Packed Service Item": {"is_stock_item": 0}, - "_Packed FG Item New 1": { - "is_stock_item": 1, - "has_serial_no": 1, - "serial_no_series": "SN-PACKED-1-.#####", - }, - "_Packed FG Item New 2": { - "is_stock_item": 1, - "has_batch_no": 1, - "create_new_batch": 1, - "batch_number_series": "BATCH-PACKED-2-.#####", - }, - "_Packed FG Item New 3": { - "is_stock_item": 1, - "has_batch_no": 1, - "create_new_batch": 1, - "batch_number_series": "BATCH-PACKED-3-.#####", - "has_serial_no": 1, - "serial_no_series": "SN-PACKED-3-.#####", - }, - }.items(): - if not frappe.db.exists("Item", item_code): - make_item(item_code, properties) - - if item_code != "_Packed Service Item": - rm_items.append(item_code) - - for rate in [100, 200]: - make_stock_entry(item=item_code, target="_Test Warehouse - _TC", qty=5, rate=rate) - - make_product_bundle("_Packed Service Item", rm_items) - dn = create_delivery_note( - item_code="_Packed Service Item", - warehouse="_Test Warehouse - _TC", - qty=5, - ) - - dn.reload() - - serial_batch_map = {} - for row in dn.packed_items: - self.assertTrue(row.serial_and_batch_bundle) - if row.item_code not in serial_batch_map: - serial_batch_map[row.item_code] = frappe._dict( - { - "serial_nos": [], - "batches": defaultdict(int), - "serial_no_valuation": defaultdict(float), - "batch_no_valuation": defaultdict(float), - } - ) - - doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) - for entry in doc.entries: - if entry.serial_no: - serial_batch_map[row.item_code].serial_nos.append(entry.serial_no) - serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no] = entry.incoming_rate - if entry.batch_no: - serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty - serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no] = entry.incoming_rate - - dn1 = make_sales_return(dn.name) - dn1.items[0].qty = -2 - dn1.submit() - dn1.reload() - - for row in dn1.packed_items: - doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) - for entry in doc.entries: - if entry.serial_no: - self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos) - self.assertEqual( - entry.incoming_rate, - serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no], - ) - serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no) - serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no) - - elif entry.batch_no: - serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty - self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches) - self.assertEqual(entry.qty, 2.0) - self.assertEqual( - entry.incoming_rate, - serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], - ) - - dn2 = make_sales_return(dn.name) - dn2.items[0].qty = -3 - dn2.submit() - dn2.reload() - - for row in dn2.packed_items: - doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) - for entry in doc.entries: - if entry.serial_no: - self.assertTrue(entry.serial_no in serial_batch_map[row.item_code].serial_nos) - self.assertEqual( - entry.incoming_rate, - serial_batch_map[row.item_code].serial_no_valuation[entry.serial_no], - ) - serial_batch_map[row.item_code].serial_nos.remove(entry.serial_no) - serial_batch_map[row.item_code].serial_no_valuation.pop(entry.serial_no) - - elif entry.batch_no: - serial_batch_map[row.item_code].batches[entry.batch_no] += entry.qty - self.assertEqual(serial_batch_map[row.item_code].batches[entry.batch_no], 0.0) - - self.assertTrue(entry.batch_no in serial_batch_map[row.item_code].batches) - - self.assertEqual(entry.qty, 3.0) - self.assertEqual( - entry.incoming_rate, - serial_batch_map[row.item_code].batch_no_valuation[entry.batch_no], - ) - - @change_settings("Stock Settings", {"allow_negative_stock": 0, "enable_stock_reservation": 1}) - def test_partial_delivery_note_against_reserved_stock(self): - from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( - get_stock_reservation_entries_for_voucher, - ) - - # create batch item - batch_item = make_item( - "_Test Batch Item For DN Reserve Check", - { - "is_stock_item": 1, - "has_batch_no": 1, - "create_new_batch": 1, - "batch_number_series": "TBDNR.#####", - }, - ) - serial_item = make_item( - "_Test Serial Item For DN Reserve Check", - { - "is_stock_item": 1, - "has_serial_no": 1, - "serial_no_series": "TSNDNR.#####", - }, - ) - - company = "_Test Company" - - warehouse = create_warehouse("Test Partial DN Reserved Stock", company=company) - customer = "_Test Customer" - - items = [batch_item.name, serial_item.name] - - for idx, item in enumerate(items): - # make inward entry for batch item - se = make_stock_entry(item_code=item, purpose="Material Receipt", qty=10, to_warehouse=warehouse) - sabb = se.items[0].serial_and_batch_bundle - - batch_no = get_batch_from_bundle(sabb) if not idx else None - serial_nos = get_serial_nos_from_bundle(sabb) if idx else None - - # make sales order and reserve the quantites against the so - so = make_sales_order(item_code=item, qty=10, rate=100, customer=customer, warehouse=warehouse) - so.submit() - so.create_stock_reservation_entries() - so.reload() - - # create a delivery note with partial quantity from resreved quantity - dn = create_dn_against_so(so=so.name, delivered_qty=5, do_not_submit=True) - dn.items[0].use_serial_batch_fields = 1 - if batch_no: - dn.items[0].batch_no = batch_no - else: - dn.items[0].serial_no = "\n".join(serial_nos[:5]) - - dn.save() - dn.submit() - - against_sales_order = dn.items[0].against_sales_order - so_detail = dn.items[0].so_detail - - sre_details = get_stock_reservation_entries_for_voucher( - so.doctype, against_sales_order, so_detail, ["reserved_qty", "delivered_qty", "status"] - ) - - # check partially delivered reserved stock - self.assertEqual(sre_details[0].status, "Partially Delivered") - self.assertEqual(sre_details[0].reserved_qty, so.items[0].qty) - self.assertEqual(sre_details[0].delivered_qty, dn.items[0].qty) - def test_negative_stock_with_higher_precision(self): original_flt_precision = frappe.db.get_default("float_precision") frappe.db.set_single_value("System Settings", "float_precision", 7) @@ -2137,7 +1524,6 @@ class TestDeliveryNote(FrappeTestCase): frappe.db.set_single_value("System Settings", "float_precision", original_flt_precision) ->>>>>>> 1bbeecff12 (fix: negative stock issue for higher precision) def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note")