fix: update per_billed value in Purchase Receipt while creating Debit Note (#43977)

* fix: update per_billed value in Purchase Receipt while creating Debit Note

* test: add unit test for validating per_billed value for partial Debit Note

(cherry picked from commit 494fd7ceea)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
This commit is contained in:
NaviN
2024-11-13 12:23:16 +05:30
committed by ruthra kumar
parent cbe92fef0c
commit da6c6dcfcb
2 changed files with 288 additions and 2 deletions

View File

@@ -883,6 +883,8 @@ def get_billed_amount_against_po(po_items):
def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False):
# Update Billing % based on pending accepted qty
buying_settings = frappe.get_single("Buying Settings")
total_amount, total_billed_amount = 0, 0
item_wise_returned_qty = get_item_wise_returned_qty(pr_doc)
@@ -890,10 +892,15 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate
returned_qty = flt(item_wise_returned_qty.get(item.name))
returned_amount = flt(returned_qty) * flt(item.rate)
pending_amount = flt(item.amount) - returned_amount
total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt
if buying_settings.bill_for_rejected_quantity_in_purchase_invoice:
pending_amount = flt(item.amount)
total_billable_amount = abs(flt(item.amount))
if pending_amount > 0:
total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt
total_amount += total_billable_amount
total_billed_amount += flt(item.billed_amt)
total_billed_amount += abs(flt(item.billed_amt))
if pr_doc.get("is_return") and not total_amount and total_billed_amount:
total_amount = total_billed_amount

View File

