Merge pull request #26159 from GangaManoj/asset-credit-note
This commit is contained in:
commit
bb493a0602
@ -13,7 +13,7 @@ from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
|
||||
from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
|
||||
from erpnext.assets.doctype.asset.depreciation \
|
||||
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
|
||||
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain
|
||||
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
|
||||
from erpnext.setup.doctype.company.company import update_company_current_month_sales
|
||||
@ -149,7 +149,7 @@ class SalesInvoice(SellingController):
|
||||
if self.update_stock:
|
||||
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
|
||||
|
||||
elif asset.status in ("Scrapped", "Cancelled", "Sold"):
|
||||
elif asset.status in ("Scrapped", "Cancelled") or (asset.status == "Sold" and not self.is_return):
|
||||
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
|
||||
|
||||
def validate_item_cost_centers(self):
|
||||
@ -918,22 +918,33 @@ class SalesInvoice(SellingController):
|
||||
for item in self.get("items"):
|
||||
if flt(item.base_net_amount, item.precision("base_net_amount")):
|
||||
if item.is_fixed_asset:
|
||||
asset = frappe.get_doc("Asset", item.asset)
|
||||
|
||||
if item.get('asset'):
|
||||
asset = frappe.get_doc("Asset", item.asset)
|
||||
else:
|
||||
frappe.throw(_(
|
||||
"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
|
||||
title=_("Missing Asset")
|
||||
)
|
||||
if (len(asset.finance_books) > 1 and not item.finance_book
|
||||
and asset.finance_books[0].finance_book):
|
||||
frappe.throw(_("Select finance book for the item {0} at row {1}")
|
||||
.format(item.item_code, item.idx))
|
||||
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
||||
item.base_net_amount, item.finance_book)
|
||||
if self.is_return:
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
|
||||
item.base_net_amount, item.finance_book)
|
||||
asset.db_set("disposal_date", None)
|
||||
else:
|
||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
||||
item.base_net_amount, item.finance_book)
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against"] = self.customer
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
|
||||
asset.db_set("disposal_date", self.posting_date)
|
||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||
self.set_asset_status(asset)
|
||||
|
||||
else:
|
||||
# Do not book income for transfer within same company
|
||||
if not self.is_internal_transfer():
|
||||
@ -959,6 +970,12 @@ class SalesInvoice(SellingController):
|
||||
erpnext.is_perpetual_inventory_enabled(self.company):
|
||||
gl_entries += super(SalesInvoice, self).get_gl_entries()
|
||||
|
||||
def set_asset_status(self, asset):
|
||||
if self.is_return:
|
||||
asset.set_status()
|
||||
else:
|
||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||
|
||||
def make_loyalty_point_redemption_gle(self, gl_entries):
|
||||
if cint(self.redeem_loyalty_points):
|
||||
gl_entries.append(
|
||||
|
@ -10,6 +10,7 @@ from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
||||
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||
from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
|
||||
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
|
||||
from frappe.model.naming import make_autoname
|
||||
@ -1069,6 +1070,36 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertFalse(si1.outstanding_amount)
|
||||
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
|
||||
|
||||
def test_gle_made_when_asset_is_returned(self):
|
||||
create_asset_data()
|
||||
asset = create_asset(item_code="Macbook Pro")
|
||||
|
||||
si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000)
|
||||
return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000)
|
||||
|
||||
disposal_account = frappe.get_cached_value("Company", "_Test Company", "disposal_account")
|
||||
|
||||
# Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000
|
||||
loss_for_si = frappe.get_all(
|
||||
"GL Entry",
|
||||
filters = {
|
||||
"voucher_no": si.name,
|
||||
"account": disposal_account
|
||||
},
|
||||
fields = ["credit", "debit"]
|
||||
)[0]
|
||||
|
||||
loss_for_return_si = frappe.get_all(
|
||||
"GL Entry",
|
||||
filters = {
|
||||
"voucher_no": return_si.name,
|
||||
"account": disposal_account
|
||||
},
|
||||
fields = ["credit", "debit"]
|
||||
)[0]
|
||||
|
||||
self.assertEqual(loss_for_si['credit'], loss_for_return_si['debit'])
|
||||
self.assertEqual(loss_for_si['debit'], loss_for_return_si['credit'])
|
||||
|
||||
def test_discount_on_net_total(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
@ -2164,6 +2195,7 @@ def create_sales_invoice(**args):
|
||||
"rate": args.rate if args.get("rate") is not None else 100,
|
||||
"income_account": args.income_account or "Sales - _TC",
|
||||
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
|
||||
"asset": args.asset or None,
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no,
|
||||
"conversion_factor": 1
|
||||
|
@ -743,7 +743,6 @@
|
||||
"fieldname": "asset",
|
||||
"fieldtype": "Link",
|
||||
"label": "Asset",
|
||||
"no_copy": 1,
|
||||
"options": "Asset"
|
||||
},
|
||||
{
|
||||
@ -826,7 +825,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-02-23 01:05:22.123527",
|
||||
"modified": "2021-06-21 23:03:11.599901",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
@ -176,22 +176,34 @@ def restore_asset(asset_name):
|
||||
|
||||
asset.set_status()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_gl_entries_on_asset_regain(asset, selling_amount=0, finance_book=None):
|
||||
fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation = \
|
||||
get_asset_details(asset, finance_book)
|
||||
|
||||
gl_entries = [
|
||||
{
|
||||
"account": fixed_asset_account,
|
||||
"debit_in_account_currency": asset.gross_purchase_amount,
|
||||
"debit": asset.gross_purchase_amount,
|
||||
"cost_center": depreciation_cost_center
|
||||
},
|
||||
{
|
||||
"account": accumulated_depr_account,
|
||||
"credit_in_account_currency": accumulated_depr_amount,
|
||||
"credit": accumulated_depr_amount,
|
||||
"cost_center": depreciation_cost_center
|
||||
}
|
||||
]
|
||||
|
||||
profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
|
||||
if profit_amount:
|
||||
get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None):
|
||||
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
|
||||
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
|
||||
depreciation_cost_center = asset.cost_center or depreciation_cost_center
|
||||
|
||||
idx = 1
|
||||
if finance_book:
|
||||
for d in asset.finance_books:
|
||||
if d.finance_book == finance_book:
|
||||
idx = d.idx
|
||||
break
|
||||
|
||||
value_after_depreciation = (asset.finance_books[idx - 1].value_after_depreciation
|
||||
if asset.calculate_depreciation else asset.value_after_depreciation)
|
||||
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation)
|
||||
fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation = \
|
||||
get_asset_details(asset, finance_book)
|
||||
|
||||
gl_entries = [
|
||||
{
|
||||
@ -210,16 +222,37 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None)
|
||||
|
||||
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
|
||||
if profit_amount:
|
||||
debit_or_credit = "debit" if profit_amount < 0 else "credit"
|
||||
gl_entries.append({
|
||||
"account": disposal_account,
|
||||
"cost_center": depreciation_cost_center,
|
||||
debit_or_credit: abs(profit_amount),
|
||||
debit_or_credit + "_in_account_currency": abs(profit_amount)
|
||||
})
|
||||
get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_asset_details(asset, finance_book=None):
|
||||
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
|
||||
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
|
||||
depreciation_cost_center = asset.cost_center or depreciation_cost_center
|
||||
|
||||
idx = 1
|
||||
if finance_book:
|
||||
for d in asset.finance_books:
|
||||
if d.finance_book == finance_book:
|
||||
idx = d.idx
|
||||
break
|
||||
|
||||
value_after_depreciation = (asset.finance_books[idx - 1].value_after_depreciation
|
||||
if asset.calculate_depreciation else asset.value_after_depreciation)
|
||||
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(value_after_depreciation)
|
||||
|
||||
return fixed_asset_account, asset, depreciation_cost_center, accumulated_depr_account, accumulated_depr_amount, disposal_account, value_after_depreciation
|
||||
|
||||
def get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center):
|
||||
debit_or_credit = "debit" if profit_amount < 0 else "credit"
|
||||
gl_entries.append({
|
||||
"account": disposal_account,
|
||||
"cost_center": depreciation_cost_center,
|
||||
debit_or_credit: abs(profit_amount),
|
||||
debit_or_credit + "_in_account_currency": abs(profit_amount)
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_disposal_account_and_cost_center(company):
|
||||
disposal_account, depreciation_cost_center = frappe.get_cached_value('Company', company,
|
||||
|
@ -587,8 +587,8 @@ def make_item_variant():
|
||||
test_records = frappe.get_test_records('Item')
|
||||
|
||||
def create_item(item_code, is_stock_item=1, valuation_rate=0, warehouse="_Test Warehouse - _TC",
|
||||
is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=0,
|
||||
company="_Test Company"):
|
||||
is_customer_provided_item=None, customer=None, is_purchase_item=None, opening_stock=0, is_fixed_asset=0,
|
||||
asset_category=None, company="_Test Company"):
|
||||
if not frappe.db.exists("Item", item_code):
|
||||
item = frappe.new_doc("Item")
|
||||
item.item_code = item_code
|
||||
@ -596,6 +596,8 @@ def create_item(item_code, is_stock_item=1, valuation_rate=0, warehouse="_Test W
|
||||
item.description = item_code
|
||||
item.item_group = "All Item Groups"
|
||||
item.is_stock_item = is_stock_item
|
||||
item.is_fixed_asset = is_fixed_asset
|
||||
item.asset_category = asset_category
|
||||
item.opening_stock = opening_stock
|
||||
item.valuation_rate = valuation_rate
|
||||
item.is_purchase_item = is_purchase_item
|
||||
|
Loading…
x
Reference in New Issue
Block a user