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

feat: introduced new account for asset purchase process #44505

Draft
wants to merge 14 commits into
base: develop
Choose a base branch
from
Draft
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 @@ -54,6 +54,9 @@
"Buildings": {
"account_type": "Fixed Asset"
},
"Clearing Account": {
"account_type": "Fixed Asset"
},
"Accumulated Depreciations": {
"account_type": "Accumulated Depreciation"
}
Expand Down
1 change: 1 addition & 0 deletions erpnext/accounts/doctype/account/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ def _make_test_records(verbose=None):
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
# fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Clearing Account", "Fixed Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
["_Test Depreciations", "Expenses", 0, "Depreciation", None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
Expand Down
37 changes: 22 additions & 15 deletions erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,31 +572,35 @@ def set_expense_account(self, for_validate=False):
account_type = (
"capital_work_in_progress_account"
if is_cwip_accounting_enabled(item.asset_category)
else "fixed_asset_account"
else "asset_clearing_account"
)
account = get_asset_category_account(
account_type, item=item.item_code, company=self.company
)
if not account:
form_link = get_link_to_form("Asset Category", item.asset_category)
throw(
_("Please set Fixed Asset Account in {} against {}.").format(
form_link, self.company
),
_("Please set Asset Account in {} against {}.").format(form_link, self.company),
title=_("Missing Account"),
)
item.expense_account = account
elif not item.expense_account and for_validate:
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
item.expense_account = None
item.asset_account = account
elif not (item.expense_account or item.asset_account) and for_validate:
throw(_("Expense Account is mandatory for item {0}").format(item.item_code or item.item_name))

def validate_expense_account(self):
for item in self.get("items"):
validate_account_head(item.idx, item.expense_account, self.company, _("Expense"))
if item.is_fixed_asset:
validate_account_head(item.idx, item.asset_account, self.company, _("Expense"))
else:
validate_account_head(item.idx, item.expense_account, self.company, _("Expense"))

def set_against_expense_account(self, force=False):
against_accounts = []
for item in self.get("items"):
if item.expense_account and (item.expense_account not in against_accounts):
if item.asset_account and item.is_fixed_asset and (item.asset_account not in against_accounts):
against_accounts.append(item.asset_account)
elif item.expense_account and (item.expense_account not in against_accounts):
against_accounts.append(item.expense_account)

self.against_expense_account = ",".join(against_accounts)
Expand Down Expand Up @@ -1042,7 +1046,7 @@ def make_item_gl_entries(self, gl_entries):
gl_entries.append(
self.get_gl_dict(
{
"account": item.expense_account,
"account": item.expense_account or item.asset_account,
"against": self.supplier,
"debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
Expand Down Expand Up @@ -1096,11 +1100,14 @@ def make_item_gl_entries(self, gl_entries):
)

else:
expense_account = (
item.expense_account
if (not item.enable_deferred_expense or self.is_return)
else item.deferred_expense_account
)
if item.is_fixed_asset:
expense_account = item.asset_account
else:
expense_account = (
item.expense_account
if (not item.enable_deferred_expense or self.is_return)
else item.deferred_expense_account
)

dummy, amount = self.get_amount_and_base_amount(item, None)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"column_break_13",
"manufacturer_part_no",
"accounting",
"asset_account",
"expense_account",
"wip_composite_asset",
"col_break5",
Expand Down Expand Up @@ -977,12 +978,18 @@
"fieldtype": "Currency",
"label": "Distributed Discount Amount",
"options": "currency"
},
{
"fieldname": "asset_account",
"fieldtype": "Link",
"label": "Asset Account",
"options": "Account"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-10-28 15:06:19.246141",
"modified": "2024-12-01 20:44:05.643955",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class PurchaseInvoiceItem(Document):
allow_zero_valuation_rate: DF.Check
amount: DF.Currency
apply_tds: DF.Check
asset_account: DF.Link | None
asset_category: DF.Link | None
asset_location: DF.Link | None
base_amount: DF.Currency
Expand Down
72 changes: 42 additions & 30 deletions erpnext/assets/doctype/asset/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import frappe
from frappe import _
from frappe.query_builder import DocType
from frappe.utils import (
cint,
flt,
Expand Down Expand Up @@ -649,29 +650,33 @@ def validate_make_gl_entry(self):

cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
cwip_account = self.get_cwip_account(cwip_enabled=cwip_enabled)
asset_clearing_account = get_asset_account(
"asset_clearing_account", self.name, self.asset_category, self.company
)

query = """SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s"""
GL_Entry = DocType("GL Entry")
if asset_bought_with_invoice:
# with invoice purchase either expense or cwip has been booked
expense_booked = frappe.db.sql(query, (purchase_document, fixed_asset_account), as_dict=1)
expense_booked = (
frappe.qb.from_(GL_Entry)
.select(GL_Entry.name)
.where((GL_Entry.voucher_no == purchase_document) & (GL_Entry.account == fixed_asset_account))
).run(as_dict=True)
if expense_booked:
# if expense is already booked from invoice then do not make gl entries regardless of cwip enabled/disabled
return False

cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
if cwip_booked:
# if cwip is booked from invoice then make gl entries regardless of cwip enabled/disabled
return True
else:
# with receipt purchase either cwip has been booked or no entries have been made
if not cwip_account:
# if cwip account isn't available do not make gl entries
return False
clearing_account_recorded = (
frappe.qb.from_(GL_Entry)
.select(GL_Entry.name)
.where((GL_Entry.voucher_no == purchase_document) & (GL_Entry.account == asset_clearing_account))
).run(as_dict=True)

cwip_booked = (
frappe.qb.from_(GL_Entry)
.select(GL_Entry.name)
.where((GL_Entry.voucher_no == purchase_document) & (GL_Entry.account == cwip_account))
).run(as_dict=True)

cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
# if cwip is not booked from receipt then do not make gl entries
# if cwip is booked from receipt then make gl entries
return cwip_booked
return cwip_booked or clearing_account_recorded

def get_purchase_document(self):
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value(
Expand Down Expand Up @@ -716,15 +721,23 @@ def make_gl_entries(self):
gl_entries = []

purchase_document = self.get_purchase_document()
fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
fixed_asset_account = self.get_fixed_asset_account()
cwip_account = None
asset_clearing_account = None
if not is_cwip_accounting_enabled(self.asset_category):
asset_clearing_account = get_asset_account(
"asset_clearing_account", self.name, self.asset_category, self.company
)
else:
cwip_account = self.get_cwip_account()

if (self.is_composite_asset or (purchase_document and self.purchase_amount)) and getdate(
self.available_for_use_date
) <= getdate():
gl_entries.append(
self.get_gl_dict(
{
"account": cwip_account,
"account": cwip_account or asset_clearing_account,
"against": fixed_asset_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
Expand All @@ -740,7 +753,7 @@ def make_gl_entries(self):
self.get_gl_dict(
{
"account": fixed_asset_account,
"against": cwip_account,
"against": cwip_account or asset_clearing_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"debit": self.purchase_amount,
Expand Down Expand Up @@ -861,17 +874,16 @@ def make_post_gl_entry():
asset_categories = frappe.db.get_all("Asset Category", fields=["name", "enable_cwip_accounting"])

for asset_category in asset_categories:
if cint(asset_category.enable_cwip_accounting):
assets = frappe.db.sql_list(
""" select name from `tabAsset`
where asset_category = %s and ifnull(booked_fixed_asset, 0) = 0
and available_for_use_date = %s""",
(asset_category.name, nowdate()),
)
assets = frappe.db.sql_list(
""" select name from `tabAsset`
where asset_category = %s and ifnull(booked_fixed_asset, 0) = 0
and available_for_use_date = %s""",
(asset_category.name, nowdate()),
)

for asset in assets:
doc = frappe.get_doc("Asset", asset)
doc.make_gl_entries()
for asset in assets:
doc = frappe.get_doc("Asset", asset)
doc.make_gl_entries()


def get_asset_naming_series():
Expand Down
19 changes: 14 additions & 5 deletions erpnext/assets/doctype/asset/test_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ def test_expense_head(self):
pr = make_purchase_receipt(item_code="Macbook Pro", qty=2, rate=200000.0, location="Test Location")
doc = make_invoice(pr.name)

self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account)
self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].asset_account)

# Capital Work In Progress
def test_cwip_accounting(self):
Expand Down Expand Up @@ -618,7 +618,10 @@ def test_cwip_accounting(self):
)
asset_doc.submit()

expected_gle = (("_Test Fixed Asset - _TC", 5250.0, 0.0), ("CWIP Account - _TC", 0.0, 5250.0))
expected_gle = (
("_Test Fixed Asset - _TC", 5250.0, 0.0),
("CWIP Account - _TC", 0.0, 5250.0),
)

gle = get_gl_entries("Asset", asset_doc.name)
self.assertSequenceEqual(gle, expected_gle)
Expand All @@ -643,8 +646,12 @@ def test_asset_cwip_toggling_cases(self):
asset_doc.available_for_use_date = nowdate()
asset_doc.calculate_depreciation = 0
asset_doc.submit()
expected_gle = (
("_Test Clearing Account - _TC", 0.0, 200000.0),
("_Test Fixed Asset - _TC", 200000.0, 0.0),
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertFalse(gle)
self.assertEqual(gle, expected_gle)

# case 1 -- PR with cwip disabled, Asset with cwip enabled
pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
Expand All @@ -656,7 +663,7 @@ def test_asset_cwip_toggling_cases(self):
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = get_gl_entries("Asset", asset_doc.name)
self.assertFalse(gle)
self.assertTrue(gle)

# case 2 -- PR with cwip enabled, Asset with cwip disabled
pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
Expand All @@ -680,7 +687,7 @@ def test_asset_cwip_toggling_cases(self):
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = get_gl_entries("Asset", asset_doc.name)
self.assertFalse(gle)
self.assertTrue(gle)

# case 4 -- PI with cwip enabled, Asset with cwip disabled
pi = make_purchase_invoice(
Expand Down Expand Up @@ -1775,6 +1782,7 @@ def create_asset_category(enable_cwip=1):
{
"company_name": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC",
"asset_clearing_account": "_Test Clearing Account - _TC",
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
"depreciation_expense_account": "_Test Depreciations - _TC",
"capital_work_in_progress_account": "CWIP Account - _TC",
Expand All @@ -1785,6 +1793,7 @@ def create_asset_category(enable_cwip=1):
{
"company_name": "_Test Company with perpetual inventory",
"fixed_asset_account": "_Test Fixed Asset - TCP1",
"asset_clearing_account": "_Test Clearing Account - _TC",
"accumulated_depreciation_account": "_Test Accumulated Depreciations - TCP1",
"depreciation_expense_account": "_Test Depreciations - TCP1",
},
Expand Down
14 changes: 14 additions & 0 deletions erpnext/assets/doctype/asset_category/asset_category.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,18 @@ frappe.ui.form.on("Asset Category", {
};
});
},

enable_cwip_accounting: function (frm) {
if (!frm.doc.enable_cwip_accounting) {
frappe.call({
method: "erpnext.assets.doctype.asset_category.asset_category.validate_cwip_disable",
args: {
accounts: frm.doc.accounts,
},
error: function (err) {
frm.set_value("enable_cwip_accounting", 1);
},
});
}
},
});
Loading
Loading