fix: valuation rate for non batchwise valuation

(cherry picked from commit b6312bca9c)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
This commit is contained in:
Rohit Waghchaure
2026-01-13 17:00:45 +05:30
committed by Mergify
parent 85f635ac4a
commit 3008c7ad82
2 changed files with 290 additions and 11 deletions

View File

@@ -4529,6 +4529,286 @@ class TestPurchaseReceipt(FrappeTestCase):
self.assertEqual(sles, [1500.0, 1500.0])
<<<<<<< HEAD
=======
@IntegrationTestCase.change_settings("Stock Settings", {"allow_negative_stock": 0})
def test_multiple_transactions_with_same_posting_datetime(self):
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.stock_ledger import NegativeStockError
item_code = make_item(
"Test Item for Multiple Txn with Same Posting Datetime", {"is_stock_item": 1}
).name
pr = make_purchase_receipt(
item_code=item_code,
qty=100,
rate=100,
posting_date=today(),
posting_time="10:00:00",
)
create_delivery_note(
item_code=item_code,
qty=100,
rate=100,
posting_date=today(),
posting_time="10:00:00",
)
make_purchase_receipt(
item_code=item_code,
qty=150,
rate=100,
posting_date=today(),
posting_time="10:00:00",
)
self.assertRaises(NegativeStockError, pr.cancel)
@IntegrationTestCase.change_settings(
"Buying Settings", {"set_landed_cost_based_on_purchase_invoice_rate": 1, "maintain_same_rate": 0}
)
def test_set_lcv_from_pi_created_against_po(self):
from erpnext.buying.doctype.purchase_order.purchase_order import (
make_purchase_invoice as make_pi_against_po,
)
from erpnext.buying.doctype.purchase_order.purchase_order import (
make_purchase_receipt as make_pr_against_po,
)
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
original_value = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
frappe.db.set_single_value("Accounts Settings", "over_billing_allowance", 100)
item_code = create_item("Test Item for LCV from PI against PO").name
po = create_purchase_order(item_code=item_code, qty=10, rate=400)
pr = make_pr_against_po(po.name)
pr.items[0].qty = 5
item = frappe.copy_doc(pr.items[0])
item.qty = 2
pr.append("items", item)
item = frappe.copy_doc(pr.items[0])
item.qty = 3
pr.append("items", item)
pr.submit()
pi = make_pi_against_po(po.name)
pi.items[0].rate = 500
pi.submit()
pr.reload()
for row in pr.items:
self.assertTrue(row.amount_difference_with_purchase_invoice)
amt_diff = 5000 * (row.qty / 10) - row.amount
self.assertEqual(row.amount_difference_with_purchase_invoice, amt_diff)
frappe.db.set_single_value("Accounts Settings", "over_billing_allowance", original_value)
def test_purchase_return_with_and_without_return_against_rejected_qty(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_return as _make_purchase_return,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_return_against_rejected_warehouse,
)
item_code = create_item("Test Item for PR against Rejected Qty").name
warehouse = "_Test Warehouse - _TC"
company = frappe.db.get_value("Warehouse", warehouse, "company")
rejected_wh = create_warehouse("_Test Rejected Warehouse", company=company)
pr = make_purchase_receipt(
item_code=item_code,
qty=10,
rejected_qty=5,
rate=100,
warehouse=warehouse,
rejected_warehouse=rejected_wh,
)
# Purchase Return against rejected qty partially
return_entry = make_purchase_return_against_rejected_warehouse(pr.name)
return_entry.items[0].qty = -2
return_entry.items[0].received_qty = -2
return_entry.save()
return_entry.submit()
pr.reload()
# Purchase Return against rejected qty partially
return_entry = _make_purchase_return(pr.name)
self.assertEqual(return_entry.items[0].qty, -10)
self.assertEqual(return_entry.items[0].rejected_qty, -3) # 5-2=3
return_entry.items[0].qty = -8
return_entry.items[0].stock_qty = -8
return_entry.items[0].received_qty = -11
return_entry.save()
return_entry.submit()
pr.reload()
# Purchase Return against rejected qty partially
return_entry = _make_purchase_return(pr.name)
self.assertEqual(return_entry.items[0].qty, -2)
self.assertEqual(return_entry.items[0].rejected_qty, 0) # 3-3=0
def test_do_not_use_batchwise_valuation_with_fifo(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
item_code = make_item(
"Test Item Do Not Use Batchwise Valuation with FIFO",
{
"is_stock_item": 1,
"has_batch_no": 1,
"batch_number_series": "BN-TESTDNUBVWF-.#####",
"valuation_method": "FIFO",
},
).name
doc = frappe.new_doc("Batch")
doc.update(
{
"batch_id": "BN-TESTDNUBVWF-00001",
"item": item_code,
}
).insert()
doc.db_set("use_batchwise_valuation", 0)
doc.reload()
self.assertTrue(doc.use_batchwise_valuation == 0)
doc = frappe.new_doc("Batch")
doc.update(
{
"batch_id": "BN-TESTDNUBVWF-00002",
"item": item_code,
}
).insert()
self.assertTrue(doc.use_batchwise_valuation == 1)
warehouse = "_Test Warehouse - _TC"
make_stock_entry(
item_code=item_code,
qty=10,
rate=100,
target=warehouse,
batch_no="BN-TESTDNUBVWF-00001",
use_serial_batch_fields=1,
)
se1 = make_stock_entry(
item_code=item_code,
qty=10,
rate=200,
target=warehouse,
batch_no="BN-TESTDNUBVWF-00001",
use_serial_batch_fields=1,
)
stock_queue = frappe.db.get_value(
"Stock Ledger Entry",
{
"item_code": item_code,
"warehouse": warehouse,
"is_cancelled": 0,
"voucher_type": "Stock Entry",
"voucher_no": se1.name,
},
"stock_queue",
)
stock_queue = frappe.parse_json(stock_queue)
self.assertEqual(stock_queue, [[10, 100.0], [10, 200.0]])
se2 = make_stock_entry(
item_code=item_code,
qty=10,
rate=2,
target=warehouse,
batch_no="BN-TESTDNUBVWF-00002",
use_serial_batch_fields=1,
)
stock_queue = frappe.db.get_value(
"Stock Ledger Entry",
{
"item_code": item_code,
"warehouse": warehouse,
"is_cancelled": 0,
"voucher_type": "Stock Entry",
"voucher_no": se2.name,
},
"stock_queue",
)
stock_queue = frappe.parse_json(stock_queue)
self.assertEqual(stock_queue, [[10, 100.0], [10, 200.0]])
se3 = make_stock_entry(
item_code=item_code,
qty=20,
source=warehouse,
batch_no="BN-TESTDNUBVWF-00001",
use_serial_batch_fields=1,
)
ste_details = frappe.db.get_value(
"Stock Ledger Entry",
{
"item_code": item_code,
"warehouse": warehouse,
"is_cancelled": 0,
"voucher_type": "Stock Entry",
"voucher_no": se3.name,
},
["stock_queue", "stock_value_difference"],
as_dict=1,
)
stock_queue = frappe.parse_json(ste_details.stock_queue)
self.assertEqual(stock_queue, [])
self.assertEqual(ste_details.stock_value_difference, 3000 * -1)
se4 = make_stock_entry(
item_code=item_code,
qty=20,
rate=0,
target=warehouse,
batch_no="BN-TESTDNUBVWF-00001",
use_serial_batch_fields=1,
do_not_submit=1,
)
se4.items[0].basic_rate = 0.0
se4.items[0].allow_zero_valuation_rate = 1
se4.submit()
stock_queue = frappe.db.get_value(
"Stock Ledger Entry",
{
"item_code": item_code,
"warehouse": warehouse,
"is_cancelled": 0,
"voucher_type": "Stock Entry",
"voucher_no": se4.name,
},
"stock_queue",
)
self.assertEqual(frappe.parse_json(stock_queue), [[20, 0.0]])
>>>>>>> b6312bca9c (fix: valuation rate for non batchwise valuation)
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier

View File

@@ -713,17 +713,16 @@ class SerialandBatchBundle(Document):
is_packed_item = True
stock_queue = []
batches = []
if prev_sle and prev_sle.stock_queue:
batches = frappe.get_all(
"Batch",
filters={
"name": ("in", [d.batch_no for d in self.entries if d.batch_no]),
"use_batchwise_valuation": 0,
},
pluck="name",
)
batches = frappe.get_all(
"Batch",
filters={
"name": ("in", [d.batch_no for d in self.entries if d.batch_no]),
"use_batchwise_valuation": 0,
},
pluck="name",
)
if prev_sle and prev_sle.stock_queue and parse_json(prev_sle.stock_queue):
if batches and valuation_method == "FIFO":
stock_queue = parse_json(prev_sle.stock_queue)
@@ -750,7 +749,7 @@ class SerialandBatchBundle(Document):
if d.qty:
d.stock_value_difference = flt(d.qty) * d.incoming_rate
if stock_queue and valuation_method == "FIFO" and d.batch_no in batches:
if valuation_method == "FIFO" and d.batch_no in batches and d.incoming_rate is not None:
stock_queue.append([d.qty, d.incoming_rate])
d.stock_queue = json.dumps(stock_queue)