Merge branch 'frappe:version-15-hotfix' into version-15-hotfix

This commit is contained in:
Abhishek Chougule
2024-08-05 17:37:11 +05:30
committed by GitHub
5 changed files with 140 additions and 10 deletions

View File

@@ -209,6 +209,11 @@ frappe.query_reports["General Ledger"] = {
label: __("Ignore Exchange Rate Revaluation Journals"), label: __("Ignore Exchange Rate Revaluation Journals"),
fieldtype: "Check", fieldtype: "Check",
}, },
{
fieldname: "ignore_cr_dr_notes",
label: __("Ignore System Generated Credit / Debit Notes"),
fieldtype: "Check",
},
], ],
}; };

View File

@@ -230,12 +230,28 @@ def get_conditions(filters):
"company": filters.get("company"), "company": filters.get("company"),
"docstatus": 1, "docstatus": 1,
"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]), "voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
"posting_date": ["between", [filters.get("from_date"), filters.get("to_date")]],
}, },
as_list=True, as_list=True,
) )
if err_journals: if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]}) filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if filters.get("ignore_cr_dr_notes"):
system_generated_cr_dr_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": filters.get("company"),
"docstatus": 1,
"voucher_type": ("in", ["Credit Note", "Debit Note"]),
"is_system_generated": 1,
"posting_date": ["between", [filters.get("from_date"), filters.get("to_date")]],
},
as_list=True,
)
if system_generated_cr_dr_journals:
filters.update({"voucher_no_not_in": [x[0] for x in system_generated_cr_dr_journals]})
if filters.get("voucher_no_not_in"): if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s") conditions.append("voucher_no not in %(voucher_no_not_in)s")

View File

@@ -2,13 +2,32 @@
# MIT License. See license.txt # MIT License. See license.txt
import frappe import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt, today from frappe.utils import flt, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.general_ledger.general_ledger import execute from erpnext.accounts.report.general_ledger.general_ledger import execute
from erpnext.controllers.sales_and_purchase_return import make_return_doc
class TestGeneralLedger(FrappeTestCase): class TestGeneralLedger(FrappeTestCase):
def setUp(self):
self.company = "_Test Company"
self.clear_old_entries()
def clear_old_entries(self):
doctype_list = [
"GL Entry",
"Payment Ledger Entry",
"Sales Invoice",
"Purchase Invoice",
"Payment Entry",
"Journal Entry",
]
for doctype in doctype_list:
qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
def test_foreign_account_balance_after_exchange_rate_revaluation(self): def test_foreign_account_balance_after_exchange_rate_revaluation(self):
""" """
Checks the correctness of balance after exchange rate revaluation Checks the correctness of balance after exchange rate revaluation
@@ -248,3 +267,68 @@ class TestGeneralLedger(FrappeTestCase):
) )
) )
self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data])) self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data]))
def test_ignore_cr_dr_notes_filter(self):
si = create_sales_invoice()
cr_note = make_return_doc(si.doctype, si.name)
cr_note.submit()
pr = frappe.get_doc("Payment Reconciliation")
pr.company = si.company
pr.party_type = "Customer"
pr.party = si.customer
pr.receivable_payable_account = si.debit_to
pr.get_unreconciled_entries()
invoices = [invoice.as_dict() for invoice in pr.invoices if invoice.invoice_number == si.name]
payments = [payment.as_dict() for payment in pr.payments if payment.reference_name == cr_note.name]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
system_generated_journal = frappe.db.get_all(
"Journal Entry",
filters={
"docstatus": 1,
"reference_type": si.doctype,
"reference_name": si.name,
"voucher_type": "Credit Note",
"is_system_generated": True,
},
fields=["name"],
)
self.assertEqual(len(system_generated_journal), 1)
expected = set([si.name, cr_note.name, system_generated_journal[0].name])
# Without ignore_cr_dr_notes
columns, data = execute(
frappe._dict(
{
"company": si.company,
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"ignore_cr_dr_notes": False,
}
)
)
actual = set([x.voucher_no for x in data if x.voucher_no])
self.assertEqual(expected, actual)
# Without ignore_cr_dr_notes
expected = set([si.name, cr_note.name])
columns, data = execute(
frappe._dict(
{
"company": si.company,
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"ignore_cr_dr_notes": True,
}
)
)
actual = set([x.voucher_no for x in data if x.voucher_no])
self.assertEqual(expected, actual)

