mirror of
https://github.com/frappe/erpnext.git
synced 2026-10-29 22:57:42 +00:00
fix(stock): correct delivery note credit exposure
This commit is contained in:
@@ -14,6 +14,8 @@ from frappe.contacts.address_and_contact import (
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.naming import set_name_by_naming_series, set_name_from_naming_options
|
||||
from frappe.model.utils.rename_doc import update_linked_doctypes
|
||||
from frappe.query_builder import Criterion
|
||||
from frappe.query_builder.functions import IfNull, Sum
|
||||
from frappe.utils import cint, cstr, flt, get_formatted_email, get_link_to_form, today
|
||||
from frappe.utils.deprecations import deprecated
|
||||
from frappe.utils.user import get_users_with_role
|
||||
@@ -696,49 +698,62 @@ def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=F
|
||||
|
||||
outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0
|
||||
|
||||
# Outstanding based on Delivery Note, which are not created against Sales Order
|
||||
outstanding_based_on_dn = 0
|
||||
DeliveryNote = frappe.qb.DocType("Delivery Note")
|
||||
DeliveryNoteItem = frappe.qb.DocType("Delivery Note Item")
|
||||
SalesInvoice = frappe.qb.DocType("Sales Invoice")
|
||||
SalesInvoiceItem = frappe.qb.DocType("Sales Invoice Item")
|
||||
|
||||
unmarked_delivery_note_items = frappe.db.sql(
|
||||
"""select
|
||||
dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total
|
||||
from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
|
||||
where
|
||||
dn.name = dn_item.parent
|
||||
and dn.customer=%s and dn.company=%s
|
||||
and dn.docstatus = 1 and dn.status not in ('Closed', 'Stopped')
|
||||
and ifnull(dn_item.against_sales_order, '') = ''
|
||||
and ifnull(dn_item.against_sales_invoice, '') = ''
|
||||
""",
|
||||
(customer, company),
|
||||
as_dict=True,
|
||||
si_subquery = (
|
||||
frappe.qb.from_(SalesInvoiceItem)
|
||||
.join(SalesInvoice)
|
||||
.on(SalesInvoice.name == SalesInvoiceItem.parent)
|
||||
.select(SalesInvoiceItem.dn_detail, Sum(SalesInvoiceItem.stock_qty).as_("billed_qty"))
|
||||
.where(SalesInvoiceItem.docstatus == 1)
|
||||
.where((SalesInvoice.is_return == 0) | (SalesInvoice.update_billed_amount_in_delivery_note == 1))
|
||||
.groupby(SalesInvoiceItem.dn_detail)
|
||||
)
|
||||
|
||||
if not unmarked_delivery_note_items:
|
||||
return outstanding_based_on_gle + outstanding_based_on_so
|
||||
|
||||
si_amounts = frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
dn_detail, sum(amount) from `tabSales Invoice Item`
|
||||
WHERE
|
||||
docstatus = 1
|
||||
and dn_detail in ({})
|
||||
GROUP BY dn_detail""".format(
|
||||
", ".join(frappe.db.escape(dn_item.name) for dn_item in unmarked_delivery_note_items)
|
||||
dn_query = (
|
||||
frappe.qb.from_(DeliveryNote)
|
||||
.join(DeliveryNoteItem)
|
||||
.on(DeliveryNote.name == DeliveryNoteItem.parent)
|
||||
.left_join(si_subquery)
|
||||
.on(DeliveryNoteItem.name == si_subquery.dn_detail)
|
||||
.select(
|
||||
Sum(
|
||||
(
|
||||
DeliveryNoteItem.base_net_amount
|
||||
* (DeliveryNoteItem.stock_qty - IfNull(si_subquery.billed_qty, 0.0))
|
||||
/ DeliveryNoteItem.stock_qty
|
||||
/ DeliveryNote.base_net_total
|
||||
)
|
||||
* DeliveryNote.base_grand_total
|
||||
)
|
||||
)
|
||||
.where(DeliveryNote.customer == customer)
|
||||
.where(DeliveryNote.company == company)
|
||||
.where(DeliveryNote.docstatus == 1)
|
||||
.where(DeliveryNote.base_net_total > 0)
|
||||
.where(DeliveryNote.status.notin(["Closed", "Stopped"]))
|
||||
.where(DeliveryNoteItem.stock_qty > 0)
|
||||
.where(DeliveryNoteItem.stock_qty > IfNull(si_subquery.billed_qty, 0.0))
|
||||
.where(
|
||||
Criterion.any(
|
||||
[DeliveryNoteItem.against_sales_order.isnull(), DeliveryNoteItem.against_sales_order == ""]
|
||||
)
|
||||
)
|
||||
.where(
|
||||
Criterion.any(
|
||||
[
|
||||
DeliveryNoteItem.against_sales_invoice.isnull(),
|
||||
DeliveryNoteItem.against_sales_invoice == "",
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
si_amounts = {si_item[0]: si_item[1] for si_item in si_amounts}
|
||||
|
||||
for dn_item in unmarked_delivery_note_items:
|
||||
dn_amount = flt(dn_item.amount)
|
||||
si_amount = flt(si_amounts.get(dn_item.name))
|
||||
|
||||
if dn_amount > si_amount and dn_item.base_net_total:
|
||||
outstanding_based_on_dn += (
|
||||
(dn_amount - si_amount) / dn_item.base_net_total
|
||||
) * dn_item.base_grand_total
|
||||
dn_res = dn_query.run()
|
||||
outstanding_based_on_dn = flt(dn_res[0][0]) if dn_res and dn_res[0][0] is not None else 0.0
|
||||
|
||||
return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn
|
||||
|
||||
|
||||
@@ -669,8 +669,12 @@ class DeliveryNote(SellingController):
|
||||
for d in self.get("items"):
|
||||
if not d.against_sales_invoice:
|
||||
validate_against_credit_limit = True
|
||||
extra_amount = self.base_grand_total
|
||||
break
|
||||
# Only submitted standalone rows are already included in customer outstanding.
|
||||
if (d.against_sales_order or self.docstatus == 0) and self.base_net_total:
|
||||
extra_amount += d.base_net_amount / self.base_net_total * self.base_grand_total
|
||||
if validate_against_credit_limit and not self.base_net_total:
|
||||
# Customer outstanding excludes zero-net deliveries, including their freight charges.
|
||||
extra_amount = self.base_grand_total
|
||||
else:
|
||||
for d in self.get("items"):
|
||||
if not (d.against_sales_order or d.against_sales_invoice):
|
||||
|
||||
Reference in New Issue
Block a user