mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-17 02:26:33 +00:00
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
This commit is contained in:
@@ -449,4 +449,5 @@ erpnext.patches.v16_0.backfill_repost_accounting_ledger_status
|
|||||||
erpnext.patches.v16_0.merge_seeded_item_group_root
|
erpnext.patches.v16_0.merge_seeded_item_group_root
|
||||||
erpnext.patches.v16_0.remove_frappe_crm_custom_fields
|
erpnext.patches.v16_0.remove_frappe_crm_custom_fields
|
||||||
erpnext.patches.v16_0.append_fieldname_to_pos_search_fields
|
erpnext.patches.v16_0.append_fieldname_to_pos_search_fields
|
||||||
erpnext.patches.v16_0.add_transaction_roles_to_sms_settings
|
erpnext.patches.v16_0.add_transaction_roles_to_sms_settings
|
||||||
|
erpnext.patches.v16_0.recalculate_returned_delivery_note_billing_status
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
"""Recalculate billing status of Delivery Notes left open by a return.
|
||||||
|
|
||||||
|
Returning the uninvoiced qty of a Delivery Note did not recalculate the original
|
||||||
|
Delivery Note, so it stayed "To Bill" / "Partially Billed" with nothing left to invoice.
|
||||||
|
"""
|
||||||
|
dn = frappe.qb.DocType("Delivery Note")
|
||||||
|
dn_item = frappe.qb.DocType("Delivery Note Item")
|
||||||
|
|
||||||
|
delivery_notes = (
|
||||||
|
frappe.qb.from_(dn)
|
||||||
|
.inner_join(dn_item)
|
||||||
|
.on(dn_item.parent == dn.name)
|
||||||
|
.select(dn.name)
|
||||||
|
.distinct()
|
||||||
|
.where(
|
||||||
|
(dn.docstatus == 1)
|
||||||
|
& (dn.is_return == 0)
|
||||||
|
& dn.status.isin(["To Bill", "Partially Billed"])
|
||||||
|
& (dn_item.returned_qty > 0)
|
||||||
|
)
|
||||||
|
.run(pluck=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for name in delivery_notes:
|
||||||
|
doc = frappe.get_doc("Delivery Note", name)
|
||||||
|
doc.update_billing_percentage(update_modified=False)
|
||||||
|
doc.load_from_db()
|
||||||
|
doc.set_status(update=True, update_modified=False)
|
||||||
@@ -1099,6 +1099,67 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
self.assertEqual(dn.per_billed, 50)
|
self.assertEqual(dn.per_billed, 50)
|
||||||
self.assertEqual(dn.status, "Partially Billed")
|
self.assertEqual(dn.status, "Partially Billed")
|
||||||
|
|
||||||
|
def test_billing_status_repair_patch(self):
|
||||||
|
"""Returns submitted before #58869 left the original Delivery Note's per_billed stale.
|
||||||
|
|
||||||
|
The repair patch recalculates such notes: a directly invoiced one whose remaining
|
||||||
|
qty was returned becomes Completed, an uninvoiced Sales Order linked one goes back
|
||||||
|
to To Bill.
|
||||||
|
"""
|
||||||
|
from erpnext.patches.v16_0 import recalculate_returned_delivery_note_billing_status as patch
|
||||||
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
|
||||||
|
|
||||||
|
# Delivery Note invoiced for 2 of 5 qty, the remaining 3 returned -> fully billed
|
||||||
|
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100)
|
||||||
|
dn = create_delivery_note(qty=5)
|
||||||
|
|
||||||
|
si = make_sales_invoice(dn.name)
|
||||||
|
si.items[0].qty = 2
|
||||||
|
si.insert()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
dn_return = make_sales_return(dn.name)
|
||||||
|
dn_return.items[0].qty = -3
|
||||||
|
dn_return.insert()
|
||||||
|
# Mimic the submit request, which reconstructs the document from client data.
|
||||||
|
frappe.get_doc(dn_return.as_dict()).submit()
|
||||||
|
|
||||||
|
dn.load_from_db()
|
||||||
|
self.assertEqual(dn.items[0].returned_qty, 3)
|
||||||
|
self.assertEqual(dn.per_billed, 100)
|
||||||
|
|
||||||
|
# Sales Order linked Delivery Note, nothing invoiced, partly returned -> unbilled
|
||||||
|
so = make_sales_order(qty=10)
|
||||||
|
so_dn = create_dn_against_so(so.name, delivered_qty=5)
|
||||||
|
|
||||||
|
so_dn_return = make_sales_return(so_dn.name)
|
||||||
|
so_dn_return.items[0].qty = -2
|
||||||
|
so_dn_return.insert()
|
||||||
|
frappe.get_doc(so_dn_return.as_dict()).submit()
|
||||||
|
|
||||||
|
so_dn.load_from_db()
|
||||||
|
self.assertEqual(so_dn.items[0].returned_qty, 2)
|
||||||
|
self.assertEqual(so_dn.per_billed, 0)
|
||||||
|
|
||||||
|
# Mimic the state left behind by a return submitted before the fix
|
||||||
|
for name, per_billed in ((dn.name, 40), (so_dn.name, 50)):
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Delivery Note",
|
||||||
|
name,
|
||||||
|
{"per_billed": per_billed, "status": "Partially Billed"},
|
||||||
|
update_modified=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
patch.execute()
|
||||||
|
|
||||||
|
dn.load_from_db()
|
||||||
|
self.assertEqual(dn.per_billed, 100)
|
||||||
|
self.assertEqual(dn.status, "Completed")
|
||||||
|
|
||||||
|
so_dn.load_from_db()
|
||||||
|
self.assertEqual(so_dn.per_billed, 0)
|
||||||
|
self.assertEqual(so_dn.status, "To Bill")
|
||||||
|
|
||||||
def test_dn_billing_status_case2(self):
|
def test_dn_billing_status_case2(self):
|
||||||
# SO -> SI and SO -> DN1, DN2
|
# SO -> SI and SO -> DN1, DN2
|
||||||
from erpnext.selling.doctype.sales_order.sales_order import (
|
from erpnext.selling.doctype.sales_order.sales_order import (
|
||||||
|
|||||||
Reference in New Issue
Block a user