View File

@@ -8,7 +8,7 @@ from itertools import groupby
import frappe import frappe
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from frappe import _ from frappe import _
from frappe.utils import cint, flt from frappe.utils import cint, flt, getdate
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
@@ -21,7 +21,15 @@ class SalesPipelineAnalytics:
def __init__(self, filters=None): def __init__(self, filters=None):
self.filters = frappe._dict(filters or {}) self.filters = frappe._dict(filters or {})
def validate_filters(self):
if not self.filters.from_date:
frappe.throw(_("From Date is mandatory"))
if not self.filters.to_date:
frappe.throw(_("To Date is mandatory"))
def run(self): def run(self):
self.validate_filters()
self.get_columns() self.get_columns()
self.get_data() self.get_data()
self.get_chart_data() self.get_chart_data()
@@ -185,7 +193,7 @@ class SalesPipelineAnalytics:
count_or_amount = info.get(based_on) count_or_amount = info.get(based_on)
if self.filters.get("pipeline_by") == "Owner": if self.filters.get("pipeline_by") == "Owner":
if value == "Not Assigned" or value == "[]" or value is None: if value == "Not Assigned" or value == "[]" or value is None or not value:
assigned_to = ["Not Assigned"] assigned_to = ["Not Assigned"]
else: else:
assigned_to = json.loads(value) assigned_to = json.loads(value)
@@ -227,10 +235,9 @@ class SalesPipelineAnalytics:
def get_month_list(self): def get_month_list(self):
month_list = [] month_list = []
current_date = date.today() current_date = getdate(self.filters.get("from_date"))
month_number = date.today().month
for _month in range(month_number, 13): while current_date < getdate(self.filters.get("to_date")):
month_list.append(current_date.strftime("%B")) month_list.append(current_date.strftime("%B"))
current_date = current_date + relativedelta(months=1) current_date = current_date + relativedelta(months=1)

View File

@@ -1,19 +1,21 @@
import unittest import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.crm.report.sales_pipeline_analytics.sales_pipeline_analytics import execute from erpnext.crm.report.sales_pipeline_analytics.sales_pipeline_analytics import execute
class TestSalesPipelineAnalytics(unittest.TestCase): class TestSalesPipelineAnalytics(FrappeTestCase):
@classmethod def setUp(self):
def setUpClass(self):
frappe.db.delete("Opportunity") frappe.db.delete("Opportunity")
create_company() create_company()
create_customer() create_customer()
create_opportunity() create_opportunity()
def test_sales_pipeline_analytics(self): def test_sales_pipeline_analytics(self):
self.from_date = "2021-01-01"
self.to_date = "2021-12-31"
self.check_for_monthly_and_number() self.check_for_monthly_and_number()
self.check_for_monthly_and_amount() self.check_for_monthly_and_amount()
self.check_for_quarterly_and_number() self.check_for_quarterly_and_number()
@@ -28,6 +30,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -43,6 +47,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -59,6 +65,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -74,6 +82,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -90,6 +100,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -105,6 +117,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -121,6 +135,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -136,6 +152,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open", "status": "Open",
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)
@@ -153,8 +171,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"opportunity_type": "Sales", "opportunity_type": "Sales",
"company": "Best Test", "company": "Best Test",
"opportunity_source": "Cold Calling", "opportunity_source": "Cold Calling",
"from_date": "2021-08-01", "from_date": self.from_date,
"to_date": "2021-08-31", "to_date": self.to_date,
} }
report = execute(filters) report = execute(filters)