Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make offsetting entry for acc dimensions in general ledger #36149

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ frappe.ui.form.on('Accounting Dimension', {
};
});

frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
let d = locals[cdt][cdn];
return {
filters: {
company: d.company,
root_type: ["in", ["Asset", "Liability"]],
is_group: 0
}
}
});

if (!frm.is_new()) {
frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
frappe.set_route("List", frm.doc.document_type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,23 @@ def validate(self):
if not self.is_new():
self.validate_document_type_change()

self.validate_dimension_defaults()

def validate_document_type_change(self):
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
if doctype_before_save != self.document_type:
message = _("Cannot change Reference Document Type.")
message += _("Please create a new Accounting Dimension if required.")
frappe.throw(message)

def validate_dimension_defaults(self):
companies = []
for default in self.get("dimension_defaults"):
if default.company not in companies:
companies.append(default.company)
else:
frappe.throw(_("Company {0} is added more than once").format(frappe.bold(default.company)))

def after_insert(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
"reference_document",
"default_dimension",
"mandatory_for_bs",
"mandatory_for_pl"
"mandatory_for_pl",
"column_break_lqns",
"automatically_post_balancing_accounting_entry",
"offsetting_account"
],
"fields": [
{
Expand Down Expand Up @@ -50,6 +53,23 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Mandatory For Profit and Loss Account"
},
{
"default": "0",
"fieldname": "automatically_post_balancing_accounting_entry",
"fieldtype": "Check",
"label": "Automatically post balancing accounting entry"
},
{
"fieldname": "offsetting_account",
"fieldtype": "Link",
"label": "Offsetting Account",
"mandatory_depends_on": "eval: doc.automatically_post_balancing_accounting_entry",
"options": "Account"
},
{
"fieldname": "column_break_lqns",
"fieldtype": "Column Break"
}
],
"istable": 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1736,6 +1736,61 @@ def test_gl_entries_for_standalone_debit_note(self):
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
self.assertAlmostEqual(returned_inv.items[0].rate, rate)

def test_offsetting_entries_for_accounting_dimensions(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.report.trial_balance.test_trial_balance import (
clear_dimension_defaults,
create_accounting_dimension,
disable_dimension,
)

create_account(
account_name="Offsetting",
company="_Test Company",
parent_account="Temporary Accounts - _TC",
)

create_accounting_dimension(company="_Test Company", offsetting_account="Offsetting - _TC")

branch1 = frappe.new_doc("Branch")
branch1.branch = "Location 1"
branch1.insert(ignore_if_duplicate=True)
branch2 = frappe.new_doc("Branch")
branch2.branch = "Location 2"
branch2.insert(ignore_if_duplicate=True)

pi = make_purchase_invoice(
company="_Test Company",
customer="_Test Supplier",
do_not_save=True,
do_not_submit=True,
rate=1000,
price_list_rate=1000,
qty=1,
)
pi.branch = branch1.branch
pi.items[0].branch = branch2.branch
pi.save()
pi.submit()

expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 1000, 0.0, nowdate(), branch2.branch],
["Creditors - _TC", 0.0, 1000, nowdate(), branch1.branch],
["Offsetting - _TC", 1000, 0.0, nowdate(), branch1.branch],
["Offsetting - _TC", 0.0, 1000, nowdate(), branch2.branch],
]

check_gl_entries(
self,
pi.name,
expected_gle,
nowdate(),
voucher_type="Purchase Invoice",
additional_columns=["branch"],
)
clear_dimension_defaults("Branch")
disable_dimension()


def set_advance_flag(company, flag, default_account):
frappe.db.set_value(
Expand All @@ -1748,9 +1803,16 @@ def set_advance_flag(company, flag, default_account):
)


def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Purchase Invoice"):
def check_gl_entries(
doc,
voucher_no,
expected_gle,
posting_date,
voucher_type="Purchase Invoice",
additional_columns=None,
):
gl = frappe.qb.DocType("GL Entry")
q = (
query = (
frappe.qb.from_(gl)
.select(gl.account, gl.debit, gl.credit, gl.posting_date)
.where(
Expand All @@ -1761,14 +1823,25 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="
)
.orderby(gl.posting_date, gl.account, gl.creation)
)
gl_entries = q.run(as_dict=True)

if additional_columns:
for col in additional_columns:
query = query.select(gl[col])

gl_entries = query.run(as_dict=True)

for i, gle in enumerate(gl_entries):
doc.assertEqual(expected_gle[i][0], gle.account)
doc.assertEqual(expected_gle[i][1], gle.debit)
doc.assertEqual(expected_gle[i][2], gle.credit)
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)

if additional_columns:
j = 4
for col in additional_columns:
doc.assertEqual(expected_gle[i][j], gle[col])
j += 1


def create_tax_witholding_category(category_name, company, account):
from erpnext.accounts.utils import get_fiscal_year
Expand Down
58 changes: 58 additions & 0 deletions erpnext/accounts/general_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def make_gl_entries(
):
if gl_map:
if not cancel:
make_acc_dimensions_offsetting_entry(gl_map)
validate_accounting_period(gl_map)
validate_disabled_accounts(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
Expand All @@ -51,6 +52,63 @@ def make_gl_entries(
make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)


def make_acc_dimensions_offsetting_entry(gl_map):
accounting_dimensions_to_offset = get_accounting_dimensions_for_offsetting_entry(
gl_map, gl_map[0].company
)
no_of_dimensions = len(accounting_dimensions_to_offset)
if no_of_dimensions == 0:
return

offsetting_entries = []

for gle in gl_map:
for dimension in accounting_dimensions_to_offset:
offsetting_entry = gle.copy()
debit = flt(gle.credit) / no_of_dimensions if gle.credit != 0 else 0
credit = flt(gle.debit) / no_of_dimensions if gle.debit != 0 else 0
offsetting_entry.update(
{
"account": dimension.offsetting_account,
"debit": debit,
"credit": credit,
"debit_in_account_currency": debit,
"credit_in_account_currency": credit,
"remarks": _("Offsetting for Accounting Dimension") + " - {0}".format(dimension.name),
"against_voucher": None,
}
)
offsetting_entry["against_voucher_type"] = None
offsetting_entries.append(offsetting_entry)

gl_map += offsetting_entries


def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
acc_dimension = frappe.qb.DocType("Accounting Dimension")
dimension_detail = frappe.qb.DocType("Accounting Dimension Detail")

acc_dimensions = (
frappe.qb.from_(acc_dimension)
.inner_join(dimension_detail)
.on(acc_dimension.name == dimension_detail.parent)
.select(acc_dimension.fieldname, acc_dimension.name, dimension_detail.offsetting_account)
.where(
(acc_dimension.disabled == 0)
& (dimension_detail.company == company)
& (dimension_detail.automatically_post_balancing_accounting_entry == 1)
)
).run(as_dict=True)

accounting_dimensions_to_offset = []
for acc_dimension in acc_dimensions:
values = set([entry.get(acc_dimension.fieldname) for entry in gl_map])
if len(values) > 1:
accounting_dimensions_to_offset.append(acc_dimension)

return accounting_dimensions_to_offset


def validate_disabled_accounts(gl_map):
accounts = [d.account for d in gl_map if d.account]

Expand Down
118 changes: 118 additions & 0 deletions erpnext/accounts/report/trial_balance/test_trial_balance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import today

from erpnext.accounts.report.trial_balance.trial_balance import execute


class TestTrialBalance(FrappeTestCase):
def setUp(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_fiscal_year

self.company = create_company()
create_cost_center(
cost_center_name="Test Cost Center",
company="Trial Balance Company",
parent_cost_center="Trial Balance Company - TBC",
)
create_account(
account_name="Offsetting",
company="Trial Balance Company",
parent_account="Temporary Accounts - TBC",
)
self.fiscal_year = get_fiscal_year(today(), company="Trial Balance Company")[0]
create_accounting_dimension()

def test_offsetting_entries_for_accounting_dimensions(self):
"""
Checks if Trial Balance Report is balanced when filtered using a particular Accounting Dimension
"""
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice

frappe.db.sql("delete from `tabSales Invoice` where company='Trial Balance Company'")
frappe.db.sql("delete from `tabGL Entry` where company='Trial Balance Company'")

branch1 = frappe.new_doc("Branch")
branch1.branch = "Location 1"
branch1.insert(ignore_if_duplicate=True)
branch2 = frappe.new_doc("Branch")
branch2.branch = "Location 2"
branch2.insert(ignore_if_duplicate=True)

si = create_sales_invoice(
company=self.company,
debit_to="Debtors - TBC",
cost_center="Test Cost Center - TBC",
income_account="Sales - TBC",
do_not_submit=1,
)
si.branch = "Location 1"
si.items[0].branch = "Location 2"
si.save()
si.submit()

filters = frappe._dict(
{"company": self.company, "fiscal_year": self.fiscal_year, "branch": ["Location 1"]}
)
total_row = execute(filters)[1][-1]
self.assertEqual(total_row["debit"], total_row["credit"])

def tearDown(self):
clear_dimension_defaults("Branch")
disable_dimension()


def create_company(**args):
args = frappe._dict(args)
company = frappe.get_doc(
{
"doctype": "Company",
"company_name": args.company_name or "Trial Balance Company",
"country": args.country or "India",
"default_currency": args.currency or "INR",
}
)
company.insert(ignore_if_duplicate=True)
return company.name


def create_accounting_dimension(**args):
args = frappe._dict(args)
document_type = args.document_type or "Branch"
if frappe.db.exists("Accounting Dimension", document_type):
accounting_dimension = frappe.get_doc("Accounting Dimension", document_type)
accounting_dimension.disabled = 0
else:
accounting_dimension = frappe.new_doc("Accounting Dimension")
accounting_dimension.document_type = document_type
accounting_dimension.insert()

accounting_dimension.set("dimension_defaults", [])
accounting_dimension.append(
"dimension_defaults",
{
"company": args.company or "Trial Balance Company",
"automatically_post_balancing_accounting_entry": 1,
"offsetting_account": args.offsetting_account or "Offsetting - TBC",
},
)
accounting_dimension.save()


def disable_dimension(**args):
args = frappe._dict(args)
document_type = args.document_type or "Branch"
dimension = frappe.get_doc("Accounting Dimension", document_type)
dimension.disabled = 1
dimension.save()


def clear_dimension_defaults(dimension_name):
accounting_dimension = frappe.get_doc("Accounting Dimension", dimension_name)
accounting_dimension.dimension_defaults = []
accounting_dimension.save()
2 changes: 1 addition & 1 deletion erpnext/accounts/report/trial_balance/trial_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def get_opening_balance(
lft, rgt = frappe.db.get_value("Cost Center", filters.cost_center, ["lft", "rgt"])
cost_center = frappe.qb.DocType("Cost Center")
opening_balance = opening_balance.where(
closing_balance.cost_center.in_(
closing_balance.cost_center.isin(
frappe.qb.from_(cost_center)
.select("name")
.where((cost_center.lft >= lft) & (cost_center.rgt <= rgt))
Expand Down
Loading