@@ -2683,6 +2683,285 @@ class TestPurchaseReceipt(FrappeTestCase):
self.assertEqual(pr.items[0].conversion_factor, 1.0)
<<<<<<< HEAD
=======
def test_purchase_receipt_return_valuation_without_use_serial_batch_field(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
batch_item = make_item(
"_Test Purchase Receipt Return Valuation Batch Item",
properties={
"has_batch_no": 1,
"create_new_batch": 1,
"is_stock_item": 1,
"batch_number_series": "BRTN-TPRBI-.#####",
},
).name
serial_item = make_item(
"_Test Purchase Receipt Return Valuation Serial Item",
properties={"has_serial_no": 1, "is_stock_item": 1, "serial_no_series": "SRTN-TPRSI-.#####"},
).name
rej_warehouse = create_warehouse("_Test Purchase Warehouse For Rejected Qty")
pr = make_purchase_receipt(
item_code=batch_item,
received_qty=10,
qty=8,
rejected_qty=2,
rejected_warehouse=rej_warehouse,
rate=300,
do_not_submit=1,
use_serial_batch_fields=0,
)
pr.append(
"items",
{
"item_code": serial_item,
"qty": 2,
"rate": 100,
"base_rate": 100,
"item_name": serial_item,
"uom": "Nos",
"stock_uom": "Nos",
"conversion_factor": 1,
"rejected_qty": 1,
"warehouse": pr.items[0].warehouse,
"use_serial_batch_fields": 0,
"rejected_warehouse": rej_warehouse,
},
)
pr.save()
pr.submit()
pr.reload()
batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle)
rejected_batch_no = get_batch_from_bundle(pr.items[0].rejected_serial_and_batch_bundle)
self.assertEqual(batch_no, rejected_batch_no)
return_entry = make_purchase_return(pr.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},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 300.00)
else:
bundle_data = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": row.serial_and_batch_bundle},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 100.00)
for row in return_entry.items:
if row.item_code == batch_item:
bundle_data = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": row.rejected_serial_and_batch_bundle},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 0)
else:
bundle_data = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": row.rejected_serial_and_batch_bundle},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 0)
def test_purchase_receipt_return_valuation_with_use_serial_batch_field(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
batch_item = make_item(
"_Test Purchase Receipt Return Valuation With Batch Item",
properties={"has_batch_no": 1, "create_new_batch": 1, "is_stock_item": 1},
).name
serial_item = make_item(
"_Test Purchase Receipt Return Valuation With Serial Item",
properties={"has_serial_no": 1, "is_stock_item": 1},
).name
rej_warehouse = create_warehouse("_Test Purchase Warehouse For Rejected Qty")
batch_no = "BATCH-RTN-BNU-TPRBI-0001"
serial_nos = ["SNU-RTN-TPRSI-0001", "SNU-RTN-TPRSI-0002", "SNU-RTN-TPRSI-0003"]
if not frappe.db.exists("Batch", batch_no):
frappe.get_doc(
{
"doctype": "Batch",
"batch_id": batch_no,
"item": batch_item,
}
).insert()
for serial_no in serial_nos:
if not frappe.db.exists("Serial No", serial_no):
frappe.get_doc(
{
"doctype": "Serial No",
"item_code": serial_item,
"serial_no": serial_no,
}
).insert()
pr = make_purchase_receipt(
item_code=batch_item,
received_qty=10,
qty=8,
rejected_qty=2,
rejected_warehouse=rej_warehouse,
batch_no=batch_no,
use_serial_batch_fields=1,
rate=300,
do_not_submit=1,
)
pr.append(
"items",
{
"item_code": serial_item,
"qty": 2,
"rate": 100,
"base_rate": 100,
"item_name": serial_item,
"uom": "Nos",
"stock_uom": "Nos",
"conversion_factor": 1,
"rejected_qty": 1,
"warehouse": pr.items[0].warehouse,
"use_serial_batch_fields": 1,
"rejected_warehouse": rej_warehouse,
"serial_no": "\n".join(serial_nos[:2]),
"rejected_serial_no": serial_nos[2],
},
)
pr.save()
pr.submit()
pr.reload()
batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle)
rejected_batch_no = get_batch_from_bundle(pr.items[0].rejected_serial_and_batch_bundle)
self.assertEqual(batch_no, rejected_batch_no)
return_entry = make_purchase_return(pr.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},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 300.00)
else:
bundle_data = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": row.serial_and_batch_bundle},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 100.00)
for row in return_entry.items:
if row.item_code == batch_item:
bundle_data = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": row.rejected_serial_and_batch_bundle},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 0)
else:
bundle_data = frappe.get_all(
"Serial and Batch Entry",
filters={"parent": row.rejected_serial_and_batch_bundle},
pluck="incoming_rate",
)
for incoming_rate in bundle_data:
self.assertEqual(incoming_rate, 0)
def test_purchase_return_partial_debit_note(self):
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
supplier_warehouse="Work In Progress - TCP1",
)
return_pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
supplier_warehouse="Work In Progress - TCP1",
is_return=1,
return_against=pr.name,
qty=-2,
do_not_submit=1,
)
return_pr.items[0].purchase_receipt_item = pr.items[0].name
return_pr.submit()
# because new_doc isn't considering is_return portion of status_updater
returned = frappe.get_doc("Purchase Receipt", return_pr.name)
returned.update_prevdoc_status()
pr.load_from_db()
# Check if Original PR updated
self.assertEqual(pr.items[0].returned_qty, 2)
self.assertEqual(pr.per_returned, 40)
# Create first partial debit_note
pi_1 = make_purchase_invoice(return_pr.name)
pi_1.items[0].qty = -1
pi_1.submit()
# Check if the first partial debit billing percentage got updated
return_pr.reload()
self.assertEqual(return_pr.per_billed, 50)
self.assertEqual(return_pr.status, "Partly Billed")
# Create second partial debit_note to complete the debit note
pi_2 = make_purchase_invoice(return_pr.name)
pi_2.items[0].qty = -1
pi_2.submit()
# Check if the second partial debit note billing percentage got updated
return_pr.reload()
self.assertEqual(return_pr.per_billed, 100)
self.assertEqual(return_pr.status, "Completed")
>>>>>>> 494fd7ceea (fix: update per_billed value in Purchase Receipt while creating Debit Note (#43977))
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier