mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 11:09:17 +00:00
Merge pull request #34511 from ruthra-kumar/manual_backport_of_34456
fix: Gross Profit reports Invoices with -ve qty for Invoices with Cr Notes (manual backport to version 13)
This commit is contained in:
@@ -8,6 +8,7 @@ from frappe.query_builder import Order
|
|||||||
from frappe.utils import cint, flt
|
from frappe.utils import cint, flt
|
||||||
|
|
||||||
from erpnext.controllers.queries import get_match_cond
|
from erpnext.controllers.queries import get_match_cond
|
||||||
|
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
|
||||||
from erpnext.stock.utils import get_incoming_rate
|
from erpnext.stock.utils import get_incoming_rate
|
||||||
|
|
||||||
|
|
||||||
@@ -465,7 +466,14 @@ class GrossProfitGenerator(object):
|
|||||||
):
|
):
|
||||||
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
||||||
for returned_item_row in returned_item_rows:
|
for returned_item_row in returned_item_rows:
|
||||||
row.qty += flt(returned_item_row.qty)
|
# returned_items 'qty' should be stateful
|
||||||
|
if returned_item_row.qty != 0:
|
||||||
|
if row.qty >= abs(returned_item_row.qty):
|
||||||
|
row.qty += returned_item_row.qty
|
||||||
|
returned_item_row.qty = 0
|
||||||
|
else:
|
||||||
|
row.qty = 0
|
||||||
|
returned_item_row.qty += row.qty
|
||||||
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
||||||
row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
|
row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
|
||||||
if flt(row.qty) or row.base_amount:
|
if flt(row.qty) or row.base_amount:
|
||||||
@@ -664,6 +672,19 @@ class GrossProfitGenerator(object):
|
|||||||
if self.filters.to_date:
|
if self.filters.to_date:
|
||||||
conditions += " and posting_date <= %(to_date)s"
|
conditions += " and posting_date <= %(to_date)s"
|
||||||
|
|
||||||
|
conditions += " and (is_return = 0 or (is_return=1 and return_against is null))"
|
||||||
|
|
||||||
|
if self.filters.item_group:
|
||||||
|
conditions += " and {0}".format(get_item_group_condition(self.filters.item_group))
|
||||||
|
|
||||||
|
if self.filters.sales_person:
|
||||||
|
conditions += """
|
||||||
|
and exists(select 1
|
||||||
|
from `tabSales Team` st
|
||||||
|
where st.parent = `tabSales Invoice`.name
|
||||||
|
and st.sales_person = %(sales_person)s)
|
||||||
|
"""
|
||||||
|
|
||||||
if self.filters.group_by == "Sales Person":
|
if self.filters.group_by == "Sales Person":
|
||||||
sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives"
|
sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives"
|
||||||
sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
|
sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
|
||||||
|
|||||||
@@ -380,3 +380,82 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
}
|
}
|
||||||
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
||||||
self.assertDictContainsSubset(expected_entry, gp_entry[0])
|
self.assertDictContainsSubset(expected_entry, gp_entry[0])
|
||||||
|
|
||||||
|
def test_crnote_against_invoice_with_multiple_instances_of_same_item(self):
|
||||||
|
"""
|
||||||
|
Item Qty for Sales Invoices with multiple instances of same item go in the -ve. Ideally, the credit noteshould cancel out the invoice items.
|
||||||
|
"""
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
|
||||||
|
|
||||||
|
# Invoice with an item added twice
|
||||||
|
sinv = self.create_sales_invoice(qty=1, rate=100, posting_date=nowdate(), do_not_submit=True)
|
||||||
|
sinv.append("items", frappe.copy_doc(sinv.items[0], ignore_no_copy=False))
|
||||||
|
sinv = sinv.save().submit()
|
||||||
|
|
||||||
|
# Create Credit Note for Invoice
|
||||||
|
cr_note = make_sales_return(sinv.name)
|
||||||
|
cr_note = cr_note.save().submit()
|
||||||
|
|
||||||
|
filters = frappe._dict(
|
||||||
|
company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice"
|
||||||
|
)
|
||||||
|
|
||||||
|
columns, data = execute(filters=filters)
|
||||||
|
expected_entry = {
|
||||||
|
"parent_invoice": sinv.name,
|
||||||
|
"currency": "INR",
|
||||||
|
"sales_invoice": self.item,
|
||||||
|
"customer": self.customer,
|
||||||
|
"posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()),
|
||||||
|
"item_code": self.item,
|
||||||
|
"item_name": self.item,
|
||||||
|
"warehouse": "Stores - _GP",
|
||||||
|
"qty": 0.0,
|
||||||
|
"avg._selling_rate": 0.0,
|
||||||
|
"valuation_rate": 0.0,
|
||||||
|
"selling_amount": -100.0,
|
||||||
|
"buying_amount": 0.0,
|
||||||
|
"gross_profit": -100.0,
|
||||||
|
"gross_profit_%": 100.0,
|
||||||
|
}
|
||||||
|
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
||||||
|
# Both items of Invoice should have '0' qty
|
||||||
|
self.assertEqual(len(gp_entry), 2)
|
||||||
|
self.assertDictContainsSubset(expected_entry, gp_entry[0])
|
||||||
|
self.assertDictContainsSubset(expected_entry, gp_entry[1])
|
||||||
|
|
||||||
|
def test_standalone_cr_notes(self):
|
||||||
|
"""
|
||||||
|
Standalone cr notes will be reported as usual
|
||||||
|
"""
|
||||||
|
# Make Cr Note
|
||||||
|
sinv = self.create_sales_invoice(
|
||||||
|
qty=-1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
|
||||||
|
)
|
||||||
|
sinv.is_return = 1
|
||||||
|
sinv = sinv.save().submit()
|
||||||
|
|
||||||
|
filters = frappe._dict(
|
||||||
|
company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice"
|
||||||
|
)
|
||||||
|
|
||||||
|
columns, data = execute(filters=filters)
|
||||||
|
expected_entry = {
|
||||||
|
"parent_invoice": sinv.name,
|
||||||
|
"currency": "INR",
|
||||||
|
"sales_invoice": self.item,
|
||||||
|
"customer": self.customer,
|
||||||
|
"posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()),
|
||||||
|
"item_code": self.item,
|
||||||
|
"item_name": self.item,
|
||||||
|
"warehouse": "Stores - _GP",
|
||||||
|
"qty": -1.0,
|
||||||
|
"avg._selling_rate": 100.0,
|
||||||
|
"valuation_rate": 0.0,
|
||||||
|
"selling_amount": -100.0,
|
||||||
|
"buying_amount": 0.0,
|
||||||
|
"gross_profit": -100.0,
|
||||||
|
"gross_profit_%": 100.0,
|
||||||
|
}
|
||||||
|
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
||||||
|
self.assertDictContainsSubset(expected_entry, gp_entry[0])
|
||||||
|
|||||||
Reference in New Issue
Block a user