mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-25 06:02:09 +01:00
perf: refactored customer ledger summary for performance
(cherry picked from commit e84e49345a)
# Conflicts:
# erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
This commit is contained in:
@@ -4,6 +4,11 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, qb, scrub
|
from frappe import _, qb, scrub
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
from frappe.query_builder import Criterion, Tuple
|
||||||
|
from frappe.query_builder.functions import IfNull
|
||||||
|
>>>>>>> e84e49345a (perf: refactored customer ledger summary for performance)
|
||||||
from frappe.utils import getdate, nowdate
|
from frappe.utils import getdate, nowdate
|
||||||
|
|
||||||
|
|
||||||
@@ -23,49 +28,82 @@ class PartyLedgerSummaryReport:
|
|||||||
self.filters.party_type = args.get("party_type")
|
self.filters.party_type = args.get("party_type")
|
||||||
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
||||||
|
|
||||||
|
self.get_paty_details()
|
||||||
|
|
||||||
|
if not self.parties:
|
||||||
|
return [], []
|
||||||
|
|
||||||
self.get_gl_entries()
|
self.get_gl_entries()
|
||||||
self.get_additional_columns()
|
|
||||||
self.get_return_invoices()
|
self.get_return_invoices()
|
||||||
self.get_party_adjustment_amounts()
|
self.get_party_adjustment_amounts()
|
||||||
|
|
||||||
columns = self.get_columns()
|
columns = self.get_columns()
|
||||||
data = self.get_data()
|
data = self.get_data()
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_additional_columns(self):
|
def get_additional_fields(self):
|
||||||
|
additional_fields = []
|
||||||
|
|
||||||
|
if self.filters.party_type == "Customer":
|
||||||
|
additional_fields = ["customer_name", "territory", "customer_group", "default_sales_partner"]
|
||||||
|
else:
|
||||||
|
additional_fields = ["supplier_name", "supplier_group"]
|
||||||
|
|
||||||
|
return additional_fields
|
||||||
|
|
||||||
|
def prepare_party_conditions(self, doctype):
|
||||||
|
conditions = []
|
||||||
|
group_field = "customer_group" if self.filters.party_type == "Customer" else "supplier_group"
|
||||||
|
|
||||||
|
if self.filters.party:
|
||||||
|
conditions.append(doctype.name == self.filters.party)
|
||||||
|
|
||||||
|
if self.filters.territory:
|
||||||
|
conditions.append(doctype.territory == self.filters.territory)
|
||||||
|
|
||||||
|
if self.filters.get(group_field):
|
||||||
|
conditions.append(doctype.get(group_field) == self.filters.get(group_field))
|
||||||
|
|
||||||
|
if self.filters.payment_terms_template:
|
||||||
|
conditions.append(doctype.payment_terms == self.filters.payment_terms_template)
|
||||||
|
|
||||||
|
if self.filters.sales_partner:
|
||||||
|
conditions.append(doctype.default_sales_partner == self.filters.sales_partner)
|
||||||
|
|
||||||
|
if self.filters.sales_person:
|
||||||
|
sales_team = qb.DocType("Sales Team")
|
||||||
|
conditions.append(
|
||||||
|
(doctype.name).isin(
|
||||||
|
qb.from_(sales_team)
|
||||||
|
.select(sales_team.parent)
|
||||||
|
.where(sales_team.sales_person == self.filters.sales_person)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return conditions
|
||||||
|
|
||||||
|
def get_paty_details(self):
|
||||||
"""
|
"""
|
||||||
Additional Columns for 'User Permission' based access control
|
Additional Columns for 'User Permission' based access control
|
||||||
"""
|
"""
|
||||||
|
self.parties = []
|
||||||
|
self.party_details = frappe._dict()
|
||||||
|
party_type = self.filters.party_type
|
||||||
|
additional_fields = self.get_additional_fields()
|
||||||
|
|
||||||
if self.filters.party_type == "Customer":
|
doctype = qb.DocType(party_type)
|
||||||
self.territories = frappe._dict({})
|
conditions = self.prepare_party_conditions(doctype)
|
||||||
self.customer_group = frappe._dict({})
|
party_details = (
|
||||||
|
qb.from_(doctype)
|
||||||
|
.select(doctype.name.as_("party"), *additional_fields)
|
||||||
|
.where(Criterion.all(conditions))
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
customer = qb.DocType("Customer")
|
for row in party_details:
|
||||||
result = (
|
self.parties.append(row.party)
|
||||||
frappe.qb.from_(customer)
|
self.party_details[row.party] = row
|
||||||
.select(
|
|
||||||
customer.name, customer.territory, customer.customer_group, customer.default_sales_partner
|
|
||||||
)
|
|
||||||
.where(customer.disabled == 0)
|
|
||||||
.run(as_dict=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
for x in result:
|
|
||||||
self.territories[x.name] = x.territory
|
|
||||||
self.customer_group[x.name] = x.customer_group
|
|
||||||
else:
|
|
||||||
self.supplier_group = frappe._dict({})
|
|
||||||
supplier = qb.DocType("Supplier")
|
|
||||||
result = (
|
|
||||||
frappe.qb.from_(supplier)
|
|
||||||
.select(supplier.name, supplier.supplier_group)
|
|
||||||
.where(supplier.disabled == 0)
|
|
||||||
.run(as_dict=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
for x in result:
|
|
||||||
self.supplier_group[x.name] = x.supplier_group
|
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
columns = [
|
columns = [
|
||||||
@@ -188,12 +226,13 @@ class PartyLedgerSummaryReport:
|
|||||||
|
|
||||||
self.party_data = frappe._dict({})
|
self.party_data = frappe._dict({})
|
||||||
for gle in self.gl_entries:
|
for gle in self.gl_entries:
|
||||||
|
party_details = self.party_details.get(gle.party)
|
||||||
self.party_data.setdefault(
|
self.party_data.setdefault(
|
||||||
gle.party,
|
gle.party,
|
||||||
frappe._dict(
|
frappe._dict(
|
||||||
{
|
{
|
||||||
"party": gle.party,
|
**party_details,
|
||||||
"party_name": gle.party_name,
|
"party_name": gle.party,
|
||||||
"opening_balance": 0,
|
"opening_balance": 0,
|
||||||
"invoiced_amount": 0,
|
"invoiced_amount": 0,
|
||||||
"paid_amount": 0,
|
"paid_amount": 0,
|
||||||
@@ -204,12 +243,6 @@ class PartyLedgerSummaryReport:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.filters.party_type == "Customer":
|
|
||||||
self.party_data[gle.party].update({"territory": self.territories.get(gle.party)})
|
|
||||||
self.party_data[gle.party].update({"customer_group": self.customer_group.get(gle.party)})
|
|
||||||
else:
|
|
||||||
self.party_data[gle.party].update({"supplier_group": self.supplier_group.get(gle.party)})
|
|
||||||
|
|
||||||
amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
|
amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
|
||||||
self.party_data[gle.party].closing_balance += amount
|
self.party_data[gle.party].closing_balance += amount
|
||||||
|
|
||||||
@@ -246,6 +279,7 @@ class PartyLedgerSummaryReport:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
def get_gl_entries(self):
|
def get_gl_entries(self):
|
||||||
|
<<<<<<< HEAD
|
||||||
conditions = self.prepare_conditions()
|
conditions = self.prepare_conditions()
|
||||||
join = join_field = ""
|
join = join_field = ""
|
||||||
if self.filters.party_type == "Customer":
|
if self.filters.party_type == "Customer":
|
||||||
@@ -274,12 +308,45 @@ class PartyLedgerSummaryReport:
|
|||||||
def prepare_conditions(self):
|
def prepare_conditions(self):
|
||||||
conditions = [""]
|
conditions = [""]
|
||||||
|
|
||||||
|
=======
|
||||||
|
gle = qb.DocType("GL Entry")
|
||||||
|
query = (
|
||||||
|
qb.from_(gle)
|
||||||
|
.select(
|
||||||
|
gle.posting_date,
|
||||||
|
gle.party,
|
||||||
|
gle.voucher_type,
|
||||||
|
gle.voucher_no,
|
||||||
|
gle.against_voucher_type,
|
||||||
|
gle.against_voucher,
|
||||||
|
gle.debit,
|
||||||
|
gle.credit,
|
||||||
|
gle.is_opening,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(gle.docstatus < 2)
|
||||||
|
& (gle.is_cancelled == 0)
|
||||||
|
& (gle.party_type == self.filters.party_type)
|
||||||
|
& (IfNull(gle.party, "") != "")
|
||||||
|
& (gle.posting_date <= self.filters.to_date)
|
||||||
|
& (gle.party.isin(self.parties))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
query = self.prepare_conditions(query)
|
||||||
|
|
||||||
|
self.gl_entries = query.run(as_dict=True)
|
||||||
|
|
||||||
|
def prepare_conditions(self, query):
|
||||||
|
gle = qb.DocType("GL Entry")
|
||||||
|
>>>>>>> e84e49345a (perf: refactored customer ledger summary for performance)
|
||||||
if self.filters.company:
|
if self.filters.company:
|
||||||
conditions.append("gle.company=%(company)s")
|
conditions.append("gle.company=%(company)s")
|
||||||
|
|
||||||
if self.filters.finance_book:
|
if self.filters.finance_book:
|
||||||
conditions.append("ifnull(finance_book,'') in (%(finance_book)s, '')")
|
conditions.append("ifnull(finance_book,'') in (%(finance_book)s, '')")
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
if self.filters.get("party"):
|
if self.filters.get("party"):
|
||||||
conditions.append("party=%(party)s")
|
conditions.append("party=%(party)s")
|
||||||
|
|
||||||
@@ -335,9 +402,37 @@ class PartyLedgerSummaryReport:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return " and ".join(conditions)
|
return " and ".join(conditions)
|
||||||
|
=======
|
||||||
|
if self.filters.cost_center:
|
||||||
|
self.filters.cost_center = get_cost_centers_with_children(self.filters.cost_center)
|
||||||
|
query = query.where((gle.cost_center).isin(self.filters.cost_center))
|
||||||
|
|
||||||
|
if self.filters.project:
|
||||||
|
query = query.where((gle.project).isin(self.filters.project))
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions(as_list=False)
|
||||||
|
|
||||||
|
if accounting_dimensions:
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
if self.filters.get(dimension.fieldname):
|
||||||
|
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
|
||||||
|
self.filters[dimension.fieldname] = get_dimension_with_children(
|
||||||
|
dimension.document_type, self.filters.get(dimension.fieldname)
|
||||||
|
)
|
||||||
|
query = query.where(
|
||||||
|
(gle[dimension.fieldname]).isin(self.filters.get(dimension.fieldname))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
query = query.where(
|
||||||
|
(gle[dimension.fieldname]).isin(self.filters.get(dimension.fieldname))
|
||||||
|
)
|
||||||
|
|
||||||
|
return query
|
||||||
|
>>>>>>> e84e49345a (perf: refactored customer ledger summary for performance)
|
||||||
|
|
||||||
def get_return_invoices(self):
|
def get_return_invoices(self):
|
||||||
doctype = "Sales Invoice" if self.filters.party_type == "Customer" else "Purchase Invoice"
|
doctype = "Sales Invoice" if self.filters.party_type == "Customer" else "Purchase Invoice"
|
||||||
|
name_field = "customer" if self.filters.party_type == "Customer" else "supplier"
|
||||||
self.return_invoices = [
|
self.return_invoices = [
|
||||||
d.name
|
d.name
|
||||||
for d in frappe.get_all(
|
for d in frappe.get_all(
|
||||||
@@ -346,6 +441,7 @@ class PartyLedgerSummaryReport:
|
|||||||
"is_return": 1,
|
"is_return": 1,
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"posting_date": ["between", [self.filters.from_date, self.filters.to_date]],
|
"posting_date": ["between", [self.filters.from_date, self.filters.to_date]],
|
||||||
|
name_field: ["in", self.parties],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -353,13 +449,18 @@ class PartyLedgerSummaryReport:
|
|||||||
def get_party_adjustment_amounts(self):
|
def get_party_adjustment_amounts(self):
|
||||||
conditions = self.prepare_conditions()
|
conditions = self.prepare_conditions()
|
||||||
account_type = "Expense Account" if self.filters.party_type == "Customer" else "Income Account"
|
account_type = "Expense Account" if self.filters.party_type == "Customer" else "Income Account"
|
||||||
|
<<<<<<< HEAD
|
||||||
income_or_expense_accounts = frappe.db.get_all(
|
income_or_expense_accounts = frappe.db.get_all(
|
||||||
"Account", filters={"account_type": account_type, "company": self.filters.company}, pluck="name"
|
"Account", filters={"account_type": account_type, "company": self.filters.company}, pluck="name"
|
||||||
)
|
)
|
||||||
|
=======
|
||||||
|
|
||||||
|
>>>>>>> e84e49345a (perf: refactored customer ledger summary for performance)
|
||||||
invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
|
invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
|
||||||
reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
|
reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
|
||||||
round_off_account = frappe.get_cached_value("Company", self.filters.company, "round_off_account")
|
round_off_account = frappe.get_cached_value("Company", self.filters.company, "round_off_account")
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
gl = qb.DocType("GL Entry")
|
gl = qb.DocType("GL Entry")
|
||||||
if not income_or_expense_accounts:
|
if not income_or_expense_accounts:
|
||||||
# prevent empty 'in' condition
|
# prevent empty 'in' condition
|
||||||
@@ -370,9 +471,19 @@ class PartyLedgerSummaryReport:
|
|||||||
income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts]
|
income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts]
|
||||||
|
|
||||||
accounts_query = (
|
accounts_query = (
|
||||||
|
=======
|
||||||
|
current_period_vouchers = set()
|
||||||
|
for gle in self.gl_entries:
|
||||||
|
if gle.posting_date >= self.filters.from_date and gle.posting_date <= self.filters.to_date:
|
||||||
|
current_period_vouchers.add((gle.voucher_type, gle.voucher_no))
|
||||||
|
|
||||||
|
gl = qb.DocType("GL Entry")
|
||||||
|
query = (
|
||||||
|
>>>>>>> e84e49345a (perf: refactored customer ledger summary for performance)
|
||||||
qb.from_(gl)
|
qb.from_(gl)
|
||||||
.select(gl.voucher_type, gl.voucher_no)
|
.select(gl.voucher_type, gl.voucher_no)
|
||||||
.where(
|
.where(
|
||||||
|
<<<<<<< HEAD
|
||||||
(gl.account.isin(income_or_expense_accounts))
|
(gl.account.isin(income_or_expense_accounts))
|
||||||
& (gl.posting_date.gte(self.filters.from_date))
|
& (gl.posting_date.gte(self.filters.from_date))
|
||||||
& (gl.posting_date.lte(self.filters.to_date))
|
& (gl.posting_date.lte(self.filters.to_date))
|
||||||
@@ -398,6 +509,17 @@ class PartyLedgerSummaryReport:
|
|||||||
self.filters,
|
self.filters,
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
=======
|
||||||
|
(gl.docstatus < 2)
|
||||||
|
& (gl.is_cancelled == 0)
|
||||||
|
& (gl.posting_date.gte(self.filters.from_date))
|
||||||
|
& (gl.posting_date.lte(self.filters.to_date))
|
||||||
|
& (Tuple((gl.voucher_type, gl.voucher_no)).isin(current_period_vouchers))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
query = self.prepare_conditions(query)
|
||||||
|
gl_entries = query.run(as_dict=True)
|
||||||
|
>>>>>>> e84e49345a (perf: refactored customer ledger summary for performance)
|
||||||
|
|
||||||
self.party_adjustment_details = {}
|
self.party_adjustment_details = {}
|
||||||
self.party_adjustment_accounts = set()
|
self.party_adjustment_accounts = set()
|
||||||
|
|||||||
Reference in New Issue
Block a user