mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-01 19:29:10 +00:00
style: format code with black
This commit is contained in:
@@ -18,30 +18,36 @@ def get_data():
|
||||
if not fiscal_year:
|
||||
return frappe._dict()
|
||||
|
||||
year_start_date = get_date_str(fiscal_year.get('year_start_date'))
|
||||
year_end_date = get_date_str(fiscal_year.get('year_end_date'))
|
||||
year_start_date = get_date_str(fiscal_year.get("year_start_date"))
|
||||
year_end_date = get_date_str(fiscal_year.get("year_end_date"))
|
||||
|
||||
return frappe._dict(
|
||||
{
|
||||
"dashboards": get_dashboards(),
|
||||
"charts": get_charts(fiscal_year, year_start_date, year_end_date),
|
||||
"number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date),
|
||||
}
|
||||
)
|
||||
|
||||
return frappe._dict({
|
||||
"dashboards": get_dashboards(),
|
||||
"charts": get_charts(fiscal_year, year_start_date, year_end_date),
|
||||
"number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date),
|
||||
})
|
||||
|
||||
def get_dashboards():
|
||||
return [{
|
||||
"name": "Asset",
|
||||
"dashboard_name": "Asset",
|
||||
"charts": [
|
||||
{ "chart": "Asset Value Analytics", "width": "Full" },
|
||||
{ "chart": "Category-wise Asset Value", "width": "Half" },
|
||||
{ "chart": "Location-wise Asset Value", "width": "Half" },
|
||||
],
|
||||
"cards": [
|
||||
{"card": "Total Assets"},
|
||||
{"card": "New Assets (This Year)"},
|
||||
{"card": "Asset Value"}
|
||||
]
|
||||
}]
|
||||
return [
|
||||
{
|
||||
"name": "Asset",
|
||||
"dashboard_name": "Asset",
|
||||
"charts": [
|
||||
{"chart": "Asset Value Analytics", "width": "Full"},
|
||||
{"chart": "Category-wise Asset Value", "width": "Half"},
|
||||
{"chart": "Location-wise Asset Value", "width": "Half"},
|
||||
],
|
||||
"cards": [
|
||||
{"card": "Total Assets"},
|
||||
{"card": "New Assets (This Year)"},
|
||||
{"card": "Asset Value"},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||
company = get_company_for_dashboards()
|
||||
@@ -58,26 +64,30 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||
"timespan": "Last Year",
|
||||
"time_interval": "Yearly",
|
||||
"timeseries": 0,
|
||||
"filters_json": json.dumps({
|
||||
"company": company,
|
||||
"status": "In Location",
|
||||
"filter_based_on": "Fiscal Year",
|
||||
"from_fiscal_year": fiscal_year.get('name'),
|
||||
"to_fiscal_year": fiscal_year.get('name'),
|
||||
"period_start_date": year_start_date,
|
||||
"period_end_date": year_end_date,
|
||||
"date_based_on": "Purchase Date",
|
||||
"group_by": "--Select a group--"
|
||||
}),
|
||||
"filters_json": json.dumps(
|
||||
{
|
||||
"company": company,
|
||||
"status": "In Location",
|
||||
"filter_based_on": "Fiscal Year",
|
||||
"from_fiscal_year": fiscal_year.get("name"),
|
||||
"to_fiscal_year": fiscal_year.get("name"),
|
||||
"period_start_date": year_start_date,
|
||||
"period_end_date": year_end_date,
|
||||
"date_based_on": "Purchase Date",
|
||||
"group_by": "--Select a group--",
|
||||
}
|
||||
),
|
||||
"type": "Bar",
|
||||
"custom_options": json.dumps({
|
||||
"type": "bar",
|
||||
"barOptions": { "stacked": 1 },
|
||||
"axisOptions": { "shortenYAxisNumbers": 1 },
|
||||
"tooltipOptions": {}
|
||||
}),
|
||||
"custom_options": json.dumps(
|
||||
{
|
||||
"type": "bar",
|
||||
"barOptions": {"stacked": 1},
|
||||
"axisOptions": {"shortenYAxisNumbers": 1},
|
||||
"tooltipOptions": {},
|
||||
}
|
||||
),
|
||||
"doctype": "Dashboard Chart",
|
||||
"y_axis": []
|
||||
"y_axis": [],
|
||||
},
|
||||
{
|
||||
"name": "Category-wise Asset Value",
|
||||
@@ -86,12 +96,14 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||
"report_name": "Fixed Asset Register",
|
||||
"x_field": "asset_category",
|
||||
"timeseries": 0,
|
||||
"filters_json": json.dumps({
|
||||
"company": company,
|
||||
"status":"In Location",
|
||||
"group_by":"Asset Category",
|
||||
"is_existing_asset":0
|
||||
}),
|
||||
"filters_json": json.dumps(
|
||||
{
|
||||
"company": company,
|
||||
"status": "In Location",
|
||||
"group_by": "Asset Category",
|
||||
"is_existing_asset": 0,
|
||||
}
|
||||
),
|
||||
"type": "Donut",
|
||||
"doctype": "Dashboard Chart",
|
||||
"y_axis": [
|
||||
@@ -100,14 +112,12 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||
"parentfield": "y_axis",
|
||||
"parenttype": "Dashboard Chart",
|
||||
"y_field": "asset_value",
|
||||
"doctype": "Dashboard Chart Field"
|
||||
"doctype": "Dashboard Chart Field",
|
||||
}
|
||||
],
|
||||
"custom_options": json.dumps({
|
||||
"type": "donut",
|
||||
"height": 300,
|
||||
"axisOptions": {"shortenYAxisNumbers": 1}
|
||||
})
|
||||
"custom_options": json.dumps(
|
||||
{"type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1}}
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "Location-wise Asset Value",
|
||||
@@ -116,12 +126,9 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||
"report_name": "Fixed Asset Register",
|
||||
"x_field": "location",
|
||||
"timeseries": 0,
|
||||
"filters_json": json.dumps({
|
||||
"company": company,
|
||||
"status":"In Location",
|
||||
"group_by":"Location",
|
||||
"is_existing_asset":0
|
||||
}),
|
||||
"filters_json": json.dumps(
|
||||
{"company": company, "status": "In Location", "group_by": "Location", "is_existing_asset": 0}
|
||||
),
|
||||
"type": "Donut",
|
||||
"doctype": "Dashboard Chart",
|
||||
"y_axis": [
|
||||
@@ -130,17 +137,16 @@ def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||
"parentfield": "y_axis",
|
||||
"parenttype": "Dashboard Chart",
|
||||
"y_field": "asset_value",
|
||||
"doctype": "Dashboard Chart Field"
|
||||
"doctype": "Dashboard Chart Field",
|
||||
}
|
||||
],
|
||||
"custom_options": json.dumps({
|
||||
"type": "donut",
|
||||
"height": 300,
|
||||
"axisOptions": {"shortenYAxisNumbers": 1}
|
||||
})
|
||||
}
|
||||
"custom_options": json.dumps(
|
||||
{"type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1}}
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_number_cards(fiscal_year, year_start_date, year_end_date):
|
||||
return [
|
||||
{
|
||||
@@ -162,9 +168,9 @@ def get_number_cards(fiscal_year, year_start_date, year_end_date):
|
||||
"is_public": 1,
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Monthly",
|
||||
"filters_json": json.dumps([
|
||||
['Asset', 'creation', 'between', [year_start_date, year_end_date]]
|
||||
]),
|
||||
"filters_json": json.dumps(
|
||||
[["Asset", "creation", "between", [year_start_date, year_end_date]]]
|
||||
),
|
||||
"doctype": "Number Card",
|
||||
},
|
||||
{
|
||||
@@ -177,6 +183,6 @@ def get_number_cards(fiscal_year, year_start_date, year_end_date):
|
||||
"show_percentage_stats": 1,
|
||||
"stats_time_interval": "Monthly",
|
||||
"filters_json": "[]",
|
||||
"doctype": "Number Card"
|
||||
}
|
||||
"doctype": "Number Card",
|
||||
},
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,6 @@ from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'non_standard_fieldnames': {
|
||||
'Asset Movement': 'asset'
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Movement'),
|
||||
'items': ['Asset Movement']
|
||||
}
|
||||
]
|
||||
"non_standard_fieldnames": {"Asset Movement": "asset"},
|
||||
"transactions": [{"label": _("Movement"), "items": ["Asset Movement"]}],
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
|
||||
def post_depreciation_entries(date=None):
|
||||
# Return if automatic booking of asset depreciation is disabled
|
||||
if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
|
||||
if not cint(
|
||||
frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")
|
||||
):
|
||||
return
|
||||
|
||||
if not date:
|
||||
@@ -22,26 +24,35 @@ def post_depreciation_entries(date=None):
|
||||
make_depreciation_entry(asset, date)
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
def get_depreciable_assets(date):
|
||||
return frappe.db.sql_list("""select distinct a.name
|
||||
return frappe.db.sql_list(
|
||||
"""select distinct a.name
|
||||
from tabAsset a, `tabDepreciation Schedule` ds
|
||||
where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s and a.calculate_depreciation = 1
|
||||
and a.status in ('Submitted', 'Partially Depreciated')
|
||||
and ifnull(ds.journal_entry, '')=''""", date)
|
||||
and ifnull(ds.journal_entry, '')=''""",
|
||||
date,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_depreciation_entry(asset_name, date=None):
|
||||
frappe.has_permission('Journal Entry', throw=True)
|
||||
frappe.has_permission("Journal Entry", throw=True)
|
||||
|
||||
if not date:
|
||||
date = today()
|
||||
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
|
||||
get_depreciation_accounts(asset)
|
||||
(
|
||||
fixed_asset_account,
|
||||
accumulated_depreciation_account,
|
||||
depreciation_expense_account,
|
||||
) = get_depreciation_accounts(asset)
|
||||
|
||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company,
|
||||
["depreciation_cost_center", "series_for_depreciation_entry"])
|
||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value(
|
||||
"Company", asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]
|
||||
)
|
||||
|
||||
depreciation_cost_center = asset.cost_center or depreciation_cost_center
|
||||
|
||||
@@ -57,14 +68,16 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
je.finance_book = d.finance_book
|
||||
je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount)
|
||||
|
||||
credit_account, debit_account = get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account)
|
||||
credit_account, debit_account = get_credit_and_debit_accounts(
|
||||
accumulated_depreciation_account, depreciation_expense_account
|
||||
)
|
||||
|
||||
credit_entry = {
|
||||
"account": credit_account,
|
||||
"credit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name,
|
||||
"cost_center": depreciation_cost_center
|
||||
"cost_center": depreciation_cost_center,
|
||||
}
|
||||
|
||||
debit_entry = {
|
||||
@@ -72,19 +85,25 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
"debit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name,
|
||||
"cost_center": depreciation_cost_center
|
||||
"cost_center": depreciation_cost_center,
|
||||
}
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
if (asset.get(dimension['fieldname']) or dimension.get('mandatory_for_bs')):
|
||||
credit_entry.update({
|
||||
dimension['fieldname']: asset.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
if asset.get(dimension["fieldname"]) or dimension.get("mandatory_for_bs"):
|
||||
credit_entry.update(
|
||||
{
|
||||
dimension["fieldname"]: asset.get(dimension["fieldname"])
|
||||
or dimension.get("default_dimension")
|
||||
}
|
||||
)
|
||||
|
||||
if (asset.get(dimension['fieldname']) or dimension.get('mandatory_for_pl')):
|
||||
debit_entry.update({
|
||||
dimension['fieldname']: asset.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
if asset.get(dimension["fieldname"]) or dimension.get("mandatory_for_pl"):
|
||||
debit_entry.update(
|
||||
{
|
||||
dimension["fieldname"]: asset.get(dimension["fieldname"])
|
||||
or dimension.get("default_dimension")
|
||||
}
|
||||
)
|
||||
|
||||
je.append("accounts", credit_entry)
|
||||
|
||||
@@ -98,7 +117,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
d.db_set("journal_entry", je.name)
|
||||
|
||||
idx = cint(d.finance_book_id)
|
||||
finance_books = asset.get('finance_books')[idx - 1]
|
||||
finance_books = asset.get("finance_books")[idx - 1]
|
||||
finance_books.value_after_depreciation -= d.depreciation_amount
|
||||
finance_books.db_update()
|
||||
|
||||
@@ -106,13 +125,20 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
|
||||
return asset
|
||||
|
||||
|
||||
def get_depreciation_accounts(asset):
|
||||
fixed_asset_account = accumulated_depreciation_account = depreciation_expense_account = None
|
||||
|
||||
accounts = frappe.db.get_value("Asset Category Account",
|
||||
filters={'parent': asset.asset_category, 'company_name': asset.company},
|
||||
fieldname = ['fixed_asset_account', 'accumulated_depreciation_account',
|
||||
'depreciation_expense_account'], as_dict=1)
|
||||
accounts = frappe.db.get_value(
|
||||
"Asset Category Account",
|
||||
filters={"parent": asset.asset_category, "company_name": asset.company},
|
||||
fieldname=[
|
||||
"fixed_asset_account",
|
||||
"accumulated_depreciation_account",
|
||||
"depreciation_expense_account",
|
||||
],
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
if accounts:
|
||||
fixed_asset_account = accounts.fixed_asset_account
|
||||
@@ -120,20 +146,29 @@ def get_depreciation_accounts(asset):
|
||||
depreciation_expense_account = accounts.depreciation_expense_account
|
||||
|
||||
if not accumulated_depreciation_account or not depreciation_expense_account:
|
||||
accounts = frappe.get_cached_value('Company', asset.company,
|
||||
["accumulated_depreciation_account", "depreciation_expense_account"])
|
||||
accounts = frappe.get_cached_value(
|
||||
"Company", asset.company, ["accumulated_depreciation_account", "depreciation_expense_account"]
|
||||
)
|
||||
|
||||
if not accumulated_depreciation_account:
|
||||
accumulated_depreciation_account = accounts[0]
|
||||
if not depreciation_expense_account:
|
||||
depreciation_expense_account = accounts[1]
|
||||
|
||||
if not fixed_asset_account or not accumulated_depreciation_account or not depreciation_expense_account:
|
||||
frappe.throw(_("Please set Depreciation related Accounts in Asset Category {0} or Company {1}")
|
||||
.format(asset.asset_category, asset.company))
|
||||
if (
|
||||
not fixed_asset_account
|
||||
or not accumulated_depreciation_account
|
||||
or not depreciation_expense_account
|
||||
):
|
||||
frappe.throw(
|
||||
_("Please set Depreciation related Accounts in Asset Category {0} or Company {1}").format(
|
||||
asset.asset_category, asset.company
|
||||
)
|
||||
)
|
||||
|
||||
return fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account
|
||||
|
||||
|
||||
def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account):
|
||||
root_type = frappe.get_value("Account", depreciation_expense_account, "root_type")
|
||||
|
||||
@@ -148,6 +183,7 @@ def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation
|
||||
|
||||
return credit_account, debit_account
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def scrap_asset(asset_name):
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
@@ -155,9 +191,13 @@ def scrap_asset(asset_name):
|
||||
if asset.docstatus != 1:
|
||||
frappe.throw(_("Asset {0} must be submitted").format(asset.name))
|
||||
elif asset.status in ("Cancelled", "Sold", "Scrapped"):
|
||||
frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
|
||||
frappe.throw(
|
||||
_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)
|
||||
)
|
||||
|
||||
depreciation_series = frappe.get_cached_value('Company', asset.company, "series_for_depreciation_entry")
|
||||
depreciation_series = frappe.get_cached_value(
|
||||
"Company", asset.company, "series_for_depreciation_entry"
|
||||
)
|
||||
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.voucher_type = "Journal Entry"
|
||||
@@ -167,10 +207,7 @@ def scrap_asset(asset_name):
|
||||
je.remark = "Scrap Entry for asset {0}".format(asset_name)
|
||||
|
||||
for entry in get_gl_entries_on_asset_disposal(asset):
|
||||
entry.update({
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset_name
|
||||
})
|
||||
entry.update({"reference_type": "Asset", "reference_name": asset_name})
|
||||
je.append("accounts", entry)
|
||||
|
||||
je.flags.ignore_permissions = True
|
||||
@@ -182,6 +219,7 @@ def scrap_asset(asset_name):
|
||||
|
||||
frappe.msgprint(_("Asset scrapped via Journal Entry {0}").format(je.name))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def restore_asset(asset_name):
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
@@ -195,23 +233,31 @@ def restore_asset(asset_name):
|
||||
|
||||
asset.set_status()
|
||||
|
||||
|
||||
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)
|
||||
(
|
||||
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
|
||||
"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
|
||||
}
|
||||
"cost_center": depreciation_cost_center,
|
||||
},
|
||||
]
|
||||
|
||||
profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
|
||||
@@ -220,23 +266,31 @@ def get_gl_entries_on_asset_regain(asset, selling_amount=0, finance_book=None):
|
||||
|
||||
return gl_entries
|
||||
|
||||
|
||||
def get_gl_entries_on_asset_disposal(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)
|
||||
(
|
||||
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,
|
||||
"credit_in_account_currency": asset.gross_purchase_amount,
|
||||
"credit": asset.gross_purchase_amount,
|
||||
"cost_center": depreciation_cost_center
|
||||
"cost_center": depreciation_cost_center,
|
||||
},
|
||||
{
|
||||
"account": accumulated_depr_account,
|
||||
"debit_in_account_currency": accumulated_depr_amount,
|
||||
"debit": accumulated_depr_amount,
|
||||
"cost_center": depreciation_cost_center
|
||||
}
|
||||
"cost_center": depreciation_cost_center,
|
||||
},
|
||||
]
|
||||
|
||||
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
|
||||
@@ -245,8 +299,11 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None)
|
||||
|
||||
return gl_entries
|
||||
|
||||
|
||||
def get_asset_details(asset, finance_book=None):
|
||||
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
|
||||
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
|
||||
|
||||
@@ -257,28 +314,46 @@ def get_asset_details(asset, finance_book=None):
|
||||
idx = d.idx
|
||||
break
|
||||
|
||||
value_after_depreciation = (asset.finance_books[idx - 1].value_after_depreciation
|
||||
if asset.calculate_depreciation else asset.value_after_depreciation)
|
||||
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
|
||||
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)
|
||||
})
|
||||
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,
|
||||
["disposal_account", "depreciation_cost_center"])
|
||||
disposal_account, depreciation_cost_center = frappe.get_cached_value(
|
||||
"Company", company, ["disposal_account", "depreciation_cost_center"]
|
||||
)
|
||||
|
||||
if not disposal_account:
|
||||
frappe.throw(_("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company))
|
||||
frappe.throw(
|
||||
_("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company)
|
||||
)
|
||||
if not depreciation_cost_center:
|
||||
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,79 +18,104 @@ class AssetCategory(Document):
|
||||
def validate_finance_books(self):
|
||||
for d in self.finance_books:
|
||||
for field in ("Total Number of Depreciations", "Frequency of Depreciation"):
|
||||
if cint(d.get(frappe.scrub(field)))<1:
|
||||
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
|
||||
if cint(d.get(frappe.scrub(field))) < 1:
|
||||
frappe.throw(
|
||||
_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError
|
||||
)
|
||||
|
||||
def validate_account_currency(self):
|
||||
account_types = [
|
||||
'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account'
|
||||
"fixed_asset_account",
|
||||
"accumulated_depreciation_account",
|
||||
"depreciation_expense_account",
|
||||
"capital_work_in_progress_account",
|
||||
]
|
||||
invalid_accounts = []
|
||||
for d in self.accounts:
|
||||
company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency')
|
||||
company_currency = frappe.get_value("Company", d.get("company_name"), "default_currency")
|
||||
for type_of_account in account_types:
|
||||
if d.get(type_of_account):
|
||||
account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency")
|
||||
if account_currency != company_currency:
|
||||
invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) }))
|
||||
invalid_accounts.append(
|
||||
frappe._dict({"type": type_of_account, "idx": d.idx, "account": d.get(type_of_account)})
|
||||
)
|
||||
|
||||
for d in invalid_accounts:
|
||||
frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.")
|
||||
.format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)),
|
||||
title=_("Invalid Account"))
|
||||
|
||||
frappe.throw(
|
||||
_("Row #{}: Currency of {} - {} doesn't matches company currency.").format(
|
||||
d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)
|
||||
),
|
||||
title=_("Invalid Account"),
|
||||
)
|
||||
|
||||
def validate_account_types(self):
|
||||
account_type_map = {
|
||||
'fixed_asset_account': {'account_type': ['Fixed Asset']},
|
||||
'accumulated_depreciation_account': {'account_type': ['Accumulated Depreciation']},
|
||||
'depreciation_expense_account': {'root_type': ['Expense', 'Income']},
|
||||
'capital_work_in_progress_account': {'account_type': ['Capital Work in Progress']}
|
||||
"fixed_asset_account": {"account_type": ["Fixed Asset"]},
|
||||
"accumulated_depreciation_account": {"account_type": ["Accumulated Depreciation"]},
|
||||
"depreciation_expense_account": {"root_type": ["Expense", "Income"]},
|
||||
"capital_work_in_progress_account": {"account_type": ["Capital Work in Progress"]},
|
||||
}
|
||||
for d in self.accounts:
|
||||
for fieldname in account_type_map.keys():
|
||||
if d.get(fieldname):
|
||||
selected_account = d.get(fieldname)
|
||||
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
|
||||
selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
|
||||
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
|
||||
selected_key_type = frappe.db.get_value("Account", selected_account, key_to_match)
|
||||
expected_key_types = account_type_map[fieldname][key_to_match]
|
||||
|
||||
if selected_key_type not in expected_key_types:
|
||||
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
|
||||
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_types)),
|
||||
title=_("Invalid Account"))
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{}: {} of {} should be {}. Please modify the account or select a different account."
|
||||
).format(
|
||||
d.idx,
|
||||
frappe.unscrub(key_to_match),
|
||||
frappe.bold(selected_account),
|
||||
frappe.bold(expected_key_types),
|
||||
),
|
||||
title=_("Invalid Account"),
|
||||
)
|
||||
|
||||
def valide_cwip_account(self):
|
||||
if self.enable_cwip_accounting:
|
||||
missing_cwip_accounts_for_company = []
|
||||
for d in self.accounts:
|
||||
if (not d.capital_work_in_progress_account and
|
||||
not frappe.db.get_value("Company", d.company_name, "capital_work_in_progress_account")):
|
||||
if not d.capital_work_in_progress_account and not frappe.db.get_value(
|
||||
"Company", d.company_name, "capital_work_in_progress_account"
|
||||
):
|
||||
missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name))
|
||||
|
||||
if missing_cwip_accounts_for_company:
|
||||
msg = _("""To enable Capital Work in Progress Accounting, """)
|
||||
msg += _("""you must select Capital Work in Progress Account in accounts table""")
|
||||
msg += "<br><br>"
|
||||
msg += _("You can also set default CWIP account in Company {}").format(", ".join(missing_cwip_accounts_for_company))
|
||||
msg += _("You can also set default CWIP account in Company {}").format(
|
||||
", ".join(missing_cwip_accounts_for_company)
|
||||
)
|
||||
frappe.throw(msg, title=_("Missing Account"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
||||
def get_asset_category_account(
|
||||
fieldname, item=None, asset=None, account=None, asset_category=None, company=None
|
||||
):
|
||||
if item and frappe.db.get_value("Item", item, "is_fixed_asset"):
|
||||
asset_category = frappe.db.get_value("Item", item, ["asset_category"])
|
||||
|
||||
elif not asset_category or not company:
|
||||
if account:
|
||||
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
|
||||
account=None
|
||||
account = None
|
||||
|
||||
if not account:
|
||||
asset_details = frappe.db.get_value("Asset", asset, ["asset_category", "company"])
|
||||
asset_category, company = asset_details or [None, None]
|
||||
|
||||
account = frappe.db.get_value("Asset Category Account",
|
||||
filters={"parent": asset_category, "company_name": company}, fieldname=fieldname)
|
||||
account = frappe.db.get_value(
|
||||
"Asset Category Account",
|
||||
filters={"parent": asset_category, "company_name": company},
|
||||
fieldname=fieldname,
|
||||
)
|
||||
|
||||
return account
|
||||
|
||||
@@ -15,12 +15,15 @@ class TestAssetCategory(unittest.TestCase):
|
||||
|
||||
asset_category.total_number_of_depreciations = 3
|
||||
asset_category.frequency_of_depreciation = 3
|
||||
asset_category.append("accounts", {
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC"
|
||||
})
|
||||
asset_category.append(
|
||||
"accounts",
|
||||
{
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC",
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
asset_category.insert(ignore_if_duplicate=True)
|
||||
@@ -28,7 +31,9 @@ class TestAssetCategory(unittest.TestCase):
|
||||
pass
|
||||
|
||||
def test_cwip_accounting(self):
|
||||
company_cwip_acc = frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account")
|
||||
company_cwip_acc = frappe.db.get_value(
|
||||
"Company", "_Test Company", "capital_work_in_progress_account"
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "")
|
||||
|
||||
asset_category = frappe.new_doc("Asset Category")
|
||||
@@ -37,11 +42,14 @@ class TestAssetCategory(unittest.TestCase):
|
||||
|
||||
asset_category.total_number_of_depreciations = 3
|
||||
asset_category.frequency_of_depreciation = 3
|
||||
asset_category.append("accounts", {
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC"
|
||||
})
|
||||
asset_category.append(
|
||||
"accounts",
|
||||
{
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, asset_category.insert)
|
||||
|
||||
@@ -11,7 +11,7 @@ from frappe.utils import add_days, add_months, add_years, getdate, nowdate
|
||||
|
||||
class AssetMaintenance(Document):
|
||||
def validate(self):
|
||||
for task in self.get('asset_maintenance_tasks'):
|
||||
for task in self.get("asset_maintenance_tasks"):
|
||||
if task.end_date and (getdate(task.start_date) >= getdate(task.end_date)):
|
||||
throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task))
|
||||
if getdate(task.next_due_date) < getdate(nowdate()):
|
||||
@@ -20,83 +20,109 @@ class AssetMaintenance(Document):
|
||||
throw(_("Row #{}: Please asign task to a member.").format(task.idx))
|
||||
|
||||
def on_update(self):
|
||||
for task in self.get('asset_maintenance_tasks'):
|
||||
for task in self.get("asset_maintenance_tasks"):
|
||||
assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date)
|
||||
self.sync_maintenance_tasks()
|
||||
|
||||
def sync_maintenance_tasks(self):
|
||||
tasks_names = []
|
||||
for task in self.get('asset_maintenance_tasks'):
|
||||
for task in self.get("asset_maintenance_tasks"):
|
||||
tasks_names.append(task.name)
|
||||
update_maintenance_log(asset_maintenance = self.name, item_code = self.item_code, item_name = self.item_name, task = task)
|
||||
asset_maintenance_logs = frappe.get_all("Asset Maintenance Log", fields=["name"], filters = {"asset_maintenance": self.name,
|
||||
"task": ("not in", tasks_names)})
|
||||
update_maintenance_log(
|
||||
asset_maintenance=self.name, item_code=self.item_code, item_name=self.item_name, task=task
|
||||
)
|
||||
asset_maintenance_logs = frappe.get_all(
|
||||
"Asset Maintenance Log",
|
||||
fields=["name"],
|
||||
filters={"asset_maintenance": self.name, "task": ("not in", tasks_names)},
|
||||
)
|
||||
if asset_maintenance_logs:
|
||||
for asset_maintenance_log in asset_maintenance_logs:
|
||||
maintenance_log = frappe.get_doc('Asset Maintenance Log', asset_maintenance_log.name)
|
||||
maintenance_log.db_set('maintenance_status', 'Cancelled')
|
||||
maintenance_log = frappe.get_doc("Asset Maintenance Log", asset_maintenance_log.name)
|
||||
maintenance_log.db_set("maintenance_status", "Cancelled")
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date):
|
||||
team_member = frappe.db.get_value('User', assign_to_member, "email")
|
||||
team_member = frappe.db.get_value("User", assign_to_member, "email")
|
||||
args = {
|
||||
'doctype' : 'Asset Maintenance',
|
||||
'assign_to' : [team_member],
|
||||
'name' : asset_maintenance_name,
|
||||
'description' : maintenance_task,
|
||||
'date' : next_due_date
|
||||
"doctype": "Asset Maintenance",
|
||||
"assign_to": [team_member],
|
||||
"name": asset_maintenance_name,
|
||||
"description": maintenance_task,
|
||||
"date": next_due_date,
|
||||
}
|
||||
if not frappe.db.sql("""select owner from `tabToDo`
|
||||
if not frappe.db.sql(
|
||||
"""select owner from `tabToDo`
|
||||
where reference_type=%(doctype)s and reference_name=%(name)s and status="Open"
|
||||
and owner=%(assign_to)s""", args):
|
||||
and owner=%(assign_to)s""",
|
||||
args,
|
||||
):
|
||||
assign_to.add(args)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def calculate_next_due_date(periodicity, start_date = None, end_date = None, last_completion_date = None, next_due_date = None):
|
||||
def calculate_next_due_date(
|
||||
periodicity, start_date=None, end_date=None, last_completion_date=None, next_due_date=None
|
||||
):
|
||||
if not start_date and not last_completion_date:
|
||||
start_date = frappe.utils.now()
|
||||
|
||||
if last_completion_date and ((start_date and last_completion_date > start_date) or not start_date):
|
||||
if last_completion_date and (
|
||||
(start_date and last_completion_date > start_date) or not start_date
|
||||
):
|
||||
start_date = last_completion_date
|
||||
if periodicity == 'Daily':
|
||||
if periodicity == "Daily":
|
||||
next_due_date = add_days(start_date, 1)
|
||||
if periodicity == 'Weekly':
|
||||
if periodicity == "Weekly":
|
||||
next_due_date = add_days(start_date, 7)
|
||||
if periodicity == 'Monthly':
|
||||
if periodicity == "Monthly":
|
||||
next_due_date = add_months(start_date, 1)
|
||||
if periodicity == 'Yearly':
|
||||
if periodicity == "Yearly":
|
||||
next_due_date = add_years(start_date, 1)
|
||||
if periodicity == '2 Yearly':
|
||||
if periodicity == "2 Yearly":
|
||||
next_due_date = add_years(start_date, 2)
|
||||
if periodicity == 'Quarterly':
|
||||
if periodicity == "Quarterly":
|
||||
next_due_date = add_months(start_date, 3)
|
||||
if end_date and ((start_date and start_date >= end_date) or (last_completion_date and last_completion_date >= end_date) or next_due_date):
|
||||
if end_date and (
|
||||
(start_date and start_date >= end_date)
|
||||
or (last_completion_date and last_completion_date >= end_date)
|
||||
or next_due_date
|
||||
):
|
||||
next_due_date = ""
|
||||
return next_due_date
|
||||
|
||||
|
||||
def update_maintenance_log(asset_maintenance, item_code, item_name, task):
|
||||
asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance,
|
||||
"task": task.name, "maintenance_status": ('in',['Planned','Overdue'])})
|
||||
asset_maintenance_log = frappe.get_value(
|
||||
"Asset Maintenance Log",
|
||||
{
|
||||
"asset_maintenance": asset_maintenance,
|
||||
"task": task.name,
|
||||
"maintenance_status": ("in", ["Planned", "Overdue"]),
|
||||
},
|
||||
)
|
||||
|
||||
if not asset_maintenance_log:
|
||||
asset_maintenance_log = frappe.get_doc({
|
||||
"doctype": "Asset Maintenance Log",
|
||||
"asset_maintenance": asset_maintenance,
|
||||
"asset_name": asset_maintenance,
|
||||
"item_code": item_code,
|
||||
"item_name": item_name,
|
||||
"task": task.name,
|
||||
"has_certificate": task.certificate_required,
|
||||
"description": task.description,
|
||||
"assign_to_name": task.assign_to_name,
|
||||
"periodicity": str(task.periodicity),
|
||||
"maintenance_type": task.maintenance_type,
|
||||
"due_date": task.next_due_date
|
||||
})
|
||||
asset_maintenance_log = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Asset Maintenance Log",
|
||||
"asset_maintenance": asset_maintenance,
|
||||
"asset_name": asset_maintenance,
|
||||
"item_code": item_code,
|
||||
"item_name": item_name,
|
||||
"task": task.name,
|
||||
"has_certificate": task.certificate_required,
|
||||
"description": task.description,
|
||||
"assign_to_name": task.assign_to_name,
|
||||
"periodicity": str(task.periodicity),
|
||||
"maintenance_type": task.maintenance_type,
|
||||
"due_date": task.next_due_date,
|
||||
}
|
||||
)
|
||||
asset_maintenance_log.insert()
|
||||
else:
|
||||
maintenance_log = frappe.get_doc('Asset Maintenance Log', asset_maintenance_log)
|
||||
maintenance_log = frappe.get_doc("Asset Maintenance Log", asset_maintenance_log)
|
||||
maintenance_log.assign_to_name = task.assign_to_name
|
||||
maintenance_log.has_certificate = task.certificate_required
|
||||
maintenance_log.description = task.description
|
||||
@@ -105,15 +131,22 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
|
||||
maintenance_log.due_date = task.next_due_date
|
||||
maintenance_log.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_team_members(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") }, "team_member")
|
||||
return frappe.db.get_values(
|
||||
"Maintenance Team Member", {"parent": filters.get("maintenance_team")}, "team_member"
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_maintenance_log(asset_name):
|
||||
return frappe.db.sql("""
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
select maintenance_status, count(asset_name) as count, asset_name
|
||||
from `tabAsset Maintenance Log`
|
||||
where asset_name=%s group by maintenance_status""",
|
||||
(asset_name), as_dict=1)
|
||||
(asset_name),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
@@ -17,11 +17,12 @@ class TestAssetMaintenance(unittest.TestCase):
|
||||
create_maintenance_team()
|
||||
|
||||
def test_create_asset_maintenance(self):
|
||||
pr = make_purchase_receipt(item_code="Photocopier",
|
||||
qty=1, rate=100000.0, location="Test Location")
|
||||
pr = make_purchase_receipt(
|
||||
item_code="Photocopier", qty=1, rate=100000.0, location="Test Location"
|
||||
)
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset_name)
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
|
||||
asset_doc = frappe.get_doc("Asset", asset_name)
|
||||
month_end_date = get_last_day(nowdate())
|
||||
|
||||
purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
|
||||
@@ -30,66 +31,74 @@ class TestAssetMaintenance(unittest.TestCase):
|
||||
asset_doc.purchase_date = purchase_date
|
||||
|
||||
asset_doc.calculate_depreciation = 1
|
||||
asset_doc.append("finance_books", {
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date
|
||||
})
|
||||
asset_doc.append(
|
||||
"finance_books",
|
||||
{
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date,
|
||||
},
|
||||
)
|
||||
|
||||
asset_doc.save()
|
||||
|
||||
if not frappe.db.exists("Asset Maintenance", "Photocopier"):
|
||||
asset_maintenance = frappe.get_doc({
|
||||
asset_maintenance = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Asset Maintenance",
|
||||
"asset_name": "Photocopier",
|
||||
"maintenance_team": "Team Awesome",
|
||||
"company": "_Test Company",
|
||||
"asset_maintenance_tasks": get_maintenance_tasks()
|
||||
}).insert()
|
||||
"asset_maintenance_tasks": get_maintenance_tasks(),
|
||||
}
|
||||
).insert()
|
||||
|
||||
next_due_date = calculate_next_due_date(nowdate(), "Monthly")
|
||||
self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
|
||||
|
||||
def test_create_asset_maintenance_log(self):
|
||||
if not frappe.db.exists("Asset Maintenance Log", "Photocopier"):
|
||||
asset_maintenance_log = frappe.get_doc({
|
||||
asset_maintenance_log = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Asset Maintenance Log",
|
||||
"asset_maintenance": "Photocopier",
|
||||
"task": "Change Oil",
|
||||
"completion_date": add_days(nowdate(), 2),
|
||||
"maintenance_status": "Completed"
|
||||
}).insert()
|
||||
"maintenance_status": "Completed",
|
||||
}
|
||||
).insert()
|
||||
asset_maintenance = frappe.get_doc("Asset Maintenance", "Photocopier")
|
||||
next_due_date = calculate_next_due_date(asset_maintenance_log.completion_date, "Monthly")
|
||||
self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
|
||||
|
||||
|
||||
def create_asset_data():
|
||||
if not frappe.db.exists("Asset Category", "Equipment"):
|
||||
create_asset_category()
|
||||
|
||||
if not frappe.db.exists("Location", "Test Location"):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Location',
|
||||
'location_name': 'Test Location'
|
||||
}).insert()
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
|
||||
|
||||
if not frappe.db.exists("Item", "Photocopier"):
|
||||
meta = frappe.get_meta('Asset')
|
||||
meta = frappe.get_meta("Asset")
|
||||
naming_series = meta.get_field("naming_series").options
|
||||
frappe.get_doc({
|
||||
"doctype": "Item",
|
||||
"item_code": "Photocopier",
|
||||
"item_name": "Photocopier",
|
||||
"item_group": "All Item Groups",
|
||||
"company": "_Test Company",
|
||||
"is_fixed_asset": 1,
|
||||
"is_stock_item": 0,
|
||||
"asset_category": "Equipment",
|
||||
"auto_create_assets": 1,
|
||||
"asset_naming_series": naming_series
|
||||
}).insert()
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": "Photocopier",
|
||||
"item_name": "Photocopier",
|
||||
"item_group": "All Item Groups",
|
||||
"company": "_Test Company",
|
||||
"is_fixed_asset": 1,
|
||||
"is_stock_item": 0,
|
||||
"asset_category": "Equipment",
|
||||
"auto_create_assets": 1,
|
||||
"asset_naming_series": naming_series,
|
||||
}
|
||||
).insert()
|
||||
|
||||
|
||||
def create_maintenance_team():
|
||||
user_list = ["marcus@abc.com", "thalia@abc.com", "mathias@abc.com"]
|
||||
@@ -97,60 +106,73 @@ def create_maintenance_team():
|
||||
frappe.get_doc({"doctype": "Role", "role_name": "Technician"}).insert()
|
||||
for user in user_list:
|
||||
if not frappe.db.get_value("User", user):
|
||||
frappe.get_doc({
|
||||
"doctype": "User",
|
||||
"email": user,
|
||||
"first_name": user,
|
||||
"new_password": "password",
|
||||
"roles": [{"doctype": "Has Role", "role": "Technician"}]
|
||||
}).insert()
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "User",
|
||||
"email": user,
|
||||
"first_name": user,
|
||||
"new_password": "password",
|
||||
"roles": [{"doctype": "Has Role", "role": "Technician"}],
|
||||
}
|
||||
).insert()
|
||||
|
||||
if not frappe.db.exists("Asset Maintenance Team", "Team Awesome"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Asset Maintenance Team",
|
||||
"maintenance_manager": "marcus@abc.com",
|
||||
"maintenance_team_name": "Team Awesome",
|
||||
"company": "_Test Company",
|
||||
"maintenance_team_members": get_maintenance_team(user_list)
|
||||
}).insert()
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Asset Maintenance Team",
|
||||
"maintenance_manager": "marcus@abc.com",
|
||||
"maintenance_team_name": "Team Awesome",
|
||||
"company": "_Test Company",
|
||||
"maintenance_team_members": get_maintenance_team(user_list),
|
||||
}
|
||||
).insert()
|
||||
|
||||
|
||||
def get_maintenance_team(user_list):
|
||||
return [{"team_member": user,
|
||||
"full_name": user,
|
||||
"maintenance_role": "Technician"
|
||||
}
|
||||
for user in user_list[1:]]
|
||||
return [
|
||||
{"team_member": user, "full_name": user, "maintenance_role": "Technician"}
|
||||
for user in user_list[1:]
|
||||
]
|
||||
|
||||
|
||||
def get_maintenance_tasks():
|
||||
return [{"maintenance_task": "Change Oil",
|
||||
return [
|
||||
{
|
||||
"maintenance_task": "Change Oil",
|
||||
"start_date": nowdate(),
|
||||
"periodicity": "Monthly",
|
||||
"maintenance_type": "Preventive Maintenance",
|
||||
"maintenance_status": "Planned",
|
||||
"assign_to": "marcus@abc.com"
|
||||
},
|
||||
{"maintenance_task": "Check Gears",
|
||||
"assign_to": "marcus@abc.com",
|
||||
},
|
||||
{
|
||||
"maintenance_task": "Check Gears",
|
||||
"start_date": nowdate(),
|
||||
"periodicity": "Yearly",
|
||||
"maintenance_type": "Calibration",
|
||||
"maintenance_status": "Planned",
|
||||
"assign_to": "thalia@abc.com"
|
||||
}
|
||||
]
|
||||
"assign_to": "thalia@abc.com",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def create_asset_category():
|
||||
asset_category = frappe.new_doc("Asset Category")
|
||||
asset_category.asset_category_name = "Equipment"
|
||||
asset_category.total_number_of_depreciations = 3
|
||||
asset_category.frequency_of_depreciation = 3
|
||||
asset_category.append("accounts", {
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC"
|
||||
})
|
||||
asset_category.append(
|
||||
"accounts",
|
||||
{
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC",
|
||||
},
|
||||
)
|
||||
asset_category.insert()
|
||||
|
||||
|
||||
def set_depreciation_settings_in_company():
|
||||
company = frappe.get_doc("Company", "_Test Company")
|
||||
company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
|
||||
|
||||
@@ -12,7 +12,10 @@ from erpnext.assets.doctype.asset_maintenance.asset_maintenance import calculate
|
||||
|
||||
class AssetMaintenanceLog(Document):
|
||||
def validate(self):
|
||||
if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in ["Completed", "Cancelled"]:
|
||||
if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in [
|
||||
"Completed",
|
||||
"Cancelled",
|
||||
]:
|
||||
self.maintenance_status = "Overdue"
|
||||
|
||||
if self.maintenance_status == "Completed" and not self.completion_date:
|
||||
@@ -22,15 +25,17 @@ class AssetMaintenanceLog(Document):
|
||||
frappe.throw(_("Please select Maintenance Status as Completed or remove Completion Date"))
|
||||
|
||||
def on_submit(self):
|
||||
if self.maintenance_status not in ['Completed', 'Cancelled']:
|
||||
if self.maintenance_status not in ["Completed", "Cancelled"]:
|
||||
frappe.throw(_("Maintenance Status has to be Cancelled or Completed to Submit"))
|
||||
self.update_maintenance_task()
|
||||
|
||||
def update_maintenance_task(self):
|
||||
asset_maintenance_doc = frappe.get_doc('Asset Maintenance Task', self.task)
|
||||
asset_maintenance_doc = frappe.get_doc("Asset Maintenance Task", self.task)
|
||||
if self.maintenance_status == "Completed":
|
||||
if asset_maintenance_doc.last_completion_date != self.completion_date:
|
||||
next_due_date = calculate_next_due_date(periodicity = self.periodicity, last_completion_date = self.completion_date)
|
||||
next_due_date = calculate_next_due_date(
|
||||
periodicity=self.periodicity, last_completion_date=self.completion_date
|
||||
)
|
||||
asset_maintenance_doc.last_completion_date = self.completion_date
|
||||
asset_maintenance_doc.next_due_date = next_due_date
|
||||
asset_maintenance_doc.maintenance_status = "Planned"
|
||||
@@ -38,11 +43,14 @@ class AssetMaintenanceLog(Document):
|
||||
if self.maintenance_status == "Cancelled":
|
||||
asset_maintenance_doc.maintenance_status = "Cancelled"
|
||||
asset_maintenance_doc.save()
|
||||
asset_maintenance_doc = frappe.get_doc('Asset Maintenance', self.asset_maintenance)
|
||||
asset_maintenance_doc = frappe.get_doc("Asset Maintenance", self.asset_maintenance)
|
||||
asset_maintenance_doc.save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_maintenance_tasks(doctype, txt, searchfield, start, page_len, filters):
|
||||
asset_maintenance_tasks = frappe.db.get_values('Asset Maintenance Task', {'parent':filters.get("asset_maintenance")}, 'maintenance_task')
|
||||
asset_maintenance_tasks = frappe.db.get_values(
|
||||
"Asset Maintenance Task", {"parent": filters.get("asset_maintenance")}, "maintenance_task"
|
||||
)
|
||||
return asset_maintenance_tasks
|
||||
|
||||
@@ -16,7 +16,7 @@ class AssetMovement(Document):
|
||||
def validate_asset(self):
|
||||
for d in self.assets:
|
||||
status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"])
|
||||
if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
|
||||
if self.purpose == "Transfer" and status in ("Draft", "Scrapped", "Sold"):
|
||||
frappe.throw(_("{0} asset cannot be transferred").format(status))
|
||||
|
||||
if company != self.company:
|
||||
@@ -27,7 +27,7 @@ class AssetMovement(Document):
|
||||
|
||||
def validate_location(self):
|
||||
for d in self.assets:
|
||||
if self.purpose in ['Transfer', 'Issue']:
|
||||
if self.purpose in ["Transfer", "Issue"]:
|
||||
if not d.source_location:
|
||||
d.source_location = frappe.db.get_value("Asset", d.asset, "location")
|
||||
|
||||
@@ -38,52 +38,76 @@ class AssetMovement(Document):
|
||||
current_location = frappe.db.get_value("Asset", d.asset, "location")
|
||||
|
||||
if current_location != d.source_location:
|
||||
frappe.throw(_("Asset {0} does not belongs to the location {1}").
|
||||
format(d.asset, d.source_location))
|
||||
frappe.throw(
|
||||
_("Asset {0} does not belongs to the location {1}").format(d.asset, d.source_location)
|
||||
)
|
||||
|
||||
if self.purpose == 'Issue':
|
||||
if self.purpose == "Issue":
|
||||
if d.target_location:
|
||||
frappe.throw(_("Issuing cannot be done to a location. \
|
||||
Please enter employee who has issued Asset {0}").format(d.asset), title="Incorrect Movement Purpose")
|
||||
frappe.throw(
|
||||
_(
|
||||
"Issuing cannot be done to a location. \
|
||||
Please enter employee who has issued Asset {0}"
|
||||
).format(d.asset),
|
||||
title="Incorrect Movement Purpose",
|
||||
)
|
||||
if not d.to_employee:
|
||||
frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset))
|
||||
|
||||
if self.purpose == 'Transfer':
|
||||
if self.purpose == "Transfer":
|
||||
if d.to_employee:
|
||||
frappe.throw(_("Transferring cannot be done to an Employee. \
|
||||
Please enter location where Asset {0} has to be transferred").format(
|
||||
d.asset), title="Incorrect Movement Purpose")
|
||||
frappe.throw(
|
||||
_(
|
||||
"Transferring cannot be done to an Employee. \
|
||||
Please enter location where Asset {0} has to be transferred"
|
||||
).format(d.asset),
|
||||
title="Incorrect Movement Purpose",
|
||||
)
|
||||
if not d.target_location:
|
||||
frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset))
|
||||
if d.source_location == d.target_location:
|
||||
frappe.throw(_("Source and Target Location cannot be same"))
|
||||
|
||||
if self.purpose == 'Receipt':
|
||||
if self.purpose == "Receipt":
|
||||
# only when asset is bought and first entry is made
|
||||
if not d.source_location and not (d.target_location or d.to_employee):
|
||||
frappe.throw(_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset))
|
||||
frappe.throw(
|
||||
_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
|
||||
)
|
||||
elif d.source_location:
|
||||
# when asset is received from an employee
|
||||
if d.target_location and not d.from_employee:
|
||||
frappe.throw(_("From employee is required while receiving Asset {0} to a target location").format(d.asset))
|
||||
frappe.throw(
|
||||
_("From employee is required while receiving Asset {0} to a target location").format(
|
||||
d.asset
|
||||
)
|
||||
)
|
||||
if d.from_employee and not d.target_location:
|
||||
frappe.throw(_("Target Location is required while receiving Asset {0} from an employee").format(d.asset))
|
||||
frappe.throw(
|
||||
_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
|
||||
)
|
||||
if d.to_employee and d.target_location:
|
||||
frappe.throw(_("Asset {0} cannot be received at a location and \
|
||||
given to employee in a single movement").format(d.asset))
|
||||
frappe.throw(
|
||||
_(
|
||||
"Asset {0} cannot be received at a location and \
|
||||
given to employee in a single movement"
|
||||
).format(d.asset)
|
||||
)
|
||||
|
||||
def validate_employee(self):
|
||||
for d in self.assets:
|
||||
if d.from_employee:
|
||||
current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
|
||||
current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
|
||||
|
||||
if current_custodian != d.from_employee:
|
||||
frappe.throw(_("Asset {0} does not belongs to the custodian {1}").
|
||||
format(d.asset, d.from_employee))
|
||||
if current_custodian != d.from_employee:
|
||||
frappe.throw(
|
||||
_("Asset {0} does not belongs to the custodian {1}").format(d.asset, d.from_employee)
|
||||
)
|
||||
|
||||
if d.to_employee and frappe.db.get_value("Employee", d.to_employee, "company") != self.company:
|
||||
frappe.throw(_("Employee {0} does not belongs to the company {1}").
|
||||
format(d.to_employee, self.company))
|
||||
frappe.throw(
|
||||
_("Employee {0} does not belongs to the company {1}").format(d.to_employee, self.company)
|
||||
)
|
||||
|
||||
def on_submit(self):
|
||||
self.set_latest_location_in_asset()
|
||||
@@ -92,14 +116,11 @@ class AssetMovement(Document):
|
||||
self.set_latest_location_in_asset()
|
||||
|
||||
def set_latest_location_in_asset(self):
|
||||
current_location, current_employee = '', ''
|
||||
current_location, current_employee = "", ""
|
||||
cond = "1=1"
|
||||
|
||||
for d in self.assets:
|
||||
args = {
|
||||
'asset': d.asset,
|
||||
'company': self.company
|
||||
}
|
||||
args = {"asset": d.asset, "company": self.company}
|
||||
|
||||
# latest entry corresponds to current document's location, employee when transaction date > previous dates
|
||||
# In case of cancellation it corresponds to previous latest document's location, employee
|
||||
@@ -114,10 +135,14 @@ class AssetMovement(Document):
|
||||
asm.docstatus=1 and {0}
|
||||
ORDER BY
|
||||
asm.transaction_date desc limit 1
|
||||
""".format(cond), args)
|
||||
""".format(
|
||||
cond
|
||||
),
|
||||
args,
|
||||
)
|
||||
if latest_movement_entry:
|
||||
current_location = latest_movement_entry[0][0]
|
||||
current_employee = latest_movement_entry[0][1]
|
||||
|
||||
frappe.db.set_value('Asset', d.asset, 'location', current_location)
|
||||
frappe.db.set_value('Asset', d.asset, 'custodian', current_employee)
|
||||
frappe.db.set_value("Asset", d.asset, "location", current_location)
|
||||
frappe.db.set_value("Asset", d.asset, "custodian", current_employee)
|
||||
|
||||
@@ -13,95 +13,122 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu
|
||||
|
||||
class TestAssetMovement(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
|
||||
frappe.db.set_value(
|
||||
"Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC"
|
||||
)
|
||||
create_asset_data()
|
||||
make_location()
|
||||
|
||||
def test_movement(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=100000.0, location="Test Location")
|
||||
pr = make_purchase_receipt(
|
||||
item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
|
||||
)
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2020-06-06'
|
||||
asset.purchase_date = '2020-06-06'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10
|
||||
})
|
||||
asset.available_for_use_date = "2020-06-06"
|
||||
asset.purchase_date = "2020-06-06"
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
},
|
||||
)
|
||||
|
||||
if asset.docstatus == 0:
|
||||
asset.submit()
|
||||
|
||||
# check asset movement is created
|
||||
if not frappe.db.exists("Location", "Test Location 2"):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Location',
|
||||
'location_name': 'Test Location 2'
|
||||
}).insert()
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location 2"}).insert()
|
||||
|
||||
movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
movement1 = create_asset_movement(
|
||||
purpose="Transfer",
|
||||
company=asset.company,
|
||||
assets=[
|
||||
{"asset": asset.name, "source_location": "Test Location", "target_location": "Test Location 2"}
|
||||
],
|
||||
reference_doctype="Purchase Receipt",
|
||||
reference_name=pr.name,
|
||||
)
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||
|
||||
create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||
assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
create_asset_movement(
|
||||
purpose="Transfer",
|
||||
company=asset.company,
|
||||
assets=[
|
||||
{"asset": asset.name, "source_location": "Test Location 2", "target_location": "Test Location"}
|
||||
],
|
||||
reference_doctype="Purchase Receipt",
|
||||
reference_name=pr.name,
|
||||
)
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||
|
||||
movement1.cancel()
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||
|
||||
employee = make_employee("testassetmovemp@example.com", company="_Test Company")
|
||||
create_asset_movement(purpose = 'Issue', company = asset.company,
|
||||
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
create_asset_movement(
|
||||
purpose="Issue",
|
||||
company=asset.company,
|
||||
assets=[{"asset": asset.name, "source_location": "Test Location", "to_employee": employee}],
|
||||
reference_doctype="Purchase Receipt",
|
||||
reference_name=pr.name,
|
||||
)
|
||||
|
||||
# after issuing asset should belong to an employee not at a location
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
|
||||
|
||||
def test_last_movement_cancellation(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=100000.0, location="Test Location")
|
||||
pr = make_purchase_receipt(
|
||||
item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
|
||||
)
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2020-06-06'
|
||||
asset.purchase_date = '2020-06-06'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10
|
||||
})
|
||||
asset.available_for_use_date = "2020-06-06"
|
||||
asset.purchase_date = "2020-06-06"
|
||||
asset.append(
|
||||
"finance_books",
|
||||
{
|
||||
"expected_value_after_useful_life": 10000,
|
||||
"next_depreciation_date": "2020-12-31",
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
},
|
||||
)
|
||||
if asset.docstatus == 0:
|
||||
asset.submit()
|
||||
|
||||
if not frappe.db.exists("Location", "Test Location 2"):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Location',
|
||||
'location_name': 'Test Location 2'
|
||||
}).insert()
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location 2"}).insert()
|
||||
|
||||
movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
|
||||
movement = frappe.get_doc({"doctype": "Asset Movement", "reference_name": pr.name})
|
||||
self.assertRaises(frappe.ValidationError, movement.cancel)
|
||||
|
||||
movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
movement1 = create_asset_movement(
|
||||
purpose="Transfer",
|
||||
company=asset.company,
|
||||
assets=[
|
||||
{"asset": asset.name, "source_location": "Test Location", "target_location": "Test Location 2"}
|
||||
],
|
||||
reference_doctype="Purchase Receipt",
|
||||
reference_name=pr.name,
|
||||
)
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||
|
||||
movement1.cancel()
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||
|
||||
|
||||
def create_asset_movement(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -109,24 +136,26 @@ def create_asset_movement(**args):
|
||||
args.transaction_date = now()
|
||||
|
||||
movement = frappe.new_doc("Asset Movement")
|
||||
movement.update({
|
||||
"assets": args.assets,
|
||||
"transaction_date": args.transaction_date,
|
||||
"company": args.company,
|
||||
'purpose': args.purpose or 'Receipt',
|
||||
'reference_doctype': args.reference_doctype,
|
||||
'reference_name': args.reference_name
|
||||
})
|
||||
movement.update(
|
||||
{
|
||||
"assets": args.assets,
|
||||
"transaction_date": args.transaction_date,
|
||||
"company": args.company,
|
||||
"purpose": args.purpose or "Receipt",
|
||||
"reference_doctype": args.reference_doctype,
|
||||
"reference_name": args.reference_name,
|
||||
}
|
||||
)
|
||||
|
||||
movement.insert()
|
||||
movement.submit()
|
||||
|
||||
return movement
|
||||
|
||||
|
||||
def make_location():
|
||||
for location in ['Pune', 'Mumbai', 'Nagpur']:
|
||||
if not frappe.db.exists('Location', location):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Location',
|
||||
'location_name': location
|
||||
}).insert(ignore_permissions = True)
|
||||
for location in ["Pune", "Mumbai", "Nagpur"]:
|
||||
if not frappe.db.exists("Location", location):
|
||||
frappe.get_doc({"doctype": "Location", "location_name": location}).insert(
|
||||
ignore_permissions=True
|
||||
)
|
||||
|
||||
@@ -13,21 +13,21 @@ from erpnext.controllers.accounts_controller import AccountsController
|
||||
|
||||
class AssetRepair(AccountsController):
|
||||
def validate(self):
|
||||
self.asset_doc = frappe.get_doc('Asset', self.asset)
|
||||
self.asset_doc = frappe.get_doc("Asset", self.asset)
|
||||
self.update_status()
|
||||
|
||||
if self.get('stock_items'):
|
||||
if self.get("stock_items"):
|
||||
self.set_total_value()
|
||||
self.calculate_total_repair_cost()
|
||||
|
||||
def update_status(self):
|
||||
if self.repair_status == 'Pending':
|
||||
frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order')
|
||||
if self.repair_status == "Pending":
|
||||
frappe.db.set_value("Asset", self.asset, "status", "Out of Order")
|
||||
else:
|
||||
self.asset_doc.set_status()
|
||||
|
||||
def set_total_value(self):
|
||||
for item in self.get('stock_items'):
|
||||
for item in self.get("stock_items"):
|
||||
item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
|
||||
|
||||
def calculate_total_repair_cost(self):
|
||||
@@ -39,14 +39,17 @@ class AssetRepair(AccountsController):
|
||||
def before_submit(self):
|
||||
self.check_repair_status()
|
||||
|
||||
if self.get('stock_consumption') or self.get('capitalize_repair_cost'):
|
||||
if self.get("stock_consumption") or self.get("capitalize_repair_cost"):
|
||||
self.increase_asset_value()
|
||||
if self.get('stock_consumption'):
|
||||
if self.get("stock_consumption"):
|
||||
self.check_for_stock_items_and_warehouse()
|
||||
self.decrease_stock_quantity()
|
||||
if self.get('capitalize_repair_cost'):
|
||||
if self.get("capitalize_repair_cost"):
|
||||
self.make_gl_entries()
|
||||
if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life:
|
||||
if (
|
||||
frappe.db.get_value("Asset", self.asset, "calculate_depreciation")
|
||||
and self.increase_in_asset_life
|
||||
):
|
||||
self.modify_depreciation_schedule()
|
||||
|
||||
self.asset_doc.flags.ignore_validate_update_after_submit = True
|
||||
@@ -54,16 +57,19 @@ class AssetRepair(AccountsController):
|
||||
self.asset_doc.save()
|
||||
|
||||
def before_cancel(self):
|
||||
self.asset_doc = frappe.get_doc('Asset', self.asset)
|
||||
self.asset_doc = frappe.get_doc("Asset", self.asset)
|
||||
|
||||
if self.get('stock_consumption') or self.get('capitalize_repair_cost'):
|
||||
if self.get("stock_consumption") or self.get("capitalize_repair_cost"):
|
||||
self.decrease_asset_value()
|
||||
if self.get('stock_consumption'):
|
||||
if self.get("stock_consumption"):
|
||||
self.increase_stock_quantity()
|
||||
if self.get('capitalize_repair_cost'):
|
||||
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
|
||||
if self.get("capitalize_repair_cost"):
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
|
||||
self.make_gl_entries(cancel=True)
|
||||
if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life:
|
||||
if (
|
||||
frappe.db.get_value("Asset", self.asset, "calculate_depreciation")
|
||||
and self.increase_in_asset_life
|
||||
):
|
||||
self.revert_depreciation_schedule_on_cancellation()
|
||||
|
||||
self.asset_doc.flags.ignore_validate_update_after_submit = True
|
||||
@@ -75,10 +81,15 @@ class AssetRepair(AccountsController):
|
||||
frappe.throw(_("Please update Repair Status."))
|
||||
|
||||
def check_for_stock_items_and_warehouse(self):
|
||||
if not self.get('stock_items'):
|
||||
frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items"))
|
||||
if not self.get("stock_items"):
|
||||
frappe.throw(
|
||||
_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items")
|
||||
)
|
||||
if not self.warehouse:
|
||||
frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse"))
|
||||
frappe.throw(
|
||||
_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."),
|
||||
title=_("Missing Warehouse"),
|
||||
)
|
||||
|
||||
def increase_asset_value(self):
|
||||
total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()
|
||||
@@ -102,35 +113,36 @@ class AssetRepair(AccountsController):
|
||||
|
||||
def get_total_value_of_stock_consumed(self):
|
||||
total_value_of_stock_consumed = 0
|
||||
if self.get('stock_consumption'):
|
||||
for item in self.get('stock_items'):
|
||||
if self.get("stock_consumption"):
|
||||
for item in self.get("stock_items"):
|
||||
total_value_of_stock_consumed += item.total_value
|
||||
|
||||
return total_value_of_stock_consumed
|
||||
|
||||
def decrease_stock_quantity(self):
|
||||
stock_entry = frappe.get_doc({
|
||||
"doctype": "Stock Entry",
|
||||
"stock_entry_type": "Material Issue",
|
||||
"company": self.company
|
||||
})
|
||||
stock_entry = frappe.get_doc(
|
||||
{"doctype": "Stock Entry", "stock_entry_type": "Material Issue", "company": self.company}
|
||||
)
|
||||
|
||||
for stock_item in self.get('stock_items'):
|
||||
stock_entry.append('items', {
|
||||
"s_warehouse": self.warehouse,
|
||||
"item_code": stock_item.item_code,
|
||||
"qty": stock_item.consumed_quantity,
|
||||
"basic_rate": stock_item.valuation_rate,
|
||||
"serial_no": stock_item.serial_no
|
||||
})
|
||||
for stock_item in self.get("stock_items"):
|
||||
stock_entry.append(
|
||||
"items",
|
||||
{
|
||||
"s_warehouse": self.warehouse,
|
||||
"item_code": stock_item.item_code,
|
||||
"qty": stock_item.consumed_quantity,
|
||||
"basic_rate": stock_item.valuation_rate,
|
||||
"serial_no": stock_item.serial_no,
|
||||
},
|
||||
)
|
||||
|
||||
stock_entry.insert()
|
||||
stock_entry.submit()
|
||||
|
||||
self.db_set('stock_entry', stock_entry.name)
|
||||
self.db_set("stock_entry", stock_entry.name)
|
||||
|
||||
def increase_stock_quantity(self):
|
||||
stock_entry = frappe.get_doc('Stock Entry', self.stock_entry)
|
||||
stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
|
||||
stock_entry.flags.ignore_links = True
|
||||
stock_entry.cancel()
|
||||
|
||||
@@ -141,63 +153,78 @@ class AssetRepair(AccountsController):
|
||||
|
||||
def get_gl_entries(self):
|
||||
gl_entries = []
|
||||
repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account')
|
||||
fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company)
|
||||
expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": expense_account,
|
||||
"credit": self.repair_cost,
|
||||
"credit_in_account_currency": self.repair_cost,
|
||||
"against": repair_and_maintenance_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"company": self.company
|
||||
}, item=self)
|
||||
repair_and_maintenance_account = frappe.db.get_value(
|
||||
"Company", self.company, "repair_and_maintenance_account"
|
||||
)
|
||||
fixed_asset_account = get_asset_account(
|
||||
"fixed_asset_account", asset=self.asset, company=self.company
|
||||
)
|
||||
expense_account = (
|
||||
frappe.get_doc("Purchase Invoice", self.purchase_invoice).items[0].expense_account
|
||||
)
|
||||
|
||||
if self.get('stock_consumption'):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"credit": self.repair_cost,
|
||||
"credit_in_account_currency": self.repair_cost,
|
||||
"against": repair_and_maintenance_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"company": self.company,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
if self.get("stock_consumption"):
|
||||
# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
|
||||
stock_entry = frappe.get_doc('Stock Entry', self.stock_entry)
|
||||
stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
|
||||
for item in stock_entry.items:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"credit": item.amount,
|
||||
"credit_in_account_currency": item.amount,
|
||||
"against": repair_and_maintenance_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"company": self.company
|
||||
}, item=self)
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item.expense_account,
|
||||
"credit": item.amount,
|
||||
"credit_in_account_currency": item.amount,
|
||||
"against": repair_and_maintenance_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"company": self.company,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": fixed_asset_account,
|
||||
"debit": self.total_repair_cost,
|
||||
"debit_in_account_currency": self.total_repair_cost,
|
||||
"against": expense_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"against_voucher_type": "Purchase Invoice",
|
||||
"against_voucher": self.purchase_invoice,
|
||||
"company": self.company
|
||||
}, item=self)
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": fixed_asset_account,
|
||||
"debit": self.total_repair_cost,
|
||||
"debit_in_account_currency": self.total_repair_cost,
|
||||
"against": expense_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
"posting_date": getdate(),
|
||||
"against_voucher_type": "Purchase Invoice",
|
||||
"against_voucher": self.purchase_invoice,
|
||||
"company": self.company,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def modify_depreciation_schedule(self):
|
||||
for row in self.asset_doc.finance_books:
|
||||
row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation
|
||||
row.total_number_of_depreciations += self.increase_in_asset_life / row.frequency_of_depreciation
|
||||
|
||||
self.asset_doc.flags.increase_in_asset_life = False
|
||||
extra_months = self.increase_in_asset_life % row.frequency_of_depreciation
|
||||
@@ -207,26 +234,29 @@ class AssetRepair(AccountsController):
|
||||
# to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation
|
||||
def calculate_last_schedule_date(self, asset, row, extra_months):
|
||||
asset.flags.increase_in_asset_life = True
|
||||
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \
|
||||
cint(asset.number_of_depreciations_booked)
|
||||
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
|
||||
asset.number_of_depreciations_booked
|
||||
)
|
||||
|
||||
# the Schedule Date in the final row of the old Depreciation Schedule
|
||||
last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date
|
||||
last_schedule_date = asset.schedules[len(asset.schedules) - 1].schedule_date
|
||||
|
||||
# the Schedule Date in the final row of the new Depreciation Schedule
|
||||
asset.to_date = add_months(last_schedule_date, extra_months)
|
||||
|
||||
# the latest possible date at which the depreciation can occur, without increasing the Total Number of Depreciations
|
||||
# if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022...
|
||||
schedule_date = add_months(row.depreciation_start_date,
|
||||
number_of_pending_depreciations * cint(row.frequency_of_depreciation))
|
||||
schedule_date = add_months(
|
||||
row.depreciation_start_date,
|
||||
number_of_pending_depreciations * cint(row.frequency_of_depreciation),
|
||||
)
|
||||
|
||||
if asset.to_date > schedule_date:
|
||||
row.total_number_of_depreciations += 1
|
||||
|
||||
def revert_depreciation_schedule_on_cancellation(self):
|
||||
for row in self.asset_doc.finance_books:
|
||||
row.total_number_of_depreciations -= self.increase_in_asset_life/row.frequency_of_depreciation
|
||||
row.total_number_of_depreciations -= self.increase_in_asset_life / row.frequency_of_depreciation
|
||||
|
||||
self.asset_doc.flags.increase_in_asset_life = False
|
||||
extra_months = self.increase_in_asset_life % row.frequency_of_depreciation
|
||||
@@ -235,23 +265,27 @@ class AssetRepair(AccountsController):
|
||||
|
||||
def calculate_last_schedule_date_before_modification(self, asset, row, extra_months):
|
||||
asset.flags.increase_in_asset_life = True
|
||||
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \
|
||||
cint(asset.number_of_depreciations_booked)
|
||||
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
|
||||
asset.number_of_depreciations_booked
|
||||
)
|
||||
|
||||
# the Schedule Date in the final row of the modified Depreciation Schedule
|
||||
last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date
|
||||
last_schedule_date = asset.schedules[len(asset.schedules) - 1].schedule_date
|
||||
|
||||
# the Schedule Date in the final row of the original Depreciation Schedule
|
||||
asset.to_date = add_months(last_schedule_date, -extra_months)
|
||||
|
||||
# the latest possible date at which the depreciation can occur, without decreasing the Total Number of Depreciations
|
||||
# if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022...
|
||||
schedule_date = add_months(row.depreciation_start_date,
|
||||
(number_of_pending_depreciations - 1) * cint(row.frequency_of_depreciation))
|
||||
schedule_date = add_months(
|
||||
row.depreciation_start_date,
|
||||
(number_of_pending_depreciations - 1) * cint(row.frequency_of_depreciation),
|
||||
)
|
||||
|
||||
if asset.to_date < schedule_date:
|
||||
row.total_number_of_depreciations -= 1
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_downtime(failure_date, completion_date):
|
||||
downtime = time_diff_in_hours(completion_date, failure_date)
|
||||
|
||||
@@ -25,7 +25,7 @@ class TestAssetRepair(unittest.TestCase):
|
||||
def test_update_status(self):
|
||||
asset = create_asset(submit=1)
|
||||
initial_status = asset.status
|
||||
asset_repair = create_asset_repair(asset = asset)
|
||||
asset_repair = create_asset_repair(asset=asset)
|
||||
|
||||
if asset_repair.repair_status == "Pending":
|
||||
asset.reload()
|
||||
@@ -37,14 +37,14 @@ class TestAssetRepair(unittest.TestCase):
|
||||
self.assertEqual(asset_status, initial_status)
|
||||
|
||||
def test_stock_item_total_value(self):
|
||||
asset_repair = create_asset_repair(stock_consumption = 1)
|
||||
asset_repair = create_asset_repair(stock_consumption=1)
|
||||
|
||||
for item in asset_repair.stock_items:
|
||||
total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
|
||||
self.assertEqual(item.total_value, total_value)
|
||||
|
||||
def test_total_repair_cost(self):
|
||||
asset_repair = create_asset_repair(stock_consumption = 1)
|
||||
asset_repair = create_asset_repair(stock_consumption=1)
|
||||
|
||||
total_repair_cost = asset_repair.repair_cost
|
||||
self.assertEqual(total_repair_cost, asset_repair.repair_cost)
|
||||
@@ -54,22 +54,22 @@ class TestAssetRepair(unittest.TestCase):
|
||||
self.assertEqual(total_repair_cost, asset_repair.total_repair_cost)
|
||||
|
||||
def test_repair_status_after_submit(self):
|
||||
asset_repair = create_asset_repair(submit = 1)
|
||||
asset_repair = create_asset_repair(submit=1)
|
||||
self.assertNotEqual(asset_repair.repair_status, "Pending")
|
||||
|
||||
def test_stock_items(self):
|
||||
asset_repair = create_asset_repair(stock_consumption = 1)
|
||||
asset_repair = create_asset_repair(stock_consumption=1)
|
||||
self.assertTrue(asset_repair.stock_consumption)
|
||||
self.assertTrue(asset_repair.stock_items)
|
||||
|
||||
def test_warehouse(self):
|
||||
asset_repair = create_asset_repair(stock_consumption = 1)
|
||||
asset_repair = create_asset_repair(stock_consumption=1)
|
||||
self.assertTrue(asset_repair.stock_consumption)
|
||||
self.assertTrue(asset_repair.warehouse)
|
||||
|
||||
def test_decrease_stock_quantity(self):
|
||||
asset_repair = create_asset_repair(stock_consumption = 1, submit = 1)
|
||||
stock_entry = frappe.get_last_doc('Stock Entry')
|
||||
asset_repair = create_asset_repair(stock_consumption=1, submit=1)
|
||||
stock_entry = frappe.get_last_doc("Stock Entry")
|
||||
|
||||
self.assertEqual(stock_entry.stock_entry_type, "Material Issue")
|
||||
self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse)
|
||||
@@ -85,58 +85,72 @@ class TestAssetRepair(unittest.TestCase):
|
||||
serial_no = serial_nos.split("\n")[0]
|
||||
|
||||
# should not raise any error
|
||||
create_asset_repair(stock_consumption = 1, item_code = stock_entry.get("items")[0].item_code,
|
||||
warehouse = "_Test Warehouse - _TC", serial_no = serial_no, submit = 1)
|
||||
create_asset_repair(
|
||||
stock_consumption=1,
|
||||
item_code=stock_entry.get("items")[0].item_code,
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
serial_no=serial_no,
|
||||
submit=1,
|
||||
)
|
||||
|
||||
# should raise error
|
||||
asset_repair = create_asset_repair(stock_consumption = 1, warehouse = "_Test Warehouse - _TC",
|
||||
item_code = stock_entry.get("items")[0].item_code)
|
||||
asset_repair = create_asset_repair(
|
||||
stock_consumption=1,
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
item_code=stock_entry.get("items")[0].item_code,
|
||||
)
|
||||
|
||||
asset_repair.repair_status = "Completed"
|
||||
self.assertRaises(SerialNoRequiredError, asset_repair.submit)
|
||||
|
||||
def test_increase_in_asset_value_due_to_stock_consumption(self):
|
||||
asset = create_asset(calculate_depreciation = 1, submit=1)
|
||||
asset = create_asset(calculate_depreciation=1, submit=1)
|
||||
initial_asset_value = get_asset_value(asset)
|
||||
asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1)
|
||||
asset_repair = create_asset_repair(asset=asset, stock_consumption=1, submit=1)
|
||||
asset.reload()
|
||||
|
||||
increase_in_asset_value = get_asset_value(asset) - initial_asset_value
|
||||
self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value)
|
||||
|
||||
def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self):
|
||||
asset = create_asset(calculate_depreciation = 1, submit=1)
|
||||
asset = create_asset(calculate_depreciation=1, submit=1)
|
||||
initial_asset_value = get_asset_value(asset)
|
||||
asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
|
||||
asset_repair = create_asset_repair(asset=asset, capitalize_repair_cost=1, submit=1)
|
||||
asset.reload()
|
||||
|
||||
increase_in_asset_value = get_asset_value(asset) - initial_asset_value
|
||||
self.assertEqual(asset_repair.repair_cost, increase_in_asset_value)
|
||||
|
||||
def test_purchase_invoice(self):
|
||||
asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1)
|
||||
asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
|
||||
self.assertTrue(asset_repair.purchase_invoice)
|
||||
|
||||
def test_gl_entries(self):
|
||||
asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1)
|
||||
gl_entry = frappe.get_last_doc('GL Entry')
|
||||
asset_repair = create_asset_repair(capitalize_repair_cost=1, submit=1)
|
||||
gl_entry = frappe.get_last_doc("GL Entry")
|
||||
self.assertEqual(asset_repair.name, gl_entry.voucher_no)
|
||||
|
||||
def test_increase_in_asset_life(self):
|
||||
asset = create_asset(calculate_depreciation = 1, submit=1)
|
||||
asset = create_asset(calculate_depreciation=1, submit=1)
|
||||
initial_num_of_depreciations = num_of_depreciations(asset)
|
||||
create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
|
||||
create_asset_repair(asset=asset, capitalize_repair_cost=1, submit=1)
|
||||
asset.reload()
|
||||
|
||||
self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset))
|
||||
self.assertEqual(asset.schedules[-1].accumulated_depreciation_amount, asset.finance_books[0].value_after_depreciation)
|
||||
self.assertEqual(
|
||||
asset.schedules[-1].accumulated_depreciation_amount,
|
||||
asset.finance_books[0].value_after_depreciation,
|
||||
)
|
||||
|
||||
|
||||
def get_asset_value(asset):
|
||||
return asset.finance_books[0].value_after_depreciation
|
||||
|
||||
|
||||
def num_of_depreciations(asset):
|
||||
return asset.finance_books[0].total_number_of_depreciations
|
||||
|
||||
|
||||
def create_asset_repair(**args):
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
@@ -146,26 +160,33 @@ def create_asset_repair(**args):
|
||||
if args.asset:
|
||||
asset = args.asset
|
||||
else:
|
||||
asset = create_asset(is_existing_asset = 1, submit=1)
|
||||
asset = create_asset(is_existing_asset=1, submit=1)
|
||||
asset_repair = frappe.new_doc("Asset Repair")
|
||||
asset_repair.update({
|
||||
"asset": asset.name,
|
||||
"asset_name": asset.asset_name,
|
||||
"failure_date": nowdate(),
|
||||
"description": "Test Description",
|
||||
"repair_cost": 0,
|
||||
"company": asset.company
|
||||
})
|
||||
asset_repair.update(
|
||||
{
|
||||
"asset": asset.name,
|
||||
"asset_name": asset.asset_name,
|
||||
"failure_date": nowdate(),
|
||||
"description": "Test Description",
|
||||
"repair_cost": 0,
|
||||
"company": asset.company,
|
||||
}
|
||||
)
|
||||
|
||||
if args.stock_consumption:
|
||||
asset_repair.stock_consumption = 1
|
||||
asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company = asset.company)
|
||||
asset_repair.append("stock_items", {
|
||||
"item_code": args.item_code or "_Test Stock Item",
|
||||
"valuation_rate": args.rate if args.get("rate") is not None else 100,
|
||||
"consumed_quantity": args.qty or 1,
|
||||
"serial_no": args.serial_no
|
||||
})
|
||||
asset_repair.warehouse = args.warehouse or create_warehouse(
|
||||
"Test Warehouse", company=asset.company
|
||||
)
|
||||
asset_repair.append(
|
||||
"stock_items",
|
||||
{
|
||||
"item_code": args.item_code or "_Test Stock Item",
|
||||
"valuation_rate": args.rate if args.get("rate") is not None else 100,
|
||||
"consumed_quantity": args.qty or 1,
|
||||
"serial_no": args.serial_no,
|
||||
},
|
||||
)
|
||||
|
||||
asset_repair.insert(ignore_if_duplicate=True)
|
||||
|
||||
@@ -174,16 +195,17 @@ def create_asset_repair(**args):
|
||||
asset_repair.cost_center = "_Test Cost Center - _TC"
|
||||
|
||||
if args.stock_consumption:
|
||||
stock_entry = frappe.get_doc({
|
||||
"doctype": "Stock Entry",
|
||||
"stock_entry_type": "Material Receipt",
|
||||
"company": asset.company
|
||||
})
|
||||
stock_entry.append('items', {
|
||||
"t_warehouse": asset_repair.warehouse,
|
||||
"item_code": asset_repair.stock_items[0].item_code,
|
||||
"qty": asset_repair.stock_items[0].consumed_quantity
|
||||
})
|
||||
stock_entry = frappe.get_doc(
|
||||
{"doctype": "Stock Entry", "stock_entry_type": "Material Receipt", "company": asset.company}
|
||||
)
|
||||
stock_entry.append(
|
||||
"items",
|
||||
{
|
||||
"t_warehouse": asset_repair.warehouse,
|
||||
"item_code": asset_repair.stock_items[0].item_code,
|
||||
"qty": asset_repair.stock_items[0].consumed_quantity,
|
||||
},
|
||||
)
|
||||
stock_entry.submit()
|
||||
|
||||
if args.capitalize_repair_cost:
|
||||
|
||||
@@ -31,10 +31,14 @@ class AssetValueAdjustment(Document):
|
||||
self.reschedule_depreciations(self.current_asset_value)
|
||||
|
||||
def validate_date(self):
|
||||
asset_purchase_date = frappe.db.get_value('Asset', self.asset, 'purchase_date')
|
||||
asset_purchase_date = frappe.db.get_value("Asset", self.asset, "purchase_date")
|
||||
if getdate(self.date) < getdate(asset_purchase_date):
|
||||
frappe.throw(_("Asset Value Adjustment cannot be posted before Asset's purchase date <b>{0}</b>.")
|
||||
.format(formatdate(asset_purchase_date)), title="Incorrect Date")
|
||||
frappe.throw(
|
||||
_("Asset Value Adjustment cannot be posted before Asset's purchase date <b>{0}</b>.").format(
|
||||
formatdate(asset_purchase_date)
|
||||
),
|
||||
title="Incorrect Date",
|
||||
)
|
||||
|
||||
def set_difference_amount(self):
|
||||
self.difference_amount = flt(self.current_asset_value - self.new_asset_value)
|
||||
@@ -45,11 +49,15 @@ class AssetValueAdjustment(Document):
|
||||
|
||||
def make_depreciation_entry(self):
|
||||
asset = frappe.get_doc("Asset", self.asset)
|
||||
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
|
||||
get_depreciation_accounts(asset)
|
||||
(
|
||||
fixed_asset_account,
|
||||
accumulated_depreciation_account,
|
||||
depreciation_expense_account,
|
||||
) = get_depreciation_accounts(asset)
|
||||
|
||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company,
|
||||
["depreciation_cost_center", "series_for_depreciation_entry"])
|
||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value(
|
||||
"Company", asset.company, ["depreciation_cost_center", "series_for_depreciation_entry"]
|
||||
)
|
||||
|
||||
je = frappe.new_doc("Journal Entry")
|
||||
je.voucher_type = "Depreciation Entry"
|
||||
@@ -62,27 +70,33 @@ class AssetValueAdjustment(Document):
|
||||
credit_entry = {
|
||||
"account": accumulated_depreciation_account,
|
||||
"credit_in_account_currency": self.difference_amount,
|
||||
"cost_center": depreciation_cost_center or self.cost_center
|
||||
"cost_center": depreciation_cost_center or self.cost_center,
|
||||
}
|
||||
|
||||
debit_entry = {
|
||||
"account": depreciation_expense_account,
|
||||
"debit_in_account_currency": self.difference_amount,
|
||||
"cost_center": depreciation_cost_center or self.cost_center
|
||||
"cost_center": depreciation_cost_center or self.cost_center,
|
||||
}
|
||||
|
||||
accounting_dimensions = get_checks_for_pl_and_bs_accounts()
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
if dimension.get('mandatory_for_bs'):
|
||||
credit_entry.update({
|
||||
dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
if dimension.get("mandatory_for_bs"):
|
||||
credit_entry.update(
|
||||
{
|
||||
dimension["fieldname"]: self.get(dimension["fieldname"])
|
||||
or dimension.get("default_dimension")
|
||||
}
|
||||
)
|
||||
|
||||
if dimension.get('mandatory_for_pl'):
|
||||
debit_entry.update({
|
||||
dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
if dimension.get("mandatory_for_pl"):
|
||||
debit_entry.update(
|
||||
{
|
||||
dimension["fieldname"]: self.get(dimension["fieldname"])
|
||||
or dimension.get("default_dimension")
|
||||
}
|
||||
)
|
||||
|
||||
je.append("accounts", credit_entry)
|
||||
je.append("accounts", debit_entry)
|
||||
@@ -93,8 +107,8 @@ class AssetValueAdjustment(Document):
|
||||
self.db_set("journal_entry", je.name)
|
||||
|
||||
def reschedule_depreciations(self, asset_value):
|
||||
asset = frappe.get_doc('Asset', self.asset)
|
||||
country = frappe.get_value('Company', self.company, 'country')
|
||||
asset = frappe.get_doc("Asset", self.asset)
|
||||
country = frappe.get_value("Company", self.company, "country")
|
||||
|
||||
for d in asset.finance_books:
|
||||
d.value_after_depreciation = asset_value
|
||||
@@ -105,8 +119,11 @@ class AssetValueAdjustment(Document):
|
||||
rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
|
||||
from_date = self.date
|
||||
else:
|
||||
no_of_depreciations = len([s.name for s in asset.schedules
|
||||
if (cint(s.finance_book_id) == d.idx and not s.journal_entry)])
|
||||
no_of_depreciations = len(
|
||||
[
|
||||
s.name for s in asset.schedules if (cint(s.finance_book_id) == d.idx and not s.journal_entry)
|
||||
]
|
||||
)
|
||||
|
||||
value_after_depreciation = d.value_after_depreciation
|
||||
for data in asset.schedules:
|
||||
@@ -132,10 +149,11 @@ class AssetValueAdjustment(Document):
|
||||
if not asset_data.journal_entry:
|
||||
asset_data.db_update()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_current_asset_value(asset, finance_book=None):
|
||||
cond = {'parent': asset, 'parenttype': 'Asset'}
|
||||
cond = {"parent": asset, "parenttype": "Asset"}
|
||||
if finance_book:
|
||||
cond.update({'finance_book': finance_book})
|
||||
cond.update({"finance_book": finance_book})
|
||||
|
||||
return frappe.db.get_value('Asset Finance Book', cond, 'value_after_depreciation')
|
||||
return frappe.db.get_value("Asset Finance Book", cond, "value_after_depreciation")
|
||||
|
||||
@@ -18,11 +18,12 @@ class TestAssetValueAdjustment(unittest.TestCase):
|
||||
create_asset_data()
|
||||
|
||||
def test_current_asset_value(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=100000.0, location="Test Location")
|
||||
pr = make_purchase_receipt(
|
||||
item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
|
||||
)
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset_name)
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
|
||||
asset_doc = frappe.get_doc("Asset", asset_name)
|
||||
|
||||
month_end_date = get_last_day(nowdate())
|
||||
purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
|
||||
@@ -30,24 +31,28 @@ class TestAssetValueAdjustment(unittest.TestCase):
|
||||
asset_doc.available_for_use_date = purchase_date
|
||||
asset_doc.purchase_date = purchase_date
|
||||
asset_doc.calculate_depreciation = 1
|
||||
asset_doc.append("finance_books", {
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date
|
||||
})
|
||||
asset_doc.append(
|
||||
"finance_books",
|
||||
{
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date,
|
||||
},
|
||||
)
|
||||
asset_doc.submit()
|
||||
|
||||
current_value = get_current_asset_value(asset_doc.name)
|
||||
self.assertEqual(current_value, 100000.0)
|
||||
|
||||
def test_asset_depreciation_value_adjustment(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=100000.0, location="Test Location")
|
||||
pr = make_purchase_receipt(
|
||||
item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location"
|
||||
)
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset_name)
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
|
||||
asset_doc = frappe.get_doc("Asset", asset_name)
|
||||
asset_doc.calculate_depreciation = 1
|
||||
|
||||
month_end_date = get_last_day(nowdate())
|
||||
@@ -56,42 +61,52 @@ class TestAssetValueAdjustment(unittest.TestCase):
|
||||
asset_doc.available_for_use_date = purchase_date
|
||||
asset_doc.purchase_date = purchase_date
|
||||
asset_doc.calculate_depreciation = 1
|
||||
asset_doc.append("finance_books", {
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date
|
||||
})
|
||||
asset_doc.append(
|
||||
"finance_books",
|
||||
{
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date,
|
||||
},
|
||||
)
|
||||
asset_doc.submit()
|
||||
|
||||
current_value = get_current_asset_value(asset_doc.name)
|
||||
adj_doc = make_asset_value_adjustment(asset = asset_doc.name,
|
||||
current_asset_value = current_value, new_asset_value = 50000.0)
|
||||
adj_doc = make_asset_value_adjustment(
|
||||
asset=asset_doc.name, current_asset_value=current_value, new_asset_value=50000.0
|
||||
)
|
||||
adj_doc.submit()
|
||||
|
||||
expected_gle = (
|
||||
("_Test Accumulated Depreciations - _TC", 0.0, 50000.0),
|
||||
("_Test Depreciations - _TC", 50000.0, 0.0)
|
||||
("_Test Depreciations - _TC", 50000.0, 0.0),
|
||||
)
|
||||
|
||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
gle = frappe.db.sql(
|
||||
"""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Journal Entry' and voucher_no = %s
|
||||
order by account""", adj_doc.journal_entry)
|
||||
order by account""",
|
||||
adj_doc.journal_entry,
|
||||
)
|
||||
|
||||
self.assertEqual(gle, expected_gle)
|
||||
|
||||
|
||||
def make_asset_value_adjustment(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "Asset Value Adjustment",
|
||||
"company": args.company or "_Test Company",
|
||||
"asset": args.asset,
|
||||
"date": args.date or nowdate(),
|
||||
"new_asset_value": args.new_asset_value,
|
||||
"current_asset_value": args.current_asset_value,
|
||||
"cost_center": args.cost_center or "Main - _TC"
|
||||
}).insert()
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Asset Value Adjustment",
|
||||
"company": args.company or "_Test Company",
|
||||
"asset": args.asset,
|
||||
"date": args.date or nowdate(),
|
||||
"new_asset_value": args.new_asset_value,
|
||||
"current_asset_value": args.current_asset_value,
|
||||
"cost_center": args.cost_center or "Main - _TC",
|
||||
}
|
||||
).insert()
|
||||
|
||||
return doc
|
||||
|
||||
@@ -13,12 +13,12 @@ EARTH_RADIUS = 6378137
|
||||
|
||||
|
||||
class Location(NestedSet):
|
||||
nsm_parent_field = 'parent_location'
|
||||
nsm_parent_field = "parent_location"
|
||||
|
||||
def validate(self):
|
||||
self.calculate_location_area()
|
||||
|
||||
if not self.is_new() and self.get('parent_location'):
|
||||
if not self.is_new() and self.get("parent_location"):
|
||||
self.update_ancestor_location_features()
|
||||
|
||||
def on_update(self):
|
||||
@@ -42,7 +42,7 @@ class Location(NestedSet):
|
||||
if not self.location:
|
||||
return []
|
||||
|
||||
features = json.loads(self.location).get('features')
|
||||
features = json.loads(self.location).get("features")
|
||||
|
||||
if not isinstance(features, list):
|
||||
features = json.loads(features)
|
||||
@@ -54,15 +54,15 @@ class Location(NestedSet):
|
||||
self.location = '{"type":"FeatureCollection","features":[]}'
|
||||
|
||||
location = json.loads(self.location)
|
||||
location['features'] = features
|
||||
location["features"] = features
|
||||
|
||||
self.db_set('location', json.dumps(location), commit=True)
|
||||
self.db_set("location", json.dumps(location), commit=True)
|
||||
|
||||
def update_ancestor_location_features(self):
|
||||
self_features = set(self.add_child_property())
|
||||
|
||||
for ancestor in self.get_ancestors():
|
||||
ancestor_doc = frappe.get_doc('Location', ancestor)
|
||||
ancestor_doc = frappe.get_doc("Location", ancestor)
|
||||
child_features, ancestor_features = ancestor_doc.feature_seperator(child_feature=self.name)
|
||||
|
||||
ancestor_features = list(set(ancestor_features))
|
||||
@@ -84,25 +84,27 @@ class Location(NestedSet):
|
||||
ancestor_features[index] = json.loads(feature)
|
||||
|
||||
ancestor_doc.set_location_features(features=ancestor_features)
|
||||
ancestor_doc.db_set('area', ancestor_doc.area + self.area_difference, commit=True)
|
||||
ancestor_doc.db_set("area", ancestor_doc.area + self.area_difference, commit=True)
|
||||
|
||||
def remove_ancestor_location_features(self):
|
||||
for ancestor in self.get_ancestors():
|
||||
ancestor_doc = frappe.get_doc('Location', ancestor)
|
||||
ancestor_doc = frappe.get_doc("Location", ancestor)
|
||||
child_features, ancestor_features = ancestor_doc.feature_seperator(child_feature=self.name)
|
||||
|
||||
for index, feature in enumerate(ancestor_features):
|
||||
ancestor_features[index] = json.loads(feature)
|
||||
|
||||
ancestor_doc.set_location_features(features=ancestor_features)
|
||||
ancestor_doc.db_set('area', ancestor_doc.area - self.area, commit=True)
|
||||
ancestor_doc.db_set("area", ancestor_doc.area - self.area, commit=True)
|
||||
|
||||
def add_child_property(self):
|
||||
features = self.get_location_features()
|
||||
filter_features = [feature for feature in features if not feature.get('properties').get('child_feature')]
|
||||
filter_features = [
|
||||
feature for feature in features if not feature.get("properties").get("child_feature")
|
||||
]
|
||||
|
||||
for index, feature in enumerate(filter_features):
|
||||
feature['properties'].update({'child_feature': True, 'feature_of': self.location_name})
|
||||
feature["properties"].update({"child_feature": True, "feature_of": self.location_name})
|
||||
filter_features[index] = json.dumps(filter_features[index])
|
||||
|
||||
return filter_features
|
||||
@@ -112,7 +114,7 @@ class Location(NestedSet):
|
||||
features = self.get_location_features()
|
||||
|
||||
for feature in features:
|
||||
if feature.get('properties').get('feature_of') == child_feature:
|
||||
if feature.get("properties").get("feature_of") == child_feature:
|
||||
child_features.extend([json.dumps(feature)])
|
||||
else:
|
||||
non_child_features.extend([json.dumps(feature)])
|
||||
@@ -126,22 +128,22 @@ def compute_area(features):
|
||||
Reference from https://github.com/scisco/area.
|
||||
|
||||
Args:
|
||||
`features` (list of dict): Features marked on the map as
|
||||
GeoJSON data
|
||||
`features` (list of dict): Features marked on the map as
|
||||
GeoJSON data
|
||||
|
||||
Returns:
|
||||
float: The approximate signed geodesic area (in sq. meters)
|
||||
float: The approximate signed geodesic area (in sq. meters)
|
||||
"""
|
||||
|
||||
layer_area = 0.0
|
||||
|
||||
for feature in features:
|
||||
feature_type = feature.get('geometry', {}).get('type')
|
||||
feature_type = feature.get("geometry", {}).get("type")
|
||||
|
||||
if feature_type == 'Polygon':
|
||||
layer_area += _polygon_area(coords=feature.get('geometry').get('coordinates'))
|
||||
elif feature_type == 'Point' and feature.get('properties').get('point_type') == 'circle':
|
||||
layer_area += math.pi * math.pow(feature.get('properties').get('radius'), 2)
|
||||
if feature_type == "Polygon":
|
||||
layer_area += _polygon_area(coords=feature.get("geometry").get("coordinates"))
|
||||
elif feature_type == "Point" and feature.get("properties").get("point_type") == "circle":
|
||||
layer_area += math.pi * math.pow(feature.get("properties").get("radius"), 2)
|
||||
|
||||
return layer_area
|
||||
|
||||
@@ -192,7 +194,8 @@ def get_children(doctype, parent=None, location=None, is_root=False):
|
||||
if parent is None or parent == "All Locations":
|
||||
parent = ""
|
||||
|
||||
return frappe.db.sql("""
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name as value,
|
||||
is_group as expandable
|
||||
@@ -201,17 +204,20 @@ def get_children(doctype, parent=None, location=None, is_root=False):
|
||||
where
|
||||
ifnull(parent_location, "")={parent}
|
||||
""".format(
|
||||
doctype=doctype,
|
||||
parent=frappe.db.escape(parent)
|
||||
), as_dict=1)
|
||||
doctype=doctype, parent=frappe.db.escape(parent)
|
||||
),
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_node():
|
||||
from frappe.desk.treeview import make_tree_args
|
||||
|
||||
args = frappe.form_dict
|
||||
args = make_tree_args(**args)
|
||||
|
||||
if args.parent_location == 'All Locations':
|
||||
if args.parent_location == "All Locations":
|
||||
args.parent_location = None
|
||||
|
||||
frappe.get_doc(args).insert()
|
||||
|
||||
@@ -6,29 +6,34 @@ import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Location')
|
||||
test_records = frappe.get_test_records("Location")
|
||||
|
||||
|
||||
class TestLocation(unittest.TestCase):
|
||||
def runTest(self):
|
||||
locations = ['Basil Farm', 'Division 1', 'Field 1', 'Block 1']
|
||||
locations = ["Basil Farm", "Division 1", "Field 1", "Block 1"]
|
||||
area = 0
|
||||
formatted_locations = []
|
||||
|
||||
for location in locations:
|
||||
doc = frappe.get_doc('Location', location)
|
||||
doc = frappe.get_doc("Location", location)
|
||||
doc.save()
|
||||
area += doc.area
|
||||
temp = json.loads(doc.location)
|
||||
temp['features'][0]['properties']['child_feature'] = True
|
||||
temp['features'][0]['properties']['feature_of'] = location
|
||||
formatted_locations.extend(temp['features'])
|
||||
temp["features"][0]["properties"]["child_feature"] = True
|
||||
temp["features"][0]["properties"]["feature_of"] = location
|
||||
formatted_locations.extend(temp["features"])
|
||||
|
||||
test_location = frappe.get_doc('Location', 'Test Location Area')
|
||||
test_location = frappe.get_doc("Location", "Test Location Area")
|
||||
test_location.save()
|
||||
|
||||
test_location_features = json.loads(test_location.get('location'))['features']
|
||||
ordered_test_location_features = sorted(test_location_features, key=lambda x: x['properties']['feature_of'])
|
||||
ordered_formatted_locations = sorted(formatted_locations, key=lambda x: x['properties']['feature_of'])
|
||||
test_location_features = json.loads(test_location.get("location"))["features"]
|
||||
ordered_test_location_features = sorted(
|
||||
test_location_features, key=lambda x: x["properties"]["feature_of"]
|
||||
)
|
||||
ordered_formatted_locations = sorted(
|
||||
formatted_locations, key=lambda x: x["properties"]["feature_of"]
|
||||
)
|
||||
|
||||
self.assertEqual(ordered_formatted_locations, ordered_test_location_features)
|
||||
self.assertEqual(area, test_location.get('area'))
|
||||
self.assertEqual(area, test_location.get("area"))
|
||||
|
||||
@@ -17,16 +17,21 @@ def execute(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
chart = prepare_chart_data(data, filters) if filters.get("group_by") not in ("Asset Category", "Location") else {}
|
||||
chart = (
|
||||
prepare_chart_data(data, filters)
|
||||
if filters.get("group_by") not in ("Asset Category", "Location")
|
||||
else {}
|
||||
)
|
||||
|
||||
return columns, data, None, chart
|
||||
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = { 'docstatus': 1 }
|
||||
conditions = {"docstatus": 1}
|
||||
status = filters.status
|
||||
date_field = frappe.scrub(filters.date_based_on or "Purchase Date")
|
||||
|
||||
if filters.get('company'):
|
||||
if filters.get("company"):
|
||||
conditions["company"] = filters.company
|
||||
if filters.filter_based_on == "Date Range":
|
||||
conditions[date_field] = ["between", [filters.from_date, filters.to_date]]
|
||||
@@ -37,23 +42,24 @@ def get_conditions(filters):
|
||||
filters.year_end_date = getdate(fiscal_year.year_end_date)
|
||||
|
||||
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
|
||||
if filters.get('is_existing_asset'):
|
||||
conditions["is_existing_asset"] = filters.get('is_existing_asset')
|
||||
if filters.get('asset_category'):
|
||||
conditions["asset_category"] = filters.get('asset_category')
|
||||
if filters.get('cost_center'):
|
||||
conditions["cost_center"] = filters.get('cost_center')
|
||||
if filters.get("is_existing_asset"):
|
||||
conditions["is_existing_asset"] = filters.get("is_existing_asset")
|
||||
if filters.get("asset_category"):
|
||||
conditions["asset_category"] = filters.get("asset_category")
|
||||
if filters.get("cost_center"):
|
||||
conditions["cost_center"] = filters.get("cost_center")
|
||||
|
||||
if status:
|
||||
# In Store assets are those that are not sold or scrapped
|
||||
operand = 'not in'
|
||||
if status not in 'In Location':
|
||||
operand = 'in'
|
||||
operand = "not in"
|
||||
if status not in "In Location":
|
||||
operand = "in"
|
||||
|
||||
conditions['status'] = (operand, ['Sold', 'Scrapped'])
|
||||
conditions["status"] = (operand, ["Sold", "Scrapped"])
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
|
||||
data = []
|
||||
@@ -74,21 +80,37 @@ def get_data(filters):
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by)
|
||||
|
||||
else:
|
||||
fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt",
|
||||
"asset_category", "purchase_date", "gross_purchase_amount", "location",
|
||||
"available_for_use_date", "purchase_invoice", "opening_accumulated_depreciation"]
|
||||
fields = [
|
||||
"name as asset_id",
|
||||
"asset_name",
|
||||
"status",
|
||||
"department",
|
||||
"cost_center",
|
||||
"purchase_receipt",
|
||||
"asset_category",
|
||||
"purchase_date",
|
||||
"gross_purchase_amount",
|
||||
"location",
|
||||
"available_for_use_date",
|
||||
"purchase_invoice",
|
||||
"opening_accumulated_depreciation",
|
||||
]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
for asset in assets_record:
|
||||
asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \
|
||||
asset_value = (
|
||||
asset.gross_purchase_amount
|
||||
- flt(asset.opening_accumulated_depreciation)
|
||||
- flt(depreciation_amount_map.get(asset.name))
|
||||
)
|
||||
row = {
|
||||
"asset_id": asset.asset_id,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt)
|
||||
or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0,
|
||||
@@ -96,21 +118,31 @@ def get_data(filters):
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value
|
||||
"asset_value": asset_value,
|
||||
}
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def prepare_chart_data(data, filters):
|
||||
labels_values_map = {}
|
||||
date_field = frappe.scrub(filters.date_based_on)
|
||||
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
filters.from_date, filters.to_date, filters.filter_based_on, "Monthly", company=filters.company)
|
||||
period_list = get_period_list(
|
||||
filters.from_fiscal_year,
|
||||
filters.to_fiscal_year,
|
||||
filters.from_date,
|
||||
filters.to_date,
|
||||
filters.filter_based_on,
|
||||
"Monthly",
|
||||
company=filters.company,
|
||||
)
|
||||
|
||||
for d in period_list:
|
||||
labels_values_map.setdefault(d.get('label'), frappe._dict({'asset_value': 0, 'depreciated_amount': 0}))
|
||||
labels_values_map.setdefault(
|
||||
d.get("label"), frappe._dict({"asset_value": 0, "depreciated_amount": 0})
|
||||
)
|
||||
|
||||
for d in data:
|
||||
date = d.get(date_field)
|
||||
@@ -120,23 +152,30 @@ def prepare_chart_data(data, filters):
|
||||
labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount")
|
||||
|
||||
return {
|
||||
"data" : {
|
||||
"data": {
|
||||
"labels": labels_values_map.keys(),
|
||||
"datasets": [
|
||||
{ 'name': _('Asset Value'), 'values': [d.get("asset_value") for d in labels_values_map.values()] },
|
||||
{ 'name': _('Depreciatied Amount'), 'values': [d.get("depreciated_amount") for d in labels_values_map.values()] }
|
||||
]
|
||||
{
|
||||
"name": _("Asset Value"),
|
||||
"values": [d.get("asset_value") for d in labels_values_map.values()],
|
||||
},
|
||||
{
|
||||
"name": _("Depreciatied Amount"),
|
||||
"values": [d.get("depreciated_amount") for d in labels_values_map.values()],
|
||||
},
|
||||
],
|
||||
},
|
||||
"type": "bar",
|
||||
"barOptions": {
|
||||
"stacked": 1
|
||||
},
|
||||
"barOptions": {"stacked": 1},
|
||||
}
|
||||
|
||||
|
||||
def get_finance_book_value_map(filters):
|
||||
date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
||||
|
||||
return frappe._dict(frappe.db.sql(''' Select
|
||||
return frappe._dict(
|
||||
frappe.db.sql(
|
||||
""" Select
|
||||
parent, SUM(depreciation_amount)
|
||||
FROM `tabDepreciation Schedule`
|
||||
WHERE
|
||||
@@ -144,27 +183,41 @@ def get_finance_book_value_map(filters):
|
||||
AND schedule_date<=%s
|
||||
AND journal_entry IS NOT NULL
|
||||
AND ifnull(finance_book, '')=%s
|
||||
GROUP BY parent''', (date, cstr(filters.finance_book or ''))))
|
||||
GROUP BY parent""",
|
||||
(date, cstr(filters.finance_book or "")),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_purchase_receipt_supplier_map():
|
||||
return frappe._dict(frappe.db.sql(''' Select
|
||||
return frappe._dict(
|
||||
frappe.db.sql(
|
||||
""" Select
|
||||
pr.name, pr.supplier
|
||||
FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
|
||||
WHERE
|
||||
pri.parent = pr.name
|
||||
AND pri.is_fixed_asset=1
|
||||
AND pr.docstatus=1
|
||||
AND pr.is_return=0'''))
|
||||
AND pr.is_return=0"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_purchase_invoice_supplier_map():
|
||||
return frappe._dict(frappe.db.sql(''' Select
|
||||
return frappe._dict(
|
||||
frappe.db.sql(
|
||||
""" Select
|
||||
pi.name, pi.supplier
|
||||
FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii
|
||||
WHERE
|
||||
pii.parent = pi.name
|
||||
AND pii.is_fixed_asset=1
|
||||
AND pi.docstatus=1
|
||||
AND pi.is_return=0'''))
|
||||
AND pi.is_return=0"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_columns(filters):
|
||||
if filters.get("group_by") in ["Asset Category", "Location"]:
|
||||
@@ -174,36 +227,36 @@ def get_columns(filters):
|
||||
"fieldtype": "Link",
|
||||
"fieldname": frappe.scrub(filters.get("group_by")),
|
||||
"options": filters.get("group_by"),
|
||||
"width": 120
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"label": _("Gross Purchase Amount"),
|
||||
"fieldname": "gross_purchase_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Opening Accumulated Depreciation"),
|
||||
"fieldname": "opening_accumulated_depreciation",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 90
|
||||
"width": 90,
|
||||
},
|
||||
{
|
||||
"label": _("Depreciated Amount"),
|
||||
"fieldname": "depreciated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Asset Value"),
|
||||
"fieldname": "asset_value",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100
|
||||
}
|
||||
"width": 100,
|
||||
},
|
||||
]
|
||||
|
||||
return [
|
||||
@@ -212,92 +265,72 @@ def get_columns(filters):
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "asset_id",
|
||||
"options": "Asset",
|
||||
"width": 60
|
||||
},
|
||||
{
|
||||
"label": _("Asset Name"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "asset_name",
|
||||
"width": 140
|
||||
"width": 60,
|
||||
},
|
||||
{"label": _("Asset Name"), "fieldtype": "Data", "fieldname": "asset_name", "width": 140},
|
||||
{
|
||||
"label": _("Asset Category"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "asset_category",
|
||||
"options": "Asset Category",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Status"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "status",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"label": _("Purchase Date"),
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "purchase_date",
|
||||
"width": 90
|
||||
"width": 100,
|
||||
},
|
||||
{"label": _("Status"), "fieldtype": "Data", "fieldname": "status", "width": 80},
|
||||
{"label": _("Purchase Date"), "fieldtype": "Date", "fieldname": "purchase_date", "width": 90},
|
||||
{
|
||||
"label": _("Available For Use Date"),
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "available_for_use_date",
|
||||
"width": 90
|
||||
"width": 90,
|
||||
},
|
||||
{
|
||||
"label": _("Gross Purchase Amount"),
|
||||
"fieldname": "gross_purchase_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Asset Value"),
|
||||
"fieldname": "asset_value",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Opening Accumulated Depreciation"),
|
||||
"fieldname": "opening_accumulated_depreciation",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 90
|
||||
"width": 90,
|
||||
},
|
||||
{
|
||||
"label": _("Depreciated Amount"),
|
||||
"fieldname": "depreciated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Cost Center"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "cost_center",
|
||||
"options": "Cost Center",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Department"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "department",
|
||||
"options": "Department",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Vendor Name"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "vendor_name",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
{"label": _("Vendor Name"), "fieldtype": "Data", "fieldname": "vendor_name", "width": 100},
|
||||
{
|
||||
"label": _("Location"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "location",
|
||||
"options": "Location",
|
||||
"width": 100
|
||||
"width": 100,
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user