diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 8ed59f452e22..faf305cfe9ce 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -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 diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 2525120cefd8..adb6e6905964 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2683,6 +2683,54 @@ def test_same_stock_and_transaction_uom_conversion_factor(self): self.assertEqual(pr.items[0].conversion_factor, 1.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") + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier