mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-17 00:25:01 +00:00
perf: performance optimizations for accounting reports by refactoring account closing balance and period closing voucher (#43798)
* fix: Gl Entry form cleanup * fix: Added indexes in gl entry table * perf: Refactored period closing voucher to handle large volume of gle * fix: fixes as per new period start and end date fields in PCV * perf: performance optimization for accounting reports * perf: performance optimizations for account closing balance patch * fix: test cases * fix: lenter issues - direct use of sql query * fix: test cases * fix: test cases * fix: test cases * fix: wrong fieldname * fix: test cases
This commit is contained in:
@@ -113,9 +113,9 @@ def get_previous_closing_entries(company, closing_date, accounting_dimensions):
|
||||
entries = []
|
||||
last_period_closing_voucher = frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)},
|
||||
filters={"docstatus": 1, "company": company, "period_end_date": ("<", closing_date)},
|
||||
fields=["name"],
|
||||
order_by="posting_date desc",
|
||||
order_by="period_end_date desc",
|
||||
limit=1,
|
||||
)
|
||||
|
||||
|
||||
@@ -101,6 +101,8 @@ def validate_accounting_period_on_doc_save(doc, method=None):
|
||||
date = doc.available_for_use_date
|
||||
elif doc.doctype == "Asset Repair":
|
||||
date = doc.completion_date
|
||||
elif doc.doctype == "Period Closing Voucher":
|
||||
date = doc.period_end_date
|
||||
else:
|
||||
date = doc.posting_date
|
||||
|
||||
|
||||
@@ -6,38 +6,50 @@
|
||||
"document_type": "Document",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"dates_section",
|
||||
"posting_date",
|
||||
"transaction_date",
|
||||
"column_break_avko",
|
||||
"fiscal_year",
|
||||
"due_date",
|
||||
"account_details_section",
|
||||
"account",
|
||||
"account_currency",
|
||||
"column_break_ifvf",
|
||||
"against",
|
||||
"party_type",
|
||||
"party",
|
||||
"cost_center",
|
||||
"debit",
|
||||
"credit",
|
||||
"account_currency",
|
||||
"debit_in_account_currency",
|
||||
"credit_in_account_currency",
|
||||
"against",
|
||||
"transaction_details_section",
|
||||
"voucher_type",
|
||||
"voucher_no",
|
||||
"voucher_subtype",
|
||||
"transaction_currency",
|
||||
"column_break_dpsx",
|
||||
"against_voucher_type",
|
||||
"against_voucher",
|
||||
"voucher_type",
|
||||
"voucher_subtype",
|
||||
"voucher_no",
|
||||
"voucher_detail_no",
|
||||
"transaction_exchange_rate",
|
||||
"amounts_section",
|
||||
"debit_in_account_currency",
|
||||
"debit",
|
||||
"debit_in_transaction_currency",
|
||||
"column_break_bm1w",
|
||||
"credit_in_account_currency",
|
||||
"credit",
|
||||
"credit_in_transaction_currency",
|
||||
"dimensions_section",
|
||||
"cost_center",
|
||||
"column_break_lmnm",
|
||||
"project",
|
||||
"remarks",
|
||||
"more_info_section",
|
||||
"finance_book",
|
||||
"company",
|
||||
"is_opening",
|
||||
"is_advance",
|
||||
"fiscal_year",
|
||||
"company",
|
||||
"finance_book",
|
||||
"column_break_8abq",
|
||||
"to_rename",
|
||||
"due_date",
|
||||
"is_cancelled",
|
||||
"transaction_currency",
|
||||
"debit_in_transaction_currency",
|
||||
"credit_in_transaction_currency",
|
||||
"transaction_exchange_rate"
|
||||
"remarks"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -285,13 +297,67 @@
|
||||
"fieldname": "voucher_subtype",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Voucher Subtype"
|
||||
},
|
||||
{
|
||||
"fieldname": "dates_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Dates"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_avko",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "account_details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Account Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_ifvf",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "transaction_details_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Transaction Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "amounts_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Amounts"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_dpsx",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "more_info_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "More Info"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_bm1w",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "dimensions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Dimensions"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_lmnm",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_8abq",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-list",
|
||||
"idx": 1,
|
||||
"in_create": 1,
|
||||
"links": [],
|
||||
"modified": "2024-07-02 14:31:51.496466",
|
||||
"modified": "2024-08-22 13:03:39.997475",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GL Entry",
|
||||
|
||||
@@ -430,8 +430,9 @@ def update_against_account(voucher_type, voucher_no):
|
||||
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
|
||||
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
|
||||
frappe.db.add_index("GL Entry", ["posting_date", "company"])
|
||||
frappe.db.add_index("GL Entry", ["party_type", "party"])
|
||||
|
||||
|
||||
def rename_gle_sle_docs():
|
||||
|
||||
@@ -1986,13 +1986,15 @@ def make_period_closing_voucher(company, cost_center, posting_date=None, submit=
|
||||
parent_account=parent_account,
|
||||
doctype="Account",
|
||||
)
|
||||
fy = get_fiscal_year(posting_date, company=company)
|
||||
pcv = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Period Closing Voucher",
|
||||
"transaction_date": posting_date or today(),
|
||||
"posting_date": posting_date or today(),
|
||||
"period_start_date": fy[1],
|
||||
"period_end_date": fy[2],
|
||||
"company": company,
|
||||
"fiscal_year": get_fiscal_year(posting_date or today(), company=company)[0],
|
||||
"fiscal_year": fy[0],
|
||||
"cost_center": cost_center,
|
||||
"closing_account_head": surplus_account,
|
||||
"remarks": "test",
|
||||
|
||||
@@ -19,6 +19,24 @@ frappe.ui.form.on("Period Closing Voucher", {
|
||||
});
|
||||
},
|
||||
|
||||
fiscal_year: function (frm) {
|
||||
if (frm.doc.fiscal_year) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.period_closing_voucher.period_closing_voucher.get_period_start_end_date",
|
||||
args: {
|
||||
fiscal_year: frm.doc.fiscal_year,
|
||||
company: frm.doc.company,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message) {
|
||||
frm.set_value("period_start_date", r.message[0]);
|
||||
frm.set_value("period_end_date", r.message[1]);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
if (frm.doc.docstatus > 0) {
|
||||
frm.add_custom_button(
|
||||
|
||||
@@ -6,39 +6,32 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"transaction_date",
|
||||
"posting_date",
|
||||
"fiscal_year",
|
||||
"year_start_date",
|
||||
"amended_from",
|
||||
"company",
|
||||
"fiscal_year",
|
||||
"period_start_date",
|
||||
"period_end_date",
|
||||
"amended_from",
|
||||
"column_break1",
|
||||
"closing_account_head",
|
||||
"remarks",
|
||||
"gle_processing_status",
|
||||
"remarks",
|
||||
"error_message"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "transaction_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Transaction Date",
|
||||
"oldfieldname": "transaction_date",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting Date",
|
||||
"oldfieldname": "posting_date",
|
||||
"oldfieldtype": "Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Closing Fiscal Year",
|
||||
"label": "Fiscal Year",
|
||||
"oldfieldname": "fiscal_year",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Fiscal Year",
|
||||
@@ -103,16 +96,25 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "year_start_date",
|
||||
"fieldname": "period_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Year Start Date"
|
||||
"label": "Period End Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "period_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Period Start Date",
|
||||
"oldfieldname": "posting_date",
|
||||
"oldfieldtype": "Date",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-09-11 20:19:11.810533",
|
||||
"modified": "2024-09-15 17:22:45.291628",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Period Closing Voucher",
|
||||
@@ -148,7 +150,7 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "posting_date, fiscal_year",
|
||||
"search_fields": "fiscal_year, period_start_date, period_end_date",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import add_days, flt
|
||||
from frappe.utils import add_days, flt, formatdate, getdate
|
||||
|
||||
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
|
||||
make_closing_entries,
|
||||
)
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
)
|
||||
from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year
|
||||
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
|
||||
|
||||
@@ -29,36 +34,386 @@ class PeriodClosingVoucher(AccountsController):
|
||||
error_message: DF.Text | None
|
||||
fiscal_year: DF.Link
|
||||
gle_processing_status: DF.Literal["In Progress", "Completed", "Failed"]
|
||||
posting_date: DF.Date
|
||||
period_end_date: DF.Date
|
||||
period_start_date: DF.Date
|
||||
remarks: DF.SmallText
|
||||
transaction_date: DF.Date | None
|
||||
year_start_date: DF.Date | None
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
self.validate_account_head()
|
||||
self.validate_posting_date()
|
||||
self.validate_start_and_end_date()
|
||||
self.check_if_previous_year_closed()
|
||||
self.block_if_future_closing_voucher_exists()
|
||||
self.check_closing_account_type()
|
||||
self.check_closing_account_currency()
|
||||
|
||||
def validate_start_and_end_date(self):
|
||||
self.fy_start_date, self.fy_end_date = frappe.db.get_value(
|
||||
"Fiscal Year", self.fiscal_year, ["year_start_date", "year_end_date"]
|
||||
)
|
||||
|
||||
prev_closed_period_end_date = get_previous_closed_period_in_current_year(
|
||||
self.fiscal_year, self.company
|
||||
)
|
||||
valid_start_date = (
|
||||
add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else self.fy_start_date
|
||||
)
|
||||
|
||||
if getdate(self.period_start_date) != getdate(valid_start_date):
|
||||
frappe.throw(_("Period Start Date must be {0}").format(formatdate(valid_start_date)))
|
||||
|
||||
if getdate(self.period_start_date) > getdate(self.period_end_date):
|
||||
frappe.throw(_("Period Start Date cannot be greater than Period End Date"))
|
||||
|
||||
if getdate(self.period_end_date) > getdate(self.fy_end_date):
|
||||
frappe.throw(_("Period End Date cannot be greater than Fiscal Year End Date"))
|
||||
|
||||
def check_if_previous_year_closed(self):
|
||||
last_year_closing = add_days(self.fy_start_date, -1)
|
||||
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
|
||||
if not previous_fiscal_year:
|
||||
return
|
||||
|
||||
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
|
||||
gle_exists_in_previous_year = frappe.db.exists(
|
||||
"GL Entry",
|
||||
{
|
||||
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
|
||||
"company": self.company,
|
||||
"is_cancelled": 0,
|
||||
},
|
||||
)
|
||||
if not gle_exists_in_previous_year:
|
||||
return
|
||||
|
||||
previous_fiscal_year_closed = frappe.db.exists(
|
||||
"Period Closing Voucher",
|
||||
{
|
||||
"period_end_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
|
||||
"docstatus": 1,
|
||||
"company": self.company,
|
||||
},
|
||||
)
|
||||
if not previous_fiscal_year_closed:
|
||||
frappe.throw(_("Previous Year is not closed, please close it first"))
|
||||
|
||||
def block_if_future_closing_voucher_exists(self):
|
||||
future_closing_voucher = self.get_future_closing_voucher()
|
||||
if future_closing_voucher and future_closing_voucher[0][0]:
|
||||
action = "cancel" if self.docstatus == 2 else "create"
|
||||
frappe.throw(
|
||||
_(
|
||||
"You cannot {0} this document because another Period Closing Entry {1} exists after {2}"
|
||||
).format(action, future_closing_voucher[0][0], self.period_end_date)
|
||||
)
|
||||
|
||||
def get_future_closing_voucher(self):
|
||||
return frappe.db.get_value(
|
||||
"Period Closing Voucher",
|
||||
{"period_end_date": (">", self.period_end_date), "docstatus": 1, "company": self.company},
|
||||
"name",
|
||||
)
|
||||
|
||||
def check_closing_account_type(self):
|
||||
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
|
||||
|
||||
if closing_account_type not in ["Liability", "Equity"]:
|
||||
frappe.throw(
|
||||
_("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head)
|
||||
)
|
||||
|
||||
def check_closing_account_currency(self):
|
||||
account_currency = get_account_currency(self.closing_account_head)
|
||||
company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
|
||||
if account_currency != company_currency:
|
||||
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
|
||||
|
||||
def on_submit(self):
|
||||
self.db_set("gle_processing_status", "In Progress")
|
||||
get_opening_entries = False
|
||||
|
||||
if not frappe.db.exists(
|
||||
"Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}
|
||||
):
|
||||
get_opening_entries = True
|
||||
|
||||
self.make_gl_entries(get_opening_entries=get_opening_entries)
|
||||
self.make_gl_entries()
|
||||
|
||||
def on_cancel(self):
|
||||
self.validate_future_closing_vouchers()
|
||||
self.db_set("gle_processing_status", "In Progress")
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||
gle_count = frappe.db.count(
|
||||
self.block_if_future_closing_voucher_exists()
|
||||
self.db_set("gle_processing_status", "In Progress")
|
||||
self.cancel_gl_entries()
|
||||
|
||||
def make_gl_entries(self):
|
||||
if self.get_gle_count_in_selected_period() > 5000:
|
||||
frappe.enqueue(
|
||||
process_gl_and_closing_entries,
|
||||
doc=self,
|
||||
timeout=1800,
|
||||
)
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"The GL Entries and closing balances will be processed in the background, it can take a few minutes."
|
||||
),
|
||||
alert=True,
|
||||
)
|
||||
else:
|
||||
process_gl_and_closing_entries(self)
|
||||
|
||||
def get_gle_count_in_selected_period(self):
|
||||
return frappe.db.count(
|
||||
"GL Entry",
|
||||
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
|
||||
{
|
||||
"posting_date": ["between", [self.period_start_date, self.period_end_date]],
|
||||
"company": self.company,
|
||||
"is_cancelled": 0,
|
||||
},
|
||||
)
|
||||
if gle_count > 5000:
|
||||
|
||||
def get_pcv_gl_entries(self):
|
||||
self.pl_accounts_reverse_gle = []
|
||||
self.closing_account_gle = []
|
||||
|
||||
pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss")
|
||||
for dimensions, account_balances in pl_account_balances.items():
|
||||
for acc, balances in account_balances.items():
|
||||
balance_in_company_currency = flt(balances.debit_in_account_currency) - flt(
|
||||
balances.credit_in_account_currency
|
||||
)
|
||||
if balance_in_company_currency and acc != "balances":
|
||||
self.pl_accounts_reverse_gle.append(
|
||||
self.get_gle_for_pl_account(acc, balances, dimensions)
|
||||
)
|
||||
|
||||
# closing liability account
|
||||
self.closing_account_gle.append(
|
||||
self.get_gle_for_closing_account(account_balances["balances"], dimensions)
|
||||
)
|
||||
|
||||
return self.pl_accounts_reverse_gle + self.closing_account_gle
|
||||
|
||||
def get_gle_for_pl_account(self, acc, balances, dimensions):
|
||||
balance_in_account_currency = flt(balances.debit_in_account_currency) - flt(
|
||||
balances.credit_in_account_currency
|
||||
)
|
||||
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
|
||||
gl_entry = frappe._dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"posting_date": self.period_end_date,
|
||||
"account": acc,
|
||||
"account_currency": balances.account_currency,
|
||||
"debit_in_account_currency": abs(balance_in_account_currency)
|
||||
if balance_in_account_currency < 0
|
||||
else 0,
|
||||
"debit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0,
|
||||
"credit_in_account_currency": abs(balance_in_account_currency)
|
||||
if balance_in_account_currency > 0
|
||||
else 0,
|
||||
"credit": abs(balance_in_company_currency) if balance_in_company_currency > 0 else 0,
|
||||
"is_period_closing_voucher_entry": 1,
|
||||
"voucher_type": "Period Closing Voucher",
|
||||
"voucher_no": self.name,
|
||||
"fiscal_year": self.fiscal_year,
|
||||
"remarks": self.remarks,
|
||||
"is_opening": "No",
|
||||
}
|
||||
)
|
||||
self.update_default_dimensions(gl_entry, dimensions)
|
||||
return gl_entry
|
||||
|
||||
def get_gle_for_closing_account(self, dimension_balance, dimensions):
|
||||
balance_in_account_currency = flt(dimension_balance.balance_in_account_currency)
|
||||
balance_in_company_currency = flt(dimension_balance.balance_in_company_currency)
|
||||
gl_entry = frappe._dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"posting_date": self.period_end_date,
|
||||
"account": self.closing_account_head,
|
||||
"account_currency": frappe.db.get_value(
|
||||
"Account", self.closing_account_head, "account_currency"
|
||||
),
|
||||
"debit_in_account_currency": balance_in_account_currency
|
||||
if balance_in_account_currency > 0
|
||||
else 0,
|
||||
"debit": balance_in_company_currency if balance_in_company_currency > 0 else 0,
|
||||
"credit_in_account_currency": abs(balance_in_account_currency)
|
||||
if balance_in_account_currency < 0
|
||||
else 0,
|
||||
"credit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0,
|
||||
"is_period_closing_voucher_entry": 1,
|
||||
"voucher_type": "Period Closing Voucher",
|
||||
"voucher_no": self.name,
|
||||
"fiscal_year": self.fiscal_year,
|
||||
"remarks": self.remarks,
|
||||
"is_opening": "No",
|
||||
}
|
||||
)
|
||||
self.update_default_dimensions(gl_entry, dimensions)
|
||||
return gl_entry
|
||||
|
||||
def update_default_dimensions(self, gl_entry, dimensions):
|
||||
for i, dimension in enumerate(self.accounting_dimension_fields):
|
||||
gl_entry[dimension] = dimensions[i]
|
||||
|
||||
def get_account_balances_based_on_dimensions(self, report_type):
|
||||
"""Get balance for dimension-wise pl accounts"""
|
||||
self.get_accounting_dimension_fields()
|
||||
acc_bal_dict = frappe._dict()
|
||||
gl_entries = []
|
||||
|
||||
with frappe.db.unbuffered_cursor():
|
||||
gl_entries = self.get_gl_entries_for_current_period(report_type, as_iterator=True)
|
||||
for gle in gl_entries:
|
||||
acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict)
|
||||
|
||||
if report_type == "Balance Sheet" and self.is_first_period_closing_voucher():
|
||||
opening_entries = self.get_gl_entries_for_current_period(report_type, only_opening_entries=True)
|
||||
for gle in opening_entries:
|
||||
acc_bal_dict = self.set_account_balance_dict(gle, acc_bal_dict)
|
||||
|
||||
return acc_bal_dict
|
||||
|
||||
def get_accounting_dimension_fields(self):
|
||||
default_dimensions = ["cost_center", "finance_book", "project"]
|
||||
self.accounting_dimension_fields = default_dimensions + get_accounting_dimensions()
|
||||
|
||||
def get_gl_entries_for_current_period(self, report_type, only_opening_entries=False, as_iterator=False):
|
||||
date_condition = ""
|
||||
if only_opening_entries:
|
||||
date_condition = "is_opening = 'Yes'"
|
||||
else:
|
||||
date_condition = f"posting_date BETWEEN '{self.period_start_date}' AND '{self.period_end_date}' and is_opening = 'No'"
|
||||
|
||||
# nosemgrep
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
name,
|
||||
posting_date,
|
||||
account,
|
||||
account_currency,
|
||||
debit_in_account_currency,
|
||||
credit_in_account_currency,
|
||||
debit,
|
||||
credit,
|
||||
{}
|
||||
FROM `tabGL Entry`
|
||||
WHERE
|
||||
{}
|
||||
AND company = %s
|
||||
AND voucher_type != 'Period Closing Voucher'
|
||||
AND EXISTS(SELECT name FROM `tabAccount` WHERE name = account AND report_type = %s)
|
||||
AND is_cancelled = 0
|
||||
""".format(
|
||||
", ".join(self.accounting_dimension_fields),
|
||||
date_condition,
|
||||
),
|
||||
(self.company, report_type),
|
||||
as_dict=1,
|
||||
as_iterator=as_iterator,
|
||||
)
|
||||
|
||||
def set_account_balance_dict(self, gle, acc_bal_dict):
|
||||
key = self.get_key(gle)
|
||||
|
||||
acc_bal_dict.setdefault(key, frappe._dict()).setdefault(
|
||||
gle.account,
|
||||
frappe._dict(
|
||||
{
|
||||
"debit_in_account_currency": 0,
|
||||
"credit_in_account_currency": 0,
|
||||
"debit": 0,
|
||||
"credit": 0,
|
||||
"account_currency": gle.account_currency,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
acc_bal_dict[key][gle.account].debit_in_account_currency += flt(gle.debit_in_account_currency)
|
||||
acc_bal_dict[key][gle.account].credit_in_account_currency += flt(gle.credit_in_account_currency)
|
||||
acc_bal_dict[key][gle.account].debit += flt(gle.debit)
|
||||
acc_bal_dict[key][gle.account].credit += flt(gle.credit)
|
||||
|
||||
# dimension-wise total balances
|
||||
acc_bal_dict[key].setdefault(
|
||||
"balances",
|
||||
frappe._dict(
|
||||
{
|
||||
"balance_in_account_currency": 0,
|
||||
"balance_in_company_currency": 0,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
balance_in_account_currency = flt(gle.debit_in_account_currency) - flt(gle.credit_in_account_currency)
|
||||
balance_in_company_currency = flt(gle.debit) - flt(gle.credit)
|
||||
|
||||
acc_bal_dict[key]["balances"].balance_in_account_currency += balance_in_account_currency
|
||||
acc_bal_dict[key]["balances"].balance_in_company_currency += balance_in_company_currency
|
||||
|
||||
return acc_bal_dict
|
||||
|
||||
def get_key(self, gle):
|
||||
return tuple([gle.get(dimension) for dimension in self.accounting_dimension_fields])
|
||||
|
||||
def get_account_closing_balances(self):
|
||||
pl_closing_entries = self.get_closing_entries_for_pl_accounts()
|
||||
bs_closing_entries = self.get_closing_entries_for_balance_sheet_accounts()
|
||||
closing_entries = pl_closing_entries + bs_closing_entries
|
||||
return closing_entries
|
||||
|
||||
def get_closing_entries_for_pl_accounts(self):
|
||||
closing_entries = copy.deepcopy(self.pl_accounts_reverse_gle)
|
||||
for d in self.pl_accounts_reverse_gle:
|
||||
# reverse debit and credit
|
||||
gle_copy = copy.deepcopy(d)
|
||||
gle_copy.debit = d.credit
|
||||
gle_copy.credit = d.debit
|
||||
gle_copy.debit_in_account_currency = d.credit_in_account_currency
|
||||
gle_copy.credit_in_account_currency = d.debit_in_account_currency
|
||||
gle_copy.is_period_closing_voucher_entry = 0
|
||||
gle_copy.period_closing_voucher = self.name
|
||||
closing_entries.append(gle_copy)
|
||||
|
||||
return closing_entries
|
||||
|
||||
def get_closing_entries_for_balance_sheet_accounts(self):
|
||||
closing_entries = []
|
||||
balance_sheet_account_balances = self.get_account_balances_based_on_dimensions(
|
||||
report_type="Balance Sheet"
|
||||
)
|
||||
|
||||
for dimensions, account_balances in balance_sheet_account_balances.items():
|
||||
for acc, balances in account_balances.items():
|
||||
balance_in_company_currency = flt(balances.debit_in_account_currency) - flt(
|
||||
balances.credit_in_account_currency
|
||||
)
|
||||
if acc != "balances" and balance_in_company_currency:
|
||||
closing_entries.append(self.get_closing_entry(acc, balances, dimensions))
|
||||
|
||||
return closing_entries
|
||||
|
||||
def get_closing_entry(self, account, balances, dimensions):
|
||||
closing_entry = frappe._dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"closing_date": self.period_end_date,
|
||||
"period_closing_voucher": self.name,
|
||||
"account": account,
|
||||
"account_currency": balances.account_currency,
|
||||
"debit_in_account_currency": flt(balances.debit_in_account_currency),
|
||||
"debit": flt(balances.debit),
|
||||
"credit_in_account_currency": flt(balances.credit_in_account_currency),
|
||||
"credit": flt(balances.credit),
|
||||
"is_period_closing_voucher_entry": 0,
|
||||
}
|
||||
)
|
||||
self.update_default_dimensions(closing_entry, dimensions)
|
||||
return closing_entry
|
||||
|
||||
def is_first_period_closing_voucher(self):
|
||||
return not frappe.db.exists(
|
||||
"Period Closing Voucher",
|
||||
{"company": self.company, "docstatus": 1, "name": ("!=", self.name)},
|
||||
)
|
||||
|
||||
def cancel_gl_entries(self):
|
||||
if self.get_gle_count_against_current_pcv() > 5000:
|
||||
frappe.enqueue(
|
||||
process_cancellation,
|
||||
voucher_type="Period Closing Voucher",
|
||||
@@ -73,308 +428,30 @@ class PeriodClosingVoucher(AccountsController):
|
||||
else:
|
||||
process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name)
|
||||
|
||||
def validate_future_closing_vouchers(self):
|
||||
if frappe.db.exists(
|
||||
"Period Closing Voucher",
|
||||
{"posting_date": (">", self.posting_date), "docstatus": 1, "company": self.company},
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"You can not cancel this Period Closing Voucher, please cancel the future Period Closing Vouchers first"
|
||||
)
|
||||
)
|
||||
|
||||
def validate_account_head(self):
|
||||
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
|
||||
|
||||
if closing_account_type not in ["Liability", "Equity"]:
|
||||
frappe.throw(
|
||||
_("Closing Account {0} must be of type Liability / Equity").format(self.closing_account_head)
|
||||
)
|
||||
|
||||
account_currency = get_account_currency(self.closing_account_head)
|
||||
company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
|
||||
if account_currency != company_currency:
|
||||
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
|
||||
|
||||
def validate_posting_date(self):
|
||||
validate_fiscal_year(
|
||||
self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
|
||||
)
|
||||
|
||||
self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1]
|
||||
|
||||
self.check_if_previous_year_closed()
|
||||
|
||||
pcv = frappe.qb.DocType("Period Closing Voucher")
|
||||
existing_entry = (
|
||||
frappe.qb.from_(pcv)
|
||||
.select(pcv.name)
|
||||
.where(
|
||||
(pcv.posting_date >= self.posting_date)
|
||||
& (pcv.fiscal_year == self.fiscal_year)
|
||||
& (pcv.docstatus == 1)
|
||||
& (pcv.company == self.company)
|
||||
)
|
||||
.run()
|
||||
)
|
||||
|
||||
if existing_entry and existing_entry[0][0]:
|
||||
frappe.throw(
|
||||
_("Another Period Closing Entry {0} has been made after {1}").format(
|
||||
existing_entry[0][0], self.posting_date
|
||||
)
|
||||
)
|
||||
|
||||
def check_if_previous_year_closed(self):
|
||||
last_year_closing = add_days(self.year_start_date, -1)
|
||||
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
|
||||
if not previous_fiscal_year:
|
||||
return
|
||||
|
||||
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
|
||||
if not frappe.db.exists(
|
||||
def get_gle_count_against_current_pcv(self):
|
||||
return frappe.db.count(
|
||||
"GL Entry",
|
||||
{
|
||||
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
|
||||
"company": self.company,
|
||||
"is_cancelled": 0,
|
||||
},
|
||||
):
|
||||
return
|
||||
|
||||
if not frappe.db.exists(
|
||||
"Period Closing Voucher",
|
||||
{
|
||||
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
|
||||
"docstatus": 1,
|
||||
"company": self.company,
|
||||
},
|
||||
):
|
||||
frappe.throw(_("Previous Year is not closed, please close it first"))
|
||||
|
||||
def make_gl_entries(self, get_opening_entries=False):
|
||||
gl_entries = self.get_gl_entries()
|
||||
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
|
||||
if len(gl_entries + closing_entries) > 3000:
|
||||
frappe.enqueue(
|
||||
process_gl_and_closing_entries,
|
||||
gl_entries=gl_entries,
|
||||
closing_entries=closing_entries,
|
||||
voucher_name=self.name,
|
||||
company=self.company,
|
||||
closing_date=self.posting_date,
|
||||
timeout=3000,
|
||||
)
|
||||
|
||||
frappe.msgprint(
|
||||
_("The GL Entries will be processed in the background, it can take a few minutes."),
|
||||
alert=True,
|
||||
)
|
||||
else:
|
||||
process_gl_and_closing_entries(
|
||||
gl_entries, closing_entries, self.name, self.company, self.posting_date
|
||||
)
|
||||
|
||||
def get_grouped_gl_entries(self, get_opening_entries=False):
|
||||
closing_entries = []
|
||||
for acc in self.get_balances_based_on_dimensions(
|
||||
group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries
|
||||
):
|
||||
closing_entries.append(self.get_closing_entries(acc))
|
||||
|
||||
return closing_entries
|
||||
|
||||
def get_gl_entries(self):
|
||||
gl_entries = []
|
||||
|
||||
# pl account
|
||||
for acc in self.get_balances_based_on_dimensions(
|
||||
group_by_account=True, report_type="Profit and Loss"
|
||||
):
|
||||
if flt(acc.bal_in_company_currency):
|
||||
gl_entries.append(self.get_gle_for_pl_account(acc))
|
||||
|
||||
# closing liability account
|
||||
for acc in self.get_balances_based_on_dimensions(
|
||||
group_by_account=False, report_type="Profit and Loss"
|
||||
):
|
||||
if flt(acc.bal_in_company_currency):
|
||||
gl_entries.append(self.get_gle_for_closing_account(acc))
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_gle_for_pl_account(self, acc):
|
||||
gl_entry = self.get_gl_dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"closing_date": self.posting_date,
|
||||
"account": acc.account,
|
||||
"cost_center": acc.cost_center,
|
||||
"finance_book": acc.finance_book,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
|
||||
if flt(acc.bal_in_account_currency) < 0
|
||||
else 0,
|
||||
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
|
||||
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
|
||||
if flt(acc.bal_in_account_currency) > 0
|
||||
else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency))
|
||||
if flt(acc.bal_in_company_currency) > 0
|
||||
else 0,
|
||||
"is_period_closing_voucher_entry": 1,
|
||||
},
|
||||
item=acc,
|
||||
)
|
||||
self.update_default_dimensions(gl_entry, acc)
|
||||
return gl_entry
|
||||
|
||||
def get_gle_for_closing_account(self, acc):
|
||||
gl_entry = self.get_gl_dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"closing_date": self.posting_date,
|
||||
"account": self.closing_account_head,
|
||||
"cost_center": acc.cost_center,
|
||||
"finance_book": acc.finance_book,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency))
|
||||
if flt(acc.bal_in_account_currency) > 0
|
||||
else 0,
|
||||
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
|
||||
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
|
||||
if flt(acc.bal_in_account_currency) < 0
|
||||
else 0,
|
||||
"credit": abs(flt(acc.bal_in_company_currency))
|
||||
if flt(acc.bal_in_company_currency) < 0
|
||||
else 0,
|
||||
"is_period_closing_voucher_entry": 1,
|
||||
},
|
||||
item=acc,
|
||||
)
|
||||
self.update_default_dimensions(gl_entry, acc)
|
||||
return gl_entry
|
||||
|
||||
def get_closing_entries(self, acc):
|
||||
closing_entry = self.get_gl_dict(
|
||||
{
|
||||
"company": self.company,
|
||||
"closing_date": self.posting_date,
|
||||
"period_closing_voucher": self.name,
|
||||
"account": acc.account,
|
||||
"cost_center": acc.cost_center,
|
||||
"finance_book": acc.finance_book,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": flt(acc.debit_in_account_currency),
|
||||
"debit": flt(acc.debit),
|
||||
"credit_in_account_currency": flt(acc.credit_in_account_currency),
|
||||
"credit": flt(acc.credit),
|
||||
},
|
||||
item=acc,
|
||||
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
|
||||
)
|
||||
|
||||
for dimension in self.accounting_dimensions:
|
||||
closing_entry.update({dimension: acc.get(dimension)})
|
||||
|
||||
return closing_entry
|
||||
|
||||
def update_default_dimensions(self, gl_entry, acc):
|
||||
if not self.accounting_dimensions:
|
||||
self.accounting_dimensions = get_accounting_dimensions()
|
||||
|
||||
for dimension in self.accounting_dimensions:
|
||||
gl_entry.update({dimension: acc.get(dimension)})
|
||||
|
||||
def get_balances_based_on_dimensions(
|
||||
self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False
|
||||
):
|
||||
"""Get balance for dimension-wise pl accounts"""
|
||||
|
||||
qb_dimension_fields = ["cost_center", "finance_book", "project"]
|
||||
|
||||
self.accounting_dimensions = get_accounting_dimensions()
|
||||
for dimension in self.accounting_dimensions:
|
||||
qb_dimension_fields.append(dimension)
|
||||
|
||||
if group_by_account:
|
||||
qb_dimension_fields.append("account")
|
||||
|
||||
account_filters = {
|
||||
"company": self.company,
|
||||
"is_group": 0,
|
||||
}
|
||||
|
||||
if report_type:
|
||||
account_filters.update({"report_type": report_type})
|
||||
|
||||
accounts = frappe.get_all("Account", filters=account_filters, pluck="name")
|
||||
|
||||
gl_entry = frappe.qb.DocType("GL Entry")
|
||||
query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency)
|
||||
|
||||
if not for_aggregation:
|
||||
query = query.select(
|
||||
(Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_(
|
||||
"bal_in_account_currency"
|
||||
),
|
||||
(Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"),
|
||||
)
|
||||
else:
|
||||
query = query.select(
|
||||
(Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"),
|
||||
(Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"),
|
||||
(Sum(gl_entry.debit)).as_("debit"),
|
||||
(Sum(gl_entry.credit)).as_("credit"),
|
||||
)
|
||||
|
||||
for dimension in qb_dimension_fields:
|
||||
query = query.select(gl_entry[dimension])
|
||||
|
||||
query = query.where(
|
||||
(gl_entry.company == self.company)
|
||||
& (gl_entry.is_cancelled == 0)
|
||||
& (gl_entry.account.isin(accounts))
|
||||
)
|
||||
|
||||
if get_opening_entries:
|
||||
query = query.where(
|
||||
( # noqa: UP034
|
||||
(gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date))
|
||||
| (gl_entry.is_opening == "Yes")
|
||||
)
|
||||
)
|
||||
else:
|
||||
query = query.where(
|
||||
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
|
||||
& gl_entry.is_opening
|
||||
== "No"
|
||||
)
|
||||
|
||||
if for_aggregation:
|
||||
query = query.where(gl_entry.voucher_type != "Period Closing Voucher")
|
||||
|
||||
for dimension in qb_dimension_fields:
|
||||
query = query.groupby(gl_entry[dimension])
|
||||
|
||||
return query.run(as_dict=1)
|
||||
|
||||
|
||||
def process_gl_and_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
|
||||
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
|
||||
make_closing_entries,
|
||||
)
|
||||
def process_gl_and_closing_entries(doc):
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
|
||||
try:
|
||||
gl_entries = doc.get_pcv_gl_entries()
|
||||
if gl_entries:
|
||||
make_gl_entries(gl_entries, merge_entries=False)
|
||||
make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
|
||||
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed")
|
||||
|
||||
closing_entries = doc.get_account_closing_balances()
|
||||
if closing_entries:
|
||||
make_closing_entries(closing_entries, doc.name, doc.company, doc.period_end_date)
|
||||
|
||||
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed")
|
||||
except Exception as e:
|
||||
frappe.db.rollback()
|
||||
frappe.log_error(e)
|
||||
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed")
|
||||
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Failed")
|
||||
|
||||
|
||||
def process_cancellation(voucher_type, voucher_no):
|
||||
@@ -395,3 +472,29 @@ def delete_closing_entries(voucher_no):
|
||||
frappe.qb.from_(closing_balance).delete().where(
|
||||
closing_balance.period_closing_voucher == voucher_no
|
||||
).run()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_period_start_end_date(fiscal_year, company):
|
||||
fy_start_date, fy_end_date = frappe.db.get_value(
|
||||
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
|
||||
)
|
||||
prev_closed_period_end_date = get_previous_closed_period_in_current_year(fiscal_year, company)
|
||||
period_start_date = (
|
||||
add_days(prev_closed_period_end_date, 1) if prev_closed_period_end_date else fy_start_date
|
||||
)
|
||||
return period_start_date, fy_end_date
|
||||
|
||||
|
||||
def get_previous_closed_period_in_current_year(fiscal_year, company):
|
||||
prev_closed_period_end_date = frappe.db.get_value(
|
||||
"Period Closing Voucher",
|
||||
filters={
|
||||
"company": company,
|
||||
"fiscal_year": fiscal_year,
|
||||
"docstatus": 1,
|
||||
},
|
||||
fieldname=["period_end_date"],
|
||||
order_by="period_end_date desc",
|
||||
)
|
||||
return prev_closed_period_end_date
|
||||
|
||||
@@ -317,16 +317,18 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
repost_doc.posting_date = today()
|
||||
repost_doc.save()
|
||||
|
||||
def make_period_closing_voucher(self, posting_date=None, submit=True):
|
||||
def make_period_closing_voucher(self, posting_date, submit=True):
|
||||
surplus_account = create_account()
|
||||
cost_center = create_cost_center("Test Cost Center 1")
|
||||
fy = get_fiscal_year(posting_date, company="Test PCV Company")
|
||||
pcv = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Period Closing Voucher",
|
||||
"transaction_date": posting_date or today(),
|
||||
"posting_date": posting_date or today(),
|
||||
"period_start_date": fy[1],
|
||||
"period_end_date": fy[2],
|
||||
"company": "Test PCV Company",
|
||||
"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
|
||||
"fiscal_year": fy[0],
|
||||
"cost_center": cost_center,
|
||||
"closing_account_head": surplus_account,
|
||||
"remarks": "test",
|
||||
|
||||
@@ -46,8 +46,8 @@ class RepostAccountingLedger(Document):
|
||||
frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
filters={"company": self.company},
|
||||
order_by="posting_date desc",
|
||||
pluck="posting_date",
|
||||
order_by="period_end_date desc",
|
||||
pluck="period_end_date",
|
||||
limit=1,
|
||||
)
|
||||
or None
|
||||
|
||||
@@ -129,13 +129,15 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
|
||||
cost_center=self.cost_center,
|
||||
rate=100,
|
||||
)
|
||||
fy = get_fiscal_year(today(), company=self.company)
|
||||
pcv = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Period Closing Voucher",
|
||||
"transaction_date": today(),
|
||||
"posting_date": today(),
|
||||
"period_start_date": fy[1],
|
||||
"period_end_date": today(),
|
||||
"company": self.company,
|
||||
"fiscal_year": get_fiscal_year(today(), company=self.company)[0],
|
||||
"fiscal_year": fy[0],
|
||||
"cost_center": self.cost_center,
|
||||
"closing_account_head": self.retained_earnings,
|
||||
"remarks": "test",
|
||||
|
||||
@@ -37,13 +37,14 @@ def make_gl_entries(
|
||||
validate_disabled_accounts(gl_map)
|
||||
gl_map = process_gl_map(gl_map, merge_entries)
|
||||
if gl_map and len(gl_map) > 1:
|
||||
create_payment_ledger_entry(
|
||||
gl_map,
|
||||
cancel=0,
|
||||
adv_adj=adv_adj,
|
||||
update_outstanding=update_outstanding,
|
||||
from_repost=from_repost,
|
||||
)
|
||||
if gl_map[0].voucher_type != "Period Closing Voucher":
|
||||
create_payment_ledger_entry(
|
||||
gl_map,
|
||||
cancel=0,
|
||||
adv_adj=adv_adj,
|
||||
update_outstanding=update_outstanding,
|
||||
from_repost=from_repost,
|
||||
)
|
||||
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
|
||||
# Post GL Map proccess there may no be any GL Entries
|
||||
elif gl_map:
|
||||
@@ -116,17 +117,16 @@ def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
|
||||
def validate_disabled_accounts(gl_map):
|
||||
accounts = [d.account for d in gl_map if d.account]
|
||||
|
||||
Account = frappe.qb.DocType("Account")
|
||||
disabled_accounts = frappe.get_all(
|
||||
"Account",
|
||||
filters={"disabled": 1, "is_group": 0, "company": gl_map[0].company},
|
||||
fields=["name"],
|
||||
)
|
||||
|
||||
disabled_accounts = (
|
||||
frappe.qb.from_(Account)
|
||||
.where(Account.name.isin(accounts) & Account.disabled == 1)
|
||||
.select(Account.name, Account.disabled)
|
||||
).run(as_dict=True)
|
||||
|
||||
if disabled_accounts:
|
||||
used_disabled_accounts = set(accounts).intersection(set([d.name for d in disabled_accounts]))
|
||||
if used_disabled_accounts:
|
||||
account_list = "<br>"
|
||||
account_list += ", ".join([frappe.bold(d.name) for d in disabled_accounts])
|
||||
account_list += ", ".join([frappe.bold(d) for d in used_disabled_accounts])
|
||||
frappe.throw(
|
||||
_("Cannot create accounting entries against disabled accounts: {0}").format(account_list),
|
||||
title=_("Disabled Account Selected"),
|
||||
@@ -708,7 +708,7 @@ def validate_against_pcv(is_opening, posting_date, company):
|
||||
)
|
||||
|
||||
last_pcv_date = frappe.db.get_value(
|
||||
"Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)"
|
||||
"Period Closing Voucher", {"docstatus": 1, "company": company}, "max(period_end_date)"
|
||||
)
|
||||
|
||||
if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):
|
||||
|
||||
@@ -385,6 +385,7 @@ class ReceivablePayableReport:
|
||||
self.delivery_notes = frappe._dict()
|
||||
|
||||
# delivery note link inside sales invoice
|
||||
# nosemgrep
|
||||
si_against_dn = frappe.db.sql(
|
||||
"""
|
||||
select parent, delivery_note
|
||||
@@ -400,6 +401,7 @@ class ReceivablePayableReport:
|
||||
if d.delivery_note:
|
||||
self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note)
|
||||
|
||||
# nosemgrep
|
||||
dn_against_si = frappe.db.sql(
|
||||
"""
|
||||
select distinct parent, against_sales_invoice
|
||||
@@ -417,13 +419,16 @@ class ReceivablePayableReport:
|
||||
def get_invoice_details(self):
|
||||
self.invoice_details = frappe._dict()
|
||||
if self.account_type == "Receivable":
|
||||
# nosemgrep
|
||||
si_list = frappe.db.sql(
|
||||
"""
|
||||
select name, due_date, po_no
|
||||
from `tabSales Invoice`
|
||||
where posting_date <= %s
|
||||
and company = %s
|
||||
and docstatus = 1
|
||||
""",
|
||||
self.filters.report_date,
|
||||
(self.filters.report_date, self.filters.company),
|
||||
as_dict=1,
|
||||
)
|
||||
for d in si_list:
|
||||
@@ -431,6 +436,7 @@ class ReceivablePayableReport:
|
||||
|
||||
# Get Sales Team
|
||||
if self.filters.show_sales_person:
|
||||
# nosemgrep
|
||||
sales_team = frappe.db.sql(
|
||||
"""
|
||||
select parent, sales_person
|
||||
@@ -445,25 +451,33 @@ class ReceivablePayableReport:
|
||||
)
|
||||
|
||||
if self.account_type == "Payable":
|
||||
# nosemgrep
|
||||
for pi in frappe.db.sql(
|
||||
"""
|
||||
select name, due_date, bill_no, bill_date
|
||||
from `tabPurchase Invoice`
|
||||
where posting_date <= %s
|
||||
where
|
||||
posting_date <= %s
|
||||
and company = %s
|
||||
and docstatus = 1
|
||||
""",
|
||||
self.filters.report_date,
|
||||
(self.filters.report_date, self.filters.company),
|
||||
as_dict=1,
|
||||
):
|
||||
self.invoice_details.setdefault(pi.name, pi)
|
||||
|
||||
# Invoices booked via Journal Entries
|
||||
# nosemgrep
|
||||
journal_entries = frappe.db.sql(
|
||||
"""
|
||||
select name, due_date, bill_no, bill_date
|
||||
from `tabJournal Entry`
|
||||
where posting_date <= %s
|
||||
where
|
||||
posting_date <= %s
|
||||
and company = %s
|
||||
and docstatus = 1
|
||||
""",
|
||||
self.filters.report_date,
|
||||
(self.filters.report_date, self.filters.company),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
@@ -472,6 +486,8 @@ class ReceivablePayableReport:
|
||||
self.invoice_details.setdefault(je.name, je)
|
||||
|
||||
def set_party_details(self, row):
|
||||
if not row.party:
|
||||
return
|
||||
# customer / supplier name
|
||||
party_details = self.get_party_details(row.party) or {}
|
||||
row.update(party_details)
|
||||
@@ -496,6 +512,7 @@ class ReceivablePayableReport:
|
||||
|
||||
def get_payment_terms(self, row):
|
||||
# build payment_terms for row
|
||||
# nosemgrep
|
||||
payment_terms_details = frappe.db.sql(
|
||||
f"""
|
||||
select
|
||||
@@ -708,6 +725,7 @@ class ReceivablePayableReport:
|
||||
def get_return_entries(self):
|
||||
doctype = "Sales Invoice" if self.account_type == "Receivable" else "Purchase Invoice"
|
||||
filters = {
|
||||
"posting_date": ("<=", self.filters.report_date),
|
||||
"is_return": 1,
|
||||
"docstatus": 1,
|
||||
"company": self.filters.company,
|
||||
@@ -815,6 +833,7 @@ class ReceivablePayableReport:
|
||||
if self.filters.get("sales_person"):
|
||||
lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"])
|
||||
|
||||
# nosemgrep
|
||||
records = frappe.db.sql(
|
||||
"""
|
||||
select distinct parent, parenttype
|
||||
|
||||
@@ -9,6 +9,7 @@ import re
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
|
||||
from pypika.terms import ExistsCriterion
|
||||
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
@@ -181,12 +182,12 @@ def get_data(
|
||||
company,
|
||||
period_list[0]["year_start_date"] if only_current_fiscal_year else None,
|
||||
period_list[-1]["to_date"],
|
||||
root.lft,
|
||||
root.rgt,
|
||||
filters,
|
||||
gl_entries_by_account,
|
||||
ignore_closing_entries=ignore_closing_entries,
|
||||
root.lft,
|
||||
root.rgt,
|
||||
root_type=root_type,
|
||||
ignore_closing_entries=ignore_closing_entries,
|
||||
)
|
||||
|
||||
calculate_values(
|
||||
@@ -419,93 +420,78 @@ def set_gl_entries_by_account(
|
||||
company,
|
||||
from_date,
|
||||
to_date,
|
||||
root_lft,
|
||||
root_rgt,
|
||||
filters,
|
||||
gl_entries_by_account,
|
||||
root_lft=None,
|
||||
root_rgt=None,
|
||||
root_type=None,
|
||||
ignore_closing_entries=False,
|
||||
ignore_opening_entries=False,
|
||||
root_type=None,
|
||||
):
|
||||
"""Returns a dict like { "account": [gl entries], ... }"""
|
||||
gl_entries = []
|
||||
|
||||
account_filters = {
|
||||
"company": company,
|
||||
"is_group": 0,
|
||||
"lft": (">=", root_lft),
|
||||
"rgt": ("<=", root_rgt),
|
||||
}
|
||||
|
||||
if root_type:
|
||||
account_filters.update(
|
||||
{
|
||||
"root_type": root_type,
|
||||
}
|
||||
# For balance sheet
|
||||
ignore_closing_balances = frappe.db.get_single_value(
|
||||
"Accounts Settings", "ignore_account_closing_balance"
|
||||
)
|
||||
if not from_date and not ignore_closing_balances:
|
||||
last_period_closing_voucher = frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
"company": filters.company,
|
||||
"period_end_date": ("<", filters["period_start_date"]),
|
||||
},
|
||||
fields=["period_end_date", "name"],
|
||||
order_by="period_end_date desc",
|
||||
limit=1,
|
||||
)
|
||||
if last_period_closing_voucher:
|
||||
gl_entries += get_accounting_entries(
|
||||
"Account Closing Balance",
|
||||
from_date,
|
||||
to_date,
|
||||
filters,
|
||||
root_lft,
|
||||
root_rgt,
|
||||
root_type,
|
||||
ignore_closing_entries,
|
||||
last_period_closing_voucher[0].name,
|
||||
)
|
||||
from_date = add_days(last_period_closing_voucher[0].period_end_date, 1)
|
||||
ignore_opening_entries = True
|
||||
|
||||
accounts_list = frappe.db.get_all(
|
||||
"Account",
|
||||
filters=account_filters,
|
||||
pluck="name",
|
||||
gl_entries += get_accounting_entries(
|
||||
"GL Entry",
|
||||
from_date,
|
||||
to_date,
|
||||
filters,
|
||||
root_lft,
|
||||
root_rgt,
|
||||
root_type,
|
||||
ignore_closing_entries,
|
||||
ignore_opening_entries=ignore_opening_entries,
|
||||
)
|
||||
|
||||
if accounts_list:
|
||||
# For balance sheet
|
||||
ignore_closing_balances = frappe.db.get_single_value(
|
||||
"Accounts Settings", "ignore_account_closing_balance"
|
||||
)
|
||||
if not from_date and not ignore_closing_balances:
|
||||
last_period_closing_voucher = frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
"company": filters.company,
|
||||
"posting_date": ("<", filters["period_start_date"]),
|
||||
},
|
||||
fields=["posting_date", "name"],
|
||||
order_by="posting_date desc",
|
||||
limit=1,
|
||||
)
|
||||
if last_period_closing_voucher:
|
||||
gl_entries += get_accounting_entries(
|
||||
"Account Closing Balance",
|
||||
from_date,
|
||||
to_date,
|
||||
accounts_list,
|
||||
filters,
|
||||
ignore_closing_entries,
|
||||
last_period_closing_voucher[0].name,
|
||||
)
|
||||
from_date = add_days(last_period_closing_voucher[0].posting_date, 1)
|
||||
ignore_opening_entries = True
|
||||
if filters and filters.get("presentation_currency"):
|
||||
convert_to_presentation_currency(gl_entries, get_currency(filters))
|
||||
|
||||
gl_entries += get_accounting_entries(
|
||||
"GL Entry",
|
||||
from_date,
|
||||
to_date,
|
||||
accounts_list,
|
||||
filters,
|
||||
ignore_closing_entries,
|
||||
ignore_opening_entries=ignore_opening_entries,
|
||||
)
|
||||
for entry in gl_entries:
|
||||
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
||||
|
||||
if filters and filters.get("presentation_currency"):
|
||||
convert_to_presentation_currency(gl_entries, get_currency(filters))
|
||||
|
||||
for entry in gl_entries:
|
||||
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
||||
|
||||
return gl_entries_by_account
|
||||
return gl_entries_by_account
|
||||
|
||||
|
||||
def get_accounting_entries(
|
||||
doctype,
|
||||
from_date,
|
||||
to_date,
|
||||
accounts,
|
||||
filters,
|
||||
ignore_closing_entries,
|
||||
root_lft=None,
|
||||
root_rgt=None,
|
||||
root_type=None,
|
||||
ignore_closing_entries=None,
|
||||
period_closing_voucher=None,
|
||||
ignore_opening_entries=False,
|
||||
):
|
||||
@@ -535,13 +521,30 @@ def get_accounting_entries(
|
||||
query = query.where(gl_entry.period_closing_voucher == period_closing_voucher)
|
||||
|
||||
query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters)
|
||||
query = query.where(gl_entry.account.isin(accounts))
|
||||
|
||||
if (root_lft and root_rgt) or root_type:
|
||||
account_filter_query = get_account_filter_query(root_lft, root_rgt, root_type, gl_entry)
|
||||
query = query.where(ExistsCriterion(account_filter_query))
|
||||
|
||||
entries = query.run(as_dict=True)
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def get_account_filter_query(root_lft, root_rgt, root_type, gl_entry):
|
||||
acc = frappe.qb.DocType("Account")
|
||||
exists_query = (
|
||||
frappe.qb.from_(acc).select(acc.name).where(acc.name == gl_entry.account).where(acc.is_group == 0)
|
||||
)
|
||||
if root_lft and root_rgt:
|
||||
exists_query = exists_query.where(acc.lft >= root_lft).where(acc.rgt <= root_rgt)
|
||||
|
||||
if root_type:
|
||||
exists_query = exists_query.where(acc.root_type == root_type)
|
||||
|
||||
return exists_query
|
||||
|
||||
|
||||
def apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters):
|
||||
gl_entry = frappe.qb.DocType(doctype)
|
||||
accounting_dimensions = get_accounting_dimensions(as_list=False)
|
||||
|
||||
@@ -94,12 +94,6 @@ def get_data(filters):
|
||||
|
||||
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
|
||||
|
||||
min_lft, max_rgt = frappe.db.sql(
|
||||
"""select min(lft), max(rgt) from `tabAccount`
|
||||
where company=%s""",
|
||||
(filters.company,),
|
||||
)[0]
|
||||
|
||||
gl_entries_by_account = {}
|
||||
|
||||
opening_balances = get_opening_balances(filters)
|
||||
@@ -112,10 +106,10 @@ def get_data(filters):
|
||||
filters.company,
|
||||
filters.from_date,
|
||||
filters.to_date,
|
||||
min_lft,
|
||||
max_rgt,
|
||||
filters,
|
||||
gl_entries_by_account,
|
||||
root_lft=None,
|
||||
root_rgt=None,
|
||||
ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period),
|
||||
ignore_opening_entries=True,
|
||||
)
|
||||
@@ -150,9 +144,9 @@ def get_rootwise_opening_balances(filters, report_type):
|
||||
if not ignore_closing_balances:
|
||||
last_period_closing_voucher = frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", filters.from_date)},
|
||||
fields=["posting_date", "name"],
|
||||
order_by="posting_date desc",
|
||||
filters={"docstatus": 1, "company": filters.company, "period_end_date": ("<", filters.from_date)},
|
||||
fields=["period_end_date", "name"],
|
||||
order_by="period_end_date desc",
|
||||
limit=1,
|
||||
)
|
||||
|
||||
@@ -168,8 +162,8 @@ def get_rootwise_opening_balances(filters, report_type):
|
||||
)
|
||||
|
||||
# Report getting generate from the mid of a fiscal year
|
||||
if getdate(last_period_closing_voucher[0].posting_date) < getdate(add_days(filters.from_date, -1)):
|
||||
start_date = add_days(last_period_closing_voucher[0].posting_date, 1)
|
||||
if getdate(last_period_closing_voucher[0].period_end_date) < getdate(add_days(filters.from_date, -1)):
|
||||
start_date = add_days(last_period_closing_voucher[0].period_end_date, 1)
|
||||
gle += get_opening_balance(
|
||||
"GL Entry", filters, report_type, accounting_dimensions, start_date=start_date
|
||||
)
|
||||
|
||||
@@ -314,6 +314,7 @@ erpnext.patches.v13_0.update_docs_link
|
||||
erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
|
||||
erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
|
||||
erpnext.patches.v14_0.set_period_start_end_date_in_pcv
|
||||
erpnext.patches.v14_0.update_closing_balances #14-07-2023
|
||||
execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
|
||||
erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts
|
||||
|
||||
@@ -15,7 +15,7 @@ def execute():
|
||||
|
||||
def find_broken_stock_entries() -> list[StockEntryCode]:
|
||||
period_closing_date = frappe.db.get_value(
|
||||
"Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc"
|
||||
"Period Closing Voucher", {"docstatus": 1}, "period_end_date", order_by="period_end_date desc"
|
||||
)
|
||||
|
||||
stock_entries_to_patch = frappe.db.sql(
|
||||
|
||||
17
erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py
Normal file
17
erpnext/patches/v14_0/set_period_start_end_date_in_pcv.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
# nosemgrep
|
||||
frappe.db.sql(
|
||||
"""
|
||||
UPDATE `tabPeriod Closing Voucher`
|
||||
SET
|
||||
period_start_date = (select year_start_date from `tabFiscal Year` where name = fiscal_year),
|
||||
period_end_date = posting_date
|
||||
"""
|
||||
)
|
||||
@@ -66,7 +66,7 @@ def get_accounts_closing_date():
|
||||
) # always returns datetime.date
|
||||
|
||||
period_closing_date = frappe.db.get_value(
|
||||
"Period Closing Voucher", {"docstatus": 1}, "posting_date", order_by="posting_date desc"
|
||||
"Period Closing Voucher", {"docstatus": 1}, "period_end_date", order_by="period_end_date desc"
|
||||
)
|
||||
|
||||
# Set most recent frozen/closing date as filter
|
||||
|
||||
@@ -7,67 +7,68 @@ import frappe
|
||||
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
|
||||
make_closing_entries,
|
||||
)
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
)
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.db.truncate("Account Closing Balance")
|
||||
|
||||
gle_fields = get_gle_fields()
|
||||
|
||||
for company in frappe.get_all("Company", pluck="name"):
|
||||
i = 0
|
||||
company_wise_order = {}
|
||||
for pcv in frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
fields=["company", "posting_date", "name"],
|
||||
filters={"docstatus": 1, "company": company},
|
||||
order_by="posting_date",
|
||||
):
|
||||
for pcv in get_period_closing_vouchers(company):
|
||||
company_wise_order.setdefault(pcv.company, [])
|
||||
if pcv.posting_date not in company_wise_order[pcv.company]:
|
||||
pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name)
|
||||
pcv_doc.year_start_date = get_fiscal_year(
|
||||
pcv.posting_date, pcv.fiscal_year, company=pcv.company
|
||||
)[1]
|
||||
if pcv.period_end_date not in company_wise_order[pcv.company]:
|
||||
pcv.pl_accounts_reverse_gle = get_pcv_gl_entries(pcv, gle_fields)
|
||||
closing_entries = pcv.get_account_closing_balances()
|
||||
if closing_entries:
|
||||
make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date)
|
||||
|
||||
# get gl entries against pcv
|
||||
gl_entries = frappe.db.get_all(
|
||||
"GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"]
|
||||
)
|
||||
for entry in gl_entries:
|
||||
entry["is_period_closing_voucher_entry"] = 1
|
||||
entry["closing_date"] = pcv_doc.posting_date
|
||||
entry["period_closing_voucher"] = pcv_doc.name
|
||||
|
||||
closing_entries = []
|
||||
|
||||
if pcv.posting_date not in company_wise_order[pcv.company]:
|
||||
# get all gl entries for the year
|
||||
closing_entries = frappe.db.get_all(
|
||||
"GL Entry",
|
||||
filters={
|
||||
"is_cancelled": 0,
|
||||
"voucher_no": ["!=", pcv.name],
|
||||
"posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]],
|
||||
"is_opening": "No",
|
||||
"company": company,
|
||||
},
|
||||
fields=["*"],
|
||||
)
|
||||
|
||||
if i == 0:
|
||||
# add opening entries only for the first pcv
|
||||
closing_entries += frappe.db.get_all(
|
||||
"GL Entry",
|
||||
filters={"is_cancelled": 0, "is_opening": "Yes", "company": company},
|
||||
fields=["*"],
|
||||
)
|
||||
|
||||
for entry in closing_entries:
|
||||
entry["closing_date"] = pcv_doc.posting_date
|
||||
entry["period_closing_voucher"] = pcv_doc.name
|
||||
|
||||
entries = gl_entries + closing_entries
|
||||
|
||||
make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date)
|
||||
company_wise_order[pcv.company].append(pcv.posting_date)
|
||||
company_wise_order[pcv.company].append(pcv.period_end_date)
|
||||
i += 1
|
||||
|
||||
|
||||
def get_gle_fields():
|
||||
default_diemnsion_fields = ["cost_center", "finance_book", "project"]
|
||||
accounting_dimension_fields = get_accounting_dimensions()
|
||||
gle_fields = [
|
||||
"name",
|
||||
"posting_date",
|
||||
"account",
|
||||
"account_currency",
|
||||
"debit",
|
||||
"credit",
|
||||
"debit_in_account_currency",
|
||||
"credit_in_account_currency",
|
||||
*default_diemnsion_fields,
|
||||
*accounting_dimension_fields,
|
||||
]
|
||||
|
||||
return gle_fields
|
||||
|
||||
|
||||
def get_period_closing_vouchers(company):
|
||||
return frappe.db.get_all(
|
||||
"Period Closing Voucher",
|
||||
fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"],
|
||||
filters={"docstatus": 1, "company": company},
|
||||
order_by="period_end_date",
|
||||
)
|
||||
|
||||
|
||||
def get_pcv_gl_entries(pcv, gle_fields):
|
||||
gl_entries = frappe.db.get_all(
|
||||
"GL Entry",
|
||||
filters={"voucher_no": pcv.name, "account": ["!=", pcv.closing_account_head], "is_cancelled": 0},
|
||||
fields=gle_fields,
|
||||
)
|
||||
for entry in gl_entries:
|
||||
entry["is_period_closing_voucher_entry"] = 1
|
||||
entry["closing_date"] = pcv.period_end_date
|
||||
entry["period_closing_voucher"] = pcv.name
|
||||
return gl_entries
|
||||
|
||||
@@ -1188,18 +1188,19 @@ def make_shipment(source_name, target_doc=None):
|
||||
# As we are using session user details in the pickup_contact then pickup_contact_person will be session user
|
||||
target.pickup_contact_person = frappe.session.user
|
||||
|
||||
contact = frappe.db.get_value(
|
||||
"Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1
|
||||
)
|
||||
delivery_contact_display = f"{source.contact_display}"
|
||||
if contact:
|
||||
if contact.email_id:
|
||||
delivery_contact_display += "<br>" + contact.email_id
|
||||
if contact.phone:
|
||||
delivery_contact_display += "<br>" + contact.phone
|
||||
if contact.mobile_no and not contact.phone:
|
||||
delivery_contact_display += "<br>" + contact.mobile_no
|
||||
target.delivery_contact = delivery_contact_display
|
||||
if source.contact_person:
|
||||
contact = frappe.db.get_value(
|
||||
"Contact", source.contact_person, ["email_id", "phone", "mobile_no"], as_dict=1
|
||||
)
|
||||
delivery_contact_display = f"{source.contact_display}"
|
||||
if contact:
|
||||
if contact.email_id:
|
||||
delivery_contact_display += "<br>" + contact.email_id
|
||||
if contact.phone:
|
||||
delivery_contact_display += "<br>" + contact.phone
|
||||
if contact.mobile_no and not contact.phone:
|
||||
delivery_contact_display += "<br>" + contact.mobile_no
|
||||
target.delivery_contact = delivery_contact_display
|
||||
|
||||
if source.shipping_address_name:
|
||||
target.delivery_address_name = source.shipping_address_name
|
||||
|
||||
@@ -125,7 +125,7 @@ class RepostItemValuation(Document):
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(Max(table.posting_date))
|
||||
.select(Max(table.period_end_date))
|
||||
.where((table.company == company) & (table.docstatus == 1))
|
||||
).run()
|
||||
|
||||
|
||||
@@ -1309,10 +1309,10 @@ class StockEntry(StockController):
|
||||
3. Check FG Item and Qty against WO if present (mfg)
|
||||
"""
|
||||
production_item, wo_qty, finished_items = None, 0, []
|
||||
|
||||
wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"])
|
||||
if wo_details:
|
||||
production_item, wo_qty = wo_details
|
||||
if self.work_order:
|
||||
wo_details = frappe.db.get_value("Work Order", self.work_order, ["production_item", "qty"])
|
||||
if wo_details:
|
||||
production_item, wo_qty = wo_details
|
||||
|
||||
for d in self.get("items"):
|
||||
if d.is_finished_item:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils.make_random import get_random
|
||||
|
||||
from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
|
||||
|
||||
@@ -11,7 +12,7 @@ DEFAULT_FILTERS = {
|
||||
}
|
||||
|
||||
|
||||
batch = frappe.db.get_value("Batch", fieldname=["name"], as_dict=True, order_by="creation desc")
|
||||
batch = get_random("Batch")
|
||||
|
||||
REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [
|
||||
("Stock Ledger", {"_optional": True}),
|
||||
|
||||
@@ -3,7 +3,7 @@ from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
INDEXED_FIELDS = {
|
||||
"Bin": ["item_code"],
|
||||
"GL Entry": ["voucher_type", "against_voucher_type"],
|
||||
"GL Entry": ["voucher_no", "posting_date", "company", "party"],
|
||||
"Purchase Order Item": ["item_code"],
|
||||
"Stock Ledger Entry": ["warehouse"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user